Add an RSSI sensor to the LIFX integration (#80993)

This commit is contained in:
Avi Miller 2022-10-27 01:12:45 +11:00 committed by GitHub
parent 0d4b1866a7
commit dde763418a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 393 additions and 74 deletions

View File

@ -57,7 +57,13 @@ CONFIG_SCHEMA = vol.All(
)
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.LIGHT, Platform.SELECT]
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.LIGHT,
Platform.SELECT,
Platform.SENSOR,
]
DISCOVERY_INTERVAL = timedelta(minutes=15)
MIGRATION_INTERVAL = timedelta(minutes=5)
@ -199,6 +205,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
coordinator.async_setup()
try:
await coordinator.async_config_entry_first_refresh()
await coordinator.sensor_coordinator.async_config_entry_first_refresh()
except ConfigEntryNotReady:
connection.async_stop()
raise

View File

@ -12,8 +12,8 @@ from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, HEV_CYCLE_STATE
from .coordinator import LIFXUpdateCoordinator
from .entity import LIFXEntity
from .coordinator import LIFXSensorUpdateCoordinator, LIFXUpdateCoordinator
from .entity import LIFXSensorEntity
from .util import lifx_features
HEV_CYCLE_STATE_SENSOR = BinarySensorEntityDescription(
@ -34,28 +34,28 @@ async def async_setup_entry(
async_add_entities(
[
LIFXHevCycleBinarySensorEntity(
coordinator=coordinator, description=HEV_CYCLE_STATE_SENSOR
coordinator=coordinator.sensor_coordinator,
description=HEV_CYCLE_STATE_SENSOR,
)
]
)
class LIFXHevCycleBinarySensorEntity(LIFXEntity, BinarySensorEntity):
class LIFXHevCycleBinarySensorEntity(LIFXSensorEntity, BinarySensorEntity):
"""LIFX HEV cycle state binary sensor."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: LIFXUpdateCoordinator,
coordinator: LIFXSensorUpdateCoordinator,
description: BinarySensorEntityDescription,
) -> None:
"""Initialise the sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_name = description.name
self._attr_unique_id = f"{coordinator.serial_number}_{description.key}"
self._attr_unique_id = f"{coordinator.parent.serial_number}_{description.key}"
self._async_update_attrs()
@callback

View File

@ -12,8 +12,8 @@ from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN, IDENTIFY, RESTART
from .coordinator import LIFXUpdateCoordinator
from .entity import LIFXEntity
from .coordinator import LIFXSensorUpdateCoordinator, LIFXUpdateCoordinator
from .entity import LIFXSensorEntity
RESTART_BUTTON_DESCRIPTION = ButtonEntityDescription(
key=RESTART,
@ -38,20 +38,22 @@ async def async_setup_entry(
domain_data = hass.data[DOMAIN]
coordinator: LIFXUpdateCoordinator = domain_data[entry.entry_id]
async_add_entities(
cls(coordinator) for cls in (LIFXRestartButton, LIFXIdentifyButton)
cls(coordinator.sensor_coordinator)
for cls in (LIFXRestartButton, LIFXIdentifyButton)
)
class LIFXButton(LIFXEntity, ButtonEntity):
class LIFXButton(LIFXSensorEntity, ButtonEntity):
"""Base LIFX button."""
_attr_has_entity_name: bool = True
_attr_should_poll: bool = False
def __init__(self, coordinator: LIFXUpdateCoordinator) -> None:
def __init__(self, coordinator: LIFXSensorUpdateCoordinator) -> None:
"""Initialise a LIFX button."""
super().__init__(coordinator)
self._attr_unique_id = (
f"{coordinator.serial_number}_{self.entity_description.key}"
f"{coordinator.parent.serial_number}_{self.entity_description.key}"
)

View File

@ -35,6 +35,7 @@ ATTR_INDICATION = "indication"
ATTR_INFRARED = "infrared"
ATTR_POWER = "power"
ATTR_REMAINING = "remaining"
ATTR_RSSI = "rssi"
ATTR_ZONES = "zones"
ATTR_THEME = "theme"

View File

@ -2,9 +2,11 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable
from datetime import timedelta
from enum import IntEnum
from functools import partial
from math import floor, log10
from typing import Any, cast
from aiolifx.aiolifx import (
@ -15,8 +17,13 @@ from aiolifx.aiolifx import (
)
from aiolifx.connection import LIFXConnection
from aiolifx_themes.themes import ThemeLibrary, ThemePainter
from awesomeversion import AwesomeVersion
from homeassistant.const import Platform
from homeassistant.const import (
SIGNAL_STRENGTH_DECIBELS,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
@ -41,8 +48,11 @@ from .util import (
lifx_features,
)
LIGHT_UPDATE_INTERVAL = 10
SENSOR_UPDATE_INTERVAL = 30
REQUEST_REFRESH_DELAY = 0.35
LIFX_IDENTIFY_DELAY = 3.0
RSSI_DBM_FW = AwesomeVersion("2.77")
class FirmwareEffect(IntEnum):
@ -69,14 +79,13 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
self.device: Light = connection.device
self.lock = asyncio.Lock()
self.active_effect = FirmwareEffect.OFF
update_interval = timedelta(seconds=10)
self.last_used_theme: str = ""
self.sensor_coordinator = LIFXSensorUpdateCoordinator(hass, self, title)
super().__init__(
hass,
_LOGGER,
name=f"{title} ({self.device.ip_addr})",
update_interval=update_interval,
update_interval=timedelta(seconds=LIGHT_UPDATE_INTERVAL),
# We don't want an immediate refresh since the device
# takes a moment to reflect the state change
request_refresh_debouncer=Debouncer(
@ -112,11 +121,6 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
"""Return the label of the bulb."""
return cast(str, self.device.label)
@property
def current_infrared_brightness(self) -> str | None:
"""Return the current infrared brightness as a string."""
return infrared_brightness_value_to_option(self.device.infrared_brightness)
async def diagnostics(self) -> dict[str, Any]:
"""Return diagnostic information about the device."""
features = lifx_features(self.device)
@ -162,19 +166,6 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
platform, DOMAIN, f"{self.serial_number}_{key}"
)
async def async_identify_bulb(self) -> None:
"""Identify the device by flashing it three times."""
bulb: Light = self.device
if bulb.power_level:
# just flash the bulb for three seconds
await self.async_set_waveform_optional(value=IDENTIFY_WAVEFORM)
return
# Turn the bulb on first, flash for 3 seconds, then turn off
await self.async_set_power(state=True, duration=1)
await self.async_set_waveform_optional(value=IDENTIFY_WAVEFORM)
await asyncio.sleep(LIFX_IDENTIFY_DELAY)
await self.async_set_power(state=False, duration=1)
async def _async_update_data(self) -> None:
"""Fetch all device data from the api."""
async with self.lock:
@ -203,12 +194,6 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
await self.async_get_color_zones()
await self.async_get_multizone_effect()
if lifx_features(self.device)["hev"]:
await self.async_get_hev_cycle()
if lifx_features(self.device)["infrared"]:
response = await async_execute_lifx(self.device.get_infrared)
async def async_get_color_zones(self) -> None:
"""Get updated color information for each zone."""
zone = 0
@ -234,17 +219,6 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
f"Timeout getting color zones from {self.name}"
) from ex
def async_get_hev_cycle_state(self) -> bool | None:
"""Return the current HEV cycle state."""
if self.device.hev_cycle is None:
return None
return bool(self.device.hev_cycle.get(ATTR_REMAINING, 0) > 0)
async def async_get_hev_cycle(self) -> None:
"""Update the HEV cycle status from a LIFX Clean bulb."""
if lifx_features(self.device)["hev"]:
await async_execute_lifx(self.device.get_hev_cycle)
async def async_set_waveform_optional(
self, value: dict[str, Any], rapid: bool = False
) -> None:
@ -381,6 +355,109 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
"""Return the enum value of the currently active firmware effect."""
return self.active_effect.value
class LIFXSensorUpdateCoordinator(DataUpdateCoordinator):
"""DataUpdateCoordinator to gather data for a specific lifx device."""
def __init__(
self,
hass: HomeAssistant,
parent: LIFXUpdateCoordinator,
title: str,
) -> None:
"""Initialize DataUpdateCoordinator."""
self.parent: LIFXUpdateCoordinator = parent
self.device: Light = parent.device
self._update_rssi: bool = False
self._rssi: int = 0
self.last_used_theme: str = ""
super().__init__(
hass,
_LOGGER,
name=f"{title} Sensors ({self.device.ip_addr})",
update_interval=timedelta(seconds=SENSOR_UPDATE_INTERVAL),
# Refresh immediately because the changes are not visible
request_refresh_debouncer=Debouncer(
hass, _LOGGER, cooldown=0, immediate=True
),
)
@property
def rssi(self) -> int:
"""Return stored RSSI value."""
return self._rssi
@property
def rssi_uom(self) -> str:
"""Return the RSSI unit of measurement."""
if AwesomeVersion(self.device.host_firmware_version) <= RSSI_DBM_FW:
return SIGNAL_STRENGTH_DECIBELS
return SIGNAL_STRENGTH_DECIBELS_MILLIWATT
@property
def current_infrared_brightness(self) -> str | None:
"""Return the current infrared brightness as a string."""
return infrared_brightness_value_to_option(self.device.infrared_brightness)
async def _async_update_data(self) -> None:
"""Fetch all device data from the api."""
if self._update_rssi is True:
await self.async_update_rssi()
if lifx_features(self.device)["hev"]:
await self.async_get_hev_cycle()
if lifx_features(self.device)["infrared"]:
await async_execute_lifx(self.device.get_infrared)
async def async_set_infrared_brightness(self, option: str) -> None:
"""Set infrared brightness."""
infrared_brightness = infrared_brightness_option_to_value(option)
await async_execute_lifx(partial(self.device.set_infrared, infrared_brightness))
async def async_identify_bulb(self) -> None:
"""Identify the device by flashing it three times."""
bulb: Light = self.device
if bulb.power_level:
# just flash the bulb for three seconds
await self.parent.async_set_waveform_optional(value=IDENTIFY_WAVEFORM)
return
# Turn the bulb on first, flash for 3 seconds, then turn off
await self.parent.async_set_power(state=True, duration=1)
await self.parent.async_set_waveform_optional(value=IDENTIFY_WAVEFORM)
await asyncio.sleep(LIFX_IDENTIFY_DELAY)
await self.parent.async_set_power(state=False, duration=1)
def async_enable_rssi_updates(self) -> Callable[[], None]:
"""Enable RSSI signal strength updates."""
@callback
def _async_disable_rssi_updates() -> None:
"""Disable RSSI updates when sensor removed."""
self._update_rssi = False
self._update_rssi = True
return _async_disable_rssi_updates
async def async_update_rssi(self) -> None:
"""Update RSSI value."""
resp = await async_execute_lifx(self.device.get_wifiinfo)
self._rssi = int(floor(10 * log10(resp.signal) + 0.5))
def async_get_hev_cycle_state(self) -> bool | None:
"""Return the current HEV cycle state."""
if self.device.hev_cycle is None:
return None
return bool(self.device.hev_cycle.get(ATTR_REMAINING, 0) > 0)
async def async_get_hev_cycle(self) -> None:
"""Update the HEV cycle status from a LIFX Clean bulb."""
if lifx_features(self.device)["hev"]:
await async_execute_lifx(self.device.get_hev_cycle)
async def async_set_hev_cycle_state(self, enable: bool, duration: int = 0) -> None:
"""Start or stop an HEV cycle on a LIFX Clean bulb."""
if lifx_features(self.device)["hev"]:
@ -388,13 +465,8 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
partial(self.device.set_hev_cycle, enable=enable, duration=duration)
)
async def async_set_infrared_brightness(self, option: str) -> None:
"""Set infrared brightness."""
infrared_brightness = infrared_brightness_option_to_value(option)
await async_execute_lifx(partial(self.device.set_infrared, infrared_brightness))
async def async_apply_theme(self, theme_name: str) -> None:
"""Apply the selected theme to the device."""
self.last_used_theme = theme_name
theme = ThemeLibrary().get_theme(theme_name)
await ThemePainter(self.hass.loop).paint(theme, [self.device])
await ThemePainter(self.hass.loop).paint(theme, [self.parent.device])

View File

@ -8,7 +8,7 @@ from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import LIFXUpdateCoordinator
from .coordinator import LIFXSensorUpdateCoordinator, LIFXUpdateCoordinator
class LIFXEntity(CoordinatorEntity[LIFXUpdateCoordinator]):
@ -26,3 +26,20 @@ class LIFXEntity(CoordinatorEntity[LIFXUpdateCoordinator]):
model=products.product_map.get(self.bulb.product, "LIFX Bulb"),
sw_version=self.bulb.host_firmware_version,
)
class LIFXSensorEntity(CoordinatorEntity[LIFXSensorUpdateCoordinator]):
"""Representation of a LIFX sensor entity with a sensor coordinator."""
def __init__(self, coordinator: LIFXSensorUpdateCoordinator) -> None:
"""Initialise the sensor."""
super().__init__(coordinator)
self.bulb = coordinator.parent.device
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.parent.serial_number)},
connections={(dr.CONNECTION_NETWORK_MAC, coordinator.parent.mac_address)},
manufacturer="LIFX",
name=coordinator.parent.label,
model=products.product_map.get(self.bulb.product, "LIFX Bulb"),
sw_version=self.bulb.host_firmware_version,
)

View File

@ -271,7 +271,9 @@ class LIFXLight(LIFXEntity, LightEntity):
"This device does not support setting HEV cycle state"
)
await self.coordinator.async_set_hev_cycle_state(power, duration or 0)
await self.coordinator.sensor_coordinator.async_set_hev_cycle_state(
power, duration or 0
)
await self.update_during_transition(duration or 0)
async def set_power(

View File

@ -15,8 +15,8 @@ from .const import (
INFRARED_BRIGHTNESS,
INFRARED_BRIGHTNESS_VALUES_MAP,
)
from .coordinator import LIFXUpdateCoordinator
from .entity import LIFXEntity
from .coordinator import LIFXSensorUpdateCoordinator, LIFXUpdateCoordinator
from .entity import LIFXSensorEntity
from .util import lifx_features
THEME_NAMES = [theme_name.lower() for theme_name in ThemeLibrary().themes]
@ -41,36 +41,41 @@ async def async_setup_entry(
) -> None:
"""Set up LIFX from a config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[LIFXEntity] = []
entities: list[LIFXSensorEntity] = []
if lifx_features(coordinator.device)["infrared"]:
entities.append(
LIFXInfraredBrightnessSelectEntity(
coordinator=coordinator, description=INFRARED_BRIGHTNESS_ENTITY
coordinator.sensor_coordinator, description=INFRARED_BRIGHTNESS_ENTITY
)
)
if lifx_features(coordinator.device)["multizone"] is True:
entities.append(
LIFXThemeSelectEntity(coordinator=coordinator, description=THEME_ENTITY)
LIFXThemeSelectEntity(
coordinator.sensor_coordinator, description=THEME_ENTITY
)
)
async_add_entities(entities)
class LIFXInfraredBrightnessSelectEntity(LIFXEntity, SelectEntity):
class LIFXInfraredBrightnessSelectEntity(LIFXSensorEntity, SelectEntity):
"""LIFX Nightvision infrared brightness configuration entity."""
_attr_has_entity_name = True
def __init__(
self, coordinator: LIFXUpdateCoordinator, description: SelectEntityDescription
self,
coordinator: LIFXSensorUpdateCoordinator,
description: SelectEntityDescription,
) -> None:
"""Initialise the IR brightness config entity."""
super().__init__(coordinator)
self.entity_description = description
self._attr_name = description.name
self._attr_unique_id = f"{coordinator.serial_number}_{description.key}"
self._attr_unique_id = f"{coordinator.parent.serial_number}_{description.key}"
self._attr_current_option = coordinator.current_infrared_brightness
@callback
@ -89,21 +94,22 @@ class LIFXInfraredBrightnessSelectEntity(LIFXEntity, SelectEntity):
await self.coordinator.async_set_infrared_brightness(option)
class LIFXThemeSelectEntity(LIFXEntity, SelectEntity):
class LIFXThemeSelectEntity(LIFXSensorEntity, SelectEntity):
"""Theme entity for LIFX multizone devices."""
_attr_has_entity_name = True
_attr_should_poll = False
def __init__(
self, coordinator: LIFXUpdateCoordinator, description: SelectEntityDescription
self,
coordinator: LIFXSensorUpdateCoordinator,
description: SelectEntityDescription,
) -> None:
"""Initialise the theme selection entity."""
super().__init__(coordinator)
self.entity_description = description
self._attr_name = description.name
self._attr_unique_id = f"{coordinator.serial_number}_{description.key}"
self._attr_unique_id = f"{coordinator.parent.serial_number}_{description.key}"
self._attr_current_option = None
@callback

View File

@ -0,0 +1,74 @@
"""Sensors for LIFX lights."""
from __future__ import annotations
from datetime import timedelta
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ATTR_RSSI, DOMAIN
from .coordinator import LIFXSensorUpdateCoordinator, LIFXUpdateCoordinator
from .entity import LIFXSensorEntity
SCAN_INTERVAL = timedelta(seconds=30)
RSSI_SENSOR = SensorEntityDescription(
key=ATTR_RSSI,
name="RSSI",
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up LIFX sensor from config entry."""
coordinator: LIFXUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities([LIFXRssiSensor(coordinator.sensor_coordinator, RSSI_SENSOR)])
class LIFXRssiSensor(LIFXSensorEntity, SensorEntity):
"""LIFX RSSI sensor."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: LIFXSensorUpdateCoordinator,
description: SensorEntityDescription,
) -> None:
"""Initialise the RSSI sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_name = description.name
self._attr_unique_id = f"{coordinator.parent.serial_number}_{description.key}"
self._attr_native_unit_of_measurement = coordinator.rssi_uom
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._async_update_attrs()
super()._handle_coordinator_update()
@callback
def _async_update_attrs(self) -> None:
"""Handle coordinator updates."""
self._attr_native_value = self.coordinator.rssi
@callback
async def async_added_to_hass(self) -> None:
"""Enable RSSI updates."""
self.async_on_remove(self.coordinator.async_enable_rssi_updates())
return await super().async_added_to_hass()

View File

@ -91,6 +91,7 @@ def _mocked_bulb() -> Light:
bulb.set_power = MockLifxCommand(bulb)
bulb.set_color = MockLifxCommand(bulb)
bulb.get_hostfirmware = MockLifxCommand(bulb)
bulb.get_wifiinfo = MockLifxCommand(bulb, signal=100)
bulb.get_version = MockLifxCommand(bulb)
bulb.set_waveform_optional = MockLifxCommand(bulb)
bulb.product = 1 # LIFX Original 1000
@ -168,6 +169,12 @@ def _mocked_tile() -> Light:
return bulb
def _mocked_bulb_old_firmware() -> Light:
bulb = _mocked_bulb()
bulb.host_firmware_version = "2.77"
return bulb
def _mocked_bulb_new_firmware() -> Light:
bulb = _mocked_bulb()
bulb.host_firmware_version = "3.90"

View File

@ -1,4 +1,4 @@
"""Test the lifx binary sensor platwform."""
"""Test the lifx binary sensor platform."""
from __future__ import annotations
from datetime import timedelta

View File

@ -0,0 +1,131 @@
"""Test the LIFX sensor platform."""
from __future__ import annotations
from datetime import timedelta
from homeassistant.components import lifx
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
CONF_HOST,
SIGNAL_STRENGTH_DECIBELS,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from . import (
DEFAULT_ENTRY_TITLE,
IP_ADDRESS,
MAC_ADDRESS,
_mocked_bulb,
_mocked_bulb_old_firmware,
_patch_config_flow_try_connect,
_patch_device,
_patch_discovery,
)
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_rssi_sensor(hass: HomeAssistant) -> None:
"""Test LIFX RSSI sensor entity."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb()
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
entity_id = "sensor.my_bulb_rssi"
entity_registry = er.async_get(hass)
entry = entity_registry.entities.get(entity_id)
assert entry
assert entry.disabled
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
# Test enabling entity
updated_entry = entity_registry.async_update_entity(
entry.entity_id, **{"disabled_by": None}
)
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await hass.config_entries.async_reload(config_entry.entry_id)
await hass.async_block_till_done()
assert updated_entry != entry
assert updated_entry.disabled is False
assert updated_entry.unit_of_measurement == SIGNAL_STRENGTH_DECIBELS_MILLIWATT
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=120))
await hass.async_block_till_done()
rssi = hass.states.get(entity_id)
assert (
rssi.attributes[ATTR_UNIT_OF_MEASUREMENT] == SIGNAL_STRENGTH_DECIBELS_MILLIWATT
)
assert rssi.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.SIGNAL_STRENGTH
assert rssi.attributes["state_class"] == SensorStateClass.MEASUREMENT
async def test_rssi_sensor_old_firmware(hass: HomeAssistant) -> None:
"""Test LIFX RSSI sensor entity."""
config_entry = MockConfigEntry(
domain=lifx.DOMAIN,
title=DEFAULT_ENTRY_TITLE,
data={CONF_HOST: IP_ADDRESS},
unique_id=MAC_ADDRESS,
)
config_entry.add_to_hass(hass)
bulb = _mocked_bulb_old_firmware()
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
await hass.async_block_till_done()
entity_id = "sensor.my_bulb_rssi"
entity_registry = er.async_get(hass)
entry = entity_registry.entities.get(entity_id)
assert entry
assert entry.disabled
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
# Test enabling entity
updated_entry = entity_registry.async_update_entity(
entry.entity_id, **{"disabled_by": None}
)
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
device=bulb
), _patch_device(device=bulb):
await hass.config_entries.async_reload(config_entry.entry_id)
await hass.async_block_till_done()
assert updated_entry != entry
assert updated_entry.disabled is False
assert updated_entry.unit_of_measurement == SIGNAL_STRENGTH_DECIBELS
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=120))
await hass.async_block_till_done()
rssi = hass.states.get(entity_id)
assert rssi.attributes[ATTR_UNIT_OF_MEASUREMENT] == SIGNAL_STRENGTH_DECIBELS
assert rssi.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.SIGNAL_STRENGTH
assert rssi.attributes["state_class"] == SensorStateClass.MEASUREMENT