New Integration: SMLIGHT SLZB-06 Adapters Integration (#118675)
* Initial SMLIGHT integration Signed-off-by: Tim Lunn <tl@smlight.tech> * Generated content Signed-off-by: Tim Lunn <tl@smlight.tech> * Cleanup LOGGING * Use runtime data * Call super first * coordinator instance attributes * Move coordinatorEntity and attr to base class * cleanup sensors * update strings to use sentence case * Improve reauth flow on incorrect credentials * Use fixture for config_flow tests and test to completion * Split uptime hndling into a new uptime sensor entity * Drop server side events and internet callback will bring this back with binary sensor Platform * consolidate coordinator setup * entity always include connections * get_hostname tweak * Add tests for init, coordinator and sensor * Use custom type SmConfigEntry * update sensor snapshot * Drop reauth flow for later PR * Use _async_setup for initial setup * drop internet to be set later * sensor fixes * config flow re * typing fixes * Bump pysmlight dependency to 0.0.12 * dont trigger invalid auth message when first loading auth step * Merge uptime sensors back into main sensor class * clarify uptime handling * Apply suggestions from code review Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * address review comments * pass host as parameter to the dataCoordinator * drop uptime sensors for a later PR * update sensor test snapshot * move coordinator unique_id to _async_setup * fix CI * Apply suggestions from code review Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * drop invalid_auth test tag * use snapshot_platform, update fixtures * Finish all tests with abort or create entry * drop coordinator tests and remove hostname support * add test for update failure on connection error * use freezer for update_failed test * fix pysmlight imports --------- Signed-off-by: Tim Lunn <tl@smlight.tech> Co-authored-by: Tim Lunn <tim@feathertop.org> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
b4648136c5
commit
98a007cb2f
23 changed files with 1871 additions and 0 deletions
|
@ -1329,6 +1329,8 @@ build.json @home-assistant/supervisor
|
||||||
/homeassistant/components/smarty/ @z0mbieprocess
|
/homeassistant/components/smarty/ @z0mbieprocess
|
||||||
/homeassistant/components/smhi/ @gjohansson-ST
|
/homeassistant/components/smhi/ @gjohansson-ST
|
||||||
/tests/components/smhi/ @gjohansson-ST
|
/tests/components/smhi/ @gjohansson-ST
|
||||||
|
/homeassistant/components/smlight/ @tl-sl
|
||||||
|
/tests/components/smlight/ @tl-sl
|
||||||
/homeassistant/components/sms/ @ocalvo
|
/homeassistant/components/sms/ @ocalvo
|
||||||
/tests/components/sms/ @ocalvo
|
/tests/components/sms/ @ocalvo
|
||||||
/homeassistant/components/snapcast/ @luar123
|
/homeassistant/components/snapcast/ @luar123
|
||||||
|
|
30
homeassistant/components/smlight/__init__.py
Normal file
30
homeassistant/components/smlight/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
"""SMLIGHT SLZB Zigbee device integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_HOST, Platform
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .coordinator import SmDataUpdateCoordinator
|
||||||
|
|
||||||
|
PLATFORMS: list[Platform] = [
|
||||||
|
Platform.SENSOR,
|
||||||
|
]
|
||||||
|
type SmConfigEntry = ConfigEntry[SmDataUpdateCoordinator]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: SmConfigEntry) -> bool:
|
||||||
|
"""Set up SMLIGHT Zigbee from a config entry."""
|
||||||
|
coordinator = SmDataUpdateCoordinator(hass, entry.data[CONF_HOST])
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
entry.runtime_data = coordinator
|
||||||
|
|
||||||
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: SmConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
151
homeassistant/components/smlight/config_flow.py
Normal file
151
homeassistant/components/smlight/config_flow.py
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
"""Config flow for SMLIGHT Zigbee integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pysmlight import Api2
|
||||||
|
from pysmlight.exceptions import SmlightAuthError, SmlightConnectionError
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components import zeroconf
|
||||||
|
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_HOST): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
STEP_AUTH_DATA_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_USERNAME): str,
|
||||||
|
vol.Required(CONF_PASSWORD): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SmlightConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for SMLIGHT Zigbee."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Initialize the config flow."""
|
||||||
|
self.client: Api2
|
||||||
|
self.host: str | None = None
|
||||||
|
|
||||||
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle the initial step."""
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
host = user_input[CONF_HOST]
|
||||||
|
self.client = Api2(host, session=async_get_clientsession(self.hass))
|
||||||
|
self.host = host
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not await self._async_check_auth_required(user_input):
|
||||||
|
return await self._async_complete_entry(user_input)
|
||||||
|
except SmlightConnectionError:
|
||||||
|
errors["base"] = "cannot_connect"
|
||||||
|
except SmlightAuthError:
|
||||||
|
return await self.async_step_auth()
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_auth(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle authentication to SLZB-06 device."""
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
try:
|
||||||
|
if not await self._async_check_auth_required(user_input):
|
||||||
|
return await self._async_complete_entry(user_input)
|
||||||
|
except SmlightConnectionError:
|
||||||
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
except SmlightAuthError:
|
||||||
|
errors["base"] = "invalid_auth"
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="auth", data_schema=STEP_AUTH_DATA_SCHEMA, errors=errors
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_zeroconf(
|
||||||
|
self, discovery_info: zeroconf.ZeroconfServiceInfo
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle a discovered Lan coordinator."""
|
||||||
|
local_name = discovery_info.hostname[:-1]
|
||||||
|
node_name = local_name.removesuffix(".local")
|
||||||
|
|
||||||
|
self.host = local_name
|
||||||
|
self.context["title_placeholders"] = {CONF_NAME: node_name}
|
||||||
|
self.client = Api2(self.host, session=async_get_clientsession(self.hass))
|
||||||
|
|
||||||
|
mac = discovery_info.properties.get("mac")
|
||||||
|
# fallback for legacy firmware
|
||||||
|
if mac is None:
|
||||||
|
info = await self.client.get_info()
|
||||||
|
mac = info.MAC
|
||||||
|
await self.async_set_unique_id(format_mac(mac))
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
return await self.async_step_confirm_discovery()
|
||||||
|
|
||||||
|
async def async_step_confirm_discovery(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle discovery confirm."""
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
user_input[CONF_HOST] = self.host
|
||||||
|
try:
|
||||||
|
if not await self._async_check_auth_required(user_input):
|
||||||
|
return await self._async_complete_entry(user_input)
|
||||||
|
|
||||||
|
except SmlightConnectionError:
|
||||||
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
|
||||||
|
except SmlightAuthError:
|
||||||
|
return await self.async_step_auth()
|
||||||
|
|
||||||
|
self._set_confirm_only()
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="confirm_discovery",
|
||||||
|
description_placeholders={"host": self.host},
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _async_check_auth_required(self, user_input: dict[str, Any]) -> bool:
|
||||||
|
"""Check if auth required and attempt to authenticate."""
|
||||||
|
if await self.client.check_auth_needed():
|
||||||
|
if user_input.get(CONF_USERNAME) and user_input.get(CONF_PASSWORD):
|
||||||
|
return not await self.client.authenticate(
|
||||||
|
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
|
||||||
|
)
|
||||||
|
raise SmlightAuthError
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def _async_complete_entry(
|
||||||
|
self, user_input: dict[str, Any]
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
info = await self.client.get_info()
|
||||||
|
await self.async_set_unique_id(format_mac(info.MAC))
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
if user_input.get(CONF_HOST) is None:
|
||||||
|
user_input[CONF_HOST] = self.host
|
||||||
|
|
||||||
|
assert info.model is not None
|
||||||
|
return self.async_create_entry(title=info.model, data=user_input)
|
11
homeassistant/components/smlight/const.py
Normal file
11
homeassistant/components/smlight/const.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
"""Constants for the SMLIGHT Zigbee integration."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
DOMAIN = "smlight"
|
||||||
|
|
||||||
|
ATTR_MANUFACTURER = "SMLIGHT"
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__package__)
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=300)
|
71
homeassistant/components/smlight/coordinator.py
Normal file
71
homeassistant/components/smlight/coordinator.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
"""DataUpdateCoordinator for Smlight."""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from pysmlight import Api2, Info, Sensors
|
||||||
|
from pysmlight.exceptions import SmlightAuthError, SmlightConnectionError
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryError
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import DOMAIN, LOGGER, SCAN_INTERVAL
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SmData:
|
||||||
|
"""SMLIGHT data stored in the DataUpdateCoordinator."""
|
||||||
|
|
||||||
|
sensors: Sensors
|
||||||
|
info: Info
|
||||||
|
|
||||||
|
|
||||||
|
class SmDataUpdateCoordinator(DataUpdateCoordinator[SmData]):
|
||||||
|
"""Class to manage fetching SMLIGHT data."""
|
||||||
|
|
||||||
|
config_entry: ConfigEntry
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, host: str) -> None:
|
||||||
|
"""Initialize the coordinator."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
LOGGER,
|
||||||
|
name=f"{DOMAIN}_{host}",
|
||||||
|
update_interval=SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.unique_id: str | None = None
|
||||||
|
self.client = Api2(host=host, session=async_get_clientsession(hass))
|
||||||
|
|
||||||
|
async def _async_setup(self) -> None:
|
||||||
|
"""Authenticate if needed during initial setup."""
|
||||||
|
if await self.client.check_auth_needed():
|
||||||
|
if (
|
||||||
|
CONF_USERNAME in self.config_entry.data
|
||||||
|
and CONF_PASSWORD in self.config_entry.data
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
await self.client.authenticate(
|
||||||
|
self.config_entry.data[CONF_USERNAME],
|
||||||
|
self.config_entry.data[CONF_PASSWORD],
|
||||||
|
)
|
||||||
|
except SmlightAuthError as err:
|
||||||
|
LOGGER.error("Failed to authenticate: %s", err)
|
||||||
|
raise ConfigEntryError from err
|
||||||
|
|
||||||
|
info = await self.client.get_info()
|
||||||
|
self.unique_id = format_mac(info.MAC)
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> SmData:
|
||||||
|
"""Fetch data from the SMLIGHT device."""
|
||||||
|
try:
|
||||||
|
return SmData(
|
||||||
|
sensors=await self.client.get_sensors(),
|
||||||
|
info=await self.client.get_info(),
|
||||||
|
)
|
||||||
|
except SmlightConnectionError as err:
|
||||||
|
raise UpdateFailed(err) from err
|
31
homeassistant/components/smlight/entity.py
Normal file
31
homeassistant/components/smlight/entity.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""Base class for all SMLIGHT entities."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.helpers.device_registry import (
|
||||||
|
CONNECTION_NETWORK_MAC,
|
||||||
|
DeviceInfo,
|
||||||
|
format_mac,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from .const import ATTR_MANUFACTURER
|
||||||
|
from .coordinator import SmDataUpdateCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
class SmEntity(CoordinatorEntity[SmDataUpdateCoordinator]):
|
||||||
|
"""Base class for all SMLight entities."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
|
||||||
|
def __init__(self, coordinator: SmDataUpdateCoordinator) -> None:
|
||||||
|
"""Initialize entity with device."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
mac = format_mac(coordinator.data.info.MAC)
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
configuration_url=f"http://{coordinator.client.host}",
|
||||||
|
connections={(CONNECTION_NETWORK_MAC, mac)},
|
||||||
|
manufacturer=ATTR_MANUFACTURER,
|
||||||
|
model=coordinator.data.info.model,
|
||||||
|
sw_version=f"core: {coordinator.data.info.sw_version} / zigbee: {coordinator.data.info.zb_version}",
|
||||||
|
)
|
15
homeassistant/components/smlight/manifest.json
Normal file
15
homeassistant/components/smlight/manifest.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"domain": "smlight",
|
||||||
|
"name": "SMLIGHT SLZB",
|
||||||
|
"codeowners": ["@tl-sl"],
|
||||||
|
"config_flow": true,
|
||||||
|
"documentation": "https://www.home-assistant.io/integrations/smlight",
|
||||||
|
"integration_type": "device",
|
||||||
|
"iot_class": "local_polling",
|
||||||
|
"requirements": ["pysmlight==0.0.12"],
|
||||||
|
"zeroconf": [
|
||||||
|
{
|
||||||
|
"type": "_slzb-06._tcp.local."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
103
homeassistant/components/smlight/sensor.py
Normal file
103
homeassistant/components/smlight/sensor.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
"""Support for SLZB-06 sensors."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from pysmlight import Sensors
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import (
|
||||||
|
SensorDeviceClass,
|
||||||
|
SensorEntity,
|
||||||
|
SensorEntityDescription,
|
||||||
|
SensorStateClass,
|
||||||
|
)
|
||||||
|
from homeassistant.const import EntityCategory, UnitOfInformation, UnitOfTemperature
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import SmConfigEntry
|
||||||
|
from .coordinator import SmDataUpdateCoordinator
|
||||||
|
from .entity import SmEntity
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class SmSensorEntityDescription(SensorEntityDescription):
|
||||||
|
"""Class describing SMLIGHT sensor entities."""
|
||||||
|
|
||||||
|
entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
value_fn: Callable[[Sensors], float | None]
|
||||||
|
|
||||||
|
|
||||||
|
SENSORS = [
|
||||||
|
SmSensorEntityDescription(
|
||||||
|
key="core_temperature",
|
||||||
|
translation_key="core_temperature",
|
||||||
|
device_class=SensorDeviceClass.TEMPERATURE,
|
||||||
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
suggested_display_precision=1,
|
||||||
|
value_fn=lambda x: x.esp32_temp,
|
||||||
|
),
|
||||||
|
SmSensorEntityDescription(
|
||||||
|
key="zigbee_temperature",
|
||||||
|
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_temp,
|
||||||
|
),
|
||||||
|
SmSensorEntityDescription(
|
||||||
|
key="ram_usage",
|
||||||
|
translation_key="ram_usage",
|
||||||
|
device_class=SensorDeviceClass.DATA_SIZE,
|
||||||
|
native_unit_of_measurement=UnitOfInformation.KILOBYTES,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
value_fn=lambda x: x.ram_usage,
|
||||||
|
),
|
||||||
|
SmSensorEntityDescription(
|
||||||
|
key="fs_usage",
|
||||||
|
translation_key="fs_usage",
|
||||||
|
device_class=SensorDeviceClass.DATA_SIZE,
|
||||||
|
native_unit_of_measurement=UnitOfInformation.KILOBYTES,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
value_fn=lambda x: x.fs_used,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: SmConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up SMLIGHT sensor based on a config entry."""
|
||||||
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
SmSensorEntity(coordinator, description) for description in SENSORS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SmSensorEntity(SmEntity, SensorEntity):
|
||||||
|
"""Representation of a slzb sensor."""
|
||||||
|
|
||||||
|
entity_description: SmSensorEntityDescription
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: SmDataUpdateCoordinator,
|
||||||
|
description: SmSensorEntityDescription,
|
||||||
|
) -> None:
|
||||||
|
"""Initiate slzb sensor."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
self.entity_description = description
|
||||||
|
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self) -> float | None:
|
||||||
|
"""Return the sensor value."""
|
||||||
|
return self.entity_description.value_fn(self.coordinator.data.sensors)
|
49
homeassistant/components/smlight/strings.json
Normal file
49
homeassistant/components/smlight/strings.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"description": "Set up SMLIGHT Zigbee Integration",
|
||||||
|
"data": {
|
||||||
|
"host": "[%key:common::config_flow::data::host%]"
|
||||||
|
},
|
||||||
|
"data_description": {
|
||||||
|
"host": "The hostname or IP address of the SMLIGHT SLZB-06x device"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"description": "Please enter the username and password",
|
||||||
|
"data": {
|
||||||
|
"username": "[%key:common::config_flow::data::username%]",
|
||||||
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"confirm_discovery": {
|
||||||
|
"description": "Do you want to set up SMLIGHT at {host}?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"sensor": {
|
||||||
|
"zigbee_temperature": {
|
||||||
|
"name": "Zigbee chip temp"
|
||||||
|
},
|
||||||
|
"core_temperature": {
|
||||||
|
"name": "Core chip temp"
|
||||||
|
},
|
||||||
|
"fs_usage": {
|
||||||
|
"name": "Filesystem usage"
|
||||||
|
},
|
||||||
|
"ram_usage": {
|
||||||
|
"name": "RAM usage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -525,6 +525,7 @@ FLOWS = {
|
||||||
"smartthings",
|
"smartthings",
|
||||||
"smarttub",
|
"smarttub",
|
||||||
"smhi",
|
"smhi",
|
||||||
|
"smlight",
|
||||||
"sms",
|
"sms",
|
||||||
"snapcast",
|
"snapcast",
|
||||||
"snooz",
|
"snooz",
|
||||||
|
|
|
@ -5588,6 +5588,12 @@
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling"
|
||||||
},
|
},
|
||||||
|
"smlight": {
|
||||||
|
"name": "SMLIGHT SLZB",
|
||||||
|
"integration_type": "device",
|
||||||
|
"config_flow": true,
|
||||||
|
"iot_class": "local_polling"
|
||||||
|
},
|
||||||
"sms": {
|
"sms": {
|
||||||
"name": "SMS notifications via GSM-modem",
|
"name": "SMS notifications via GSM-modem",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
|
|
|
@ -747,6 +747,9 @@ ZEROCONF = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"_slzb-06._tcp.local.": [
|
"_slzb-06._tcp.local.": [
|
||||||
|
{
|
||||||
|
"domain": "smlight",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"domain": "zha",
|
"domain": "zha",
|
||||||
"name": "slzb-06*",
|
"name": "slzb-06*",
|
||||||
|
|
|
@ -2210,6 +2210,9 @@ pysmartthings==0.7.8
|
||||||
# homeassistant.components.edl21
|
# homeassistant.components.edl21
|
||||||
pysml==0.0.12
|
pysml==0.0.12
|
||||||
|
|
||||||
|
# homeassistant.components.smlight
|
||||||
|
pysmlight==0.0.12
|
||||||
|
|
||||||
# homeassistant.components.snmp
|
# homeassistant.components.snmp
|
||||||
pysnmp==6.2.5
|
pysnmp==6.2.5
|
||||||
|
|
||||||
|
|
|
@ -1764,6 +1764,9 @@ pysmartthings==0.7.8
|
||||||
# homeassistant.components.edl21
|
# homeassistant.components.edl21
|
||||||
pysml==0.0.12
|
pysml==0.0.12
|
||||||
|
|
||||||
|
# homeassistant.components.smlight
|
||||||
|
pysmlight==0.0.12
|
||||||
|
|
||||||
# homeassistant.components.snmp
|
# homeassistant.components.snmp
|
||||||
pysnmp==6.2.5
|
pysnmp==6.2.5
|
||||||
|
|
||||||
|
|
1
tests/components/smlight/__init__.py
Normal file
1
tests/components/smlight/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""Tests for the SMLIGHT Zigbee adapter integration."""
|
74
tests/components/smlight/conftest.py
Normal file
74
tests/components/smlight/conftest.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
"""Common fixtures for the SMLIGHT Zigbee tests."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
from pysmlight.web import Info, Sensors
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.smlight.const import DOMAIN
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, load_json_object_fixture
|
||||||
|
|
||||||
|
MOCK_HOST = "slzb-06.local"
|
||||||
|
MOCK_USERNAME = "test-user"
|
||||||
|
MOCK_PASSWORD = "test-pass"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
|
"""Return the default mocked config entry."""
|
||||||
|
return MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
CONF_USERNAME: MOCK_USERNAME,
|
||||||
|
CONF_PASSWORD: MOCK_PASSWORD,
|
||||||
|
},
|
||||||
|
unique_id="aa:bb:cc:dd:ee:ff",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
|
"""Override async_setup_entry."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.smlight.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
|
yield mock_setup_entry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_smlight_client(request: pytest.FixtureRequest) -> Generator[MagicMock]:
|
||||||
|
"""Mock the SMLIGHT API client."""
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.smlight.coordinator.Api2", autospec=True
|
||||||
|
) as smlight_mock,
|
||||||
|
patch("homeassistant.components.smlight.config_flow.Api2", new=smlight_mock),
|
||||||
|
):
|
||||||
|
api = smlight_mock.return_value
|
||||||
|
api.host = MOCK_HOST
|
||||||
|
api.get_info.return_value = Info.from_dict(
|
||||||
|
load_json_object_fixture("info.json", DOMAIN)
|
||||||
|
)
|
||||||
|
api.get_sensors.return_value = Sensors.from_dict(
|
||||||
|
load_json_object_fixture("sensors.json", DOMAIN)
|
||||||
|
)
|
||||||
|
|
||||||
|
api.check_auth_needed.return_value = False
|
||||||
|
api.authenticate.return_value = True
|
||||||
|
|
||||||
|
yield api
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_integration(hass: HomeAssistant, mock_config_entry: MockConfigEntry):
|
||||||
|
"""Set up the integration."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
return mock_config_entry
|
16
tests/components/smlight/fixtures/info.json
Normal file
16
tests/components/smlight/fixtures/info.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"coord_mode": 0,
|
||||||
|
"device_ip": "192.168.1.161",
|
||||||
|
"fs_total": 3456,
|
||||||
|
"fw_channel": "dev",
|
||||||
|
"MAC": "AA:BB:CC:DD:EE:FF",
|
||||||
|
"model": "SLZB-06p7",
|
||||||
|
"ram_total": 296,
|
||||||
|
"sw_version": "v2.3.1.dev",
|
||||||
|
"wifi_mode": 0,
|
||||||
|
"zb_flash_size": 704,
|
||||||
|
"zb_hw": "CC2652P7",
|
||||||
|
"zb_ram_size": 152,
|
||||||
|
"zb_version": -1,
|
||||||
|
"zb_type": -1
|
||||||
|
}
|
14
tests/components/smlight/fixtures/sensors.json
Normal file
14
tests/components/smlight/fixtures/sensors.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"esp32_temp": 35.0,
|
||||||
|
"zb_temp": 32.7,
|
||||||
|
"uptime": 508125,
|
||||||
|
"socket_uptime": 127,
|
||||||
|
"ram_usage": 99,
|
||||||
|
"fs_used": 188,
|
||||||
|
"ethernet": true,
|
||||||
|
"wifi_connected": false,
|
||||||
|
"wifi_status": 255,
|
||||||
|
"disable_leds": false,
|
||||||
|
"night_mode": false,
|
||||||
|
"auto_zigbee": false
|
||||||
|
}
|
33
tests/components/smlight/snapshots/test_init.ambr
Normal file
33
tests/components/smlight/snapshots/test_init.ambr
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_device_info
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': 'http://slzb-06.local',
|
||||||
|
'connections': set({
|
||||||
|
tuple(
|
||||||
|
'mac',
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'SMLIGHT',
|
||||||
|
'model': 'SLZB-06p7',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'Mock Title',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': 'core: v2.3.1.dev / zigbee: -1',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
741
tests/components/smlight/snapshots/test_sensor.ambr
Normal file
741
tests/components/smlight/snapshots/test_sensor.ambr
Normal file
|
@ -0,0 +1,741 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_sensors[sensor.mock_title_core_chip_temp-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.mock_title_core_chip_temp',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Core chip temp',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'core_temperature',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_core_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_core_chip_temp-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'Mock Title Core chip temp',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mock_title_core_chip_temp',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '35.0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_filesystem_usage-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.mock_title_filesystem_usage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Filesystem usage',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'fs_usage',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_fs_usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_filesystem_usage-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'data_size',
|
||||||
|
'friendly_name': 'Mock Title Filesystem usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mock_title_filesystem_usage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '188',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_ram_usage-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.mock_title_ram_usage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'RAM usage',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'ram_usage',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_ram_usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_ram_usage-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'data_size',
|
||||||
|
'friendly_name': 'Mock Title RAM usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mock_title_ram_usage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '99',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_zigbee_chip_temp-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.mock_title_zigbee_chip_temp',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Zigbee chip temp',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'zigbee_temperature',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_zigbee_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.mock_title_zigbee_chip_temp-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'Mock Title Zigbee chip temp',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.mock_title_zigbee_chip_temp',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '32.7',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_core_chip_temp-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_core_chip_temp',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Core chip temp',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'core_temperature',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_core_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_core_chip_temp-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'slzb-06 Core chip temp',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_core_chip_temp',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '35.0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_core_chip_temp]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'slzb-06 Core chip temp',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_core_chip_temp',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '35.0',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_core_chip_temp].1
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_core_chip_temp',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Core chip temp',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'core_temperature',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_core_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_core_chip_temp].2
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': 'http://slzb-06.local',
|
||||||
|
'connections': set({
|
||||||
|
tuple(
|
||||||
|
'mac',
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'SMLIGHT',
|
||||||
|
'model': 'SLZB-06p7',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'slzb-06',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': 'core: v2.3.1.dev / zigbee: -1',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_filesystem_usage-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_filesystem_usage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Filesystem usage',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'fs_usage',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_fs_usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_filesystem_usage-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'data_size',
|
||||||
|
'friendly_name': 'slzb-06 Filesystem usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_filesystem_usage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '188',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_filesystem_usage]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'data_size',
|
||||||
|
'friendly_name': 'slzb-06 Filesystem usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_filesystem_usage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '188',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_filesystem_usage].1
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_filesystem_usage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Filesystem usage',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'fs_usage',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_fs_usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_filesystem_usage].2
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': 'http://slzb-06.local',
|
||||||
|
'connections': set({
|
||||||
|
tuple(
|
||||||
|
'mac',
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'SMLIGHT',
|
||||||
|
'model': 'SLZB-06p7',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'slzb-06',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': 'core: v2.3.1.dev / zigbee: -1',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_ram_usage-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_ram_usage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'RAM usage',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'ram_usage',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_ram_usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_ram_usage-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'data_size',
|
||||||
|
'friendly_name': 'slzb-06 RAM usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_ram_usage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '99',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_ram_usage]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'data_size',
|
||||||
|
'friendly_name': 'slzb-06 RAM usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_ram_usage',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '99',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_ram_usage].1
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_ram_usage',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.DATA_SIZE: 'data_size'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'RAM usage',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'ram_usage',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_ram_usage',
|
||||||
|
'unit_of_measurement': <UnitOfInformation.KILOBYTES: 'kB'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_ram_usage].2
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': 'http://slzb-06.local',
|
||||||
|
'connections': set({
|
||||||
|
tuple(
|
||||||
|
'mac',
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'SMLIGHT',
|
||||||
|
'model': 'SLZB-06p7',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'slzb-06',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': 'core: v2.3.1.dev / zigbee: -1',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_zigbee_chip_temp-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_zigbee_chip_temp',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Zigbee chip temp',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'zigbee_temperature',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_zigbee_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_zigbee_chip_temp-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'slzb-06 Zigbee chip temp',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_zigbee_chip_temp',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '32.7',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_zigbee_chip_temp]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'slzb-06 Zigbee chip temp',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.slzb_06_zigbee_chip_temp',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '32.7',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_zigbee_chip_temp].1
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'sensor',
|
||||||
|
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||||
|
'entity_id': 'sensor.slzb_06_zigbee_chip_temp',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
'sensor': dict({
|
||||||
|
'suggested_display_precision': 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Zigbee chip temp',
|
||||||
|
'platform': 'smlight',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'zigbee_temperature',
|
||||||
|
'unique_id': 'aa:bb:cc:dd:ee:ff_zigbee_temperature',
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_sensors[sensor.slzb_06_zigbee_chip_temp].2
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': 'http://slzb-06.local',
|
||||||
|
'connections': set({
|
||||||
|
tuple(
|
||||||
|
'mac',
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'SMLIGHT',
|
||||||
|
'model': 'SLZB-06p7',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'slzb-06',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': 'core: v2.3.1.dev / zigbee: -1',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
365
tests/components/smlight/test_config_flow.py
Normal file
365
tests/components/smlight/test_config_flow.py
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
"""Test the SMLIGHT SLZB config flow."""
|
||||||
|
|
||||||
|
from ipaddress import ip_address
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
|
||||||
|
from pysmlight.exceptions import SmlightAuthError, SmlightConnectionError
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components import zeroconf
|
||||||
|
from homeassistant.components.smlight.const import DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
|
from .conftest import MOCK_HOST, MOCK_PASSWORD, MOCK_USERNAME
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
DISCOVERY_INFO = zeroconf.ZeroconfServiceInfo(
|
||||||
|
ip_address=ip_address("127.0.0.1"),
|
||||||
|
ip_addresses=[ip_address("127.0.0.1")],
|
||||||
|
hostname="slzb-06.local.",
|
||||||
|
name="mock_name",
|
||||||
|
port=6638,
|
||||||
|
properties={"mac": "AA:BB:CC:DD:EE:FF"},
|
||||||
|
type="mock_type",
|
||||||
|
)
|
||||||
|
|
||||||
|
DISCOVERY_INFO_LEGACY = zeroconf.ZeroconfServiceInfo(
|
||||||
|
ip_address=ip_address("127.0.0.1"),
|
||||||
|
ip_addresses=[ip_address("127.0.0.1")],
|
||||||
|
hostname="slzb-06.local.",
|
||||||
|
name="mock_name",
|
||||||
|
port=6638,
|
||||||
|
properties={},
|
||||||
|
type="mock_type",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_smlight_client")
|
||||||
|
async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||||
|
"""Test the full manual user flow."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result2["title"] == "SLZB-06p7"
|
||||||
|
assert result2["data"] == {
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
}
|
||||||
|
assert result2["context"]["unique_id"] == "aa:bb:cc:dd:ee:ff"
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zeroconf_flow(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_smlight_client: MagicMock,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the zeroconf flow."""
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=DISCOVERY_INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["description_placeholders"] == {"host": MOCK_HOST}
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "confirm_discovery"
|
||||||
|
|
||||||
|
progress = hass.config_entries.flow.async_progress()
|
||||||
|
assert len(progress) == 1
|
||||||
|
assert progress[0]["flow_id"] == result["flow_id"]
|
||||||
|
assert progress[0]["context"]["confirm_only"] is True
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result2["context"]["source"] == "zeroconf"
|
||||||
|
assert result2["context"]["unique_id"] == "aa:bb:cc:dd:ee:ff"
|
||||||
|
assert result2["title"] == "SLZB-06p7"
|
||||||
|
assert result2["data"] == {
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
assert len(mock_smlight_client.get_info.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zeroconf_flow_auth(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_smlight_client: MagicMock,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the full zeroconf flow including authentication."""
|
||||||
|
mock_smlight_client.check_auth_needed.return_value = True
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=DISCOVERY_INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["description_placeholders"] == {"host": MOCK_HOST}
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "confirm_discovery"
|
||||||
|
|
||||||
|
progress = hass.config_entries.flow.async_progress()
|
||||||
|
assert len(progress) == 1
|
||||||
|
assert progress[0]["flow_id"] == result["flow_id"]
|
||||||
|
assert progress[0]["context"]["confirm_only"] is True
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.FORM
|
||||||
|
assert result2["step_id"] == "auth"
|
||||||
|
|
||||||
|
progress2 = hass.config_entries.flow.async_progress()
|
||||||
|
assert len(progress2) == 1
|
||||||
|
assert progress2[0]["flow_id"] == result["flow_id"]
|
||||||
|
|
||||||
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
CONF_USERNAME: MOCK_USERNAME,
|
||||||
|
CONF_PASSWORD: MOCK_PASSWORD,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result3["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result3["context"]["source"] == "zeroconf"
|
||||||
|
assert result3["context"]["unique_id"] == "aa:bb:cc:dd:ee:ff"
|
||||||
|
assert result3["title"] == "SLZB-06p7"
|
||||||
|
assert result3["data"] == {
|
||||||
|
CONF_USERNAME: MOCK_USERNAME,
|
||||||
|
CONF_PASSWORD: MOCK_PASSWORD,
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
assert len(mock_smlight_client.get_info.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_smlight_client")
|
||||||
|
async def test_user_device_exists_abort(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test we abort user flow if device already configured."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_smlight_client")
|
||||||
|
async def test_zeroconf_device_exists_abort(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test we abort zeroconf flow if device already configured."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_ZEROCONF},
|
||||||
|
data=DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_invalid_auth(
|
||||||
|
hass: HomeAssistant, mock_smlight_client: MagicMock, mock_setup_entry: AsyncMock
|
||||||
|
) -> None:
|
||||||
|
"""Test we handle invalid auth."""
|
||||||
|
mock_smlight_client.check_auth_needed.return_value = True
|
||||||
|
mock_smlight_client.authenticate.side_effect = SmlightAuthError
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "auth"
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "test",
|
||||||
|
CONF_PASSWORD: "bad",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.FORM
|
||||||
|
assert result2["errors"] == {"base": "invalid_auth"}
|
||||||
|
assert result2["step_id"] == "auth"
|
||||||
|
|
||||||
|
mock_smlight_client.authenticate.side_effect = None
|
||||||
|
|
||||||
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: "test",
|
||||||
|
CONF_PASSWORD: "good",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result3["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result3["title"] == "SLZB-06p7"
|
||||||
|
assert result3["data"] == {
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
CONF_USERNAME: "test",
|
||||||
|
CONF_PASSWORD: "good",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
assert len(mock_smlight_client.get_info.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_cannot_connect(
|
||||||
|
hass: HomeAssistant, mock_smlight_client: MagicMock, mock_setup_entry: AsyncMock
|
||||||
|
) -> None:
|
||||||
|
"""Test we handle user cannot connect error."""
|
||||||
|
mock_smlight_client.check_auth_needed.side_effect = SmlightConnectionError
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_HOST: "unknown.local",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
mock_smlight_client.check_auth_needed.side_effect = None
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result2["title"] == "SLZB-06p7"
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
assert len(mock_smlight_client.get_info.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_auth_cannot_connect(
|
||||||
|
hass: HomeAssistant, mock_smlight_client: MagicMock
|
||||||
|
) -> None:
|
||||||
|
"""Test we abort auth step on cannot connect error."""
|
||||||
|
mock_smlight_client.check_auth_needed.return_value = True
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "auth"
|
||||||
|
|
||||||
|
mock_smlight_client.check_auth_needed.side_effect = SmlightConnectionError
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_USERNAME: MOCK_USERNAME,
|
||||||
|
CONF_PASSWORD: MOCK_PASSWORD,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.ABORT
|
||||||
|
assert result2["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zeroconf_cannot_connect(
|
||||||
|
hass: HomeAssistant, mock_smlight_client: MagicMock
|
||||||
|
) -> None:
|
||||||
|
"""Test we abort flow on zeroconf cannot connect error."""
|
||||||
|
mock_smlight_client.check_auth_needed.side_effect = SmlightConnectionError
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_ZEROCONF},
|
||||||
|
data=DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "confirm_discovery"
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.ABORT
|
||||||
|
assert result2["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_smlight_client")
|
||||||
|
async def test_zeroconf_legacy_mac(
|
||||||
|
hass: HomeAssistant, mock_smlight_client: MagicMock, mock_setup_entry: AsyncMock
|
||||||
|
) -> None:
|
||||||
|
"""Test we can get unique id MAC address for older firmwares."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_ZEROCONF},
|
||||||
|
data=DISCOVERY_INFO_LEGACY,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["description_placeholders"] == {"host": MOCK_HOST}
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input={}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result2["context"]["source"] == "zeroconf"
|
||||||
|
assert result2["context"]["unique_id"] == "aa:bb:cc:dd:ee:ff"
|
||||||
|
assert result2["title"] == "SLZB-06p7"
|
||||||
|
assert result2["data"] == {
|
||||||
|
CONF_HOST: MOCK_HOST,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
assert len(mock_smlight_client.get_info.mock_calls) == 2
|
94
tests/components/smlight/test_init.py
Normal file
94
tests/components/smlight/test_init.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
"Test SMLIGHT SLZB device integration initialization."
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
from pysmlight.exceptions import SmlightAuthError, SmlightConnectionError
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.smlight.const import SCAN_INTERVAL
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.const import STATE_UNAVAILABLE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
|
||||||
|
from .conftest import setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
pytestmark = [
|
||||||
|
pytest.mark.usefixtures(
|
||||||
|
"mock_smlight_client",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_setup_entry(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test async_setup_entry."""
|
||||||
|
entry = await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
assert entry.unique_id == "aa:bb:cc:dd:ee:ff"
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_setup_auth_failed(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_smlight_client: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test async_setup_entry when authentication fails."""
|
||||||
|
mock_smlight_client.check_auth_needed.return_value = True
|
||||||
|
mock_smlight_client.authenticate.side_effect = SmlightAuthError
|
||||||
|
entry = await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_update_failed(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_smlight_client: MagicMock,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test update failed due to connection error."""
|
||||||
|
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
entity = hass.states.get("sensor.mock_title_core_chip_temp")
|
||||||
|
assert entity.state is not STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
mock_smlight_client.get_info.side_effect = SmlightConnectionError
|
||||||
|
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity = hass.states.get("sensor.mock_title_core_chip_temp")
|
||||||
|
assert entity is not None
|
||||||
|
assert entity.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_info(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test device registry information."""
|
||||||
|
entry = await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
device_entry = device_registry.async_get_device(
|
||||||
|
connections={(dr.CONNECTION_NETWORK_MAC, entry.unique_id)}
|
||||||
|
)
|
||||||
|
assert device_entry is not None
|
||||||
|
assert device_entry == snapshot
|
54
tests/components/smlight/test_sensor.py
Normal file
54
tests/components/smlight/test_sensor.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
"""Tests for the SMLIGHT sensor platform."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.const import 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
|
||||||
|
|
||||||
|
pytestmark = [
|
||||||
|
pytest.mark.usefixtures(
|
||||||
|
"mock_smlight_client",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def platforms() -> Platform | list[Platform]:
|
||||||
|
"""Platforms, which should be loaded during the test."""
|
||||||
|
return Platform.SENSOR
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_sensors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test the SMLIGHT sensors."""
|
||||||
|
entry = await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_disabled_by_default_sensors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test the disabled by default SMLIGHT sensors."""
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
for sensor in ("ram_usage", "filesystem_usage"):
|
||||||
|
assert not hass.states.get(f"sensor.mock_title_{sensor}")
|
||||||
|
|
||||||
|
assert (entry := entity_registry.async_get(f"sensor.mock_title_{sensor}"))
|
||||||
|
assert entry.disabled
|
||||||
|
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
|
Loading…
Add table
Reference in a new issue