Entities for secondary temperature values created by certain Xiaomi devices in deCONZ (#49724)

* Create sensors for secondary temperature values created by certain Xiaomi devices

* Fix tests
pull/49742/head
Robert Svensson 2021-04-27 08:43:06 +02:00 committed by GitHub
parent b27e9e376d
commit 58ad3b61f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 10 deletions

View File

@ -114,6 +114,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
):
entities.append(DeconzSensor(sensor, gateway))
if sensor.secondary_temperature:
known_temperature_sensors = set(gateway.entities[DOMAIN])
new_temperature_sensor = DeconzTemperature(sensor, gateway)
if new_temperature_sensor.unique_id not in known_temperature_sensors:
entities.append(new_temperature_sensor)
if entities:
async_add_entities(entities)
@ -192,6 +198,47 @@ class DeconzSensor(DeconzDevice, SensorEntity):
return attr
class DeconzTemperature(DeconzDevice, SensorEntity):
"""Representation of a deCONZ temperature sensor.
Extra temperature sensor on certain Xiaomi devices.
"""
TYPE = DOMAIN
@property
def unique_id(self):
"""Return a unique identifier for this device."""
return f"{self.serial}-temperature"
@callback
def async_update_callback(self, force_update=False):
"""Update the sensor's state."""
keys = {"temperature", "reachable"}
if force_update or self._device.changed_keys.intersection(keys):
super().async_update_callback(force_update=force_update)
@property
def state(self):
"""Return the state of the sensor."""
return self._device.secondary_temperature
@property
def name(self):
"""Return the name of the temperature sensor."""
return f"{self._device.name} Temperature"
@property
def device_class(self):
"""Return the class of the sensor."""
return DEVICE_CLASS_TEMPERATURE
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this sensor."""
return TEMP_CELSIUS
class DeconzBattery(DeconzDevice, SensorEntity):
"""Battery class for when a device is only represented as an event."""

View File

@ -13,7 +13,13 @@ from homeassistant.components.deconz.const import (
DOMAIN as DECONZ_DOMAIN,
)
from homeassistant.components.deconz.services import SERVICE_DEVICE_REFRESH
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE
from homeassistant.const import (
ATTR_DEVICE_CLASS,
DEVICE_CLASS_TEMPERATURE,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_registry import async_entries_for_config_entry
@ -72,15 +78,21 @@ async def test_binary_sensors(hass, aioclient_mock, mock_deconz_websocket):
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 3
assert len(hass.states.async_all()) == 5
presence_sensor = hass.states.get("binary_sensor.presence_sensor")
assert presence_sensor.state == STATE_OFF
assert presence_sensor.attributes["device_class"] == DEVICE_CLASS_MOTION
assert presence_sensor.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_MOTION
presence_temp = hass.states.get("sensor.presence_sensor_temperature")
assert presence_temp.state == "0.1"
assert presence_temp.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_TEMPERATURE
assert hass.states.get("binary_sensor.temperature_sensor") is None
assert hass.states.get("binary_sensor.clip_presence_sensor") is None
vibration_sensor = hass.states.get("binary_sensor.vibration_sensor")
assert vibration_sensor.state == STATE_ON
assert vibration_sensor.attributes["device_class"] == DEVICE_CLASS_VIBRATION
assert vibration_sensor.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_VIBRATION
vibration_temp = hass.states.get("sensor.vibration_sensor_temperature")
assert vibration_temp.state == "0.1"
assert vibration_temp.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_TEMPERATURE
event_changed_sensor = {
"t": "event",

View File

@ -12,6 +12,7 @@ from homeassistant.const import (
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
@ -89,13 +90,17 @@ async def test_sensors(hass, aioclient_mock, mock_deconz_websocket):
with patch.dict(DECONZ_WEB_REQUEST, data):
config_entry = await setup_deconz_integration(hass, aioclient_mock)
assert len(hass.states.async_all()) == 5
assert len(hass.states.async_all()) == 6
light_level_sensor = hass.states.get("sensor.light_level_sensor")
assert light_level_sensor.state == "999.8"
assert light_level_sensor.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_ILLUMINANCE
assert light_level_sensor.attributes[ATTR_DAYLIGHT] == 6955
light_level_temp = hass.states.get("sensor.light_level_sensor_temperature")
assert light_level_temp.state == "0.1"
assert light_level_temp.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_TEMPERATURE
assert not hass.states.get("sensor.presence_sensor")
assert not hass.states.get("sensor.switch_1")
assert not hass.states.get("sensor.switch_1_battery_level")
@ -130,6 +135,19 @@ async def test_sensors(hass, aioclient_mock, mock_deconz_websocket):
assert hass.states.get("sensor.light_level_sensor").state == "1.6"
# Event signals new temperature value
event_changed_sensor = {
"t": "event",
"e": "changed",
"r": "sensors",
"id": "1",
"config": {"temperature": 100},
}
await mock_deconz_websocket(data=event_changed_sensor)
assert hass.states.get("sensor.light_level_sensor_temperature").state == "1.0"
# Event signals new battery level
event_changed_sensor = {
@ -148,7 +166,7 @@ async def test_sensors(hass, aioclient_mock, mock_deconz_websocket):
await hass.config_entries.async_unload(config_entry.entry_id)
states = hass.states.async_all()
assert len(states) == 5
assert len(states) == 6
for state in states:
assert state.state == STATE_UNAVAILABLE
@ -187,7 +205,7 @@ async def test_allow_clip_sensors(hass, aioclient_mock):
options={CONF_ALLOW_CLIP_SENSOR: True},
)
assert len(hass.states.async_all()) == 2
assert len(hass.states.async_all()) == 3
assert hass.states.get("sensor.clip_light_level_sensor").state == "999.8"
# Disallow clip sensors
@ -197,7 +215,7 @@ async def test_allow_clip_sensors(hass, aioclient_mock):
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
assert len(hass.states.async_all()) == 2
assert not hass.states.get("sensor.clip_light_level_sensor")
# Allow clip sensors
@ -207,7 +225,7 @@ async def test_allow_clip_sensors(hass, aioclient_mock):
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 2
assert len(hass.states.async_all()) == 3
assert hass.states.get("sensor.clip_light_level_sensor").state == "999.8"
@ -235,7 +253,7 @@ async def test_add_new_sensor(hass, aioclient_mock, mock_deconz_websocket):
await mock_deconz_websocket(data=event_added_sensor)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
assert len(hass.states.async_all()) == 2
assert hass.states.get("sensor.light_level_sensor").state == "999.8"