mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Use google assistant TemperatureControl trait to report sensor (#46491)
* CHG: use TemperatureControl trait to report sensor * fixup: blacked * fixup: flaked * fixup: flaked * Adjust tests * fixup test and rebase * test coverage
This commit is contained in:
parent
7f6572893d
commit
591d09c177
@ -88,6 +88,7 @@ TRAIT_BRIGHTNESS = f"{PREFIX_TRAITS}Brightness"
|
|||||||
TRAIT_COLOR_SETTING = f"{PREFIX_TRAITS}ColorSetting"
|
TRAIT_COLOR_SETTING = f"{PREFIX_TRAITS}ColorSetting"
|
||||||
TRAIT_SCENE = f"{PREFIX_TRAITS}Scene"
|
TRAIT_SCENE = f"{PREFIX_TRAITS}Scene"
|
||||||
TRAIT_TEMPERATURE_SETTING = f"{PREFIX_TRAITS}TemperatureSetting"
|
TRAIT_TEMPERATURE_SETTING = f"{PREFIX_TRAITS}TemperatureSetting"
|
||||||
|
TRAIT_TEMPERATURE_CONTROL = f"{PREFIX_TRAITS}TemperatureControl"
|
||||||
TRAIT_LOCKUNLOCK = f"{PREFIX_TRAITS}LockUnlock"
|
TRAIT_LOCKUNLOCK = f"{PREFIX_TRAITS}LockUnlock"
|
||||||
TRAIT_FANSPEED = f"{PREFIX_TRAITS}FanSpeed"
|
TRAIT_FANSPEED = f"{PREFIX_TRAITS}FanSpeed"
|
||||||
TRAIT_MODES = f"{PREFIX_TRAITS}Modes"
|
TRAIT_MODES = f"{PREFIX_TRAITS}Modes"
|
||||||
@ -683,6 +684,52 @@ class StartStopTrait(_Trait):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register_trait
|
||||||
|
class TemperatureControlTrait(_Trait):
|
||||||
|
"""Trait for devices (other than thermostats) that support controlling temperature. Workaround for Temperature sensors.
|
||||||
|
|
||||||
|
https://developers.google.com/assistant/smarthome/traits/temperaturecontrol
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = TRAIT_TEMPERATURE_CONTROL
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def supported(domain, features, device_class, _):
|
||||||
|
"""Test if state is supported."""
|
||||||
|
return (
|
||||||
|
domain == sensor.DOMAIN and device_class == sensor.DEVICE_CLASS_TEMPERATURE
|
||||||
|
)
|
||||||
|
|
||||||
|
def sync_attributes(self):
|
||||||
|
"""Return temperature attributes for a sync request."""
|
||||||
|
return {
|
||||||
|
"temperatureUnitForUX": _google_temp_unit(
|
||||||
|
self.hass.config.units.temperature_unit
|
||||||
|
),
|
||||||
|
"queryOnlyTemperatureSetting": True,
|
||||||
|
"temperatureRange": {
|
||||||
|
"minThresholdCelsius": -100,
|
||||||
|
"maxThresholdCelsius": 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def query_attributes(self):
|
||||||
|
"""Return temperature states."""
|
||||||
|
response = {}
|
||||||
|
unit = self.hass.config.units.temperature_unit
|
||||||
|
current_temp = self.state.state
|
||||||
|
if current_temp not in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
||||||
|
temp = round(temp_util.convert(float(current_temp), unit, TEMP_CELSIUS), 1)
|
||||||
|
response["temperatureSetpointCelsius"] = temp
|
||||||
|
response["temperatureAmbientCelsius"] = temp
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def execute(self, command, data, params, challenge):
|
||||||
|
"""Unsupported."""
|
||||||
|
raise SmartHomeError(ERR_NOT_SUPPORTED, "Execute is not supported by sensor")
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
class TemperatureSettingTrait(_Trait):
|
class TemperatureSettingTrait(_Trait):
|
||||||
"""Trait to offer handling both temperature point and modes functionality.
|
"""Trait to offer handling both temperature point and modes functionality.
|
||||||
@ -715,12 +762,7 @@ 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:
|
return domain == climate.DOMAIN
|
||||||
return True
|
|
||||||
|
|
||||||
return (
|
|
||||||
domain == sensor.DOMAIN and device_class == sensor.DEVICE_CLASS_TEMPERATURE
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def climate_google_modes(self):
|
def climate_google_modes(self):
|
||||||
@ -743,18 +785,10 @@ class TemperatureSettingTrait(_Trait):
|
|||||||
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 = {}
|
response = {}
|
||||||
attrs = self.state.attributes
|
|
||||||
domain = self.state.domain
|
|
||||||
response["thermostatTemperatureUnit"] = _google_temp_unit(
|
response["thermostatTemperatureUnit"] = _google_temp_unit(
|
||||||
self.hass.config.units.temperature_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 = self.climate_google_modes
|
modes = self.climate_google_modes
|
||||||
|
|
||||||
# Some integrations don't support modes (e.g. opentherm), but Google doesn't
|
# Some integrations don't support modes (e.g. opentherm), but Google doesn't
|
||||||
@ -776,18 +810,8 @@ class TemperatureSettingTrait(_Trait):
|
|||||||
"""Return temperature point and modes query attributes."""
|
"""Return temperature point and modes query attributes."""
|
||||||
response = {}
|
response = {}
|
||||||
attrs = self.state.attributes
|
attrs = self.state.attributes
|
||||||
domain = self.state.domain
|
|
||||||
unit = self.hass.config.units.temperature_unit
|
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 not in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
|
||||||
response["thermostatTemperatureAmbient"] = round(
|
|
||||||
temp_util.convert(float(current_temp), unit, TEMP_CELSIUS), 1
|
|
||||||
)
|
|
||||||
|
|
||||||
elif domain == climate.DOMAIN:
|
|
||||||
operation = self.state.state
|
operation = self.state.state
|
||||||
preset = attrs.get(climate.ATTR_PRESET_MODE)
|
preset = attrs.get(climate.ATTR_PRESET_MODE)
|
||||||
supported = attrs.get(ATTR_SUPPORTED_FEATURES, 0)
|
supported = attrs.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||||
@ -840,12 +864,6 @@ 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]
|
||||||
|
@ -954,6 +954,29 @@ async def test_temperature_setting_climate_setpoint_auto(hass):
|
|||||||
assert calls[0].data == {ATTR_ENTITY_ID: "climate.bla", ATTR_TEMPERATURE: 19}
|
assert calls[0].data == {ATTR_ENTITY_ID: "climate.bla", ATTR_TEMPERATURE: 19}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_temperature_control(hass):
|
||||||
|
"""Test TemperatureControl trait support for sensor domain."""
|
||||||
|
hass.config.units.temperature_unit = TEMP_CELSIUS
|
||||||
|
|
||||||
|
trt = trait.TemperatureControlTrait(
|
||||||
|
hass,
|
||||||
|
State("sensor.temp", 18),
|
||||||
|
BASIC_CONFIG,
|
||||||
|
)
|
||||||
|
assert trt.sync_attributes() == {
|
||||||
|
"queryOnlyTemperatureSetting": True,
|
||||||
|
"temperatureUnitForUX": "C",
|
||||||
|
"temperatureRange": {"maxThresholdCelsius": 100, "minThresholdCelsius": -100},
|
||||||
|
}
|
||||||
|
assert trt.query_attributes() == {
|
||||||
|
"temperatureSetpointCelsius": 18,
|
||||||
|
"temperatureAmbientCelsius": 18,
|
||||||
|
}
|
||||||
|
with pytest.raises(helpers.SmartHomeError) as err:
|
||||||
|
await trt.execute(trait.COMMAND_ONOFF, BASIC_DATA, {"on": False}, {})
|
||||||
|
assert err.value.code == const.ERR_NOT_SUPPORTED
|
||||||
|
|
||||||
|
|
||||||
async def test_humidity_setting_humidifier_setpoint(hass):
|
async def test_humidity_setting_humidifier_setpoint(hass):
|
||||||
"""Test HumiditySetting trait support for humidifier domain - setpoint."""
|
"""Test HumiditySetting trait support for humidifier domain - setpoint."""
|
||||||
assert helpers.get_google_type(humidifier.DOMAIN, None) is not None
|
assert helpers.get_google_type(humidifier.DOMAIN, None) is not None
|
||||||
@ -2380,16 +2403,16 @@ async def test_media_player_mute(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_temperature_setting_sensor(hass):
|
async def test_temperature_control_sensor(hass):
|
||||||
"""Test TemperatureSetting trait support for temperature sensor."""
|
"""Test TemperatureControl trait support for temperature sensor."""
|
||||||
assert (
|
assert (
|
||||||
helpers.get_google_type(sensor.DOMAIN, sensor.DEVICE_CLASS_TEMPERATURE)
|
helpers.get_google_type(sensor.DOMAIN, sensor.DEVICE_CLASS_TEMPERATURE)
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
assert not trait.TemperatureSettingTrait.supported(
|
assert not trait.TemperatureControlTrait.supported(
|
||||||
sensor.DOMAIN, 0, sensor.DEVICE_CLASS_HUMIDITY, None
|
sensor.DOMAIN, 0, sensor.DEVICE_CLASS_HUMIDITY, None
|
||||||
)
|
)
|
||||||
assert trait.TemperatureSettingTrait.supported(
|
assert trait.TemperatureControlTrait.supported(
|
||||||
sensor.DOMAIN, 0, sensor.DEVICE_CLASS_TEMPERATURE, None
|
sensor.DOMAIN, 0, sensor.DEVICE_CLASS_TEMPERATURE, None
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2403,11 +2426,11 @@ async def test_temperature_setting_sensor(hass):
|
|||||||
(TEMP_FAHRENHEIT, "F", "unknown", None),
|
(TEMP_FAHRENHEIT, "F", "unknown", None),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_temperature_setting_sensor_data(hass, unit_in, unit_out, state, ambient):
|
async def test_temperature_control_sensor_data(hass, unit_in, unit_out, state, ambient):
|
||||||
"""Test TemperatureSetting trait support for temperature sensor."""
|
"""Test TemperatureControl trait support for temperature sensor."""
|
||||||
hass.config.units.temperature_unit = unit_in
|
hass.config.units.temperature_unit = unit_in
|
||||||
|
|
||||||
trt = trait.TemperatureSettingTrait(
|
trt = trait.TemperatureControlTrait(
|
||||||
hass,
|
hass,
|
||||||
State(
|
State(
|
||||||
"sensor.test", state, {ATTR_DEVICE_CLASS: sensor.DEVICE_CLASS_TEMPERATURE}
|
"sensor.test", state, {ATTR_DEVICE_CLASS: sensor.DEVICE_CLASS_TEMPERATURE}
|
||||||
@ -2417,11 +2440,15 @@ async def test_temperature_setting_sensor_data(hass, unit_in, unit_out, state, a
|
|||||||
|
|
||||||
assert trt.sync_attributes() == {
|
assert trt.sync_attributes() == {
|
||||||
"queryOnlyTemperatureSetting": True,
|
"queryOnlyTemperatureSetting": True,
|
||||||
"thermostatTemperatureUnit": unit_out,
|
"temperatureUnitForUX": unit_out,
|
||||||
|
"temperatureRange": {"maxThresholdCelsius": 100, "minThresholdCelsius": -100},
|
||||||
}
|
}
|
||||||
|
|
||||||
if ambient:
|
if ambient:
|
||||||
assert trt.query_attributes() == {"thermostatTemperatureAmbient": ambient}
|
assert trt.query_attributes() == {
|
||||||
|
"temperatureAmbientCelsius": ambient,
|
||||||
|
"temperatureSetpointCelsius": ambient,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
assert trt.query_attributes() == {}
|
assert trt.query_attributes() == {}
|
||||||
hass.config.units.temperature_unit = TEMP_CELSIUS
|
hass.config.units.temperature_unit = TEMP_CELSIUS
|
||||||
|
Loading…
x
Reference in New Issue
Block a user