Konnected component follow up (#14491)

* make device_discovered synchronous

* small fixes from code review

* use dispatcher to update sensor state

* update switch state based on response from the device

* interpolate entity_id into dispatcher signal

* cleanup lint

* change coroutine to callback
This commit is contained in:
Nate Clark 2018-05-17 14:19:05 -04:00 committed by Sebastian Muszynski
parent 144524fbbb
commit ed3efc8712
3 changed files with 57 additions and 38 deletions

View File

@ -4,13 +4,16 @@ Support for wired binary sensors attached to a Konnected device.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.konnected/ https://home-assistant.io/components/binary_sensor.konnected/
""" """
import asyncio
import logging import logging
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.konnected import (DOMAIN, PIN_TO_ZONE) from homeassistant.components.konnected import (
DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, SIGNAL_SENSOR_UPDATE)
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICES, CONF_TYPE, CONF_NAME, CONF_BINARY_SENSORS, ATTR_STATE) CONF_DEVICES, CONF_TYPE, CONF_NAME, CONF_BINARY_SENSORS, ATTR_ENTITY_ID,
ATTR_STATE)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -23,7 +26,7 @@ async def async_setup_platform(hass, config, async_add_devices,
if discovery_info is None: if discovery_info is None:
return return
data = hass.data[DOMAIN] data = hass.data[KONNECTED_DOMAIN]
device_id = discovery_info['device_id'] device_id = discovery_info['device_id']
sensors = [KonnectedBinarySensor(device_id, pin_num, pin_data) sensors = [KonnectedBinarySensor(device_id, pin_num, pin_data)
for pin_num, pin_data in for pin_num, pin_data in
@ -43,7 +46,6 @@ class KonnectedBinarySensor(BinarySensorDevice):
self._device_class = self._data.get(CONF_TYPE) self._device_class = self._data.get(CONF_TYPE)
self._name = self._data.get(CONF_NAME, 'Konnected {} Zone {}'.format( self._name = self._data.get(CONF_NAME, 'Konnected {} Zone {}'.format(
device_id, PIN_TO_ZONE[pin_num])) device_id, PIN_TO_ZONE[pin_num]))
self._data['entity'] = self
_LOGGER.debug('Created new Konnected sensor: %s', self._name) _LOGGER.debug('Created new Konnected sensor: %s', self._name)
@property @property
@ -66,9 +68,15 @@ class KonnectedBinarySensor(BinarySensorDevice):
"""Return the device class.""" """Return the device class."""
return self._device_class return self._device_class
@asyncio.coroutine async def async_added_to_hass(self):
"""Store entity_id and register state change callback."""
self._data[ATTR_ENTITY_ID] = self.entity_id
async_dispatcher_connect(
self.hass, SIGNAL_SENSOR_UPDATE.format(self.entity_id),
self.async_set_state)
@callback
def async_set_state(self, state): def async_set_state(self, state):
"""Update the sensor's state.""" """Update the sensor's state."""
self._state = state self._state = state
self._data[ATTR_STATE] = state
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()

View File

@ -19,7 +19,8 @@ from homeassistant.const import (
HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR, HTTP_UNAUTHORIZED, HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR, HTTP_UNAUTHORIZED,
CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES, CONF_HOST, CONF_PORT, CONF_DEVICES, CONF_BINARY_SENSORS, CONF_SWITCHES, CONF_HOST, CONF_PORT,
CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE, CONF_ACCESS_TOKEN, CONF_ID, CONF_NAME, CONF_TYPE, CONF_PIN, CONF_ZONE, CONF_ACCESS_TOKEN,
ATTR_STATE) ATTR_ENTITY_ID, ATTR_STATE)
from homeassistant.helpers.dispatcher import async_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
@ -75,6 +76,7 @@ DEPENDENCIES = ['http', 'discovery']
ENDPOINT_ROOT = '/api/konnected' ENDPOINT_ROOT = '/api/konnected'
UPDATE_ENDPOINT = (ENDPOINT_ROOT + r'/device/{device_id:[a-zA-Z0-9]+}') UPDATE_ENDPOINT = (ENDPOINT_ROOT + r'/device/{device_id:[a-zA-Z0-9]+}')
SIGNAL_SENSOR_UPDATE = 'konnected.{}.update'
async def async_setup(hass, config): async def async_setup(hass, config):
@ -87,19 +89,19 @@ async def async_setup(hass, config):
if DOMAIN not in hass.data: if DOMAIN not in hass.data:
hass.data[DOMAIN] = {CONF_ACCESS_TOKEN: access_token} hass.data[DOMAIN] = {CONF_ACCESS_TOKEN: access_token}
async def async_device_discovered(service, info): def device_discovered(service, info):
"""Call when a Konnected device has been discovered.""" """Call when a Konnected device has been discovered."""
_LOGGER.debug("Discovered a new Konnected device: %s", info) _LOGGER.debug("Discovered a new Konnected device: %s", info)
host = info.get(CONF_HOST) host = info.get(CONF_HOST)
port = info.get(CONF_PORT) port = info.get(CONF_PORT)
device = KonnectedDevice(hass, host, port, cfg) device = KonnectedDevice(hass, host, port, cfg)
await device.async_setup() device.setup()
discovery.async_listen( discovery.async_listen(
hass, hass,
SERVICE_KONNECTED, SERVICE_KONNECTED,
async_device_discovered) device_discovered)
hass.http.register_view(KonnectedView(access_token)) hass.http.register_view(KonnectedView(access_token))
@ -121,17 +123,17 @@ class KonnectedDevice(object):
self.status = self.client.get_status() self.status = self.client.get_status()
_LOGGER.info('Initialized Konnected device %s', self.device_id) _LOGGER.info('Initialized Konnected device %s', self.device_id)
async def async_setup(self): def setup(self):
"""Set up a newly discovered Konnected device.""" """Set up a newly discovered Konnected device."""
user_config = self.config() user_config = self.config()
if user_config: if user_config:
_LOGGER.debug('Configuring Konnected device %s', self.device_id) _LOGGER.debug('Configuring Konnected device %s', self.device_id)
self.save_data() self.save_data()
await self.async_sync_device_config() self.sync_device_config()
await discovery.async_load_platform( discovery.load_platform(
self.hass, 'binary_sensor', self.hass, 'binary_sensor',
DOMAIN, {'device_id': self.device_id}) DOMAIN, {'device_id': self.device_id})
await discovery.async_load_platform( discovery.load_platform(
self.hass, 'switch', DOMAIN, self.hass, 'switch', DOMAIN,
{'device_id': self.device_id}) {'device_id': self.device_id})
@ -225,7 +227,7 @@ class KonnectedDevice(object):
def sensor_configuration(self): def sensor_configuration(self):
"""Return the configuration map for syncing sensors.""" """Return the configuration map for syncing sensors."""
return [{'pin': p} for p in return [{'pin': p} for p in
self.stored_configuration[CONF_BINARY_SENSORS].keys()] self.stored_configuration[CONF_BINARY_SENSORS]]
def actuator_configuration(self): def actuator_configuration(self):
"""Return the configuration map for syncing actuators.""" """Return the configuration map for syncing actuators."""
@ -235,7 +237,7 @@ class KonnectedDevice(object):
for p, data in for p, data in
self.stored_configuration[CONF_SWITCHES].items()] self.stored_configuration[CONF_SWITCHES].items()]
async def async_sync_device_config(self): def sync_device_config(self):
"""Sync the new pin configuration to the Konnected device.""" """Sync the new pin configuration to the Konnected device."""
desired_sensor_configuration = self.sensor_configuration() desired_sensor_configuration = self.sensor_configuration()
current_sensor_configuration = [ current_sensor_configuration = [
@ -306,10 +308,12 @@ class KonnectedView(HomeAssistantView):
if pin_data is None: if pin_data is None:
return self.json_message('unregistered sensor/actuator', return self.json_message('unregistered sensor/actuator',
status_code=HTTP_BAD_REQUEST) status_code=HTTP_BAD_REQUEST)
entity = pin_data.get('entity')
if entity is None: entity_id = pin_data.get(ATTR_ENTITY_ID)
if entity_id is None:
return self.json_message('uninitialized sensor/actuator', return self.json_message('uninitialized sensor/actuator',
status_code=HTTP_INTERNAL_SERVER_ERROR) status_code=HTTP_INTERNAL_SERVER_ERROR)
await entity.async_set_state(state) async_dispatcher_send(
hass, SIGNAL_SENSOR_UPDATE.format(entity_id), state)
return self.json_message('ok') return self.json_message('ok')

View File

@ -5,11 +5,11 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.konnected/ https://home-assistant.io/components/switch.konnected/
""" """
import asyncio
import logging import logging
from homeassistant.components.konnected import ( from homeassistant.components.konnected import (
DOMAIN, PIN_TO_ZONE, CONF_ACTIVATION, STATE_LOW, STATE_HIGH) DOMAIN as KONNECTED_DOMAIN, PIN_TO_ZONE, CONF_ACTIVATION,
STATE_LOW, STATE_HIGH)
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (CONF_DEVICES, CONF_SWITCHES, ATTR_STATE) from homeassistant.const import (CONF_DEVICES, CONF_SWITCHES, ATTR_STATE)
@ -24,7 +24,7 @@ async def async_setup_platform(hass, config, async_add_devices,
if discovery_info is None: if discovery_info is None:
return return
data = hass.data[DOMAIN] data = hass.data[KONNECTED_DOMAIN]
device_id = discovery_info['device_id'] device_id = discovery_info['device_id']
client = data[CONF_DEVICES][device_id]['client'] client = data[CONF_DEVICES][device_id]['client']
switches = [KonnectedSwitch(device_id, pin_num, pin_data, client) switches = [KonnectedSwitch(device_id, pin_num, pin_data, client)
@ -41,12 +41,11 @@ class KonnectedSwitch(ToggleEntity):
self._data = data self._data = data
self._device_id = device_id self._device_id = device_id
self._pin_num = pin_num self._pin_num = pin_num
self._state = self._data.get(ATTR_STATE)
self._activation = self._data.get(CONF_ACTIVATION, STATE_HIGH) self._activation = self._data.get(CONF_ACTIVATION, STATE_HIGH)
self._state = self._boolean_state(self._data.get(ATTR_STATE))
self._name = self._data.get( self._name = self._data.get(
'name', 'Konnected {} Actuator {}'.format( 'name', 'Konnected {} Actuator {}'.format(
device_id, PIN_TO_ZONE[pin_num])) device_id, PIN_TO_ZONE[pin_num]))
self._data['entity'] = self
self._client = client self._client = client
_LOGGER.debug('Created new switch: %s', self._name) _LOGGER.debug('Created new switch: %s', self._name)
@ -62,26 +61,34 @@ class KonnectedSwitch(ToggleEntity):
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Send a command to turn on the switch.""" """Send a command to turn on the switch."""
self._client.put_device(self._pin_num, resp = self._client.put_device(
int(self._activation == STATE_HIGH)) self._pin_num, int(self._activation == STATE_HIGH))
self._set_state(True)
if resp.get(ATTR_STATE) is not None:
self._set_state(self._boolean_state(resp.get(ATTR_STATE)))
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Send a command to turn off the switch.""" """Send a command to turn off the switch."""
self._client.put_device(self._pin_num, resp = self._client.put_device(
int(self._activation == STATE_LOW)) self._pin_num, int(self._activation == STATE_LOW))
self._set_state(False)
if resp.get(ATTR_STATE) is not None:
self._set_state(self._boolean_state(resp.get(ATTR_STATE)))
def _boolean_state(self, int_state):
if int_state is None:
return False
if int_state == 0:
return self._activation == STATE_LOW
if int_state == 1:
return self._activation == STATE_HIGH
def _set_state(self, state): def _set_state(self, state):
self._state = state self._state = state
self._data[ATTR_STATE] = state
self.schedule_update_ha_state() self.schedule_update_ha_state()
_LOGGER.debug('Setting status of %s actuator pin %s to %s', _LOGGER.debug('Setting status of %s actuator pin %s to %s',
self._device_id, self.name, state) self._device_id, self.name, state)
@asyncio.coroutine async def async_added_to_hass(self):
def async_set_state(self, state): """Store entity_id."""
"""Update the switch's state.""" self._data['entity_id'] = self.entity_id
self._state = state
self._data[ATTR_STATE] = state
self.async_schedule_update_ha_state()