From af73e54aee51944c72ce2afd54c90cf617273ab4 Mon Sep 17 00:00:00 2001
From: Heine Furubotten <hfurubotten@users.noreply.github.com>
Date: Thu, 7 Nov 2019 14:47:44 +0100
Subject: [PATCH] Add azure servicebus notify service (#27566)

* Add azure servicebus notify service

* files added to .coveragerc

* fix: import content type from const

* Moved imports to top level

* Code review fixes
+ added code owner
+ fixed config validation with has at least one
+ seperate attributes for dto to asb
* fixed doc link
* async setup instead of sync

* rename all the things
- removed too many ifs
* changed setup back to sync
+ comment about sync IO in lib

* More informative logging

* logging exception -> error
---
 .coveragerc                                   |   1 +
 CODEOWNERS                                    |   1 +
 .../components/azure_service_bus/__init__.py  |   1 +
 .../azure_service_bus/manifest.json           |  12 ++
 .../components/azure_service_bus/notify.py    | 106 ++++++++++++++++++
 requirements_all.txt                          |   3 +
 6 files changed, 124 insertions(+)
 create mode 100644 homeassistant/components/azure_service_bus/__init__.py
 create mode 100644 homeassistant/components/azure_service_bus/manifest.json
 create mode 100644 homeassistant/components/azure_service_bus/notify.py

diff --git a/.coveragerc b/.coveragerc
index e6f09d60eff..169b73b7899 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -69,6 +69,7 @@ omit =
     homeassistant/components/avea/light.py
     homeassistant/components/avion/light.py
     homeassistant/components/azure_event_hub/*
+    homeassistant/components/azure_service_bus/*
     homeassistant/components/baidu/tts.py
     homeassistant/components/beewi_smartclim/sensor.py
     homeassistant/components/bbb_gpio/*
diff --git a/CODEOWNERS b/CODEOWNERS
index 27e06d874e1..0a02fbc5321 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -43,6 +43,7 @@ homeassistant/components/awair/* @danielsjf
 homeassistant/components/aws/* @awarecan @robbiet480
 homeassistant/components/axis/* @kane610
 homeassistant/components/azure_event_hub/* @eavanvalkenburg
+homeassistant/components/azure_service_bus/* @hfurubotten
 homeassistant/components/beewi_smartclim/* @alemuro
 homeassistant/components/bitcoin/* @fabaff
 homeassistant/components/bizkaibus/* @UgaitzEtxebarria
diff --git a/homeassistant/components/azure_service_bus/__init__.py b/homeassistant/components/azure_service_bus/__init__.py
new file mode 100644
index 00000000000..f18dc9eb66c
--- /dev/null
+++ b/homeassistant/components/azure_service_bus/__init__.py
@@ -0,0 +1 @@
+"""The Azure Service Bus integration."""
diff --git a/homeassistant/components/azure_service_bus/manifest.json b/homeassistant/components/azure_service_bus/manifest.json
new file mode 100644
index 00000000000..fa6d1c20b7f
--- /dev/null
+++ b/homeassistant/components/azure_service_bus/manifest.json
@@ -0,0 +1,12 @@
+{
+  "domain": "azure_service_bus",
+  "name": "Azure Service Bus",
+  "documentation": "https://www.home-assistant.io/integrations/azure_service_bus",
+  "requirements": [
+    "azure-servicebus==0.50.1"
+  ],
+  "dependencies": [],
+  "codeowners": [
+    "@hfurubotten"
+  ]
+}
\ No newline at end of file
diff --git a/homeassistant/components/azure_service_bus/notify.py b/homeassistant/components/azure_service_bus/notify.py
new file mode 100644
index 00000000000..e7c85adede8
--- /dev/null
+++ b/homeassistant/components/azure_service_bus/notify.py
@@ -0,0 +1,106 @@
+"""Support for azure service bus notification."""
+import json
+import logging
+
+from azure.servicebus.aio import Message, ServiceBusClient
+from azure.servicebus.common.errors import (
+    MessageSendFailed,
+    ServiceBusConnectionError,
+    ServiceBusResourceNotFound,
+)
+import voluptuous as vol
+
+from homeassistant.components.notify import (
+    ATTR_DATA,
+    ATTR_TARGET,
+    ATTR_TITLE,
+    PLATFORM_SCHEMA,
+    BaseNotificationService,
+)
+from homeassistant.const import CONTENT_TYPE_JSON
+import homeassistant.helpers.config_validation as cv
+
+CONF_CONNECTION_STRING = "connection_string"
+CONF_QUEUE_NAME = "queue"
+CONF_TOPIC_NAME = "topic"
+
+ATTR_ASB_MESSAGE = "message"
+ATTR_ASB_TITLE = "title"
+ATTR_ASB_TARGET = "target"
+
+PLATFORM_SCHEMA = vol.All(
+    cv.has_at_least_one_key(CONF_QUEUE_NAME, CONF_TOPIC_NAME),
+    PLATFORM_SCHEMA.extend(
+        {
+            vol.Required(CONF_CONNECTION_STRING): cv.string,
+            vol.Exclusive(
+                CONF_QUEUE_NAME, "output", "Can only send to a queue or a topic."
+            ): cv.string,
+            vol.Exclusive(
+                CONF_TOPIC_NAME, "output", "Can only send to a queue or a topic."
+            ): cv.string,
+        }
+    ),
+)
+
+_LOGGER = logging.getLogger(__name__)
+
+
+def get_service(hass, config, discovery_info=None):
+    """Get the notification service."""
+    connection_string = config[CONF_CONNECTION_STRING]
+    queue_name = config.get(CONF_QUEUE_NAME)
+    topic_name = config.get(CONF_TOPIC_NAME)
+
+    # Library can do synchronous IO when creating the clients.
+    # Passes in loop here, but can't run setup on the event loop.
+    servicebus = ServiceBusClient.from_connection_string(
+        connection_string, loop=hass.loop
+    )
+
+    try:
+        if queue_name:
+            client = servicebus.get_queue(queue_name)
+        else:
+            client = servicebus.get_topic(topic_name)
+    except (ServiceBusConnectionError, ServiceBusResourceNotFound) as err:
+        _LOGGER.error(
+            "Connection error while creating client for queue/topic '%s'. %s",
+            queue_name or topic_name,
+            err,
+        )
+        return None
+
+    return ServiceBusNotificationService(client)
+
+
+class ServiceBusNotificationService(BaseNotificationService):
+    """Implement the notification service for the service bus service."""
+
+    def __init__(self, client):
+        """Initialize the service."""
+        self._client = client
+
+    async def async_send_message(self, message, **kwargs):
+        """Send a message."""
+        dto = {ATTR_ASB_MESSAGE: message}
+
+        if ATTR_TITLE in kwargs:
+            dto[ATTR_ASB_TITLE] = kwargs[ATTR_TITLE]
+        if ATTR_TARGET in kwargs:
+            dto[ATTR_ASB_TARGET] = kwargs[ATTR_TARGET]
+
+        data = kwargs.get(ATTR_DATA)
+        if data:
+            dto.update(data)
+
+        queue_message = Message(json.dumps(dto))
+        queue_message.properties.content_type = CONTENT_TYPE_JSON
+        try:
+            await self._client.send(queue_message)
+        except MessageSendFailed as err:
+            _LOGGER.error(
+                "Could not send service bus notification to %s. %s",
+                self._client.name,
+                err,
+            )
diff --git a/requirements_all.txt b/requirements_all.txt
index ca1ae68e4a6..8843a6693ab 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -263,6 +263,9 @@ axis==25
 # homeassistant.components.azure_event_hub
 azure-eventhub==1.3.1
 
+# homeassistant.components.azure_service_bus
+azure-servicebus==0.50.1
+
 # homeassistant.components.baidu
 baidu-aip==1.6.6
 
-- 
GitLab