diff --git a/.coveragerc b/.coveragerc index 859e1c0f92c..389d289ea20 100644 --- a/.coveragerc +++ b/.coveragerc @@ -604,6 +604,7 @@ omit = homeassistant/components/skybeacon/sensor.py homeassistant/components/skybell/* homeassistant/components/slack/notify.py + homeassistant/components/sinch/* homeassistant/components/slide/* homeassistant/components/sma/sensor.py homeassistant/components/smappee/* diff --git a/CODEOWNERS b/CODEOWNERS index 40f1e93cfb9..2f228105cbb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -257,6 +257,7 @@ homeassistant/components/shell_command/* @home-assistant/core homeassistant/components/shiftr/* @fabaff homeassistant/components/shodan/* @fabaff homeassistant/components/simplisafe/* @bachya +homeassistant/components/sinch/* @bendikrb homeassistant/components/slide/* @ualex73 homeassistant/components/sma/* @kellerza homeassistant/components/smarthab/* @outadoc diff --git a/homeassistant/components/sinch/__init__.py b/homeassistant/components/sinch/__init__.py new file mode 100644 index 00000000000..43a5f2b2a5c --- /dev/null +++ b/homeassistant/components/sinch/__init__.py @@ -0,0 +1 @@ +"""Component to integrate with Sinch SMS API.""" diff --git a/homeassistant/components/sinch/manifest.json b/homeassistant/components/sinch/manifest.json new file mode 100644 index 00000000000..a1864428fee --- /dev/null +++ b/homeassistant/components/sinch/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "sinch", + "name": "Sinch", + "documentation": "https://www.home-assistant.io/components/sinch", + "dependencies": [], + "codeowners": [ + "@bendikrb" + ], + "requirements": [ + "clx-sdk-xms==1.0.0" + ] +} \ No newline at end of file diff --git a/homeassistant/components/sinch/notify.py b/homeassistant/components/sinch/notify.py new file mode 100644 index 00000000000..173873c0a6c --- /dev/null +++ b/homeassistant/components/sinch/notify.py @@ -0,0 +1,97 @@ +"""Support for Sinch notifications.""" +import logging + +import voluptuous as vol +from clx.xms.api import MtBatchTextSmsResult +from clx.xms.client import Client +from clx.xms.exceptions import ( + ErrorResponseException, + UnexpectedResponseException, + UnauthorizedException, + NotFoundException, +) + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.notify import ( + ATTR_MESSAGE, + ATTR_DATA, + ATTR_TARGET, + PLATFORM_SCHEMA, + BaseNotificationService, +) +from homeassistant.const import CONF_API_KEY, CONF_SENDER + +DOMAIN = "sinch" + +CONF_SERVICE_PLAN_ID = "service_plan_id" +CONF_DEFAULT_RECIPIENTS = "default_recipients" + +ATTR_SENDER = CONF_SENDER + +DEFAULT_SENDER = "Home Assistant" + +_LOGGER = logging.getLogger(__name__) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_API_KEY): cv.string, + vol.Required(CONF_SERVICE_PLAN_ID): cv.string, + vol.Optional(CONF_SENDER, default=DEFAULT_SENDER): cv.string, + vol.Optional(CONF_DEFAULT_RECIPIENTS, default=[]): vol.All( + cv.ensure_list, [cv.string] + ), + } +) + + +def get_service(hass, config, discovery_info=None): + """Get the Sinch notification service.""" + return SinchNotificationService(config) + + +class SinchNotificationService(BaseNotificationService): + """Send Notifications to Sinch SMS recipients.""" + + def __init__(self, config): + """Initialize the service.""" + self.default_recipients = config[CONF_DEFAULT_RECIPIENTS] + self.sender = config[CONF_SENDER] + self.client = Client(config[CONF_SERVICE_PLAN_ID], config[CONF_API_KEY]) + + def send_message(self, message="", **kwargs): + """Send a message to a user.""" + targets = kwargs.get(ATTR_TARGET, self.default_recipients) + data = kwargs.get(ATTR_DATA, {}) + + clx_args = {ATTR_MESSAGE: message, ATTR_SENDER: self.sender} + + if ATTR_SENDER in data: + clx_args[ATTR_SENDER] = data[ATTR_SENDER] + + if not targets: + _LOGGER.error("At least 1 target is required") + return + + try: + for target in targets: + result: MtBatchTextSmsResult = self.client.create_text_message( + clx_args[ATTR_SENDER], target, clx_args[ATTR_MESSAGE] + ) + batch_id = result.batch_id + _LOGGER.debug( + 'Successfully sent SMS to "%s" (batch_id: %s)', target, batch_id + ) + except ErrorResponseException as ex: + _LOGGER.error( + "Caught ErrorResponseException. Response code: %d (%s)", + ex.error_code, + ex, + ) + except NotFoundException as ex: + _LOGGER.error("Caught NotFoundException (request URL: %s)", ex.url) + except UnauthorizedException as ex: + _LOGGER.error( + "Caught UnauthorizedException (service plan: %s)", ex.service_plan_id + ) + except UnexpectedResponseException as ex: + _LOGGER.error("Caught UnexpectedResponseException: %s", ex) diff --git a/requirements_all.txt b/requirements_all.txt index df24fde50cf..9fe8b6622de 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -346,6 +346,9 @@ ciscosparkapi==0.4.2 # homeassistant.components.cppm_tracker clearpasspy==1.0.2 +# homeassistant.components.sinch +clx-sdk-xms==1.0.0 + # homeassistant.components.co2signal co2signal==0.4.2