diff --git a/homeassistant/components/konnected/__init__.py b/homeassistant/components/konnected/__init__.py index e3f9a46743d..3a2dc3b2417 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -14,34 +14,25 @@ from homeassistant.components.discovery import SERVICE_KONNECTED from homeassistant.components.http import HomeAssistantView from homeassistant.const import ( 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) + HTTP_UNAUTHORIZED, CONF_DEVICES, CONF_BINARY_SENSORS, CONF_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 dispatcher_send from homeassistant.helpers import discovery from homeassistant.helpers import config_validation as cv +from .const import ( + CONF_ACTIVATION, CONF_API_HOST, + CONF_MOMENTARY, CONF_PAUSE, CONF_POLL_INTERVAL, CONF_REPEAT, + CONF_INVERSE, CONF_BLINK, CONF_DISCOVERY, CONF_DHT_SENSORS, + CONF_DS18B20_SENSORS, DOMAIN, STATE_LOW, STATE_HIGH, PIN_TO_ZONE, + ZONE_TO_PIN, ENDPOINT_ROOT, UPDATE_ENDPOINT, SIGNAL_SENSOR_UPDATE) +from .handlers import HANDLERS + _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['konnected==0.1.4'] - -DOMAIN = 'konnected' - -CONF_ACTIVATION = 'activation' -CONF_API_HOST = 'api_host' -CONF_MOMENTARY = 'momentary' -CONF_PAUSE = 'pause' -CONF_REPEAT = 'repeat' -CONF_INVERSE = 'inverse' -CONF_BLINK = 'blink' -CONF_DISCOVERY = 'discovery' - -STATE_LOW = 'low' -STATE_HIGH = 'high' - -PIN_TO_ZONE = {1: 1, 2: 2, 5: 3, 6: 4, 7: 5, 8: 'out', 9: 6} -ZONE_TO_PIN = {zone: pin for pin, zone in PIN_TO_ZONE.items()} +REQUIREMENTS = ['konnected==0.1.5'] _BINARY_SENSOR_SCHEMA = vol.All( vol.Schema({ @@ -53,6 +44,18 @@ _BINARY_SENSOR_SCHEMA = vol.All( }), cv.has_at_least_one_key(CONF_PIN, CONF_ZONE) ) +_SENSOR_SCHEMA = vol.All( + vol.Schema({ + vol.Exclusive(CONF_PIN, 's_pin'): vol.Any(*PIN_TO_ZONE), + vol.Exclusive(CONF_ZONE, 's_pin'): vol.Any(*ZONE_TO_PIN), + vol.Required(CONF_TYPE): + vol.All(vol.Lower, vol.In(['dht', 'ds18b20'])), + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_POLL_INTERVAL): + vol.All(vol.Coerce(int), vol.Range(min=1)), + }), cv.has_at_least_one_key(CONF_PIN, CONF_ZONE) +) + _SWITCH_SCHEMA = vol.All( vol.Schema({ vol.Exclusive(CONF_PIN, 'a_pin'): vol.Any(*PIN_TO_ZONE), @@ -79,6 +82,8 @@ CONFIG_SCHEMA = vol.Schema( vol.Required(CONF_ID): cv.matches_regex("[0-9a-f]{12}"), vol.Optional(CONF_BINARY_SENSORS): vol.All( cv.ensure_list, [_BINARY_SENSOR_SCHEMA]), + vol.Optional(CONF_SENSORS): vol.All( + cv.ensure_list, [_SENSOR_SCHEMA]), vol.Optional(CONF_SWITCHES): vol.All( cv.ensure_list, [_SWITCH_SCHEMA]), vol.Optional(CONF_HOST): cv.string, @@ -93,10 +98,6 @@ CONFIG_SCHEMA = vol.Schema( DEPENDENCIES = ['http'] -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): """Set up the Konnected platform.""" @@ -180,30 +181,30 @@ class ConfiguredDevice: def save_data(self): """Save the device configuration to `hass.data`.""" - sensors = {} + binary_sensors = {} for entity in self.config.get(CONF_BINARY_SENSORS) or []: if CONF_ZONE in entity: pin = ZONE_TO_PIN[entity[CONF_ZONE]] else: pin = entity[CONF_PIN] - sensors[pin] = { + binary_sensors[pin] = { CONF_TYPE: entity[CONF_TYPE], CONF_NAME: entity.get(CONF_NAME, 'Konnected {} Zone {}'.format( self.device_id[6:], PIN_TO_ZONE[pin])), CONF_INVERSE: entity.get(CONF_INVERSE), ATTR_STATE: None } - _LOGGER.debug('Set up sensor %s (initial state: %s)', - sensors[pin].get('name'), - sensors[pin].get(ATTR_STATE)) + _LOGGER.debug('Set up binary_sensor %s (initial state: %s)', + binary_sensors[pin].get('name'), + binary_sensors[pin].get(ATTR_STATE)) actuators = [] for entity in self.config.get(CONF_SWITCHES) or []: - if 'zone' in entity: - pin = ZONE_TO_PIN[entity['zone']] + if CONF_ZONE in entity: + pin = ZONE_TO_PIN[entity[CONF_ZONE]] else: - pin = entity['pin'] + pin = entity[CONF_PIN] act = { CONF_PIN: pin, @@ -216,10 +217,32 @@ class ConfiguredDevice: CONF_PAUSE: entity.get(CONF_PAUSE), CONF_REPEAT: entity.get(CONF_REPEAT)} actuators.append(act) - _LOGGER.debug('Set up actuator %s', act) + _LOGGER.debug('Set up switch %s', act) + + sensors = [] + for entity in self.config.get(CONF_SENSORS) or []: + if CONF_ZONE in entity: + pin = ZONE_TO_PIN[entity[CONF_ZONE]] + else: + pin = entity[CONF_PIN] + + sensor = { + CONF_PIN: pin, + CONF_NAME: entity.get( + CONF_NAME, 'Konnected {} Sensor {}'.format( + self.device_id[6:], PIN_TO_ZONE[pin])), + CONF_TYPE: entity[CONF_TYPE], + CONF_POLL_INTERVAL: entity.get(CONF_POLL_INTERVAL) + } + sensors.append(sensor) + _LOGGER.debug('Set up %s sensor %s (initial state: %s)', + sensor.get(CONF_TYPE), + sensor.get(CONF_NAME), + sensor.get(ATTR_STATE)) device_data = { - CONF_BINARY_SENSORS: sensors, + CONF_BINARY_SENSORS: binary_sensors, + CONF_SENSORS: sensors, CONF_SWITCHES: actuators, CONF_BLINK: self.config.get(CONF_BLINK), CONF_DISCOVERY: self.config.get(CONF_DISCOVERY) @@ -232,12 +255,10 @@ class ConfiguredDevice: DOMAIN, CONF_DEVICES, self.device_id, device_data) self.hass.data[DOMAIN][CONF_DEVICES][self.device_id] = device_data - discovery.load_platform( - self.hass, 'binary_sensor', DOMAIN, - {'device_id': self.device_id}, self.hass_config) - discovery.load_platform( - self.hass, 'switch', DOMAIN, - {'device_id': self.device_id}, self.hass_config) + for platform in ['binary_sensor', 'sensor', 'switch']: + discovery.load_platform( + self.hass, platform, DOMAIN, + {'device_id': self.device_id}, self.hass_config) class DiscoveredDevice: @@ -283,8 +304,8 @@ class DiscoveredDevice: """Return the configuration stored in `hass.data` for this device.""" return self.hass.data[DOMAIN][CONF_DEVICES].get(self.device_id) - def sensor_configuration(self): - """Return the configuration map for syncing sensors.""" + def binary_sensor_configuration(self): + """Return the configuration map for syncing binary sensors.""" return [{'pin': p} for p in self.stored_configuration[CONF_BINARY_SENSORS]] @@ -295,6 +316,19 @@ class DiscoveredDevice: else 1)} for data in self.stored_configuration[CONF_SWITCHES]] + def dht_sensor_configuration(self): + """Return the configuration map for syncing DHT sensors.""" + return [{CONF_PIN: sensor[CONF_PIN], + CONF_POLL_INTERVAL: sensor[CONF_POLL_INTERVAL]} for sensor + in self.stored_configuration[CONF_SENSORS] + if sensor[CONF_TYPE] == 'dht'] + + def ds18b20_sensor_configuration(self): + """Return the configuration map for syncing DS18B20 sensors.""" + return [{'pin': sensor[CONF_PIN]} for sensor + in self.stored_configuration[CONF_SENSORS] + if sensor[CONF_TYPE] == 'ds18b20'] + def update_initial_states(self): """Update the initial state of each sensor from status poll.""" for sensor_data in self.status.get('sensors'): @@ -311,57 +345,55 @@ class DiscoveredDevice: SIGNAL_SENSOR_UPDATE.format(entity_id), state) - def sync_device_config(self): - """Sync the new pin configuration to the Konnected device.""" - desired_sensor_configuration = self.sensor_configuration() - current_sensor_configuration = [ - {'pin': s[CONF_PIN]} for s in self.status.get('sensors')] - _LOGGER.debug('%s: desired sensor config: %s', self.device_id, - desired_sensor_configuration) - _LOGGER.debug('%s: current sensor config: %s', self.device_id, - current_sensor_configuration) - - desired_actuator_config = self.actuator_configuration() - current_actuator_config = self.status.get('actuators') - _LOGGER.debug('%s: desired actuator config: %s', self.device_id, - desired_actuator_config) - _LOGGER.debug('%s: current actuator config: %s', self.device_id, - current_actuator_config) - + def desired_settings_payload(self): + """Return a dict representing the desired device configuration.""" desired_api_host = \ self.hass.data[DOMAIN].get(CONF_API_HOST) or \ self.hass.config.api.base_url desired_api_endpoint = desired_api_host + ENDPOINT_ROOT - current_api_endpoint = self.status.get('endpoint') - _LOGGER.debug('%s: desired api endpoint: %s', self.device_id, - desired_api_endpoint) - _LOGGER.debug('%s: current api endpoint: %s', self.device_id, - current_api_endpoint) + return { + 'sensors': self.binary_sensor_configuration(), + 'actuators': self.actuator_configuration(), + 'dht_sensors': self.dht_sensor_configuration(), + 'ds18b20_sensors': self.ds18b20_sensor_configuration(), + 'auth_token': self.hass.data[DOMAIN].get(CONF_ACCESS_TOKEN), + 'endpoint': desired_api_endpoint, + 'blink': self.stored_configuration.get(CONF_BLINK), + 'discovery': self.stored_configuration.get(CONF_DISCOVERY) + } - if (desired_sensor_configuration != current_sensor_configuration) or \ - (current_actuator_config != desired_actuator_config) or \ - (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)): + def current_settings_payload(self): + """Return a dict of configuration currently stored on the device.""" + settings = self.status['settings'] + if not settings: + settings = {} + + return { + 'sensors': [ + {'pin': s[CONF_PIN]} for s in self.status.get('sensors')], + 'actuators': self.status.get('actuators'), + 'dht_sensors': self.status.get(CONF_DHT_SENSORS), + 'ds18b20_sensors': self.status.get(CONF_DS18B20_SENSORS), + 'auth_token': settings.get('token'), + 'endpoint': settings.get('apiUrl'), + 'blink': settings.get(CONF_BLINK), + 'discovery': settings.get(CONF_DISCOVERY) + } + + def sync_device_config(self): + """Sync the new pin configuration to the Konnected device if needed.""" + _LOGGER.debug('Device %s settings payload: %s', self.device_id, + self.desired_settings_payload()) + if self.desired_settings_payload() != self.current_settings_payload(): _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, - blink=self.stored_configuration.get(CONF_BLINK), - discovery=self.stored_configuration.get(CONF_DISCOVERY) - ) + self.client.put_settings(**self.desired_settings_payload()) class KonnectedView(HomeAssistantView): """View creates an endpoint to receive push updates from the device.""" url = UPDATE_ENDPOINT - extra_urls = [UPDATE_ENDPOINT + '/{pin_num}/{state}'] name = 'api:konnected' requires_auth = False # Uses access token from configuration @@ -406,8 +438,7 @@ class KonnectedView(HomeAssistantView): hass.states.get(pin[ATTR_ENTITY_ID]).state, pin[CONF_ACTIVATION])}) - async def put(self, request: Request, device_id, - pin_num=None, state=None) -> Response: + async def put(self, request: Request, device_id) -> Response: """Receive a sensor update via PUT request and async set state.""" hass = request.app['hass'] data = hass.data[DOMAIN] @@ -415,11 +446,10 @@ class KonnectedView(HomeAssistantView): try: # Konnected 2.2.0 and above supports JSON payloads payload = await request.json() pin_num = payload['pin'] - state = payload['state'] except json.decoder.JSONDecodeError: - _LOGGER.warning(("Your Konnected device software may be out of " - "date. Visit https://help.konnected.io for " - "updating instructions.")) + _LOGGER.error(("Your Konnected device software may be out of " + "date. Visit https://help.konnected.io for " + "updating instructions.")) auth = request.headers.get(AUTHORIZATION, None) if not hmac.compare_digest('Bearer {}'.format(self.auth_token), auth): @@ -430,20 +460,20 @@ class KonnectedView(HomeAssistantView): if device is None: return self.json_message('unregistered device', status_code=HTTP_BAD_REQUEST) - pin_data = device[CONF_BINARY_SENSORS].get(pin_num) + pin_data = device[CONF_BINARY_SENSORS].get(pin_num) or \ + next((s for s in device[CONF_SENSORS] if s[CONF_PIN] == pin_num), + None) if pin_data is None: return self.json_message('unregistered sensor/actuator', status_code=HTTP_BAD_REQUEST) - entity_id = pin_data.get(ATTR_ENTITY_ID) - if entity_id is None: - return self.json_message('uninitialized sensor/actuator', - status_code=HTTP_NOT_FOUND) - state = bool(int(state)) - if pin_data.get(CONF_INVERSE): - state = not state + pin_data['device_id'] = device_id + + for attr in ['state', 'temp', 'humi', 'addr']: + value = payload.get(attr) + handler = HANDLERS.get(attr) + if value is not None and handler: + hass.async_create_task(handler(hass, pin_data, payload)) - async_dispatcher_send( - hass, SIGNAL_SENSOR_UPDATE.format(entity_id), state) return self.json_message('ok') diff --git a/homeassistant/components/konnected/binary_sensor.py b/homeassistant/components/konnected/binary_sensor.py index cb15e44e798..a47f81b9556 100644 --- a/homeassistant/components/konnected/binary_sensor.py +++ b/homeassistant/components/konnected/binary_sensor.py @@ -39,9 +39,13 @@ class KonnectedBinarySensor(BinarySensorDevice): self._pin_num = pin_num self._state = self._data.get(ATTR_STATE) 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])) - _LOGGER.debug("Created new Konnected sensor: %s", self._name) + self._unique_id = '{}-{}'.format(device_id, PIN_TO_ZONE[pin_num]) + self._name = self._data.get(CONF_NAME) + + @property + def unique_id(self) -> str: + """Return the unique id.""" + return self._unique_id @property def name(self): diff --git a/homeassistant/components/konnected/const.py b/homeassistant/components/konnected/const.py new file mode 100644 index 00000000000..88293adfc81 --- /dev/null +++ b/homeassistant/components/konnected/const.py @@ -0,0 +1,27 @@ +"""Konnected constants.""" + +DOMAIN = 'konnected' + +CONF_ACTIVATION = 'activation' +CONF_API_HOST = 'api_host' +CONF_MOMENTARY = 'momentary' +CONF_PAUSE = 'pause' +CONF_POLL_INTERVAL = 'poll_interval' +CONF_PRECISION = 'precision' +CONF_REPEAT = 'repeat' +CONF_INVERSE = 'inverse' +CONF_BLINK = 'blink' +CONF_DISCOVERY = 'discovery' +CONF_DHT_SENSORS = 'dht_sensors' +CONF_DS18B20_SENSORS = 'ds18b20_sensors' + +STATE_LOW = 'low' +STATE_HIGH = 'high' + +PIN_TO_ZONE = {1: 1, 2: 2, 5: 3, 6: 4, 7: 5, 8: 'out', 9: 6} +ZONE_TO_PIN = {zone: pin for pin, zone in PIN_TO_ZONE.items()} + +ENDPOINT_ROOT = '/api/konnected' +UPDATE_ENDPOINT = (ENDPOINT_ROOT + r'/device/{device_id:[a-zA-Z0-9]+}') +SIGNAL_SENSOR_UPDATE = 'konnected.{}.update' +SIGNAL_DS18B20_NEW = 'konnected.ds18b20.new' diff --git a/homeassistant/components/konnected/handlers.py b/homeassistant/components/konnected/handlers.py new file mode 100644 index 00000000000..6e92e7f20c8 --- /dev/null +++ b/homeassistant/components/konnected/handlers.py @@ -0,0 +1,62 @@ +"""Handle Konnected messages.""" +import logging + +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.util import decorator +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_STATE, + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY) + +from .const import (CONF_INVERSE, SIGNAL_SENSOR_UPDATE, SIGNAL_DS18B20_NEW) + +_LOGGER = logging.getLogger(__name__) +HANDLERS = decorator.Registry() + + +@HANDLERS.register('state') +async def async_handle_state_update(hass, context, msg): + """Handle a binary sensor state update.""" + _LOGGER.debug("[state handler] context: %s msg: %s", context, msg) + entity_id = context.get(ATTR_ENTITY_ID) + state = bool(int(msg.get(ATTR_STATE))) + if msg.get(CONF_INVERSE): + state = not state + + async_dispatcher_send( + hass, SIGNAL_SENSOR_UPDATE.format(entity_id), state) + + +@HANDLERS.register('temp') +async def async_handle_temp_update(hass, context, msg): + """Handle a temperature sensor state update.""" + _LOGGER.debug("[temp handler] context: %s msg: %s", context, msg) + entity_id, temp = context.get(DEVICE_CLASS_TEMPERATURE), msg.get('temp') + if entity_id: + async_dispatcher_send( + hass, SIGNAL_SENSOR_UPDATE.format(entity_id), temp) + + +@HANDLERS.register('humi') +async def async_handle_humi_update(hass, context, msg): + """Handle a humidity sensor state update.""" + _LOGGER.debug("[humi handler] context: %s msg: %s", context, msg) + entity_id, humi = context.get(DEVICE_CLASS_HUMIDITY), msg.get('humi') + if entity_id: + async_dispatcher_send( + hass, SIGNAL_SENSOR_UPDATE.format(entity_id), humi) + + +@HANDLERS.register('addr') +async def async_handle_addr_update(hass, context, msg): + """Handle an addressable sensor update.""" + _LOGGER.debug("[addr handler] context: %s msg: %s", context, msg) + addr, temp = msg.get('addr'), msg.get('temp') + entity_id = context.get(addr) + if entity_id: + async_dispatcher_send( + hass, SIGNAL_SENSOR_UPDATE.format(entity_id), temp) + else: + msg['device_id'] = context.get('device_id') + msg['temperature'] = temp + msg['addr'] = addr + async_dispatcher_send(hass, SIGNAL_DS18B20_NEW, msg) diff --git a/homeassistant/components/konnected/sensor.py b/homeassistant/components/konnected/sensor.py new file mode 100644 index 00000000000..eb3f5511346 --- /dev/null +++ b/homeassistant/components/konnected/sensor.py @@ -0,0 +1,124 @@ +"""Support for DHT and DS18B20 sensors attached to a Konnected device.""" +import logging + +from homeassistant.components.konnected.const import ( + DOMAIN as KONNECTED_DOMAIN, SIGNAL_DS18B20_NEW, SIGNAL_SENSOR_UPDATE) +from homeassistant.const import ( + CONF_DEVICES, CONF_PIN, CONF_TYPE, CONF_NAME, CONF_SENSORS, + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['konnected'] + +SENSOR_TYPES = { + DEVICE_CLASS_TEMPERATURE: ['Temperature', TEMP_CELSIUS], + DEVICE_CLASS_HUMIDITY: ['Humidity', '%'] +} + + +async def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up sensors attached to a Konnected device.""" + if discovery_info is None: + return + + data = hass.data[KONNECTED_DOMAIN] + device_id = discovery_info['device_id'] + sensors = [] + + # Initialize all DHT sensors. + dht_sensors = [sensor for sensor + in data[CONF_DEVICES][device_id][CONF_SENSORS] + if sensor[CONF_TYPE] == 'dht'] + for sensor in dht_sensors: + sensors.append( + KonnectedSensor(device_id, sensor, DEVICE_CLASS_TEMPERATURE)) + sensors.append( + KonnectedSensor(device_id, sensor, DEVICE_CLASS_HUMIDITY)) + + async_add_entities(sensors) + + @callback + def async_add_ds18b20(attrs): + """Add new KonnectedSensor representing a ds18b20 sensor.""" + sensor_config = next((s for s + in data[CONF_DEVICES][device_id][CONF_SENSORS] + if s[CONF_TYPE] == 'ds18b20' + and s[CONF_PIN] == attrs.get(CONF_PIN)), None) + + async_add_entities([ + KonnectedSensor(device_id, sensor_config, DEVICE_CLASS_TEMPERATURE, + addr=attrs.get('addr'), + initial_state=attrs.get('temp')) + ], True) + + # DS18B20 sensors entities are initialized when they report for the first + # time. Set up a listener for that signal from the Konnected component. + async_dispatcher_connect(hass, SIGNAL_DS18B20_NEW, async_add_ds18b20) + + +class KonnectedSensor(Entity): + """Represents a Konnected DHT Sensor.""" + + def __init__(self, device_id, data, sensor_type, addr=None, + initial_state=None): + """Initialize the entity for a single sensor_type.""" + self._addr = addr + self._data = data + self._device_id = device_id + self._type = sensor_type + self._pin_num = self._data.get(CONF_PIN) + self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] + self._unique_id = addr or '{}-{}-{}'.format( + device_id, self._pin_num, sensor_type) + + # set initial state if known at initialization + self._state = initial_state + if self._state: + self._state = round(float(self._state), 1) + + # set entity name if given + self._name = self._data.get(CONF_NAME) + if self._name: + self._name += ' ' + SENSOR_TYPES[sensor_type][0] + + @property + def unique_id(self) -> str: + """Return the unique id.""" + return self._unique_id + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return self._unit_of_measurement + + async def async_added_to_hass(self): + """Store entity_id and register state change callback.""" + entity_id_key = self._addr or self._type + self._data[entity_id_key] = 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.""" + if self._type == DEVICE_CLASS_HUMIDITY: + self._state = int(float(state)) + else: + self._state = round(float(state), 1) + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/konnected/switch.py b/homeassistant/components/konnected/switch.py index 897933e6d80..1a4b495297e 100644 --- a/homeassistant/components/konnected/switch.py +++ b/homeassistant/components/konnected/switch.py @@ -6,7 +6,7 @@ from homeassistant.components.konnected import ( CONF_PAUSE, CONF_REPEAT, STATE_LOW, STATE_HIGH) from homeassistant.helpers.entity import ToggleEntity from homeassistant.const import ( - CONF_DEVICES, CONF_SWITCHES, CONF_PIN, ATTR_STATE) + ATTR_STATE, CONF_DEVICES, CONF_NAME, CONF_PIN, CONF_SWITCHES) _LOGGER = logging.getLogger(__name__) @@ -40,10 +40,13 @@ class KonnectedSwitch(ToggleEntity): self._pause = self._data.get(CONF_PAUSE) self._repeat = self._data.get(CONF_REPEAT) 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])) - _LOGGER.debug("Created new switch: %s", self._name) + self._unique_id = '{}-{}'.format(device_id, PIN_TO_ZONE[pin_num]) + self._name = self._data.get(CONF_NAME) + + @property + def unique_id(self) -> str: + """Return the unique id.""" + return self._unique_id @property def name(self): diff --git a/requirements_all.txt b/requirements_all.txt index 63931513439..691ad6d1539 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -607,7 +607,7 @@ keyrings.alt==3.1.1 kiwiki-client==0.1.1 # homeassistant.components.konnected -konnected==0.1.4 +konnected==0.1.5 # homeassistant.components.eufy lakeside==0.12