From ed3efc871237f0749335e5f09689292513a5203d Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Thu, 17 May 2018 14:19:05 -0400 Subject: [PATCH] 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 --- .../components/binary_sensor/konnected.py | 22 +++++++--- homeassistant/components/konnected.py | 30 +++++++------ homeassistant/components/switch/konnected.py | 43 +++++++++++-------- 3 files changed, 57 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/binary_sensor/konnected.py b/homeassistant/components/binary_sensor/konnected.py index c7e2b7c84fe..9a16ca5e1ab 100644 --- a/homeassistant/components/binary_sensor/konnected.py +++ b/homeassistant/components/binary_sensor/konnected.py @@ -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 https://home-assistant.io/components/binary_sensor.konnected/ """ -import asyncio import logging 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 ( - 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__) @@ -23,7 +26,7 @@ async def async_setup_platform(hass, config, async_add_devices, if discovery_info is None: return - data = hass.data[DOMAIN] + data = hass.data[KONNECTED_DOMAIN] device_id = discovery_info['device_id'] sensors = [KonnectedBinarySensor(device_id, pin_num, pin_data) for pin_num, pin_data in @@ -43,7 +46,6 @@ class KonnectedBinarySensor(BinarySensorDevice): self._device_class = self._data.get(CONF_TYPE) self._name = self._data.get(CONF_NAME, 'Konnected {} Zone {}'.format( device_id, PIN_TO_ZONE[pin_num])) - self._data['entity'] = self _LOGGER.debug('Created new Konnected sensor: %s', self._name) @property @@ -66,9 +68,15 @@ class KonnectedBinarySensor(BinarySensorDevice): """Return the 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): """Update the sensor's state.""" self._state = state - self._data[ATTR_STATE] = state self.async_schedule_update_ha_state() diff --git a/homeassistant/components/konnected.py b/homeassistant/components/konnected.py index 8c5578f10e4..70b66f84ae9 100644 --- a/homeassistant/components/konnected.py +++ b/homeassistant/components/konnected.py @@ -19,7 +19,8 @@ from homeassistant.const import ( HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR, 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_STATE) + ATTR_ENTITY_ID, ATTR_STATE) +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers import discovery from homeassistant.helpers import config_validation as cv @@ -75,6 +76,7 @@ DEPENDENCIES = ['http', 'discovery'] ENDPOINT_ROOT = '/api/konnected' UPDATE_ENDPOINT = (ENDPOINT_ROOT + r'/device/{device_id:[a-zA-Z0-9]+}') +SIGNAL_SENSOR_UPDATE = 'konnected.{}.update' async def async_setup(hass, config): @@ -87,19 +89,19 @@ async def async_setup(hass, config): if DOMAIN not in hass.data: 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.""" _LOGGER.debug("Discovered a new Konnected device: %s", info) host = info.get(CONF_HOST) port = info.get(CONF_PORT) device = KonnectedDevice(hass, host, port, cfg) - await device.async_setup() + device.setup() discovery.async_listen( hass, SERVICE_KONNECTED, - async_device_discovered) + device_discovered) hass.http.register_view(KonnectedView(access_token)) @@ -121,17 +123,17 @@ class KonnectedDevice(object): self.status = self.client.get_status() _LOGGER.info('Initialized Konnected device %s', self.device_id) - async def async_setup(self): + def setup(self): """Set up a newly discovered Konnected device.""" user_config = self.config() if user_config: _LOGGER.debug('Configuring Konnected device %s', self.device_id) self.save_data() - await self.async_sync_device_config() - await discovery.async_load_platform( + self.sync_device_config() + discovery.load_platform( self.hass, 'binary_sensor', DOMAIN, {'device_id': self.device_id}) - await discovery.async_load_platform( + discovery.load_platform( self.hass, 'switch', DOMAIN, {'device_id': self.device_id}) @@ -225,7 +227,7 @@ class KonnectedDevice(object): def sensor_configuration(self): """Return the configuration map for syncing sensors.""" return [{'pin': p} for p in - self.stored_configuration[CONF_BINARY_SENSORS].keys()] + self.stored_configuration[CONF_BINARY_SENSORS]] def actuator_configuration(self): """Return the configuration map for syncing actuators.""" @@ -235,7 +237,7 @@ class KonnectedDevice(object): for p, data in 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.""" desired_sensor_configuration = self.sensor_configuration() current_sensor_configuration = [ @@ -306,10 +308,12 @@ class KonnectedView(HomeAssistantView): if pin_data is None: return self.json_message('unregistered sensor/actuator', 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', 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') diff --git a/homeassistant/components/switch/konnected.py b/homeassistant/components/switch/konnected.py index e88f9826678..53c6406b28a 100644 --- a/homeassistant/components/switch/konnected.py +++ b/homeassistant/components/switch/konnected.py @@ -5,11 +5,11 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.konnected/ """ -import asyncio import logging 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.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: return - data = hass.data[DOMAIN] + data = hass.data[KONNECTED_DOMAIN] device_id = discovery_info['device_id'] client = data[CONF_DEVICES][device_id]['client'] switches = [KonnectedSwitch(device_id, pin_num, pin_data, client) @@ -41,12 +41,11 @@ class KonnectedSwitch(ToggleEntity): self._data = data self._device_id = device_id self._pin_num = pin_num - self._state = self._data.get(ATTR_STATE) self._activation = self._data.get(CONF_ACTIVATION, STATE_HIGH) + self._state = self._boolean_state(self._data.get(ATTR_STATE)) self._name = self._data.get( 'name', 'Konnected {} Actuator {}'.format( device_id, PIN_TO_ZONE[pin_num])) - self._data['entity'] = self self._client = client _LOGGER.debug('Created new switch: %s', self._name) @@ -62,26 +61,34 @@ class KonnectedSwitch(ToggleEntity): def turn_on(self, **kwargs): """Send a command to turn on the switch.""" - self._client.put_device(self._pin_num, - int(self._activation == STATE_HIGH)) - self._set_state(True) + resp = self._client.put_device( + self._pin_num, int(self._activation == STATE_HIGH)) + + if resp.get(ATTR_STATE) is not None: + self._set_state(self._boolean_state(resp.get(ATTR_STATE))) def turn_off(self, **kwargs): """Send a command to turn off the switch.""" - self._client.put_device(self._pin_num, - int(self._activation == STATE_LOW)) - self._set_state(False) + resp = self._client.put_device( + self._pin_num, int(self._activation == STATE_LOW)) + + 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): self._state = state - self._data[ATTR_STATE] = state self.schedule_update_ha_state() _LOGGER.debug('Setting status of %s actuator pin %s to %s', self._device_id, self.name, state) - @asyncio.coroutine - def async_set_state(self, state): - """Update the switch's state.""" - self._state = state - self._data[ATTR_STATE] = state - self.async_schedule_update_ha_state() + async def async_added_to_hass(self): + """Store entity_id.""" + self._data['entity_id'] = self.entity_id