From 11004bcf34b836c2f65a93f08828396e084b5a69 Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Tue, 16 Oct 2018 18:39:32 -0400 Subject: [PATCH] Manual IP & port configuration for Konnected devices (#17120) * add capability for manually specifying IP and port of Konnected device(s) * add config options for blink and discovery settings * import konnected only in functions where needed * updates from code review feedback * convert manual_discovery to async * code review updates; use correct sync vs async --- homeassistant/components/konnected.py | 75 +++++++++++++++++++++------ requirements_all.txt | 2 +- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/konnected.py b/homeassistant/components/konnected.py index 388fa41f36f..21e2fbba4c7 100644 --- a/homeassistant/components/konnected.py +++ b/homeassistant/components/konnected.py @@ -4,9 +4,11 @@ Support for Konnected devices. For more details about this component, please refer to the documentation at https://home-assistant.io/components/konnected/ """ -import logging +import asyncio import hmac import json +import logging + import voluptuous as vol from aiohttp.hdrs import AUTHORIZATION @@ -16,17 +18,18 @@ from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA from homeassistant.components.discovery import SERVICE_KONNECTED from homeassistant.components.http import HomeAssistantView from homeassistant.const import ( - HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED, - CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES, CONF_HOST, CONF_PORT, - CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE, CONF_ACCESS_TOKEN, - ATTR_ENTITY_ID, ATTR_STATE, STATE_ON) -from homeassistant.helpers.dispatcher import async_dispatcher_send + EVENT_HOMEASSISTANT_START, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, + HTTP_UNAUTHORIZED, CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES, + CONF_HOST, CONF_PORT, CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE, + CONF_ACCESS_TOKEN, ATTR_ENTITY_ID, ATTR_STATE, STATE_ON) +from homeassistant.helpers.dispatcher import ( + async_dispatcher_send, dispatcher_send) from homeassistant.helpers import discovery from homeassistant.helpers import config_validation as cv _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['konnected==0.1.2'] +REQUIREMENTS = ['konnected==0.1.4'] DOMAIN = 'konnected' @@ -36,6 +39,8 @@ CONF_MOMENTARY = 'momentary' CONF_PAUSE = 'pause' CONF_REPEAT = 'repeat' CONF_INVERSE = 'inverse' +CONF_BLINK = 'blink' +CONF_DISCOVERY = 'discovery' STATE_LOW = 'low' STATE_HIGH = 'high' @@ -49,7 +54,7 @@ _BINARY_SENSOR_SCHEMA = vol.All( vol.Exclusive(CONF_ZONE, 's_pin'): vol.Any(*ZONE_TO_PIN), vol.Required(CONF_TYPE): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_INVERSE): cv.boolean, + vol.Optional(CONF_INVERSE, default=False): cv.boolean, }), cv.has_at_least_one_key(CONF_PIN, CONF_ZONE) ) @@ -81,6 +86,10 @@ CONFIG_SCHEMA = vol.Schema( cv.ensure_list, [_BINARY_SENSOR_SCHEMA]), vol.Optional(CONF_SWITCHES): vol.All( cv.ensure_list, [_SWITCH_SCHEMA]), + vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_PORT): cv.port, + vol.Optional(CONF_BLINK, default=True): cv.boolean, + vol.Optional(CONF_DISCOVERY, default=True): cv.boolean, }], }), }, @@ -96,6 +105,8 @@ SIGNAL_SENSOR_UPDATE = 'konnected.{}.update' async def async_setup(hass, config): """Set up the Konnected platform.""" + import konnected + cfg = config.get(DOMAIN) if cfg is None: cfg = {} @@ -107,10 +118,8 @@ async def async_setup(hass, config): CONF_API_HOST: cfg.get(CONF_API_HOST) } - def device_discovered(service, info): - """Call when a Konnected device has been discovered.""" - host = info.get(CONF_HOST) - port = info.get(CONF_PORT) + def setup_device(host, port): + """Set up a Konnected device at `host` listening on `port`.""" discovered = DiscoveredDevice(hass, host, port) if discovered.is_configured: discovered.setup() @@ -119,6 +128,33 @@ async def async_setup(hass, config): " but not specified in configuration.yaml", discovered.device_id) + def device_discovered(service, info): + """Call when a Konnected device has been discovered.""" + host = info.get(CONF_HOST) + port = info.get(CONF_PORT) + setup_device(host, port) + + async def manual_discovery(event): + """Init devices on the network with manually assigned addresses.""" + specified = [dev for dev in cfg.get(CONF_DEVICES) if + dev.get(CONF_HOST) and dev.get(CONF_PORT)] + + while specified: + for dev in specified: + _LOGGER.debug("Discovering Konnected device %s at %s:%s", + dev.get(CONF_ID), + dev.get(CONF_HOST), + dev.get(CONF_PORT)) + try: + await hass.async_add_executor_job(setup_device, + dev.get(CONF_HOST), + dev.get(CONF_PORT)) + specified.remove(dev) + except konnected.Client.ClientError as err: + _LOGGER.error(err) + await asyncio.sleep(10) # try again in 10 seconds + + # Initialize devices specified in the configuration on boot for device in cfg.get(CONF_DEVICES): ConfiguredDevice(hass, device).save_data() @@ -128,6 +164,7 @@ async def async_setup(hass, config): device_discovered) hass.http.register_view(KonnectedView(access_token)) + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, manual_discovery) return True @@ -188,6 +225,8 @@ class ConfiguredDevice: device_data = { CONF_BINARY_SENSORS: sensors, CONF_SWITCHES: actuators, + CONF_BLINK: self.config.get(CONF_BLINK), + CONF_DISCOVERY: self.config.get(CONF_DISCOVERY) } if CONF_DEVICES not in self.hass.data[DOMAIN]: @@ -271,7 +310,7 @@ class DiscoveredDevice: if sensor_config.get(CONF_INVERSE): state = not state - async_dispatcher_send( + dispatcher_send( self.hass, SIGNAL_SENSOR_UPDATE.format(entity_id), state) @@ -306,13 +345,19 @@ class DiscoveredDevice: if (desired_sensor_configuration != current_sensor_configuration) or \ (current_actuator_config != desired_actuator_config) or \ - (current_api_endpoint != desired_api_endpoint): + (current_api_endpoint != desired_api_endpoint) or \ + (self.status.get(CONF_BLINK) != + self.stored_configuration.get(CONF_BLINK)) or \ + (self.status.get(CONF_DISCOVERY) != + self.stored_configuration.get(CONF_DISCOVERY)): _LOGGER.info('pushing settings to device %s', self.device_id) self.client.put_settings( desired_sensor_configuration, desired_actuator_config, self.hass.data[DOMAIN].get(CONF_ACCESS_TOKEN), - desired_api_endpoint + desired_api_endpoint, + blink=self.stored_configuration.get(CONF_BLINK), + discovery=self.stored_configuration.get(CONF_DISCOVERY) ) diff --git a/requirements_all.txt b/requirements_all.txt index 7820ed6ba86..aa66a533bf0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -531,7 +531,7 @@ keyrings.alt==3.1 kiwiki-client==0.1.1 # homeassistant.components.konnected -konnected==0.1.2 +konnected==0.1.4 # homeassistant.components.eufy lakeside==0.10