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
This commit is contained in:
Nate Clark 2018-10-16 18:39:32 -04:00 committed by Martin Hjelmare
parent 83db673bd0
commit 11004bcf34
2 changed files with 61 additions and 16 deletions

View File

@ -4,9 +4,11 @@ Support for Konnected devices.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/konnected/ https://home-assistant.io/components/konnected/
""" """
import logging import asyncio
import hmac import hmac
import json import json
import logging
import voluptuous as vol import voluptuous as vol
from aiohttp.hdrs import AUTHORIZATION 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.discovery import SERVICE_KONNECTED
from homeassistant.components.http import HomeAssistantView from homeassistant.components.http import HomeAssistantView
from homeassistant.const import ( from homeassistant.const import (
HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED, EVENT_HOMEASSISTANT_START, HTTP_BAD_REQUEST, HTTP_NOT_FOUND,
CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES, CONF_HOST, CONF_PORT, HTTP_UNAUTHORIZED, CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES,
CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE, CONF_ACCESS_TOKEN, CONF_HOST, CONF_PORT, CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE,
ATTR_ENTITY_ID, ATTR_STATE, STATE_ON) CONF_ACCESS_TOKEN, ATTR_ENTITY_ID, ATTR_STATE, STATE_ON)
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import (
async_dispatcher_send, dispatcher_send)
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['konnected==0.1.2'] REQUIREMENTS = ['konnected==0.1.4']
DOMAIN = 'konnected' DOMAIN = 'konnected'
@ -36,6 +39,8 @@ CONF_MOMENTARY = 'momentary'
CONF_PAUSE = 'pause' CONF_PAUSE = 'pause'
CONF_REPEAT = 'repeat' CONF_REPEAT = 'repeat'
CONF_INVERSE = 'inverse' CONF_INVERSE = 'inverse'
CONF_BLINK = 'blink'
CONF_DISCOVERY = 'discovery'
STATE_LOW = 'low' STATE_LOW = 'low'
STATE_HIGH = 'high' STATE_HIGH = 'high'
@ -49,7 +54,7 @@ _BINARY_SENSOR_SCHEMA = vol.All(
vol.Exclusive(CONF_ZONE, 's_pin'): vol.Any(*ZONE_TO_PIN), vol.Exclusive(CONF_ZONE, 's_pin'): vol.Any(*ZONE_TO_PIN),
vol.Required(CONF_TYPE): DEVICE_CLASSES_SCHEMA, vol.Required(CONF_TYPE): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_NAME): cv.string, 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) }), cv.has_at_least_one_key(CONF_PIN, CONF_ZONE)
) )
@ -81,6 +86,10 @@ CONFIG_SCHEMA = vol.Schema(
cv.ensure_list, [_BINARY_SENSOR_SCHEMA]), cv.ensure_list, [_BINARY_SENSOR_SCHEMA]),
vol.Optional(CONF_SWITCHES): vol.All( vol.Optional(CONF_SWITCHES): vol.All(
cv.ensure_list, [_SWITCH_SCHEMA]), 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): async def async_setup(hass, config):
"""Set up the Konnected platform.""" """Set up the Konnected platform."""
import konnected
cfg = config.get(DOMAIN) cfg = config.get(DOMAIN)
if cfg is None: if cfg is None:
cfg = {} cfg = {}
@ -107,10 +118,8 @@ async def async_setup(hass, config):
CONF_API_HOST: cfg.get(CONF_API_HOST) CONF_API_HOST: cfg.get(CONF_API_HOST)
} }
def device_discovered(service, info): def setup_device(host, port):
"""Call when a Konnected device has been discovered.""" """Set up a Konnected device at `host` listening on `port`."""
host = info.get(CONF_HOST)
port = info.get(CONF_PORT)
discovered = DiscoveredDevice(hass, host, port) discovered = DiscoveredDevice(hass, host, port)
if discovered.is_configured: if discovered.is_configured:
discovered.setup() discovered.setup()
@ -119,6 +128,33 @@ async def async_setup(hass, config):
" but not specified in configuration.yaml", " but not specified in configuration.yaml",
discovered.device_id) 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): for device in cfg.get(CONF_DEVICES):
ConfiguredDevice(hass, device).save_data() ConfiguredDevice(hass, device).save_data()
@ -128,6 +164,7 @@ async def async_setup(hass, config):
device_discovered) device_discovered)
hass.http.register_view(KonnectedView(access_token)) hass.http.register_view(KonnectedView(access_token))
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, manual_discovery)
return True return True
@ -188,6 +225,8 @@ class ConfiguredDevice:
device_data = { device_data = {
CONF_BINARY_SENSORS: sensors, CONF_BINARY_SENSORS: sensors,
CONF_SWITCHES: actuators, 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]: if CONF_DEVICES not in self.hass.data[DOMAIN]:
@ -271,7 +310,7 @@ class DiscoveredDevice:
if sensor_config.get(CONF_INVERSE): if sensor_config.get(CONF_INVERSE):
state = not state state = not state
async_dispatcher_send( dispatcher_send(
self.hass, self.hass,
SIGNAL_SENSOR_UPDATE.format(entity_id), SIGNAL_SENSOR_UPDATE.format(entity_id),
state) state)
@ -306,13 +345,19 @@ class DiscoveredDevice:
if (desired_sensor_configuration != current_sensor_configuration) or \ if (desired_sensor_configuration != current_sensor_configuration) or \
(current_actuator_config != desired_actuator_config) 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) _LOGGER.info('pushing settings to device %s', self.device_id)
self.client.put_settings( self.client.put_settings(
desired_sensor_configuration, desired_sensor_configuration,
desired_actuator_config, desired_actuator_config,
self.hass.data[DOMAIN].get(CONF_ACCESS_TOKEN), 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)
) )

View File

@ -531,7 +531,7 @@ keyrings.alt==3.1
kiwiki-client==0.1.1 kiwiki-client==0.1.1
# homeassistant.components.konnected # homeassistant.components.konnected
konnected==0.1.2 konnected==0.1.4
# homeassistant.components.eufy # homeassistant.components.eufy
lakeside==0.10 lakeside==0.10