Add temperature sensor support to google smarthome thermostat device (#24264)

* Add temperature sensor support to google smarthome thermostat device

* fix lint for trait_test

* Reset temperature unit in tests

* Address comment
This commit is contained in:
Paul Bottein 2019-06-03 21:40:16 +02:00 committed by Paulus Schoutsen
parent 9ed5b70d01
commit 5085ce8ab1
3 changed files with 133 additions and 60 deletions

View File

@ -12,6 +12,7 @@ from homeassistant.components import (
media_player, media_player,
scene, scene,
script, script,
sensor,
switch, switch,
vacuum, vacuum,
) )
@ -108,6 +109,7 @@ DEVICE_CLASS_TO_GOOGLE_TYPES = {
(binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_WINDOW): TYPE_SENSOR, (binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_WINDOW): TYPE_SENSOR,
(media_player.DOMAIN, media_player.DEVICE_CLASS_TV): TYPE_TV, (media_player.DOMAIN, media_player.DEVICE_CLASS_TV): TYPE_TV,
(media_player.DOMAIN, media_player.DEVICE_CLASS_SPEAKER): TYPE_SPEAKER, (media_player.DOMAIN, media_player.DEVICE_CLASS_SPEAKER): TYPE_SPEAKER,
(sensor.DOMAIN, sensor.DEVICE_CLASS_TEMPERATURE): TYPE_SENSOR,
} }
CHALLENGE_ACK_NEEDED = 'ackNeeded' CHALLENGE_ACK_NEEDED = 'ackNeeded'

View File

@ -13,6 +13,7 @@ from homeassistant.components import (
lock, lock,
scene, scene,
script, script,
sensor,
switch, switch,
vacuum, vacuum,
) )
@ -550,56 +551,81 @@ class TemperatureSettingTrait(_Trait):
@staticmethod @staticmethod
def supported(domain, features, device_class): def supported(domain, features, device_class):
"""Test if state is supported.""" """Test if state is supported."""
if domain != climate.DOMAIN: if domain == climate.DOMAIN:
return False
return features & climate.SUPPORT_OPERATION_MODE return features & climate.SUPPORT_OPERATION_MODE
return (domain == sensor.DOMAIN
and device_class == sensor.DEVICE_CLASS_TEMPERATURE)
def sync_attributes(self): def sync_attributes(self):
"""Return temperature point and modes attributes for a sync request.""" """Return temperature point and modes attributes for a sync request."""
response = {}
attrs = self.state.attributes
domain = self.state.domain
response['thermostatTemperatureUnit'] = _google_temp_unit(
self.hass.config.units.temperature_unit)
if domain == sensor.DOMAIN:
device_class = attrs.get(ATTR_DEVICE_CLASS)
if device_class == sensor.DEVICE_CLASS_TEMPERATURE:
response["queryOnlyTemperatureSetting"] = True
elif domain == climate.DOMAIN:
modes = [] modes = []
supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) supported = attrs.get(ATTR_SUPPORTED_FEATURES)
if supported & climate.SUPPORT_ON_OFF != 0: if supported & climate.SUPPORT_ON_OFF != 0:
modes.append(STATE_OFF) modes.append(STATE_OFF)
modes.append(STATE_ON) modes.append(STATE_ON)
if supported & climate.SUPPORT_OPERATION_MODE != 0: if supported & climate.SUPPORT_OPERATION_MODE != 0:
for mode in self.state.attributes.get(climate.ATTR_OPERATION_LIST, for mode in attrs.get(climate.ATTR_OPERATION_LIST, []):
[]):
google_mode = self.hass_to_google.get(mode) google_mode = self.hass_to_google.get(mode)
if google_mode and google_mode not in modes: if google_mode and google_mode not in modes:
modes.append(google_mode) modes.append(google_mode)
response['availableThermostatModes'] = ','.join(modes)
return { return response
'availableThermostatModes': ','.join(modes),
'thermostatTemperatureUnit': _google_temp_unit(
self.hass.config.units.temperature_unit)
}
def query_attributes(self): def query_attributes(self):
"""Return temperature point and modes query attributes.""" """Return temperature point and modes query attributes."""
attrs = self.state.attributes
response = {} response = {}
attrs = self.state.attributes
domain = self.state.domain
unit = self.hass.config.units.temperature_unit
if domain == sensor.DOMAIN:
device_class = attrs.get(ATTR_DEVICE_CLASS)
if device_class == sensor.DEVICE_CLASS_TEMPERATURE:
current_temp = self.state.state
if current_temp is not None:
response['thermostatTemperatureAmbient'] = \
round(temp_util.convert(
float(current_temp),
unit,
TEMP_CELSIUS
), 1)
elif domain == climate.DOMAIN:
operation = attrs.get(climate.ATTR_OPERATION_MODE) operation = attrs.get(climate.ATTR_OPERATION_MODE)
supported = self.state.attributes.get(ATTR_SUPPORTED_FEATURES) supported = attrs.get(ATTR_SUPPORTED_FEATURES)
if (supported & climate.SUPPORT_ON_OFF if (supported & climate.SUPPORT_ON_OFF
and self.state.state == STATE_OFF): and self.state.state == STATE_OFF):
response['thermostatMode'] = 'off' response['thermostatMode'] = 'off'
elif (supported & climate.SUPPORT_OPERATION_MODE and elif (supported & climate.SUPPORT_OPERATION_MODE
operation in self.hass_to_google): and operation in self.hass_to_google):
response['thermostatMode'] = self.hass_to_google[operation] response['thermostatMode'] = self.hass_to_google[operation]
elif supported & climate.SUPPORT_ON_OFF: elif supported & climate.SUPPORT_ON_OFF:
response['thermostatMode'] = 'on' response['thermostatMode'] = 'on'
unit = self.hass.config.units.temperature_unit
current_temp = attrs.get(climate.ATTR_CURRENT_TEMPERATURE) current_temp = attrs.get(climate.ATTR_CURRENT_TEMPERATURE)
if current_temp is not None: if current_temp is not None:
response['thermostatTemperatureAmbient'] = \ response['thermostatTemperatureAmbient'] = \
round(temp_util.convert(current_temp, unit, TEMP_CELSIUS), 1) round(temp_util.convert(
current_temp,
unit,
TEMP_CELSIUS
), 1)
current_humidity = attrs.get(climate.ATTR_CURRENT_HUMIDITY) current_humidity = attrs.get(climate.ATTR_CURRENT_HUMIDITY)
if current_humidity is not None: if current_humidity is not None:
@ -620,9 +646,15 @@ class TemperatureSettingTrait(_Trait):
target_temp = attrs.get(ATTR_TEMPERATURE) target_temp = attrs.get(ATTR_TEMPERATURE)
if target_temp is not None: if target_temp is not None:
target_temp = round( target_temp = round(
temp_util.convert(target_temp, unit, TEMP_CELSIUS), 1) temp_util.convert(
response['thermostatTemperatureSetpointHigh'] = target_temp target_temp,
response['thermostatTemperatureSetpointLow'] = target_temp unit,
TEMP_CELSIUS
), 1)
response['thermostatTemperatureSetpointHigh'] = \
target_temp
response['thermostatTemperatureSetpointLow'] = \
target_temp
else: else:
target_temp = attrs.get(ATTR_TEMPERATURE) target_temp = attrs.get(ATTR_TEMPERATURE)
if target_temp is not None: if target_temp is not None:
@ -633,6 +665,12 @@ class TemperatureSettingTrait(_Trait):
async def execute(self, command, data, params, challenge): async def execute(self, command, data, params, challenge):
"""Execute a temperature point or mode command.""" """Execute a temperature point or mode command."""
domain = self.state.domain
if domain == sensor.DOMAIN:
raise SmartHomeError(
ERR_NOT_SUPPORTED,
'Execute is not supported by sensor')
# All sent in temperatures are always in Celsius # All sent in temperatures are always in Celsius
unit = self.hass.config.units.temperature_unit unit = self.hass.config.units.temperature_unit
min_temp = self.state.attributes[climate.ATTR_MIN_TEMP] min_temp = self.state.attributes[climate.ATTR_MIN_TEMP]
@ -687,8 +725,8 @@ class TemperatureSettingTrait(_Trait):
ATTR_ENTITY_ID: self.state.entity_id, ATTR_ENTITY_ID: self.state.entity_id,
} }
if(supported & climate.SUPPORT_TARGET_TEMPERATURE_HIGH and if(supported & climate.SUPPORT_TARGET_TEMPERATURE_HIGH
supported & climate.SUPPORT_TARGET_TEMPERATURE_LOW): and supported & climate.SUPPORT_TARGET_TEMPERATURE_LOW):
svc_data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high svc_data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high
svc_data[climate.ATTR_TARGET_TEMP_LOW] = temp_low svc_data[climate.ATTR_TARGET_TEMP_LOW] = temp_low
else: else:

View File

@ -14,6 +14,7 @@ from homeassistant.components import (
media_player, media_player,
scene, scene,
script, script,
sensor,
switch, switch,
vacuum, vacuum,
group, group,
@ -1380,3 +1381,35 @@ async def test_volume_media_player_relative(hass):
ATTR_ENTITY_ID: 'media_player.bla', ATTR_ENTITY_ID: 'media_player.bla',
media_player.ATTR_MEDIA_VOLUME_LEVEL: .5 media_player.ATTR_MEDIA_VOLUME_LEVEL: .5
} }
async def test_temperature_setting_sensor(hass):
"""Test TemperatureSetting trait support for temperature sensor."""
assert helpers.get_google_type(sensor.DOMAIN,
sensor.DEVICE_CLASS_TEMPERATURE) is not None
assert not trait.TemperatureSettingTrait.supported(
sensor.DOMAIN,
0,
sensor.DEVICE_CLASS_HUMIDITY
)
assert trait.TemperatureSettingTrait.supported(
sensor.DOMAIN,
0,
sensor.DEVICE_CLASS_TEMPERATURE
)
hass.config.units.temperature_unit = TEMP_FAHRENHEIT
trt = trait.TemperatureSettingTrait(hass, State('sensor.test', "70", {
ATTR_DEVICE_CLASS: sensor.DEVICE_CLASS_TEMPERATURE,
}), BASIC_CONFIG)
assert trt.sync_attributes() == {
'queryOnlyTemperatureSetting': True,
'thermostatTemperatureUnit': 'F',
}
assert trt.query_attributes() == {
'thermostatTemperatureAmbient': 21.1
}
hass.config.units.temperature_unit = TEMP_CELSIUS