From 12e679c14daf9fac8c68a3e8c314cde1a980f21f Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sat, 2 Jun 2018 18:54:48 -0700 Subject: [PATCH] Assign device class to nest sensors (#14746) * Assign device class to nest sensors sensor/nest.NestSensor => /nest.NestSensorDevice, so that BinarySensor platform do not reference Sensor platform anymore * Resolve code review comment * Follow code review comment --- .../components/binary_sensor/nest.py | 54 ++++--- homeassistant/components/nest.py | 55 ++++++- homeassistant/components/sensor/nest.py | 151 ++++++------------ 3 files changed, 130 insertions(+), 130 deletions(-) diff --git a/homeassistant/components/binary_sensor/nest.py b/homeassistant/components/binary_sensor/nest.py index 008b6eed1e4..882ff142e8c 100644 --- a/homeassistant/components/binary_sensor/nest.py +++ b/homeassistant/components/binary_sensor/nest.py @@ -7,32 +7,31 @@ https://home-assistant.io/components/binary_sensor.nest/ from itertools import chain import logging -from homeassistant.components.binary_sensor import (BinarySensorDevice) -from homeassistant.components.nest import DATA_NEST -from homeassistant.components.sensor.nest import NestSensor +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.components.nest import DATA_NEST, NestSensorDevice from homeassistant.const import CONF_MONITORED_CONDITIONS DEPENDENCIES = ['nest'] -BINARY_TYPES = ['online'] +BINARY_TYPES = {'online': 'connectivity'} -CLIMATE_BINARY_TYPES = [ - 'fan', - 'is_using_emergency_heat', - 'is_locked', - 'has_leaf', -] +CLIMATE_BINARY_TYPES = { + 'fan': None, + 'is_using_emergency_heat': 'heat', + 'is_locked': None, + 'has_leaf': None, +} -CAMERA_BINARY_TYPES = [ - 'motion_detected', - 'sound_detected', - 'person_detected', -] +CAMERA_BINARY_TYPES = { + 'motion_detected': 'motion', + 'sound_detected': 'sound', + 'person_detected': 'occupancy', +} -STRUCTURE_BINARY_TYPES = [ - 'away', - # 'security_state', # wait for pending python-nest update -] +STRUCTURE_BINARY_TYPES = { + 'away': None, + # 'security_state', # pending python-nest update +} STRUCTURE_BINARY_STATE_MAP = { 'away': {'away': True, 'home': False}, @@ -50,8 +49,8 @@ _BINARY_TYPES_DEPRECATED = [ 'hvac_emer_heat_state', ] -_VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \ - + CAMERA_BINARY_TYPES + STRUCTURE_BINARY_TYPES +_VALID_BINARY_SENSOR_TYPES = {**BINARY_TYPES, **CLIMATE_BINARY_TYPES, + **CAMERA_BINARY_TYPES, **STRUCTURE_BINARY_TYPES} _LOGGER = logging.getLogger(__name__) @@ -105,7 +104,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): add_devices(sensors, True) -class NestBinarySensor(NestSensor, BinarySensorDevice): +class NestBinarySensor(NestSensorDevice, BinarySensorDevice): """Represents a Nest binary sensor.""" @property @@ -113,6 +112,11 @@ class NestBinarySensor(NestSensor, BinarySensorDevice): """Return true if the binary sensor is on.""" return self._state + @property + def device_class(self): + """Return the device class of the binary sensor.""" + return _VALID_BINARY_SENSOR_TYPES.get(self.variable) + def update(self): """Retrieve latest state.""" value = getattr(self.device, self.variable) @@ -133,9 +137,9 @@ class NestActivityZoneSensor(NestBinarySensor): self._name = "{} {} activity".format(self._name, self.zone.name) @property - def name(self): - """Return the name of the nest, if any.""" - return self._name + def device_class(self): + """Return the device class of the binary sensor.""" + return 'motion' def update(self): """Retrieve latest state.""" diff --git a/homeassistant/components/nest.py b/homeassistant/components/nest.py index 365f0593c8d..16a0b80d1fd 100644 --- a/homeassistant/components/nest.py +++ b/homeassistant/components/nest.py @@ -15,7 +15,9 @@ from homeassistant.const import ( CONF_MONITORED_CONDITIONS, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) from homeassistant.helpers import discovery, config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.dispatcher import async_dispatcher_send, \ + async_dispatcher_connect +from homeassistant.helpers.entity import Entity REQUIREMENTS = ['python-nest==4.0.1'] @@ -272,3 +274,54 @@ class NestDevice(object): except socket.error: _LOGGER.error( "Connection error logging into the nest web service.") + + +class NestSensorDevice(Entity): + """Representation of a Nest sensor.""" + + def __init__(self, structure, device, variable): + """Initialize the sensor.""" + self.structure = structure + self.variable = variable + + if device is not None: + # device specific + self.device = device + self._name = "{} {}".format(self.device.name_long, + self.variable.replace('_', ' ')) + else: + # structure only + self.device = structure + self._name = "{} {}".format(self.structure.name, + self.variable.replace('_', ' ')) + + self._state = None + self._unit = None + + @property + def name(self): + """Return the name of the nest, if any.""" + return self._name + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return self._unit + + @property + def should_poll(self): + """Do not need poll thanks using Nest streaming API.""" + return False + + def update(self): + """Do not use NestSensorDevice directly.""" + raise NotImplementedError + + async def async_added_to_hass(self): + """Register update signal handler.""" + async def async_update_state(): + """Update sensor state.""" + await self.async_update_ha_state(True) + + async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, + async_update_state) diff --git a/homeassistant/components/sensor/nest.py b/homeassistant/components/sensor/nest.py index 46a2206a9f7..00d18c7fe10 100644 --- a/homeassistant/components/sensor/nest.py +++ b/homeassistant/components/sensor/nest.py @@ -4,49 +4,44 @@ Support for Nest Thermostat Sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.nest/ """ -from itertools import chain import logging -from homeassistant.components.nest import DATA_NEST, SIGNAL_NEST_UPDATE -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import Entity +from homeassistant.components.nest import DATA_NEST, NestSensorDevice from homeassistant.const import ( TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_MONITORED_CONDITIONS, - DEVICE_CLASS_TEMPERATURE) + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY) DEPENDENCIES = ['nest'] -SENSOR_TYPES = ['humidity', - 'operation_mode', - 'hvac_state'] +SENSOR_TYPES = ['humidity', 'operation_mode', 'hvac_state'] -SENSOR_TYPES_DEPRECATED = ['last_ip', - 'local_ip', - 'last_connection'] +TEMP_SENSOR_TYPES = ['temperature', 'target'] -DEPRECATED_WEATHER_VARS = {'weather_humidity': 'humidity', - 'weather_temperature': 'temperature', - 'weather_condition': 'condition', - 'wind_speed': 'kph', - 'wind_direction': 'direction'} - -SENSOR_UNITS = {'humidity': '%', 'temperature': '°C'} - -PROTECT_VARS = ['co_status', 'smoke_status', 'battery_health'] - -PROTECT_VARS_DEPRECATED = ['battery_level'] - -SENSOR_TEMP_TYPES = ['temperature', 'target'] +PROTECT_SENSOR_TYPES = ['co_status', 'smoke_status', 'battery_health'] STRUCTURE_SENSOR_TYPES = ['eta'] +_VALID_SENSOR_TYPES = SENSOR_TYPES + TEMP_SENSOR_TYPES + PROTECT_SENSOR_TYPES \ + + STRUCTURE_SENSOR_TYPES + +SENSOR_UNITS = {'humidity': '%'} + +SENSOR_DEVICE_CLASSES = {'humidity': DEVICE_CLASS_HUMIDITY} + VARIABLE_NAME_MAPPING = {'eta': 'eta_begin', 'operation_mode': 'mode'} -_SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED \ - + list(DEPRECATED_WEATHER_VARS.keys()) + PROTECT_VARS_DEPRECATED +SENSOR_TYPES_DEPRECATED = ['last_ip', + 'local_ip', + 'last_connection', + 'battery_level'] -_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS \ - + STRUCTURE_SENSOR_TYPES +DEPRECATED_WEATHER_VARS = ['weather_humidity', + 'weather_temperature', + 'weather_condition', + 'wind_speed', + 'wind_direction'] + +_SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED + DEPRECATED_WEATHER_VARS _LOGGER = logging.getLogger(__name__) @@ -76,7 +71,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "monitored_conditions. See " "https://home-assistant.io/components/" "binary_sensor.nest/ for valid options.") - _LOGGER.error(wstr) all_sensors = [] @@ -84,70 +78,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): all_sensors += [NestBasicSensor(structure, None, variable) for variable in conditions if variable in STRUCTURE_SENSOR_TYPES] - for structure, device in chain(nest.thermostats(), nest.smoke_co_alarms()): - sensors = [NestBasicSensor(structure, device, variable) - for variable in conditions - if variable in SENSOR_TYPES and device.is_thermostat] - sensors += [NestTempSensor(structure, device, variable) - for variable in conditions - if variable in SENSOR_TEMP_TYPES and device.is_thermostat] - sensors += [NestProtectSensor(structure, device, variable) - for variable in conditions - if variable in PROTECT_VARS and device.is_smoke_co_alarm] - all_sensors.extend(sensors) + + for structure, device in nest.thermostats(): + all_sensors += [NestBasicSensor(structure, device, variable) + for variable in conditions + if variable in SENSOR_TYPES] + all_sensors += [NestTempSensor(structure, device, variable) + for variable in conditions + if variable in TEMP_SENSOR_TYPES] + + for structure, device in nest.smoke_co_alarms(): + all_sensors += [NestBasicSensor(structure, device, variable) + for variable in conditions + if variable in PROTECT_SENSOR_TYPES] add_devices(all_sensors, True) -class NestSensor(Entity): - """Representation of a Nest sensor.""" - - def __init__(self, structure, device, variable): - """Initialize the sensor.""" - self.structure = structure - self.variable = variable - - if device is not None: - # device specific - self.device = device - self._location = self.device.where - self._name = "{} {}".format(self.device.name_long, - self.variable.replace('_', ' ')) - else: - # structure only - self.device = structure - self._name = "{} {}".format(self.structure.name, - self.variable.replace('_', ' ')) - - self._state = None - self._unit = None - - @property - def name(self): - """Return the name of the nest, if any.""" - return self._name - - @property - def unit_of_measurement(self): - """Return the unit the value is expressed in.""" - return self._unit - - @property - def should_poll(self): - """Do not need poll thanks using Nest streaming API.""" - return False - - async def async_added_to_hass(self): - """Register update signal handler.""" - async def async_update_state(): - """Update sensor state.""" - await self.async_update_ha_state(True) - - async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, - async_update_state) - - -class NestBasicSensor(NestSensor): +class NestBasicSensor(NestSensorDevice): """Representation a basic Nest sensor.""" @property @@ -155,18 +103,26 @@ class NestBasicSensor(NestSensor): """Return the state of the sensor.""" return self._state + @property + def device_class(self): + """Return the device class of the sensor.""" + return SENSOR_DEVICE_CLASSES.get(self.variable) + def update(self): """Retrieve latest state.""" - self._unit = SENSOR_UNITS.get(self.variable, None) + self._unit = SENSOR_UNITS.get(self.variable) if self.variable in VARIABLE_NAME_MAPPING: self._state = getattr(self.device, VARIABLE_NAME_MAPPING[self.variable]) + elif self.variable in PROTECT_SENSOR_TYPES: + # keep backward compatibility + self._state = getattr(self.device, self.variable).capitalize() else: self._state = getattr(self.device, self.variable) -class NestTempSensor(NestSensor): +class NestTempSensor(NestSensorDevice): """Representation of a Nest Temperature sensor.""" @property @@ -195,16 +151,3 @@ class NestTempSensor(NestSensor): self._state = "%s-%s" % (int(low), int(high)) else: self._state = round(temp, 1) - - -class NestProtectSensor(NestSensor): - """Return the state of nest protect.""" - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - def update(self): - """Retrieve latest state.""" - self._state = getattr(self.device, self.variable).capitalize()