Skip to content
Snippets Groups Projects
Commit 12e679c1 authored by Jason Hu's avatar Jason Hu Committed by Martin Hjelmare
Browse files

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
parent 28ef94c3
No related branches found
No related tags found
No related merge requests found
...@@ -7,32 +7,31 @@ https://home-assistant.io/components/binary_sensor.nest/ ...@@ -7,32 +7,31 @@ https://home-assistant.io/components/binary_sensor.nest/
from itertools import chain from itertools import chain
import logging import logging
from homeassistant.components.binary_sensor import (BinarySensorDevice) from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.nest import DATA_NEST from homeassistant.components.nest import DATA_NEST, NestSensorDevice
from homeassistant.components.sensor.nest import NestSensor
from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.const import CONF_MONITORED_CONDITIONS
DEPENDENCIES = ['nest'] DEPENDENCIES = ['nest']
BINARY_TYPES = ['online'] BINARY_TYPES = {'online': 'connectivity'}
CLIMATE_BINARY_TYPES = [ CLIMATE_BINARY_TYPES = {
'fan', 'fan': None,
'is_using_emergency_heat', 'is_using_emergency_heat': 'heat',
'is_locked', 'is_locked': None,
'has_leaf', 'has_leaf': None,
] }
CAMERA_BINARY_TYPES = [ CAMERA_BINARY_TYPES = {
'motion_detected', 'motion_detected': 'motion',
'sound_detected', 'sound_detected': 'sound',
'person_detected', 'person_detected': 'occupancy',
] }
STRUCTURE_BINARY_TYPES = [ STRUCTURE_BINARY_TYPES = {
'away', 'away': None,
# 'security_state', # wait for pending python-nest update # 'security_state', # pending python-nest update
] }
STRUCTURE_BINARY_STATE_MAP = { STRUCTURE_BINARY_STATE_MAP = {
'away': {'away': True, 'home': False}, 'away': {'away': True, 'home': False},
...@@ -50,8 +49,8 @@ _BINARY_TYPES_DEPRECATED = [ ...@@ -50,8 +49,8 @@ _BINARY_TYPES_DEPRECATED = [
'hvac_emer_heat_state', 'hvac_emer_heat_state',
] ]
_VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \ _VALID_BINARY_SENSOR_TYPES = {**BINARY_TYPES, **CLIMATE_BINARY_TYPES,
+ CAMERA_BINARY_TYPES + STRUCTURE_BINARY_TYPES **CAMERA_BINARY_TYPES, **STRUCTURE_BINARY_TYPES}
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
...@@ -105,7 +104,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ...@@ -105,7 +104,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(sensors, True) add_devices(sensors, True)
class NestBinarySensor(NestSensor, BinarySensorDevice): class NestBinarySensor(NestSensorDevice, BinarySensorDevice):
"""Represents a Nest binary sensor.""" """Represents a Nest binary sensor."""
@property @property
...@@ -113,6 +112,11 @@ class NestBinarySensor(NestSensor, BinarySensorDevice): ...@@ -113,6 +112,11 @@ class NestBinarySensor(NestSensor, BinarySensorDevice):
"""Return true if the binary sensor is on.""" """Return true if the binary sensor is on."""
return self._state 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): def update(self):
"""Retrieve latest state.""" """Retrieve latest state."""
value = getattr(self.device, self.variable) value = getattr(self.device, self.variable)
...@@ -133,9 +137,9 @@ class NestActivityZoneSensor(NestBinarySensor): ...@@ -133,9 +137,9 @@ class NestActivityZoneSensor(NestBinarySensor):
self._name = "{} {} activity".format(self._name, self.zone.name) self._name = "{} {} activity".format(self._name, self.zone.name)
@property @property
def name(self): def device_class(self):
"""Return the name of the nest, if any.""" """Return the device class of the binary sensor."""
return self._name return 'motion'
def update(self): def update(self):
"""Retrieve latest state.""" """Retrieve latest state."""
......
...@@ -15,7 +15,9 @@ from homeassistant.const import ( ...@@ -15,7 +15,9 @@ from homeassistant.const import (
CONF_MONITORED_CONDITIONS, CONF_MONITORED_CONDITIONS,
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP) EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery, config_validation as cv 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'] REQUIREMENTS = ['python-nest==4.0.1']
...@@ -272,3 +274,54 @@ class NestDevice(object): ...@@ -272,3 +274,54 @@ class NestDevice(object):
except socket.error: except socket.error:
_LOGGER.error( _LOGGER.error(
"Connection error logging into the nest web service.") "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)
...@@ -4,49 +4,44 @@ Support for Nest Thermostat Sensors. ...@@ -4,49 +4,44 @@ Support for Nest Thermostat Sensors.
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/sensor.nest/ https://home-assistant.io/components/sensor.nest/
""" """
from itertools import chain
import logging import logging
from homeassistant.components.nest import DATA_NEST, SIGNAL_NEST_UPDATE from homeassistant.components.nest import DATA_NEST, NestSensorDevice
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import (
TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_MONITORED_CONDITIONS, TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_MONITORED_CONDITIONS,
DEVICE_CLASS_TEMPERATURE) DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY)
DEPENDENCIES = ['nest'] DEPENDENCIES = ['nest']
SENSOR_TYPES = ['humidity', SENSOR_TYPES = ['humidity', 'operation_mode', 'hvac_state']
'operation_mode',
'hvac_state']
SENSOR_TYPES_DEPRECATED = ['last_ip', TEMP_SENSOR_TYPES = ['temperature', 'target']
'local_ip',
'last_connection']
DEPRECATED_WEATHER_VARS = {'weather_humidity': 'humidity',
'weather_temperature': 'temperature',
'weather_condition': 'condition',
'wind_speed': 'kph',
'wind_direction': 'direction'}
SENSOR_UNITS = {'humidity': '%', 'temperature': '°C'} PROTECT_SENSOR_TYPES = ['co_status', 'smoke_status', 'battery_health']
PROTECT_VARS = ['co_status', 'smoke_status', 'battery_health'] STRUCTURE_SENSOR_TYPES = ['eta']
PROTECT_VARS_DEPRECATED = ['battery_level'] _VALID_SENSOR_TYPES = SENSOR_TYPES + TEMP_SENSOR_TYPES + PROTECT_SENSOR_TYPES \
+ STRUCTURE_SENSOR_TYPES
SENSOR_TEMP_TYPES = ['temperature', 'target'] SENSOR_UNITS = {'humidity': '%'}
STRUCTURE_SENSOR_TYPES = ['eta'] SENSOR_DEVICE_CLASSES = {'humidity': DEVICE_CLASS_HUMIDITY}
VARIABLE_NAME_MAPPING = {'eta': 'eta_begin', 'operation_mode': 'mode'} VARIABLE_NAME_MAPPING = {'eta': 'eta_begin', 'operation_mode': 'mode'}
_SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED \ SENSOR_TYPES_DEPRECATED = ['last_ip',
+ list(DEPRECATED_WEATHER_VARS.keys()) + PROTECT_VARS_DEPRECATED 'local_ip',
'last_connection',
'battery_level']
DEPRECATED_WEATHER_VARS = ['weather_humidity',
'weather_temperature',
'weather_condition',
'wind_speed',
'wind_direction']
_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS \ _SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED + DEPRECATED_WEATHER_VARS
+ STRUCTURE_SENSOR_TYPES
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
...@@ -76,7 +71,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ...@@ -76,7 +71,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"monitored_conditions. See " "monitored_conditions. See "
"https://home-assistant.io/components/" "https://home-assistant.io/components/"
"binary_sensor.nest/ for valid options.") "binary_sensor.nest/ for valid options.")
_LOGGER.error(wstr) _LOGGER.error(wstr)
all_sensors = [] all_sensors = []
...@@ -84,70 +78,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None): ...@@ -84,70 +78,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
all_sensors += [NestBasicSensor(structure, None, variable) all_sensors += [NestBasicSensor(structure, None, variable)
for variable in conditions for variable in conditions
if variable in STRUCTURE_SENSOR_TYPES] 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)
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 for structure, device in nest.thermostats():
def should_poll(self): all_sensors += [NestBasicSensor(structure, device, variable)
"""Do not need poll thanks using Nest streaming API.""" for variable in conditions
return False if variable in SENSOR_TYPES]
all_sensors += [NestTempSensor(structure, device, variable)
for variable in conditions
if variable in TEMP_SENSOR_TYPES]
async def async_added_to_hass(self): for structure, device in nest.smoke_co_alarms():
"""Register update signal handler.""" all_sensors += [NestBasicSensor(structure, device, variable)
async def async_update_state(): for variable in conditions
"""Update sensor state.""" if variable in PROTECT_SENSOR_TYPES]
await self.async_update_ha_state(True)
async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, add_devices(all_sensors, True)
async_update_state)
class NestBasicSensor(NestSensor): class NestBasicSensor(NestSensorDevice):
"""Representation a basic Nest sensor.""" """Representation a basic Nest sensor."""
@property @property
...@@ -155,18 +103,26 @@ class NestBasicSensor(NestSensor): ...@@ -155,18 +103,26 @@ class NestBasicSensor(NestSensor):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self._state 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): def update(self):
"""Retrieve latest state.""" """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: if self.variable in VARIABLE_NAME_MAPPING:
self._state = getattr(self.device, self._state = getattr(self.device,
VARIABLE_NAME_MAPPING[self.variable]) VARIABLE_NAME_MAPPING[self.variable])
elif self.variable in PROTECT_SENSOR_TYPES:
# keep backward compatibility
self._state = getattr(self.device, self.variable).capitalize()
else: else:
self._state = getattr(self.device, self.variable) self._state = getattr(self.device, self.variable)
class NestTempSensor(NestSensor): class NestTempSensor(NestSensorDevice):
"""Representation of a Nest Temperature sensor.""" """Representation of a Nest Temperature sensor."""
@property @property
...@@ -195,16 +151,3 @@ class NestTempSensor(NestSensor): ...@@ -195,16 +151,3 @@ class NestTempSensor(NestSensor):
self._state = "%s-%s" % (int(low), int(high)) self._state = "%s-%s" % (int(low), int(high))
else: else:
self._state = round(temp, 1) 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()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment