Nest further improvements (#4655)

* Further improvements on nest platform

- fix binary sensor
- add deprecations for monitored_conditions
- better names for sensors (includes device type)

* lint

* Remove unused weather sensor

* Fix to python-nest to a specific commit

* lint

* lint

* lint

* lint
This commit is contained in:
Josh Nichols 2016-12-03 12:26:47 -05:00 committed by Paulus Schoutsen
parent af7de8d5ae
commit 64a5bff5b2
4 changed files with 143 additions and 56 deletions

View File

@ -4,17 +4,34 @@ Support for Nest Thermostat Binary 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/binary_sensor.nest/ https://home-assistant.io/components/binary_sensor.nest/
""" """
from itertools import chain
import logging
import voluptuous as vol import voluptuous as vol
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA) BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.sensor.nest import NestSensor from homeassistant.components.sensor.nest import NestSensor
from homeassistant.const import (CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS) from homeassistant.const import (CONF_SCAN_INTERVAL, CONF_MONITORED_CONDITIONS)
from homeassistant.components.nest import DATA_NEST from homeassistant.components.nest import (
DATA_NEST, is_thermostat, is_camera)
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
DEPENDENCIES = ['nest'] DEPENDENCIES = ['nest']
BINARY_TYPES = ['fan',
BINARY_TYPES = ['online']
CLIMATE_BINARY_TYPES = ['fan',
'is_using_emergency_heat',
'is_locked',
'has_leaf']
CAMERA_BINARY_TYPES = [
'motion_detected',
'sound_detected',
'person_detected']
_BINARY_TYPES_DEPRECATED = [
'hvac_ac_state', 'hvac_ac_state',
'hvac_aux_heater_state', 'hvac_aux_heater_state',
'hvac_heater_state', 'hvac_heater_state',
@ -22,28 +39,62 @@ BINARY_TYPES = ['fan',
'hvac_heat_x3_state', 'hvac_heat_x3_state',
'hvac_alt_heat_state', 'hvac_alt_heat_state',
'hvac_alt_heat_x2_state', 'hvac_alt_heat_x2_state',
'hvac_emer_heat_state', 'hvac_emer_heat_state']
'online']
_VALID_BINARY_SENSOR_TYPES = BINARY_TYPES + CLIMATE_BINARY_TYPES \
+ CAMERA_BINARY_TYPES
_VALID_BINARY_SENSOR_TYPES_WITH_DEPRECATED = _VALID_BINARY_SENSOR_TYPES \
+ _BINARY_TYPES_DEPRECATED
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SCAN_INTERVAL): vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)), vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS): vol.Required(CONF_MONITORED_CONDITIONS):
vol.All(cv.ensure_list, [vol.In(BINARY_TYPES)]), vol.All(cv.ensure_list,
[vol.In(_VALID_BINARY_SENSOR_TYPES_WITH_DEPRECATED)])
}) })
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Nest binary sensors.""" """Setup Nest binary sensors."""
nest = hass.data[DATA_NEST] nest = hass.data[DATA_NEST]
conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_BINARY_SENSOR_TYPES)
all_sensors = [] for variable in conf:
for structure, device in nest.devices(): if variable in _BINARY_TYPES_DEPRECATED:
all_sensors.extend( wstr = (variable + " is no a longer supported "
[NestBinarySensor(structure, device, variable) "monitored_conditions. See "
for variable in config[CONF_MONITORED_CONDITIONS]]) "https://home-assistant.io/components/binary_sensor.nest/ "
"for valid options, or remove monitored_conditions "
"entirely to get a reasonable default")
_LOGGER.error(wstr)
add_devices(all_sensors, True) sensors = []
device_chain = chain(nest.devices(),
nest.protect_devices(),
nest.camera_devices())
for structure, device in device_chain:
sensors += [NestBinarySensor(structure, device, variable)
for variable in conf
if variable in BINARY_TYPES]
sensors += [NestBinarySensor(structure, device, variable)
for variable in conf
if variable in CLIMATE_BINARY_TYPES
and is_thermostat(device)]
if is_camera(device):
sensors += [NestBinarySensor(structure, device, variable)
for variable in conf
if variable in CAMERA_BINARY_TYPES]
for activity_zone in device.activity_zones:
sensors += [NestActivityZoneSensor(structure,
device,
activity_zone)]
add_devices(sensors, True)
class NestBinarySensor(NestSensor, BinarySensorDevice): class NestBinarySensor(NestSensor, BinarySensorDevice):
@ -57,3 +108,21 @@ class NestBinarySensor(NestSensor, BinarySensorDevice):
def update(self): def update(self):
"""Retrieve latest state.""" """Retrieve latest state."""
self._state = bool(getattr(self.device, self.variable)) self._state = bool(getattr(self.device, self.variable))
class NestActivityZoneSensor(NestBinarySensor):
"""Represents a Nest binary sensor for activity in a zone."""
def __init__(self, structure, device, zone):
"""Initialize the sensor."""
super(NestActivityZoneSensor, self).__init__(structure, device, None)
self.zone = zone
@property
def name(self):
"""Return the name of the nest, if any."""
return "{} {} activity".format(self._name, self.zone.name)
def update(self):
"""Retrieve latest state."""
self._state = self.device.has_ongoing_motion_in_zone(self.zone.zone_id)

View File

@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = [ REQUIREMENTS = [
'git+https://github.com/technicalpickles/python-nest.git' 'git+https://github.com/technicalpickles/python-nest.git'
'@nest-cam' '@0be5c8a6307ee81540f21aac4fcd22cc5d98c988' # nest-cam branch
'#python-nest==3.0.0'] '#python-nest==3.0.0']
DOMAIN = 'nest' DOMAIN = 'nest'
@ -89,6 +89,7 @@ def setup_nest(hass, nest, config, pin=None):
_LOGGER.debug("proceeding with discovery") _LOGGER.debug("proceeding with discovery")
discovery.load_platform(hass, 'climate', DOMAIN, {}, config) discovery.load_platform(hass, 'climate', DOMAIN, {}, config)
discovery.load_platform(hass, 'sensor', DOMAIN, {}, config) discovery.load_platform(hass, 'sensor', DOMAIN, {}, config)
discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config)
discovery.load_platform(hass, 'camera', DOMAIN, {}, config) discovery.load_platform(hass, 'camera', DOMAIN, {}, config)
_LOGGER.debug("setup done") _LOGGER.debug("setup done")
@ -172,3 +173,18 @@ 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.")
def is_thermostat(device):
"""Target devices that are Nest Thermostats."""
return bool(device.__class__.__name__ == 'Device')
def is_protect(device):
"""Target devices that are Nest Protect Smoke Alarms."""
return bool(device.__class__.__name__ == 'ProtectDevice')
def is_camera(device):
"""Target devices that are Nest Protect Smoke Alarms."""
return bool(device.__class__.__name__ == 'CameraDevice')

View File

@ -5,6 +5,7 @@ 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 from itertools import chain
import logging
import voluptuous as vol import voluptuous as vol
@ -17,11 +18,13 @@ from homeassistant.const import (
DEPENDENCIES = ['nest'] DEPENDENCIES = ['nest']
SENSOR_TYPES = ['humidity', SENSOR_TYPES = ['humidity',
'operation_mode', 'operation_mode']
SENSOR_TYPES_DEPRECATED = ['last_ip',
'local_ip',
'last_connection'] 'last_connection']
SENSOR_TYPES_DEPRECATED = ['battery_health', SENSOR_TYPES_DEPRECATED = ['last_ip',
'last_ip',
'local_ip'] 'local_ip']
WEATHER_VARS = {} WEATHER_VARS = {}
@ -43,22 +46,48 @@ PROTECT_VARS_DEPRECATED = ['battery_level']
SENSOR_TEMP_TYPES = ['temperature', 'target'] SENSOR_TEMP_TYPES = ['temperature', 'target']
_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS + \ _SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED \
list(WEATHER_VARS.keys()) + list(DEPRECATED_WEATHER_VARS.keys()) + PROTECT_VARS_DEPRECATED
_VALID_SENSOR_TYPES = SENSOR_TYPES + SENSOR_TEMP_TYPES + PROTECT_VARS \
+ list(WEATHER_VARS.keys())
_VALID_SENSOR_TYPES_WITH_DEPRECATED = _VALID_SENSOR_TYPES \
+ _SENSOR_TYPES_DEPRECATED
PLATFORM_SCHEMA = vol.Schema({ PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): DOMAIN, vol.Required(CONF_PLATFORM): DOMAIN,
vol.Optional(CONF_SCAN_INTERVAL): vol.Optional(CONF_SCAN_INTERVAL):
vol.All(vol.Coerce(int), vol.Range(min=1)), vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Required(CONF_MONITORED_CONDITIONS): [vol.In(_VALID_SENSOR_TYPES)], vol.Required(CONF_MONITORED_CONDITIONS):
[vol.In(_VALID_SENSOR_TYPES_WITH_DEPRECATED)]
}) })
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Nest Sensor.""" """Setup the Nest Sensor."""
nest = hass.data[DATA_NEST] nest = hass.data[DATA_NEST]
conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_SENSOR_TYPES) conf = config.get(CONF_MONITORED_CONDITIONS, _VALID_SENSOR_TYPES)
for variable in conf:
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, or remove monitored_conditions "
"entirely to get a reasonable default")
_LOGGER.error(wstr)
all_sensors = [] all_sensors = []
for structure, device in chain(nest.devices(), nest.protect_devices()): for structure, device in chain(nest.devices(), nest.protect_devices()):
sensors = [NestBasicSensor(structure, device, variable) sensors = [NestBasicSensor(structure, device, variable)
@ -67,10 +96,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
sensors += [NestTempSensor(structure, device, variable) sensors += [NestTempSensor(structure, device, variable)
for variable in conf for variable in conf
if variable in SENSOR_TEMP_TYPES and is_thermostat(device)] if variable in SENSOR_TEMP_TYPES and is_thermostat(device)]
sensors += [NestWeatherSensor(structure, device,
WEATHER_VARS[variable])
for variable in conf
if variable in WEATHER_VARS and is_thermostat(device)]
sensors += [NestProtectSensor(structure, device, variable) sensors += [NestProtectSensor(structure, device, variable)
for variable in conf for variable in conf
if variable in PROTECT_VARS and is_protect(device)] if variable in PROTECT_VARS and is_protect(device)]
@ -100,13 +125,13 @@ class NestSensor(Entity):
# device specific # device specific
self._location = self.device.where self._location = self.device.where
self._name = self.device.name self._name = self.device.name_long
self._state = None self._state = None
@property @property
def name(self): def name(self):
"""Return the name of the nest, if any.""" """Return the name of the nest, if any."""
return "{} {}".format(self._name, self.variable) return "{} {}".format(self._name, self.variable.replace("_", " "))
class NestBasicSensor(NestSensor): class NestBasicSensor(NestSensor):
@ -159,29 +184,6 @@ class NestTempSensor(NestSensor):
self._state = round(temp, 1) self._state = round(temp, 1)
class NestWeatherSensor(NestSensor):
"""Representation a basic Nest Weather Conditions sensor."""
@property
def state(self):
"""Return the state of the sensor."""
return self._state
def update(self):
"""Retrieve latest state."""
if self.variable == 'kph' or self.variable == 'direction':
self._state = getattr(self.structure.weather.current.wind,
self.variable)
else:
self._state = getattr(self.structure.weather.current,
self.variable)
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return SENSOR_UNITS.get(self.variable, None)
class NestProtectSensor(NestSensor): class NestProtectSensor(NestSensor):
"""Return the state of nest protect.""" """Return the state of nest protect."""

View File

@ -131,7 +131,7 @@ fuzzywuzzy==0.14.0
# gattlib==0.20150805 # gattlib==0.20150805
# homeassistant.components.nest # homeassistant.components.nest
git+https://github.com/technicalpickles/python-nest.git@nest-cam#python-nest==3.0.0 git+https://github.com/technicalpickles/python-nest.git@0be5c8a6307ee81540f21aac4fcd22cc5d98c988#python-nest==3.0.0
# homeassistant.components.notify.gntp # homeassistant.components.notify.gntp
gntp==1.0.3 gntp==1.0.3