hass-core/homeassistant/components/sensor/nest.py
Jason Hu cba8333a13 Change nest to cloud push (#14656)
* Change nest component to Cloud Push

Change sensors.nest, binary_sensors.nest and climate.nest to push mode
nest camera still need poll to update snapshot image
Also change nest component to async

* Flake8 lint

* Fix async_notify_errors, it is not a coroutine

* Fix pylint

* Fix pylint, function name should shall shorter than 32

* Use dispatcher helper instead event bus

* Use async_update_ha_state(True)

* Refactoring load_platform

Move service registration into async_setup_nest(),
 resolve an issue that before the first time configuration done,
 set_mode service should not be registered

* Fix an issue that authorization failure may leave a blocked thread

* Pylinting

* async_nest_update_callback => async_update_state to avoid confusion

* Move signal handler register to async_added_to_hass

* Better handle nest api error

* Remove unnecessary register for binary_sensor

* Remove unused import

* Upgrade to python-nest 4.0.1

Fix a thread race condition issue

* Address my own comments

* Address my own comment
2018-06-01 10:44:58 -04:00

210 lines
6.7 KiB
Python

"""
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.const import (
TEMP_CELSIUS, TEMP_FAHRENHEIT, CONF_MONITORED_CONDITIONS,
DEVICE_CLASS_TEMPERATURE)
DEPENDENCIES = ['nest']
SENSOR_TYPES = ['humidity',
'operation_mode',
'hvac_state']
SENSOR_TYPES_DEPRECATED = ['last_ip',
'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_VARS = ['co_status', 'smoke_status', 'battery_health']
PROTECT_VARS_DEPRECATED = ['battery_level']
SENSOR_TEMP_TYPES = ['temperature', 'target']
STRUCTURE_SENSOR_TYPES = ['eta']
VARIABLE_NAME_MAPPING = {'eta': 'eta_begin', 'operation_mode': 'mode'}
_SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED \
+ list(DEPRECATED_WEATHER_VARS.keys()) + PROTECT_VARS_DEPRECATED
_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS \
+ STRUCTURE_SENSOR_TYPES
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Nest Sensor."""
if discovery_info is None:
return
nest = hass.data[DATA_NEST]
# Add all available sensors if no Nest sensor config is set
if discovery_info == {}:
conditions = _VALID_SENSOR_TYPES
else:
conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {})
for variable in conditions:
if variable in _SENSOR_TYPES_DEPRECATED:
if variable in DEPRECATED_WEATHER_VARS:
wstr = ("Nest no longer provides weather data like %s. See "
"https://home-assistant.io/components/#weather "
"for a list of other weather components to use." %
variable)
else:
wstr = (variable + " is no a longer supported "
"monitored_conditions. See "
"https://home-assistant.io/components/"
"binary_sensor.nest/ for valid options.")
_LOGGER.error(wstr)
all_sensors = []
for structure in nest.structures():
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)
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):
"""Representation a basic Nest sensor."""
@property
def state(self):
"""Return the state of the sensor."""
return self._state
def update(self):
"""Retrieve latest state."""
self._unit = SENSOR_UNITS.get(self.variable, None)
if self.variable in VARIABLE_NAME_MAPPING:
self._state = getattr(self.device,
VARIABLE_NAME_MAPPING[self.variable])
else:
self._state = getattr(self.device, self.variable)
class NestTempSensor(NestSensor):
"""Representation of a Nest Temperature sensor."""
@property
def state(self):
"""Return the state of the sensor."""
return self._state
@property
def device_class(self):
"""Return the device class of the sensor."""
return DEVICE_CLASS_TEMPERATURE
def update(self):
"""Retrieve latest state."""
if self.device.temperature_scale == 'C':
self._unit = TEMP_CELSIUS
else:
self._unit = TEMP_FAHRENHEIT
temp = getattr(self.device, self.variable)
if temp is None:
self._state = None
if isinstance(temp, tuple):
low, high = temp
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()