Assistant sensors (#55480)

This commit is contained in:
Matthew Garrett 2021-08-30 20:33:06 -07:00 committed by GitHub
parent 368cac7e5d
commit dd21bf73fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 0 deletions

View File

@ -133,6 +133,7 @@ DOMAIN_TO_GOOGLE_TYPES = {
media_player.DOMAIN: TYPE_SETTOP,
scene.DOMAIN: TYPE_SCENE,
script.DOMAIN: TYPE_SCENE,
sensor.DOMAIN: TYPE_SENSOR,
select.DOMAIN: TYPE_SENSOR,
switch.DOMAIN: TYPE_SWITCH,
vacuum.DOMAIN: TYPE_VACUUM,

View File

@ -108,6 +108,7 @@ TRAIT_MEDIA_STATE = f"{PREFIX_TRAITS}MediaState"
TRAIT_CHANNEL = f"{PREFIX_TRAITS}Channel"
TRAIT_LOCATOR = f"{PREFIX_TRAITS}Locator"
TRAIT_ENERGYSTORAGE = f"{PREFIX_TRAITS}EnergyStorage"
TRAIT_SENSOR_STATE = f"{PREFIX_TRAITS}SensorState"
PREFIX_COMMANDS = "action.devices.commands."
COMMAND_ONOFF = f"{PREFIX_COMMANDS}OnOff"
@ -2286,3 +2287,61 @@ class ChannelTrait(_Trait):
blocking=True,
context=data.context,
)
@register_trait
class SensorStateTrait(_Trait):
"""Trait to get sensor state.
https://developers.google.com/actions/smarthome/traits/sensorstate
"""
sensor_types = {
sensor.DEVICE_CLASS_AQI: ("AirQuality", "AQI"),
sensor.DEVICE_CLASS_CO: ("CarbonDioxideLevel", "PARTS_PER_MILLION"),
sensor.DEVICE_CLASS_CO2: ("CarbonMonoxideLevel", "PARTS_PER_MILLION"),
sensor.DEVICE_CLASS_PM25: ("PM2.5", "MICROGRAMS_PER_CUBIC_METER"),
sensor.DEVICE_CLASS_PM10: ("PM10", "MICROGRAMS_PER_CUBIC_METER"),
sensor.DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS: (
"VolatileOrganicCompounds",
"PARTS_PER_MILLION",
),
}
name = TRAIT_SENSOR_STATE
commands = []
@staticmethod
def supported(domain, features, device_class, _):
"""Test if state is supported."""
return domain == sensor.DOMAIN and device_class in (
sensor.DEVICE_CLASS_AQI,
sensor.DEVICE_CLASS_CO,
sensor.DEVICE_CLASS_CO2,
sensor.DEVICE_CLASS_PM25,
sensor.DEVICE_CLASS_PM10,
sensor.DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
)
def sync_attributes(self):
"""Return attributes for a sync request."""
device_class = self.state.attributes.get(ATTR_DEVICE_CLASS)
data = self.sensor_types.get(device_class)
if data is not None:
return {
"sensorStatesSupported": {
"name": data[0],
"numericCapabilities": {"rawValueUnit": data[1]},
}
}
def query_attributes(self):
"""Return the attributes of this trait for this entity."""
device_class = self.state.attributes.get(ATTR_DEVICE_CLASS)
data = self.sensor_types.get(device_class)
if data is not None:
return {
"currentSensorStateData": [
{"name": data[0], "rawValue": self.state.state}
]
}

View File

@ -3003,3 +3003,56 @@ async def test_channel(hass):
with pytest.raises(SmartHomeError, match="Unsupported command"):
await trt.execute("Unknown command", BASIC_DATA, {"channelNumber": "1"}, {})
assert len(media_player_calls) == 1
async def test_sensorstate(hass):
"""Test SensorState trait support for sensor domain."""
sensor_types = {
sensor.DEVICE_CLASS_AQI: ("AirQuality", "AQI"),
sensor.DEVICE_CLASS_CO: ("CarbonDioxideLevel", "PARTS_PER_MILLION"),
sensor.DEVICE_CLASS_CO2: ("CarbonMonoxideLevel", "PARTS_PER_MILLION"),
sensor.DEVICE_CLASS_PM25: ("PM2.5", "MICROGRAMS_PER_CUBIC_METER"),
sensor.DEVICE_CLASS_PM10: ("PM10", "MICROGRAMS_PER_CUBIC_METER"),
sensor.DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS: (
"VolatileOrganicCompounds",
"PARTS_PER_MILLION",
),
}
for sensor_type in sensor_types:
assert helpers.get_google_type(sensor.DOMAIN, None) is not None
assert trait.SensorStateTrait.supported(sensor.DOMAIN, None, sensor_type, None)
trt = trait.SensorStateTrait(
hass,
State(
"sensor.test",
100.0,
{
"device_class": sensor_type,
},
),
BASIC_CONFIG,
)
name = sensor_types[sensor_type][0]
unit = sensor_types[sensor_type][1]
assert trt.sync_attributes() == {
"sensorStatesSupported": {
"name": name,
"numericCapabilities": {"rawValueUnit": unit},
}
}
assert trt.query_attributes() == {
"currentSensorStateData": [{"name": name, "rawValue": "100.0"}]
}
assert helpers.get_google_type(sensor.DOMAIN, None) is not None
assert (
trait.SensorStateTrait.supported(
sensor.DOMAIN, None, sensor.DEVICE_CLASS_MONETARY, None
)
is False
)