diff --git a/.coveragerc b/.coveragerc index 45dbeb9cb60ee68d53285ca9f987d97424ffad2a..b89b3a6be604c9c8b3a9f8a009e15e176e5f05ea 100644 --- a/.coveragerc +++ b/.coveragerc @@ -87,6 +87,8 @@ omit = homeassistant/components/doorbird.py homeassistant/components/*/doorbird.py + homeassistant/components/dovado/* + homeassistant/components/dweet.py homeassistant/components/*/dweet.py @@ -754,7 +756,6 @@ omit = homeassistant/components/sensor/dht.py homeassistant/components/sensor/discogs.py homeassistant/components/sensor/dnsip.py - homeassistant/components/sensor/dovado.py homeassistant/components/sensor/domain_expiry.py homeassistant/components/sensor/dte_energy_bridge.py homeassistant/components/sensor/dublin_bus_transport.py diff --git a/homeassistant/components/dovado/__init__.py b/homeassistant/components/dovado/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7a50ac815b1be2f7df6270243354382a0e6ceacb --- /dev/null +++ b/homeassistant/components/dovado/__init__.py @@ -0,0 +1,79 @@ +""" +Support for Dovado router. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/dovado/ +""" +import logging +from datetime import timedelta + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.const import ( + CONF_USERNAME, CONF_PASSWORD, CONF_HOST, CONF_PORT, + DEVICE_DEFAULT_NAME) +from homeassistant.util import Throttle + +_LOGGER = logging.getLogger(__name__) + +REQUIREMENTS = ['dovado==0.4.1'] + +DOMAIN = 'dovado' + +CONFIG_SCHEMA = vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_PORT): cv.port, +}) + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) + + +def setup(hass, config): + """Set up the Dovado component.""" + import dovado + + hass.data[DOMAIN] = DovadoData( + dovado.Dovado( + config[CONF_USERNAME], + config[CONF_PASSWORD], + config.get(CONF_HOST), + config.get(CONF_PORT) + ) + ) + return True + + +class DovadoData: + """Maintains a connection to the router.""" + + def __init__(self, client): + """Set up a new Dovado connection.""" + self._client = client + self.state = {} + + @property + def name(self): + """Name of the router.""" + return self.state.get("product name", DEVICE_DEFAULT_NAME) + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Update device state.""" + try: + self.state = self._client.state or {} + if not self.state: + return False + self.state.update( + connected=self.state.get("modem status") == "CONNECTED") + _LOGGER.debug("Received: %s", self.state) + return True + except OSError as error: + _LOGGER.warning("Could not contact the router: %s", error) + + @property + def client(self): + """Dovado client instance.""" + return self._client diff --git a/homeassistant/components/dovado/notify.py b/homeassistant/components/dovado/notify.py new file mode 100644 index 0000000000000000000000000000000000000000..00036378a7848f42c4307b92ae7da54bc19f3093 --- /dev/null +++ b/homeassistant/components/dovado/notify.py @@ -0,0 +1,38 @@ +""" +Support for SMS notifications from the Dovado router. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/notify.dovado/ +""" +import logging + +from homeassistant.components.dovado import DOMAIN as DOVADO_DOMAIN +from homeassistant.components.notify import BaseNotificationService, \ + ATTR_TARGET + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['dovado'] + + +def get_service(hass, config, discovery_info=None): + """Get the Dovado Router SMS notification service.""" + return DovadoSMSNotificationService(hass.data[DOVADO_DOMAIN].client) + + +class DovadoSMSNotificationService(BaseNotificationService): + """Implement the notification service for the Dovado SMS component.""" + + def __init__(self, client): + """Initialize the service.""" + self._client = client + + def send_message(self, message, **kwargs): + """Send SMS to the specified target phone number.""" + target = kwargs.get(ATTR_TARGET) + + if not target: + _LOGGER.error("One target is required") + return + + self._client.send_sms(target, message) diff --git a/homeassistant/components/dovado/sensor.py b/homeassistant/components/dovado/sensor.py new file mode 100644 index 0000000000000000000000000000000000000000..b89275b179558d350432a62cc0dc843bcb6893e6 --- /dev/null +++ b/homeassistant/components/dovado/sensor.py @@ -0,0 +1,116 @@ +""" +Support for sensors from the Dovado router. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.dovado/ +""" +import logging +import re +from datetime import timedelta + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.dovado import DOMAIN as DOVADO_DOMAIN +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import CONF_SENSORS +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['dovado'] + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) + +SENSOR_UPLOAD = 'upload' +SENSOR_DOWNLOAD = 'download' +SENSOR_SIGNAL = 'signal' +SENSOR_NETWORK = 'network' +SENSOR_SMS_UNREAD = 'sms' + +SENSORS = { + SENSOR_NETWORK: ('signal strength', 'Network', None, + 'mdi:access-point-network'), + SENSOR_SIGNAL: ('signal strength', 'Signal Strength', '%', + 'mdi:signal'), + SENSOR_SMS_UNREAD: ('sms unread', 'SMS unread', '', + 'mdi:message-text-outline'), + SENSOR_UPLOAD: ('traffic modem tx', 'Sent', 'GB', + 'mdi:cloud-upload'), + SENSOR_DOWNLOAD: ('traffic modem rx', 'Received', 'GB', + 'mdi:cloud-download'), +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_SENSORS): vol.All( + cv.ensure_list, [vol.In(SENSORS)] + ), +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Dovado sensor platform.""" + dovado = hass.data[DOVADO_DOMAIN] + + entities = [] + for sensor in config[CONF_SENSORS]: + entities.append(DovadoSensor(dovado, sensor)) + + add_entities(entities) + + +class DovadoSensor(Entity): + """Representation of a Dovado sensor.""" + + def __init__(self, data, sensor): + """Initialize the sensor.""" + self._data = data + self._sensor = sensor + self._state = self._compute_state() + + def _compute_state(self): + state = self._data.state.get(SENSORS[self._sensor][0]) + if self._sensor == SENSOR_NETWORK: + match = re.search(r"\((.+)\)", state) + return match.group(1) if match else None + if self._sensor == SENSOR_SIGNAL: + try: + return int(state.split()[0]) + except ValueError: + return None + if self._sensor == SENSOR_SMS_UNREAD: + return int(state) + if self._sensor in [SENSOR_UPLOAD, SENSOR_DOWNLOAD]: + return round(float(state) / 1e6, 1) + return state + + def update(self): + """Update sensor values.""" + self._data.update() + self._state = self._compute_state() + + @property + def name(self): + """Return the name of the sensor.""" + return "{} {}".format(self._data.name, SENSORS[self._sensor][1]) + + @property + def state(self): + """Return the sensor state.""" + return self._state + + @property + def icon(self): + """Return the icon for the sensor.""" + return SENSORS[self._sensor][3] + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return SENSORS[self._sensor][2] + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return {k: v for k, v in self._data.state.items() + if k not in ['date', 'time']} diff --git a/homeassistant/components/sensor/dovado.py b/homeassistant/components/sensor/dovado.py deleted file mode 100644 index 03c2ad601dfc452d772db84414fa5fab1c0f2843..0000000000000000000000000000000000000000 --- a/homeassistant/components/sensor/dovado.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -Support for Dovado router. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.dovado/ -""" -import logging -import re -from datetime import timedelta - -import voluptuous as vol - -from homeassistant.helpers.entity import Entity -from homeassistant.util import Throttle -from homeassistant.util import slugify -import homeassistant.helpers.config_validation as cv -from homeassistant.const import ( - CONF_USERNAME, CONF_PASSWORD, CONF_HOST, CONF_PORT, CONF_SENSORS, - DEVICE_DEFAULT_NAME) -from homeassistant.components.sensor import (DOMAIN, PLATFORM_SCHEMA) - -_LOGGER = logging.getLogger(__name__) - -REQUIREMENTS = ['dovado==0.4.1'] - -MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) - -SENSOR_UPLOAD = 'upload' -SENSOR_DOWNLOAD = 'download' -SENSOR_SIGNAL = 'signal' -SENSOR_NETWORK = 'network' -SENSOR_SMS_UNREAD = 'sms' - -SENSORS = { - SENSOR_NETWORK: ('signal strength', 'Network', None, - 'mdi:access-point-network'), - SENSOR_SIGNAL: ('signal strength', 'Signal Strength', '%', - 'mdi:signal'), - SENSOR_SMS_UNREAD: ('sms unread', 'SMS unread', '', - 'mdi:message-text-outline'), - SENSOR_UPLOAD: ('traffic modem tx', 'Sent', 'GB', - 'mdi:cloud-upload'), - SENSOR_DOWNLOAD: ('traffic modem rx', 'Received', 'GB', - 'mdi:cloud-download'), -} - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_HOST): cv.string, - vol.Optional(CONF_PORT): cv.port, - vol.Optional(CONF_SENSORS): - vol.All(cv.ensure_list, [vol.In(SENSORS)]), -}) - - -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the Dovado platform for sensors.""" - return Dovado().setup(hass, config, add_entities) - - -class Dovado: - """A connection to the router.""" - - def __init__(self): - """Initialize.""" - self.state = {} - self._dovado = None - - def setup(self, hass, config, add_entities): - """Set up the connection.""" - import dovado - self._dovado = dovado.Dovado( - config.get(CONF_USERNAME), config.get(CONF_PASSWORD), - config.get(CONF_HOST), config.get(CONF_PORT)) - - if not self.update(): - return False - - def send_sms(service): - """Send SMS through the router.""" - number = service.data.get('number') - message = service.data.get('message') - _LOGGER.debug("message for %s: %s", number, message) - self._dovado.send_sms(number, message) - - if self.state.get('sms') == 'enabled': - service_name = slugify("{} {}".format(self.name, 'send_sms')) - hass.services.register(DOMAIN, service_name, send_sms) - - for sensor in SENSORS: - if sensor in config.get(CONF_SENSORS, [sensor]): - add_entities([DovadoSensor(self, sensor)]) - - return True - - @property - def name(self): - """Name of the router.""" - return self.state.get("product name", DEVICE_DEFAULT_NAME) - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): - """Update device state.""" - _LOGGER.info("Updating") - try: - self.state = self._dovado.state or {} - if not self.state: - return False - self.state.update( - connected=self.state.get("modem status") == "CONNECTED") - _LOGGER.debug("Received: %s", self.state) - return True - except OSError as error: - _LOGGER.warning("Could not contact the router: %s", error) - - -class DovadoSensor(Entity): - """Representation of a Dovado sensor.""" - - def __init__(self, dovado, sensor): - """Initialize the sensor.""" - self._dovado = dovado - self._sensor = sensor - self._state = self._compute_state() - - def _compute_state(self): - state = self._dovado.state.get(SENSORS[self._sensor][0]) - if self._sensor == SENSOR_NETWORK: - match = re.search(r"\((.+)\)", state) - return match.group(1) if match else None - if self._sensor == SENSOR_SIGNAL: - try: - return int(state.split()[0]) - except ValueError: - return 0 - if self._sensor == SENSOR_SMS_UNREAD: - return int(state) - if self._sensor in [SENSOR_UPLOAD, SENSOR_DOWNLOAD]: - return round(float(state) / 1e6, 1) - return state - - def update(self): - """Update sensor values.""" - self._dovado.update() - self._state = self._compute_state() - - @property - def name(self): - """Return the name of the sensor.""" - return "{} {}".format(self._dovado.name, SENSORS[self._sensor][1]) - - @property - def state(self): - """Return the sensor state.""" - return self._state - - @property - def icon(self): - """Return the icon for the sensor.""" - return SENSORS[self._sensor][3] - - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return SENSORS[self._sensor][2] - - @property - def device_state_attributes(self): - """Return the state attributes.""" - return {k: v for k, v in self._dovado.state.items() - if k not in ['date', 'time']} diff --git a/requirements_all.txt b/requirements_all.txt index f1d964d3e39dd708807697a9d8b71a48fb5b9642..637bb16d8005d6a330d996db95aa56b8407d9085 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -332,7 +332,7 @@ dlipower==0.7.165 # homeassistant.components.doorbird doorbirdpy==2.0.6 -# homeassistant.components.sensor.dovado +# homeassistant.components.dovado dovado==0.4.1 # homeassistant.components.sensor.dsmr