From 1e934e3a2b27f637ddb77ce746bdea6bdac7829c Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Mon, 18 Feb 2019 13:39:07 -0500 Subject: [PATCH] add support for ds18b20 sensors --- .../components/konnected/__init__.py | 31 ++++++++++- homeassistant/components/sensor/konnected.py | 55 +++++++++++++------ 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/konnected/__init__.py b/homeassistant/components/konnected/__init__.py index e6d62ed6e4369a..516225dcbae4bd 100644 --- a/homeassistant/components/konnected/__init__.py +++ b/homeassistant/components/konnected/__init__.py @@ -110,6 +110,7 @@ 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' async def async_setup(hass, config): @@ -323,7 +324,7 @@ def stored_configuration(self): return self.hass.data[DOMAIN][CONF_DEVICES].get(self.device_id) def binary_sensor_configuration(self): - """Return the configuration map for syncing sensors.""" + """Return the configuration map for syncing binary sensors.""" return [{'pin': p} for p in self.stored_configuration[CONF_BINARY_SENSORS]] @@ -340,6 +341,12 @@ def dht_sensor_configuration(self): filter(lambda s: s[CONF_TYPE] == 'dht', self.stored_configuration[CONF_SENSORS])] + def ds18b20_sensor_configuration(self): + """Return the configuration map for syncing DS18B20 sensors.""" + return [{'pin': sensor[CONF_PIN]} for sensor in + filter(lambda s: s[CONF_TYPE] == 'ds18b20', + self.stored_configuration[CONF_SENSORS])] + def update_initial_states(self): """Update the initial state of each sensor from status poll.""" for sensor_data in self.status.get('sensors'): @@ -380,6 +387,13 @@ def sync_device_config(self): _LOGGER.debug('%s: current dht sensor config: %s', self.device_id, current_dht_config) + desired_ds18b20_config = self.ds18b20_sensor_configuration() + current_ds18b20_config = self.status.get(CONF_DHT_SENSORS) + _LOGGER.debug('%s: desired ds18b20 sensor config: %s', self.device_id, + desired_ds18b20_config) + _LOGGER.debug('%s: current ds18b20 sensor config: %s', self.device_id, + current_ds18b20_config) + desired_api_host = \ self.hass.data[DOMAIN].get(CONF_API_HOST) or \ self.hass.config.api.base_url @@ -404,6 +418,7 @@ def sync_device_config(self): sensors=desired_sensor_configuration, actuators=desired_actuator_config, dht_sensors=desired_dht_config, + ds18b20_sensors=desired_ds18b20_config, auth_token=self.hass.data[DOMAIN].get(CONF_ACCESS_TOKEN), endpoint=desired_api_endpoint, blink=self.stored_configuration.get(CONF_BLINK), @@ -502,6 +517,20 @@ async def put(self, request: Request, device_id, hass, SIGNAL_SENSOR_UPDATE.format(entity_id), state) temp, humi = payload.get('temp'), payload.get('humi') + addr = payload.get('addr') + + if addr: + entity_id = pin_data.get(addr) + if entity_id: + async_dispatcher_send( + hass, SIGNAL_SENSOR_UPDATE.format(entity_id), temp) + else: + sensor_data = pin_data + sensor_data['device_id'] = device_id + sensor_data['temperature'] = temp + sensor_data['addr'] = addr + async_dispatcher_send( + hass, SIGNAL_DS18B20_NEW, sensor_data) if temp: entity_id = pin_data.get('temperature') async_dispatcher_send( diff --git a/homeassistant/components/sensor/konnected.py b/homeassistant/components/sensor/konnected.py index 9034d08f151233..9314abe807b20a 100644 --- a/homeassistant/components/sensor/konnected.py +++ b/homeassistant/components/sensor/konnected.py @@ -7,11 +7,10 @@ import logging from homeassistant.components.konnected import ( - DOMAIN as KONNECTED_DOMAIN, SIGNAL_SENSOR_UPDATE) + 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, - ATTR_STATE, TEMP_FAHRENHEIT) + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, TEMP_FAHRENHEIT) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity @@ -29,7 +28,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): - """Set up binary sensors attached to a Konnected device.""" + """Set up sensors attached to a Konnected device.""" if discovery_info is None: return @@ -39,17 +38,31 @@ async def async_setup_platform(hass, config, async_add_entities, data = hass.data[KONNECTED_DOMAIN] device_id = discovery_info['device_id'] sensors = [] + + # Initialize all DHT sensors. for sensor in filter(lambda s: s[CONF_TYPE] == 'dht', data[CONF_DEVICES][device_id][CONF_SENSORS]): sensors.append( - KonnectedDHTSensor(device_id, sensor, DEVICE_CLASS_TEMPERATURE)) + KonnectedSensor(device_id, sensor, DEVICE_CLASS_TEMPERATURE)) sensors.append( - KonnectedDHTSensor(device_id, sensor, DEVICE_CLASS_HUMIDITY)) + KonnectedSensor(device_id, sensor, DEVICE_CLASS_HUMIDITY)) async_add_entities(sensors) + @callback + def async_add_ds18b20(data): + """Add new KonnectedSensor representing a ds18b20 sensor.""" + async_add_entities([ + KonnectedSensor(data.get('device_id'), data, + DEVICE_CLASS_TEMPERATURE) + ], 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 KonnectedDHTSensor(Entity): + +class KonnectedSensor(Entity): """Represents a Konnected DHT Sensor.""" def __init__(self, device_id, data, sensor_type): @@ -58,14 +71,26 @@ def __init__(self, device_id, data, sensor_type): self._device_id = device_id self._type = sensor_type self._pin_num = self._data.get(CONF_PIN) - self._state = self._data.get(ATTR_STATE) - self._device_class = DEVICE_CLASS_TEMPERATURE self._unit_of_measurement = SENSOR_TYPES[sensor_type][1] + self._state = None self._name = '{} {}'.format( self._data.get(CONF_NAME, 'Konnected {} Pin {}'.format( device_id, self._pin_num)), SENSOR_TYPES[sensor_type][0]) + if sensor_type == DEVICE_CLASS_TEMPERATURE: + self._state = self.temperature(self._data.get(sensor_type)) + + def temperature(self, number=None): + """Format temperature and convert to Fahrenheit if necessary.""" + if number is None: + return None + + number = float(number) + if self._unit_of_measurement == TEMP_FAHRENHEIT: + number = celsius_to_fahrenheit(number) + return round(number, 1) + @property def name(self): """Return the name of the sensor.""" @@ -83,7 +108,8 @@ def unit_of_measurement(self): async def async_added_to_hass(self): """Store entity_id and register state change callback.""" - self._data[self._type] = self.entity_id + entity_id_key = self._data.get('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) @@ -91,11 +117,8 @@ async def async_added_to_hass(self): @callback def async_set_state(self, state): """Update the sensor's state.""" - state = float(state) - if self._unit_of_measurement == TEMP_FAHRENHEIT: - self._state = round(celsius_to_fahrenheit(state), 1) - elif self._unit_of_measurement == '%': - self._state = int(state) + if self._type == DEVICE_CLASS_TEMPERATURE: + self._state = self.temperature(state) else: - self._state = round(state, 1) + self._state = int(float(state)) self.async_schedule_update_ha_state()