mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Add SMLIGHT sensor entities for second radio (#137403)
* Add sensors for second radio * Add test for zigbee2 sensor * Update homeassistant/components/smlight/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * drop useless replace * Fix test failure * Fix code coverage in config flow * Update homeassistant/components/smlight/sensor.py Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * fix conversion of iterator to list * Remove assert on radios * simplify handling of radios further --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
e10801af80
commit
043603c9be
@ -51,14 +51,14 @@ class SmlightConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self.client = Api2(self._host, session=async_get_clientsession(self.hass))
|
||||
|
||||
try:
|
||||
info = await self.client.get_info()
|
||||
self._host = str(info.device_ip)
|
||||
self._device_name = str(info.hostname)
|
||||
|
||||
if info.model not in Devices:
|
||||
return self.async_abort(reason="unsupported_device")
|
||||
|
||||
if not await self._async_check_auth_required(user_input):
|
||||
info = await self.client.get_info()
|
||||
self._host = str(info.device_ip)
|
||||
self._device_name = str(info.hostname)
|
||||
|
||||
if info.model not in Devices:
|
||||
return self.async_abort(reason="unsupported_device")
|
||||
|
||||
return await self._async_complete_entry(user_input)
|
||||
except SmlightConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
@ -128,13 +128,13 @@ class SmlightConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
if user_input is not None:
|
||||
try:
|
||||
info = await self.client.get_info()
|
||||
|
||||
if info.model not in Devices:
|
||||
return self.async_abort(reason="unsupported_device")
|
||||
|
||||
if not await self._async_check_auth_required(user_input):
|
||||
return await self._async_complete_entry(user_input)
|
||||
info = await self.client.get_info()
|
||||
|
||||
if info.model not in Devices:
|
||||
return self.async_abort(reason="unsupported_device")
|
||||
|
||||
return await self._async_complete_entry(user_input)
|
||||
|
||||
except SmlightConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
@ -37,7 +37,7 @@ class SmSensorEntityDescription(SensorEntityDescription):
|
||||
class SmInfoEntityDescription(SensorEntityDescription):
|
||||
"""Class describing SMLIGHT information entities."""
|
||||
|
||||
value_fn: Callable[[Info], StateType]
|
||||
value_fn: Callable[[Info, int], StateType]
|
||||
|
||||
|
||||
INFO: list[SmInfoEntityDescription] = [
|
||||
@ -46,24 +46,25 @@ INFO: list[SmInfoEntityDescription] = [
|
||||
translation_key="device_mode",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["eth", "wifi", "usb"],
|
||||
value_fn=lambda x: x.coord_mode,
|
||||
value_fn=lambda x, idx: x.coord_mode,
|
||||
),
|
||||
SmInfoEntityDescription(
|
||||
key="firmware_channel",
|
||||
translation_key="firmware_channel",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["dev", "release"],
|
||||
value_fn=lambda x: x.fw_channel,
|
||||
),
|
||||
SmInfoEntityDescription(
|
||||
key="zigbee_type",
|
||||
translation_key="zigbee_type",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["coordinator", "router", "thread"],
|
||||
value_fn=lambda x: x.zb_type,
|
||||
value_fn=lambda x, idx: x.fw_channel,
|
||||
),
|
||||
]
|
||||
|
||||
RADIO_INFO = SmInfoEntityDescription(
|
||||
key="zigbee_type",
|
||||
translation_key="zigbee_type",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["coordinator", "router", "thread"],
|
||||
value_fn=lambda x, idx: x.radios[idx].zb_type,
|
||||
)
|
||||
|
||||
|
||||
SENSORS: list[SmSensorEntityDescription] = [
|
||||
SmSensorEntityDescription(
|
||||
@ -102,6 +103,16 @@ SENSORS: list[SmSensorEntityDescription] = [
|
||||
),
|
||||
]
|
||||
|
||||
EXTRA_SENSOR = SmSensorEntityDescription(
|
||||
key="zigbee_temperature_2",
|
||||
translation_key="zigbee_temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda x: x.zb_temp2,
|
||||
)
|
||||
|
||||
UPTIME: list[SmSensorEntityDescription] = [
|
||||
SmSensorEntityDescription(
|
||||
key="core_uptime",
|
||||
@ -127,8 +138,7 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up SMLIGHT sensor based on a config entry."""
|
||||
coordinator = entry.runtime_data.data
|
||||
|
||||
async_add_entities(
|
||||
entities: list[SmEntity] = list(
|
||||
chain(
|
||||
(SmInfoSensorEntity(coordinator, description) for description in INFO),
|
||||
(SmSensorEntity(coordinator, description) for description in SENSORS),
|
||||
@ -136,6 +146,16 @@ async def async_setup_entry(
|
||||
)
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
SmInfoSensorEntity(coordinator, RADIO_INFO, idx)
|
||||
for idx, _ in enumerate(coordinator.data.info.radios)
|
||||
)
|
||||
|
||||
if coordinator.data.sensors.zb_temp2 is not None:
|
||||
entities.append(SmSensorEntity(coordinator, EXTRA_SENSOR))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class SmSensorEntity(SmEntity, SensorEntity):
|
||||
"""Representation of a slzb sensor."""
|
||||
@ -172,17 +192,20 @@ class SmInfoSensorEntity(SmEntity, SensorEntity):
|
||||
self,
|
||||
coordinator: SmDataUpdateCoordinator,
|
||||
description: SmInfoEntityDescription,
|
||||
idx: int = 0,
|
||||
) -> None:
|
||||
"""Initiate slzb sensor."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}"
|
||||
self.idx = idx
|
||||
sensor = f"_{idx}" if idx else ""
|
||||
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}{sensor}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the sensor value."""
|
||||
value = self.entity_description.value_fn(self.coordinator.data.info)
|
||||
value = self.entity_description.value_fn(self.coordinator.data.info, self.idx)
|
||||
options = self.entity_description.options
|
||||
|
||||
if isinstance(value, int) and options is not None:
|
||||
|
@ -193,7 +193,7 @@ async def test_zeroconf_flow_auth(
|
||||
}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert len(mock_smlight_client.get_info.mock_calls) == 3
|
||||
assert len(mock_smlight_client.get_info.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_zeroconf_unsupported_abort(
|
||||
@ -406,7 +406,7 @@ async def test_user_invalid_auth(
|
||||
}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert len(mock_smlight_client.get_info.mock_calls) == 3
|
||||
assert len(mock_smlight_client.get_info.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_user_cannot_connect(
|
||||
@ -443,7 +443,7 @@ async def test_user_cannot_connect(
|
||||
assert result2["title"] == "SLZB-06p7"
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert len(mock_smlight_client.get_info.mock_calls) == 3
|
||||
assert len(mock_smlight_client.get_info.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_auth_cannot_connect(
|
||||
|
@ -2,17 +2,18 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pysmlight import Sensors
|
||||
from pysmlight import Info, Sensors
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.smlight.const import DOMAIN
|
||||
from homeassistant.const import STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from .conftest import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
from tests.common import MockConfigEntry, load_json_object_fixture, snapshot_platform
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.usefixtures(
|
||||
@ -73,3 +74,38 @@ async def test_zigbee_uptime_disconnected(
|
||||
|
||||
state = hass.states.get("sensor.mock_title_zigbee_uptime")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_zigbee2_temp_sensor(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test for zb_temp2 if device has second radio."""
|
||||
mock_smlight_client.get_sensors.return_value = Sensors(zb_temp2=20.45)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get("sensor.mock_title_zigbee_chip_temp_2")
|
||||
assert state
|
||||
assert state.state == "20.45"
|
||||
|
||||
|
||||
async def test_zigbee_type_sensors(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test for zigbee type sensor with second radio."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = Info.from_dict(
|
||||
load_json_object_fixture("info-MR1.json", DOMAIN)
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get("sensor.mock_title_zigbee_type")
|
||||
assert state
|
||||
assert state.state == "coordinator"
|
||||
|
||||
state = hass.states.get("sensor.mock_title_zigbee_type_2")
|
||||
assert state
|
||||
assert state.state == "router"
|
||||
|
Loading…
x
Reference in New Issue
Block a user