mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Use sub-devices for Shelly multi-channel devices (#144100)
* Shelly RPC sub-devices
* Better varaible name
* Add get_rpc_device_info helper
* Revert channel name changes
* Use get_rpc_device_info
* Add get_rpc_device_info helper
* Use get_block_device_info
* Use helpers in the button platform
* Fix channel name and roller mode for block devices
* Fix EM3 gen1
* Fix channel name for RPC devices
* Revert test changes
* Fix/improve test_block_get_block_channel_name
* Fix test_get_rpc_channel_name_multiple_components
* Fix tests
* Fix tests
* Fix tests
* Use key instead of index to generate sub-device identifier
* Improve logic for Pro RGBWW PM
* Split channels for em1
* Better channel name
* Cleaning
* has_entity_name is True
* Add get_block_sub_device_name() function
* Improve block functions
* Add get_rpc_sub_device_name() function
* Remove _attr_name
* Remove name for button with device class
* Fix names of virtual components
* Better Input name
* Fix get_rpc_channel_name()
* Fix names for Inputs
* get_rpc_channel_name() improvement
* Better variable name
* Clean RPC functions
* Fix input_name type
* Fix test
* Fix entity_ids for Blu Trv
* Fix get_block_channel_name()
* Fix for Blu Trv, once again
* Revert name for reboot button
* Fix button tests
* Fix tests
* Fix coordinator tests
* Fix tests for cover platform
* Fix tests for event platform
* Fix entity_ids in init tests
* Fix get_block_channel_name() for lights
* Fix tests for light platform
* Fix test for logbook
* Update snapshots for number platform
* Fix tests for sensor platform
* Fix tests for switch platform
* Fix tests for utils
* Uncomment
* Fix tests for flood
* Fix Valve entity name
* Fix climate tests
* Fix test for diagnostics
* Fix tests for init
* Remove old snapshots
* Add tests for 2PM Gen3
* Add comment
* More tests
* Cleaning
* Clean fixtures
* Update tests
* Anonymize coordinates in fixtures
* Split Pro 3EM entities into sub-devices
* Make sub-device names more unique
* 3EM (gen1) does not support sub-devices
* Coverage
* Rename "device temperature" sensor to the "relay temperature"
* Update tests after rebase
* Support sub-devices for 3EM (gen1)
* Mark has-entity-name rule as done 🎉
* Rename `relay temperature` to `temperature`
This commit is contained in:
parent
d4333665fc
commit
7f4cc99a3e
@ -15,7 +15,6 @@ from homeassistant.components.binary_sensor import (
|
||||
)
|
||||
from homeassistant.const import STATE_ON, EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
@ -36,6 +35,7 @@ from .entity import (
|
||||
)
|
||||
from .utils import (
|
||||
async_remove_orphaned_entities,
|
||||
get_blu_trv_device_info,
|
||||
get_device_entry_gen,
|
||||
get_virtual_component_ids,
|
||||
is_block_momentary_input,
|
||||
@ -87,8 +87,8 @@ class RpcBluTrvBinarySensor(RpcBinarySensor):
|
||||
|
||||
super().__init__(coordinator, key, attribute, description)
|
||||
ble_addr: str = coordinator.device.config[key]["addr"]
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_BLUETOOTH, ble_addr)}
|
||||
self._attr_device_info = get_blu_trv_device_info(
|
||||
coordinator.device.config[key], ble_addr, coordinator.mac
|
||||
)
|
||||
|
||||
|
||||
@ -190,7 +190,6 @@ RPC_SENSORS: Final = {
|
||||
"input": RpcBinarySensorDescription(
|
||||
key="input",
|
||||
sub_key="state",
|
||||
name="Input",
|
||||
device_class=BinarySensorDeviceClass.POWER,
|
||||
entity_registry_enabled_default=False,
|
||||
removal_condition=is_rpc_momentary_input,
|
||||
@ -264,7 +263,6 @@ RPC_SENSORS: Final = {
|
||||
"boolean": RpcBinarySensorDescription(
|
||||
key="boolean",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
),
|
||||
"calibration": RpcBinarySensorDescription(
|
||||
key="blutrv",
|
||||
|
@ -19,18 +19,20 @@ from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_BLUETOOTH,
|
||||
CONNECTION_NETWORK_MAC,
|
||||
DeviceInfo,
|
||||
)
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .const import DOMAIN, LOGGER, SHELLY_GAS_MODELS
|
||||
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
|
||||
from .utils import get_device_entry_gen, get_rpc_key_ids
|
||||
from .utils import (
|
||||
get_block_device_info,
|
||||
get_blu_trv_device_info,
|
||||
get_device_entry_gen,
|
||||
get_rpc_device_info,
|
||||
get_rpc_key_ids,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -168,6 +170,7 @@ class ShellyBaseButton(
|
||||
):
|
||||
"""Defines a Shelly base button."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: ShellyButtonDescription[
|
||||
ShellyRpcCoordinator | ShellyBlockCoordinator
|
||||
]
|
||||
@ -228,8 +231,15 @@ class ShellyButton(ShellyBaseButton):
|
||||
"""Initialize Shelly button."""
|
||||
super().__init__(coordinator, description)
|
||||
|
||||
self._attr_name = f"{coordinator.device.name} {description.name}"
|
||||
self._attr_unique_id = f"{coordinator.mac}_{description.key}"
|
||||
if isinstance(coordinator, ShellyBlockCoordinator):
|
||||
self._attr_device_info = get_block_device_info(
|
||||
coordinator.device, coordinator.mac
|
||||
)
|
||||
else:
|
||||
self._attr_device_info = get_rpc_device_info(
|
||||
coordinator.device, coordinator.mac
|
||||
)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
)
|
||||
@ -256,15 +266,11 @@ class ShellyBluTrvButton(ShellyBaseButton):
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator, description)
|
||||
|
||||
ble_addr: str = coordinator.device.config[f"{BLU_TRV_IDENTIFIER}:{id_}"]["addr"]
|
||||
device_name = (
|
||||
coordinator.device.config[f"{BLU_TRV_IDENTIFIER}:{id_}"]["name"]
|
||||
or f"shellyblutrv-{ble_addr.replace(':', '')}"
|
||||
)
|
||||
self._attr_name = f"{device_name} {description.name}"
|
||||
config = coordinator.device.config[f"{BLU_TRV_IDENTIFIER}:{id_}"]
|
||||
ble_addr: str = config["addr"]
|
||||
self._attr_unique_id = f"{ble_addr}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_BLUETOOTH, ble_addr)}
|
||||
self._attr_device_info = get_blu_trv_device_info(
|
||||
config, ble_addr, coordinator.mac
|
||||
)
|
||||
self._id = id_
|
||||
|
||||
|
@ -7,7 +7,7 @@ from dataclasses import asdict, dataclass
|
||||
from typing import Any, cast
|
||||
|
||||
from aioshelly.block_device import Block
|
||||
from aioshelly.const import BLU_TRV_IDENTIFIER, BLU_TRV_MODEL_NAME, RPC_GENERATIONS
|
||||
from aioshelly.const import BLU_TRV_IDENTIFIER, RPC_GENERATIONS
|
||||
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
@ -22,11 +22,6 @@ from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, State, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_BLUETOOTH,
|
||||
CONNECTION_NETWORK_MAC,
|
||||
DeviceInfo,
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity
|
||||
@ -46,6 +41,9 @@ from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoo
|
||||
from .entity import ShellyRpcEntity, rpc_call
|
||||
from .utils import (
|
||||
async_remove_shelly_entity,
|
||||
get_block_device_info,
|
||||
get_block_entity_name,
|
||||
get_blu_trv_device_info,
|
||||
get_device_entry_gen,
|
||||
get_rpc_key_ids,
|
||||
is_rpc_thermostat_internal_actuator,
|
||||
@ -181,6 +179,7 @@ class BlockSleepingClimate(
|
||||
)
|
||||
_attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -199,7 +198,6 @@ class BlockSleepingClimate(
|
||||
self.last_state_attributes: Mapping[str, Any]
|
||||
self._preset_modes: list[str] = []
|
||||
self._last_target_temp = SHTRV_01_TEMPERATURE_SETTINGS["default"]
|
||||
self._attr_name = coordinator.name
|
||||
|
||||
if self.block is not None and self.device_block is not None:
|
||||
self._unique_id = f"{self.coordinator.mac}-{self.block.description}"
|
||||
@ -212,8 +210,11 @@ class BlockSleepingClimate(
|
||||
]
|
||||
elif entry is not None:
|
||||
self._unique_id = entry.unique_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)},
|
||||
self._attr_device_info = get_block_device_info(
|
||||
coordinator.device, coordinator.mac, sensor_block
|
||||
)
|
||||
self._attr_name = get_block_entity_name(
|
||||
self.coordinator.device, sensor_block, None
|
||||
)
|
||||
|
||||
self._channel = cast(int, self._unique_id.split("_")[1])
|
||||
@ -553,7 +554,6 @@ class RpcBluTrvClimate(ShellyRpcEntity, ClimateEntity):
|
||||
_attr_hvac_mode = HVACMode.HEAT
|
||||
_attr_target_temperature_step = BLU_TRV_TEMPERATURE_SETTINGS["step"]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: ShellyRpcCoordinator, id_: int) -> None:
|
||||
"""Initialize."""
|
||||
@ -563,19 +563,9 @@ class RpcBluTrvClimate(ShellyRpcEntity, ClimateEntity):
|
||||
self._config = coordinator.device.config[f"{BLU_TRV_IDENTIFIER}:{id_}"]
|
||||
ble_addr: str = self._config["addr"]
|
||||
self._attr_unique_id = f"{ble_addr}-{self.key}"
|
||||
name = self._config["name"] or f"shellyblutrv-{ble_addr.replace(':', '')}"
|
||||
model_id = self._config.get("local_name")
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_BLUETOOTH, ble_addr)},
|
||||
identifiers={(DOMAIN, ble_addr)},
|
||||
via_device=(DOMAIN, self.coordinator.mac),
|
||||
manufacturer="Shelly",
|
||||
model=BLU_TRV_MODEL_NAME.get(model_id),
|
||||
model_id=model_id,
|
||||
name=name,
|
||||
self._attr_device_info = get_blu_trv_device_info(
|
||||
self._config, ble_addr, self.coordinator.mac
|
||||
)
|
||||
# Added intentionally to the constructor to avoid double name from base class
|
||||
self._attr_name = None
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
|
@ -258,6 +258,7 @@ DEVICES_WITHOUT_FIRMWARE_CHANGELOG = (
|
||||
|
||||
CONF_GEN = "gen"
|
||||
|
||||
VIRTUAL_COMPONENTS = ("boolean", "enum", "input", "number", "text")
|
||||
VIRTUAL_COMPONENTS_MAP = {
|
||||
"binary_sensor": {"types": ["boolean"], "modes": ["label"]},
|
||||
"number": {"types": ["number"], "modes": ["field", "slider"]},
|
||||
@ -285,3 +286,5 @@ ROLE_TO_DEVICE_CLASS_MAP = {
|
||||
# We want to check only the first 5 KB of the script if it contains emitEvent()
|
||||
# so that the integration startup remains fast.
|
||||
MAX_SCRIPT_SIZE = 5120
|
||||
|
||||
All_LIGHT_TYPES = ("cct", "light", "rgb", "rgbw")
|
||||
|
@ -13,7 +13,6 @@ from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCal
|
||||
from homeassistant.core import HomeAssistant, State, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
@ -24,7 +23,9 @@ from .const import CONF_SLEEP_PERIOD, DOMAIN, LOGGER
|
||||
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
|
||||
from .utils import (
|
||||
async_remove_shelly_entity,
|
||||
get_block_device_info,
|
||||
get_block_entity_name,
|
||||
get_rpc_device_info,
|
||||
get_rpc_entity_name,
|
||||
get_rpc_key_instances,
|
||||
)
|
||||
@ -353,13 +354,15 @@ def rpc_call[_T: ShellyRpcEntity, **_P](
|
||||
class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
||||
"""Helper class to represent a block entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None:
|
||||
"""Initialize Shelly entity."""
|
||||
super().__init__(coordinator)
|
||||
self.block = block
|
||||
self._attr_name = get_block_entity_name(coordinator.device, block)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
self._attr_device_info = get_block_device_info(
|
||||
coordinator.device, coordinator.mac, block
|
||||
)
|
||||
self._attr_unique_id = f"{coordinator.mac}-{block.description}"
|
||||
|
||||
@ -395,12 +398,14 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
||||
class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]):
|
||||
"""Helper class to represent a rpc entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: ShellyRpcCoordinator, key: str) -> None:
|
||||
"""Initialize Shelly entity."""
|
||||
super().__init__(coordinator)
|
||||
self.key = key
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
self._attr_device_info = get_rpc_device_info(
|
||||
coordinator.device, coordinator.mac, key
|
||||
)
|
||||
self._attr_unique_id = f"{coordinator.mac}-{key}"
|
||||
self._attr_name = get_rpc_entity_name(coordinator.device, key)
|
||||
@ -497,6 +502,7 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, Entity):
|
||||
class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
||||
"""Class to load info from REST."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: RestEntityDescription
|
||||
|
||||
def __init__(
|
||||
@ -514,8 +520,8 @@ class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
||||
coordinator.device, None, description.name
|
||||
)
|
||||
self._attr_unique_id = f"{coordinator.mac}-{attribute}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
self._attr_device_info = get_block_device_info(
|
||||
coordinator.device, coordinator.mac
|
||||
)
|
||||
self._last_value = None
|
||||
|
||||
@ -623,8 +629,8 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity):
|
||||
self.block: Block | None = block # type: ignore[assignment]
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
self._attr_device_info = get_block_device_info(
|
||||
coordinator.device, coordinator.mac, block
|
||||
)
|
||||
|
||||
if block is not None:
|
||||
@ -632,7 +638,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity):
|
||||
f"{self.coordinator.mac}-{block.description}-{attribute}"
|
||||
)
|
||||
self._attr_name = get_block_entity_name(
|
||||
self.coordinator.device, block, self.entity_description.name
|
||||
coordinator.device, block, description.name
|
||||
)
|
||||
elif entry is not None:
|
||||
self._attr_unique_id = entry.unique_id
|
||||
@ -691,8 +697,8 @@ class ShellySleepingRpcAttributeEntity(ShellyRpcAttributeEntity):
|
||||
self.attribute = attribute
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
self._attr_device_info = get_rpc_device_info(
|
||||
coordinator.device, coordinator.mac, key
|
||||
)
|
||||
self._attr_unique_id = self._attr_unique_id = (
|
||||
f"{coordinator.mac}-{key}-{attribute}"
|
||||
|
@ -17,7 +17,6 @@ from homeassistant.components.event import (
|
||||
EventEntityDescription,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@ -32,6 +31,7 @@ from .utils import (
|
||||
async_remove_orphaned_entities,
|
||||
async_remove_shelly_entity,
|
||||
get_device_entry_gen,
|
||||
get_rpc_device_info,
|
||||
get_rpc_entity_name,
|
||||
get_rpc_key_instances,
|
||||
is_block_momentary_input,
|
||||
@ -77,7 +77,6 @@ SCRIPT_EVENT: Final = ShellyRpcEventDescription(
|
||||
translation_key="script",
|
||||
device_class=None,
|
||||
entity_registry_enabled_default=False,
|
||||
has_entity_name=True,
|
||||
)
|
||||
|
||||
|
||||
@ -195,6 +194,7 @@ class ShellyBlockEvent(ShellyBlockEntity, EventEntity):
|
||||
class ShellyRpcEvent(CoordinatorEntity[ShellyRpcCoordinator], EventEntity):
|
||||
"""Represent RPC event entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: ShellyRpcEventDescription
|
||||
|
||||
def __init__(
|
||||
@ -206,8 +206,8 @@ class ShellyRpcEvent(CoordinatorEntity[ShellyRpcCoordinator], EventEntity):
|
||||
"""Initialize Shelly entity."""
|
||||
super().__init__(coordinator)
|
||||
self.event_id = int(key.split(":")[-1])
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
self._attr_device_info = get_rpc_device_info(
|
||||
coordinator.device, coordinator.mac, key
|
||||
)
|
||||
self._attr_unique_id = f"{coordinator.mac}-{key}"
|
||||
self._attr_name = get_rpc_entity_name(coordinator.device, key)
|
||||
|
@ -43,7 +43,7 @@ def async_describe_events(
|
||||
rpc_coordinator = get_rpc_coordinator_by_device_id(hass, device_id)
|
||||
if rpc_coordinator and rpc_coordinator.device.initialized:
|
||||
key = f"input:{channel - 1}"
|
||||
input_name = get_rpc_entity_name(rpc_coordinator.device, key)
|
||||
input_name = f"{rpc_coordinator.device.name} {get_rpc_entity_name(rpc_coordinator.device, key)}"
|
||||
|
||||
elif click_type in BLOCK_INPUTS_EVENTS_TYPES:
|
||||
block_coordinator = get_block_coordinator_by_device_id(hass, device_id)
|
||||
|
@ -21,7 +21,6 @@ from homeassistant.components.number import (
|
||||
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
|
||||
@ -38,6 +37,7 @@ from .entity import (
|
||||
)
|
||||
from .utils import (
|
||||
async_remove_orphaned_entities,
|
||||
get_blu_trv_device_info,
|
||||
get_device_entry_gen,
|
||||
get_virtual_component_ids,
|
||||
)
|
||||
@ -124,8 +124,8 @@ class RpcBluTrvNumber(RpcNumber):
|
||||
|
||||
super().__init__(coordinator, key, attribute, description)
|
||||
ble_addr: str = coordinator.device.config[key]["addr"]
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_BLUETOOTH, ble_addr)}
|
||||
self._attr_device_info = get_blu_trv_device_info(
|
||||
coordinator.device.config[key], ble_addr, coordinator.mac
|
||||
)
|
||||
|
||||
|
||||
@ -183,7 +183,6 @@ RPC_NUMBERS: Final = {
|
||||
"number": RpcNumberDescription(
|
||||
key="number",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
max_fn=lambda config: config["max"],
|
||||
min_fn=lambda config: config["min"],
|
||||
mode_fn=lambda config: VIRTUAL_NUMBER_MODE_MAP.get(
|
||||
|
@ -17,7 +17,7 @@ rules:
|
||||
docs-removal-instructions: done
|
||||
entity-event-setup: done
|
||||
entity-unique-id: done
|
||||
has-entity-name: todo
|
||||
has-entity-name: done
|
||||
runtime-data: done
|
||||
test-before-configure: done
|
||||
test-before-setup: done
|
||||
|
@ -40,7 +40,6 @@ RPC_SELECT_ENTITIES: Final = {
|
||||
"enum": RpcSelectDescription(
|
||||
key="enum",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ from homeassistant.const import (
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
from homeassistant.helpers.typing import StateType
|
||||
@ -56,8 +55,10 @@ from .entity import (
|
||||
)
|
||||
from .utils import (
|
||||
async_remove_orphaned_entities,
|
||||
get_blu_trv_device_info,
|
||||
get_device_entry_gen,
|
||||
get_device_uptime,
|
||||
get_rpc_device_info,
|
||||
get_shelly_air_lamp_life,
|
||||
get_virtual_component_ids,
|
||||
is_rpc_wifi_stations_disabled,
|
||||
@ -76,6 +77,7 @@ class RpcSensorDescription(RpcEntityDescription, SensorEntityDescription):
|
||||
"""Class to describe a RPC sensor."""
|
||||
|
||||
device_class_fn: Callable[[dict], SensorDeviceClass | None] | None = None
|
||||
emeter_phase: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@ -121,6 +123,26 @@ class RpcSensor(ShellyRpcAttributeEntity, SensorEntity):
|
||||
return self.option_map[attribute_value]
|
||||
|
||||
|
||||
class RpcEmeterPhaseSensor(RpcSensor):
|
||||
"""Represent a RPC energy meter phase sensor."""
|
||||
|
||||
entity_description: RpcSensorDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ShellyRpcCoordinator,
|
||||
key: str,
|
||||
attribute: str,
|
||||
description: RpcSensorDescription,
|
||||
) -> None:
|
||||
"""Initialize select."""
|
||||
super().__init__(coordinator, key, attribute, description)
|
||||
|
||||
self._attr_device_info = get_rpc_device_info(
|
||||
coordinator.device, coordinator.mac, key, description.emeter_phase
|
||||
)
|
||||
|
||||
|
||||
class RpcBluTrvSensor(RpcSensor):
|
||||
"""Represent a RPC BluTrv sensor."""
|
||||
|
||||
@ -135,8 +157,8 @@ class RpcBluTrvSensor(RpcSensor):
|
||||
|
||||
super().__init__(coordinator, key, attribute, description)
|
||||
ble_addr: str = coordinator.device.config[key]["addr"]
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_BLUETOOTH, ble_addr)}
|
||||
self._attr_device_info = get_blu_trv_device_info(
|
||||
coordinator.device.config[key], ble_addr, coordinator.mac
|
||||
)
|
||||
|
||||
|
||||
@ -507,26 +529,32 @@ RPC_SENSORS: Final = {
|
||||
"a_act_power": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="a_act_power",
|
||||
name="Phase A active power",
|
||||
name="Active power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_act_power": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="b_act_power",
|
||||
name="Phase B active power",
|
||||
name="Active power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_act_power": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="c_act_power",
|
||||
name="Phase C active power",
|
||||
name="Active power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"total_act_power": RpcSensorDescription(
|
||||
key="em",
|
||||
@ -539,26 +567,32 @@ RPC_SENSORS: Final = {
|
||||
"a_aprt_power": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="a_aprt_power",
|
||||
name="Phase A apparent power",
|
||||
name="Apparent power",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_aprt_power": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="b_aprt_power",
|
||||
name="Phase B apparent power",
|
||||
name="Apparent power",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_aprt_power": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="c_aprt_power",
|
||||
name="Phase C apparent power",
|
||||
name="Apparent power",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"aprt_power_em1": RpcSensorDescription(
|
||||
key="em1",
|
||||
@ -586,23 +620,29 @@ RPC_SENSORS: Final = {
|
||||
"a_pf": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="a_pf",
|
||||
name="Phase A power factor",
|
||||
name="Power factor",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_pf": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="b_pf",
|
||||
name="Phase B power factor",
|
||||
name="Power factor",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_pf": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="c_pf",
|
||||
name="Phase C power factor",
|
||||
name="Power factor",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"voltage": RpcSensorDescription(
|
||||
key="switch",
|
||||
@ -684,29 +724,35 @@ RPC_SENSORS: Final = {
|
||||
"a_voltage": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="a_voltage",
|
||||
name="Phase A voltage",
|
||||
name="Voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_voltage": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="b_voltage",
|
||||
name="Phase B voltage",
|
||||
name="Voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_voltage": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="c_voltage",
|
||||
name="Phase C voltage",
|
||||
name="Voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"current": RpcSensorDescription(
|
||||
key="switch",
|
||||
@ -781,29 +827,35 @@ RPC_SENSORS: Final = {
|
||||
"a_current": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="a_current",
|
||||
name="Phase A current",
|
||||
name="Current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_current": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="b_current",
|
||||
name="Phase B current",
|
||||
name="Current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_current": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="c_current",
|
||||
name="Phase C current",
|
||||
name="Current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"n_current": RpcSensorDescription(
|
||||
key="em",
|
||||
@ -944,7 +996,7 @@ RPC_SENSORS: Final = {
|
||||
"a_total_act_energy": RpcSensorDescription(
|
||||
key="emdata",
|
||||
sub_key="a_total_act_energy",
|
||||
name="Phase A total active energy",
|
||||
name="Total active energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: float(status),
|
||||
@ -952,11 +1004,13 @@ RPC_SENSORS: Final = {
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_total_act_energy": RpcSensorDescription(
|
||||
key="emdata",
|
||||
sub_key="b_total_act_energy",
|
||||
name="Phase B total active energy",
|
||||
name="Total active energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: float(status),
|
||||
@ -964,11 +1018,13 @@ RPC_SENSORS: Final = {
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_total_act_energy": RpcSensorDescription(
|
||||
key="emdata",
|
||||
sub_key="c_total_act_energy",
|
||||
name="Phase C total active energy",
|
||||
name="Total active energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: float(status),
|
||||
@ -976,6 +1032,8 @@ RPC_SENSORS: Final = {
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"total_act_ret": RpcSensorDescription(
|
||||
key="emdata",
|
||||
@ -1003,7 +1061,7 @@ RPC_SENSORS: Final = {
|
||||
"a_total_act_ret_energy": RpcSensorDescription(
|
||||
key="emdata",
|
||||
sub_key="a_total_act_ret_energy",
|
||||
name="Phase A total active returned energy",
|
||||
name="Total active returned energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: float(status),
|
||||
@ -1011,11 +1069,13 @@ RPC_SENSORS: Final = {
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_total_act_ret_energy": RpcSensorDescription(
|
||||
key="emdata",
|
||||
sub_key="b_total_act_ret_energy",
|
||||
name="Phase B total active returned energy",
|
||||
name="Total active returned energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: float(status),
|
||||
@ -1023,11 +1083,13 @@ RPC_SENSORS: Final = {
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_total_act_ret_energy": RpcSensorDescription(
|
||||
key="emdata",
|
||||
sub_key="c_total_act_ret_energy",
|
||||
name="Phase C total active returned energy",
|
||||
name="Total active returned energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
value=lambda status, _: float(status),
|
||||
@ -1035,6 +1097,8 @@ RPC_SENSORS: Final = {
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"freq": RpcSensorDescription(
|
||||
key="switch",
|
||||
@ -1069,32 +1133,38 @@ RPC_SENSORS: Final = {
|
||||
"a_freq": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="a_freq",
|
||||
name="Phase A frequency",
|
||||
name="Frequency",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="A",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"b_freq": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="b_freq",
|
||||
name="Phase B frequency",
|
||||
name="Frequency",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="B",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"c_freq": RpcSensorDescription(
|
||||
key="em",
|
||||
sub_key="c_freq",
|
||||
name="Phase C frequency",
|
||||
name="Frequency",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
emeter_phase="C",
|
||||
entity_class=RpcEmeterPhaseSensor,
|
||||
),
|
||||
"illuminance": RpcSensorDescription(
|
||||
key="illuminance",
|
||||
@ -1107,7 +1177,7 @@ RPC_SENSORS: Final = {
|
||||
"temperature": RpcSensorDescription(
|
||||
key="switch",
|
||||
sub_key="temperature",
|
||||
name="Device temperature",
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
value=lambda status, _: status["tC"],
|
||||
suggested_display_precision=1,
|
||||
@ -1120,7 +1190,7 @@ RPC_SENSORS: Final = {
|
||||
"temperature_light": RpcSensorDescription(
|
||||
key="light",
|
||||
sub_key="temperature",
|
||||
name="Device temperature",
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
value=lambda status, _: status["tC"],
|
||||
suggested_display_precision=1,
|
||||
@ -1133,7 +1203,7 @@ RPC_SENSORS: Final = {
|
||||
"temperature_cct": RpcSensorDescription(
|
||||
key="cct",
|
||||
sub_key="temperature",
|
||||
name="Device temperature",
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
value=lambda status, _: status["tC"],
|
||||
suggested_display_precision=1,
|
||||
@ -1146,7 +1216,7 @@ RPC_SENSORS: Final = {
|
||||
"temperature_rgb": RpcSensorDescription(
|
||||
key="rgb",
|
||||
sub_key="temperature",
|
||||
name="Device temperature",
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
value=lambda status, _: status["tC"],
|
||||
suggested_display_precision=1,
|
||||
@ -1159,7 +1229,7 @@ RPC_SENSORS: Final = {
|
||||
"temperature_rgbw": RpcSensorDescription(
|
||||
key="rgbw",
|
||||
sub_key="temperature",
|
||||
name="Device temperature",
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
value=lambda status, _: status["tC"],
|
||||
suggested_display_precision=1,
|
||||
@ -1308,12 +1378,10 @@ RPC_SENSORS: Final = {
|
||||
"text": RpcSensorDescription(
|
||||
key="text",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
),
|
||||
"number": RpcSensorDescription(
|
||||
key="number",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
unit=lambda config: config["meta"]["ui"]["unit"]
|
||||
if config["meta"]["ui"]["unit"]
|
||||
else None,
|
||||
@ -1324,7 +1392,6 @@ RPC_SENSORS: Final = {
|
||||
"enum": RpcSensorDescription(
|
||||
key="enum",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
options_fn=lambda config: config["options"],
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
),
|
||||
|
@ -291,7 +291,6 @@ class RpcSwitch(ShellyRpcAttributeEntity, SwitchEntity):
|
||||
"""Entity that controls a switch on RPC based Shelly devices."""
|
||||
|
||||
entity_description: RpcSwitchDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
@ -316,9 +315,6 @@ class RpcSwitch(ShellyRpcAttributeEntity, SwitchEntity):
|
||||
class RpcRelaySwitch(RpcSwitch):
|
||||
"""Entity that controls a switch on RPC based Shelly devices."""
|
||||
|
||||
# False to avoid double naming as True is inerithed from base class
|
||||
_attr_has_entity_name = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ShellyRpcCoordinator,
|
||||
|
@ -40,7 +40,6 @@ RPC_TEXT_ENTITIES: Final = {
|
||||
"text": RpcTextDescription(
|
||||
key="text",
|
||||
sub_key="value",
|
||||
has_entity_name=True,
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ from aiohttp.web import Request, WebSocketResponse
|
||||
from aioshelly.block_device import COAP, Block, BlockDevice
|
||||
from aioshelly.const import (
|
||||
BLOCK_GENERATIONS,
|
||||
BLU_TRV_IDENTIFIER,
|
||||
BLU_TRV_MODEL_NAME,
|
||||
DEFAULT_COAP_PORT,
|
||||
DEFAULT_HTTP_PORT,
|
||||
MODEL_1L,
|
||||
@ -40,7 +42,11 @@ from homeassistant.helpers import (
|
||||
issue_registry as ir,
|
||||
singleton,
|
||||
)
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_BLUETOOTH,
|
||||
CONNECTION_NETWORK_MAC,
|
||||
DeviceInfo,
|
||||
)
|
||||
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
@ -65,7 +71,9 @@ from .const import (
|
||||
SHELLY_EMIT_EVENT_PATTERN,
|
||||
SHIX3_1_INPUTS_EVENTS_TYPES,
|
||||
UPTIME_DEVIATION,
|
||||
VIRTUAL_COMPONENTS,
|
||||
VIRTUAL_COMPONENTS_MAP,
|
||||
All_LIGHT_TYPES,
|
||||
)
|
||||
|
||||
|
||||
@ -109,26 +117,24 @@ def get_block_entity_name(
|
||||
device: BlockDevice,
|
||||
block: Block | None,
|
||||
description: str | None = None,
|
||||
) -> str:
|
||||
) -> str | None:
|
||||
"""Naming for block based switch and sensors."""
|
||||
channel_name = get_block_channel_name(device, block)
|
||||
|
||||
if description:
|
||||
return f"{channel_name} {description.lower()}"
|
||||
return f"{channel_name} {description.lower()}" if channel_name else description
|
||||
|
||||
return channel_name
|
||||
|
||||
|
||||
def get_block_channel_name(device: BlockDevice, block: Block | None) -> str:
|
||||
def get_block_channel_name(device: BlockDevice, block: Block | None) -> str | None:
|
||||
"""Get name based on device and channel name."""
|
||||
entity_name = device.name
|
||||
|
||||
if (
|
||||
not block
|
||||
or block.type == "device"
|
||||
or block.type in ("device", "light", "relay", "emeter")
|
||||
or get_number_of_channels(device, block) == 1
|
||||
):
|
||||
return entity_name
|
||||
return None
|
||||
|
||||
assert block.channel
|
||||
|
||||
@ -140,12 +146,28 @@ def get_block_channel_name(device: BlockDevice, block: Block | None) -> str:
|
||||
if channel_name:
|
||||
return channel_name
|
||||
|
||||
base = ord("1")
|
||||
|
||||
return f"Channel {chr(int(block.channel) + base)}"
|
||||
|
||||
|
||||
def get_block_sub_device_name(device: BlockDevice, block: Block) -> str:
|
||||
"""Get name of block sub-device."""
|
||||
if TYPE_CHECKING:
|
||||
assert block.channel
|
||||
|
||||
mode = cast(str, block.type) + "s"
|
||||
if mode in device.settings:
|
||||
if channel_name := device.settings[mode][int(block.channel)].get("name"):
|
||||
return cast(str, channel_name)
|
||||
|
||||
if device.settings["device"]["type"] == MODEL_EM3:
|
||||
base = ord("A")
|
||||
else:
|
||||
base = ord("1")
|
||||
return f"{device.name} Phase {chr(int(block.channel) + base)}"
|
||||
|
||||
return f"{entity_name} channel {chr(int(block.channel) + base)}"
|
||||
base = ord("1")
|
||||
|
||||
return f"{device.name} Channel {chr(int(block.channel) + base)}"
|
||||
|
||||
|
||||
def is_block_momentary_input(
|
||||
@ -364,39 +386,64 @@ def get_shelly_model_name(
|
||||
return cast(str, MODEL_NAMES.get(model))
|
||||
|
||||
|
||||
def get_rpc_channel_name(device: RpcDevice, key: str) -> str:
|
||||
def get_rpc_channel_name(device: RpcDevice, key: str) -> str | None:
|
||||
"""Get name based on device and channel name."""
|
||||
if BLU_TRV_IDENTIFIER in key:
|
||||
return None
|
||||
|
||||
instances = len(
|
||||
get_rpc_key_instances(device.status, key.split(":")[0], all_lights=True)
|
||||
)
|
||||
component = key.split(":")[0]
|
||||
component_id = key.split(":")[-1]
|
||||
|
||||
if key in device.config and key != "em:0":
|
||||
# workaround for Pro 3EM, we don't want to get name for em:0
|
||||
if component_name := device.config[key].get("name"):
|
||||
if component in (*VIRTUAL_COMPONENTS, "script"):
|
||||
return cast(str, component_name)
|
||||
|
||||
return cast(str, component_name) if instances == 1 else None
|
||||
|
||||
if component in VIRTUAL_COMPONENTS:
|
||||
return f"{component.title()} {component_id}"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_rpc_sub_device_name(
|
||||
device: RpcDevice, key: str, emeter_phase: str | None = None
|
||||
) -> str:
|
||||
"""Get name based on device and channel name."""
|
||||
if key in device.config and key != "em:0":
|
||||
# workaround for Pro 3EM, we don't want to get name for em:0
|
||||
if entity_name := device.config[key].get("name"):
|
||||
return cast(str, entity_name)
|
||||
|
||||
key = key.replace("emdata", "em")
|
||||
key = key.replace("em1data", "em1")
|
||||
device_name = device.name
|
||||
entity_name: str | None = None
|
||||
if key in device.config:
|
||||
entity_name = device.config[key].get("name")
|
||||
|
||||
if entity_name is None:
|
||||
channel = key.split(":")[0]
|
||||
channel_id = key.split(":")[-1]
|
||||
if key.startswith(("cover:", "input:", "light:", "switch:", "thermostat:")):
|
||||
return f"{device_name} {channel.title()} {channel_id}"
|
||||
if key.startswith(("cct", "rgb:", "rgbw:")):
|
||||
return f"{device_name} {channel.upper()} light {channel_id}"
|
||||
if key.startswith("em1"):
|
||||
return f"{device_name} EM{channel_id}"
|
||||
if key.startswith(("boolean:", "enum:", "number:", "text:")):
|
||||
return f"{channel.title()} {channel_id}"
|
||||
return device_name
|
||||
component = key.split(":")[0]
|
||||
component_id = key.split(":")[-1]
|
||||
|
||||
return entity_name
|
||||
if component in ("cct", "rgb", "rgbw"):
|
||||
return f"{device.name} {component.upper()} light {component_id}"
|
||||
if component == "em1":
|
||||
return f"{device.name} Energy Meter {component_id}"
|
||||
if component == "em" and emeter_phase is not None:
|
||||
return f"{device.name} Phase {emeter_phase}"
|
||||
|
||||
return f"{device.name} {component.title()} {component_id}"
|
||||
|
||||
|
||||
def get_rpc_entity_name(
|
||||
device: RpcDevice, key: str, description: str | None = None
|
||||
) -> str:
|
||||
) -> str | None:
|
||||
"""Naming for RPC based switch and sensors."""
|
||||
channel_name = get_rpc_channel_name(device, key)
|
||||
|
||||
if description:
|
||||
return f"{channel_name} {description.lower()}"
|
||||
return f"{channel_name} {description.lower()}" if channel_name else description
|
||||
|
||||
return channel_name
|
||||
|
||||
@ -406,7 +453,9 @@ def get_device_entry_gen(entry: ConfigEntry) -> int:
|
||||
return entry.data.get(CONF_GEN, 1)
|
||||
|
||||
|
||||
def get_rpc_key_instances(keys_dict: dict[str, Any], key: str) -> list[str]:
|
||||
def get_rpc_key_instances(
|
||||
keys_dict: dict[str, Any], key: str, all_lights: bool = False
|
||||
) -> list[str]:
|
||||
"""Return list of key instances for RPC device from a dict."""
|
||||
if key in keys_dict:
|
||||
return [key]
|
||||
@ -414,6 +463,9 @@ def get_rpc_key_instances(keys_dict: dict[str, Any], key: str) -> list[str]:
|
||||
if key == "switch" and "cover:0" in keys_dict:
|
||||
key = "cover"
|
||||
|
||||
if key in All_LIGHT_TYPES and all_lights:
|
||||
return [k for k in keys_dict if k.startswith(All_LIGHT_TYPES)]
|
||||
|
||||
return [k for k in keys_dict if k.startswith(f"{key}:")]
|
||||
|
||||
|
||||
@ -691,3 +743,81 @@ async def get_rpc_scripts_event_types(
|
||||
script_events[script_id] = await get_rpc_script_event_types(device, script_id)
|
||||
|
||||
return script_events
|
||||
|
||||
|
||||
def get_rpc_device_info(
|
||||
device: RpcDevice,
|
||||
mac: str,
|
||||
key: str | None = None,
|
||||
emeter_phase: str | None = None,
|
||||
) -> DeviceInfo:
|
||||
"""Return device info for RPC device."""
|
||||
if key is None:
|
||||
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})
|
||||
|
||||
# workaround for Pro EM50
|
||||
key = key.replace("em1data", "em1")
|
||||
# workaround for Pro 3EM
|
||||
key = key.replace("emdata", "em")
|
||||
|
||||
key_parts = key.split(":")
|
||||
component = key_parts[0]
|
||||
idx = key_parts[1] if len(key_parts) > 1 else None
|
||||
|
||||
if emeter_phase is not None:
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{mac}-{key}-{emeter_phase.lower()}")},
|
||||
name=get_rpc_sub_device_name(device, key, emeter_phase),
|
||||
manufacturer="Shelly",
|
||||
via_device=(DOMAIN, mac),
|
||||
)
|
||||
|
||||
if (
|
||||
component not in (*All_LIGHT_TYPES, "cover", "em1", "switch")
|
||||
or idx is None
|
||||
or len(get_rpc_key_instances(device.status, component, all_lights=True)) < 2
|
||||
):
|
||||
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})
|
||||
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{mac}-{key}")},
|
||||
name=get_rpc_sub_device_name(device, key),
|
||||
manufacturer="Shelly",
|
||||
via_device=(DOMAIN, mac),
|
||||
)
|
||||
|
||||
|
||||
def get_blu_trv_device_info(
|
||||
config: dict[str, Any], ble_addr: str, parent_mac: str
|
||||
) -> DeviceInfo:
|
||||
"""Return device info for RPC device."""
|
||||
model_id = config.get("local_name")
|
||||
return DeviceInfo(
|
||||
connections={(CONNECTION_BLUETOOTH, ble_addr)},
|
||||
identifiers={(DOMAIN, ble_addr)},
|
||||
via_device=(DOMAIN, parent_mac),
|
||||
manufacturer="Shelly",
|
||||
model=BLU_TRV_MODEL_NAME.get(model_id) if model_id else None,
|
||||
model_id=config.get("local_name"),
|
||||
name=config["name"] or f"shellyblutrv-{ble_addr.replace(':', '')}",
|
||||
)
|
||||
|
||||
|
||||
def get_block_device_info(
|
||||
device: BlockDevice, mac: str, block: Block | None = None
|
||||
) -> DeviceInfo:
|
||||
"""Return device info for Block device."""
|
||||
if (
|
||||
block is None
|
||||
or block.type not in ("light", "relay", "emeter")
|
||||
or device.settings.get("mode") == "roller"
|
||||
or get_number_of_channels(device, block) < 2
|
||||
):
|
||||
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})
|
||||
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{mac}-{block.description}")},
|
||||
name=get_block_sub_device_name(device, block),
|
||||
manufacturer="Shelly",
|
||||
via_device=(DOMAIN, mac),
|
||||
)
|
||||
|
@ -53,7 +53,7 @@ async def init_integration(
|
||||
data[CONF_GEN] = gen
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, unique_id=MOCK_MAC, options=options
|
||||
domain=DOMAIN, data=data, unique_id=MOCK_MAC, options=options, title="Test name"
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
|
@ -189,7 +189,7 @@ MOCK_BLOCKS = [
|
||||
]
|
||||
|
||||
MOCK_CONFIG = {
|
||||
"input:0": {"id": 0, "name": "Test name input 0", "type": "button"},
|
||||
"input:0": {"id": 0, "name": "Test input 0", "type": "button"},
|
||||
"input:1": {
|
||||
"id": 1,
|
||||
"type": "analog",
|
||||
@ -204,7 +204,7 @@ MOCK_CONFIG = {
|
||||
"xcounts": {"expr": None, "unit": None},
|
||||
"xfreq": {"expr": None, "unit": None},
|
||||
},
|
||||
"flood:0": {"id": 0, "name": "Test name"},
|
||||
"flood:0": {"id": 0, "name": "Kitchen"},
|
||||
"light:0": {"name": "test light_0"},
|
||||
"light:1": {"name": "test light_1"},
|
||||
"light:2": {"name": "test light_2"},
|
||||
|
259
tests/components/shelly/fixtures/2pm_gen3.json
Normal file
259
tests/components/shelly/fixtures/2pm_gen3.json
Normal file
@ -0,0 +1,259 @@
|
||||
{
|
||||
"config": {
|
||||
"ble": {
|
||||
"enable": true,
|
||||
"rpc": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"bthome": {},
|
||||
"cloud": {
|
||||
"enable": false,
|
||||
"server": "iot.shelly.cloud:6012/jrpc"
|
||||
},
|
||||
"input:0": {
|
||||
"enable": true,
|
||||
"factory_reset": true,
|
||||
"id": 0,
|
||||
"invert": false,
|
||||
"name": null,
|
||||
"type": "switch"
|
||||
},
|
||||
"input:1": {
|
||||
"enable": true,
|
||||
"factory_reset": true,
|
||||
"id": 1,
|
||||
"invert": false,
|
||||
"name": null,
|
||||
"type": "switch"
|
||||
},
|
||||
"knx": {
|
||||
"enable": false,
|
||||
"ia": "15.15.255",
|
||||
"routing": {
|
||||
"addr": "224.0.23.12:3671"
|
||||
}
|
||||
},
|
||||
"matter": {
|
||||
"enable": false
|
||||
},
|
||||
"mqtt": {
|
||||
"client_id": "shelly2pmg3-aabbccddeeff",
|
||||
"enable": true,
|
||||
"enable_control": true,
|
||||
"enable_rpc": true,
|
||||
"rpc_ntf": true,
|
||||
"server": "mqtt.test.server",
|
||||
"ssl_ca": null,
|
||||
"status_ntf": true,
|
||||
"topic_prefix": "shelly2pmg3-aabbccddeeff",
|
||||
"use_client_cert": false,
|
||||
"user": "iot"
|
||||
},
|
||||
"switch:0": {
|
||||
"auto_off": false,
|
||||
"auto_off_delay": 60.0,
|
||||
"auto_on": false,
|
||||
"auto_on_delay": 60.0,
|
||||
"autorecover_voltage_errors": false,
|
||||
"current_limit": 10.0,
|
||||
"id": 0,
|
||||
"in_locked": false,
|
||||
"in_mode": "follow",
|
||||
"initial_state": "match_input",
|
||||
"name": null,
|
||||
"power_limit": 2800,
|
||||
"reverse": false,
|
||||
"undervoltage_limit": 0,
|
||||
"voltage_limit": 280
|
||||
},
|
||||
"switch:1": {
|
||||
"auto_off": false,
|
||||
"auto_off_delay": 60.0,
|
||||
"auto_on": false,
|
||||
"auto_on_delay": 60.0,
|
||||
"autorecover_voltage_errors": false,
|
||||
"current_limit": 10.0,
|
||||
"id": 1,
|
||||
"in_locked": false,
|
||||
"in_mode": "follow",
|
||||
"initial_state": "match_input",
|
||||
"name": null,
|
||||
"power_limit": 2800,
|
||||
"reverse": false,
|
||||
"undervoltage_limit": 0,
|
||||
"voltage_limit": 280
|
||||
},
|
||||
"sys": {
|
||||
"cfg_rev": 170,
|
||||
"debug": {
|
||||
"file_level": null,
|
||||
"level": 2,
|
||||
"mqtt": {
|
||||
"enable": false
|
||||
},
|
||||
"udp": {
|
||||
"addr": null
|
||||
},
|
||||
"websocket": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"addon_type": null,
|
||||
"discoverable": true,
|
||||
"eco_mode": true,
|
||||
"fw_id": "20250508-110823/1.6.1-g8dbd358",
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"name": "Test Name",
|
||||
"profile": "switch"
|
||||
},
|
||||
"location": {
|
||||
"lat": 15.2201,
|
||||
"lon": 33.0121,
|
||||
"tz": "Europe/Warsaw"
|
||||
},
|
||||
"rpc_udp": {
|
||||
"dst_addr": null,
|
||||
"listen_port": null
|
||||
},
|
||||
"sntp": {
|
||||
"server": "sntp.test.server"
|
||||
}
|
||||
},
|
||||
"wifi": {
|
||||
"sta": {
|
||||
"ssid": "Wifi-Network-Name",
|
||||
"is_open": false,
|
||||
"enable": true,
|
||||
"ipv4mode": "dhcp",
|
||||
"ip": null,
|
||||
"netmask": null,
|
||||
"gw": null,
|
||||
"nameserver": null
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"enable": false,
|
||||
"server": null,
|
||||
"ssl_ca": "ca.pem"
|
||||
}
|
||||
},
|
||||
"shelly": {
|
||||
"app": "S2PMG3",
|
||||
"auth_domain": null,
|
||||
"auth_en": false,
|
||||
"fw_id": "20250508-110823/1.6.1-g8dbd358",
|
||||
"gen": 3,
|
||||
"id": "shelly2pmg3-aabbccddeeff",
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"matter": false,
|
||||
"model": "S3SW-002P16EU",
|
||||
"name": "Test Name",
|
||||
"profile": "switch",
|
||||
"slot": 0,
|
||||
"ver": "1.6.1"
|
||||
},
|
||||
"status": {
|
||||
"ble": {},
|
||||
"bthome": {},
|
||||
"cloud": {
|
||||
"connected": false
|
||||
},
|
||||
"input:0": {
|
||||
"id": 0,
|
||||
"state": false
|
||||
},
|
||||
"input:1": {
|
||||
"id": 1,
|
||||
"state": false
|
||||
},
|
||||
"knx": {},
|
||||
"matter": {
|
||||
"commissionable": false,
|
||||
"num_fabrics": 0
|
||||
},
|
||||
"mqtt": {
|
||||
"connected": true
|
||||
},
|
||||
"switch:0": {
|
||||
"aenergy": {
|
||||
"by_minute": [0.0, 0.0, 0.0],
|
||||
"minute_ts": 1747488720,
|
||||
"total": 0.0
|
||||
},
|
||||
"apower": 0.0,
|
||||
"current": 0.0,
|
||||
"freq": 50.0,
|
||||
"id": 0,
|
||||
"output": false,
|
||||
"pf": 0.0,
|
||||
"ret_aenergy": {
|
||||
"by_minute": [0.0, 0.0, 0.0],
|
||||
"minute_ts": 1747488720,
|
||||
"total": 0.0
|
||||
},
|
||||
"source": "init",
|
||||
"temperature": {
|
||||
"tC": 40.6,
|
||||
"tF": 105.1
|
||||
},
|
||||
"voltage": 216.2
|
||||
},
|
||||
"switch:1": {
|
||||
"aenergy": {
|
||||
"by_minute": [0.0, 0.0, 0.0],
|
||||
"minute_ts": 1747488720,
|
||||
"total": 0.0
|
||||
},
|
||||
"apower": 0.0,
|
||||
"current": 0.0,
|
||||
"freq": 50.0,
|
||||
"id": 1,
|
||||
"output": false,
|
||||
"pf": 0.0,
|
||||
"ret_aenergy": {
|
||||
"by_minute": [0.0, 0.0, 0.0],
|
||||
"minute_ts": 1747488720,
|
||||
"total": 0.0
|
||||
},
|
||||
"source": "init",
|
||||
"temperature": {
|
||||
"tC": 40.6,
|
||||
"tF": 105.1
|
||||
},
|
||||
"voltage": 216.3
|
||||
},
|
||||
"sys": {
|
||||
"available_updates": {},
|
||||
"btrelay_rev": 0,
|
||||
"cfg_rev": 170,
|
||||
"fs_free": 430080,
|
||||
"fs_size": 917504,
|
||||
"kvs_rev": 0,
|
||||
"last_sync_ts": 1747488676,
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"ram_free": 66440,
|
||||
"ram_min_free": 49448,
|
||||
"ram_size": 245788,
|
||||
"reset_reason": 3,
|
||||
"restart_required": false,
|
||||
"schedule_rev": 22,
|
||||
"time": "15:32",
|
||||
"unixtime": 1747488776,
|
||||
"uptime": 103,
|
||||
"utc_offset": 7200,
|
||||
"webhook_rev": 22
|
||||
},
|
||||
"wifi": {
|
||||
"rssi": -52,
|
||||
"ssid": "Wifi-Network-Name",
|
||||
"sta_ip": "192.168.2.24",
|
||||
"sta_ip6": [],
|
||||
"status": "got ip"
|
||||
},
|
||||
"ws": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
242
tests/components/shelly/fixtures/2pm_gen3_cover.json
Normal file
242
tests/components/shelly/fixtures/2pm_gen3_cover.json
Normal file
@ -0,0 +1,242 @@
|
||||
{
|
||||
"config": {
|
||||
"ble": {
|
||||
"enable": true,
|
||||
"rpc": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"bthome": {},
|
||||
"cloud": {
|
||||
"enable": false,
|
||||
"server": "iot.shelly.cloud:6012/jrpc"
|
||||
},
|
||||
"cover:0": {
|
||||
"current_limit": 10.0,
|
||||
"id": 0,
|
||||
"in_locked": false,
|
||||
"in_mode": "dual",
|
||||
"initial_state": "stopped",
|
||||
"invert_directions": false,
|
||||
"maintenance_mode": false,
|
||||
"maxtime_close": 60.0,
|
||||
"maxtime_open": 60.0,
|
||||
"motor": {
|
||||
"idle_confirm_period": 0.25,
|
||||
"idle_power_thr": 2.0
|
||||
},
|
||||
"name": null,
|
||||
"obstruction_detection": {
|
||||
"action": "stop",
|
||||
"direction": "both",
|
||||
"enable": false,
|
||||
"holdoff": 1.0,
|
||||
"power_thr": 1000
|
||||
},
|
||||
"power_limit": 2800,
|
||||
"safety_switch": {
|
||||
"action": "stop",
|
||||
"allowed_move": null,
|
||||
"direction": "both",
|
||||
"enable": false
|
||||
},
|
||||
"slat": {
|
||||
"close_time": 1.5,
|
||||
"enable": false,
|
||||
"open_time": 1.5,
|
||||
"precise_ctl": false,
|
||||
"retain_pos": false,
|
||||
"step": 20
|
||||
},
|
||||
"swap_inputs": false,
|
||||
"undervoltage_limit": 0,
|
||||
"voltage_limit": 280
|
||||
},
|
||||
"input:0": {
|
||||
"enable": true,
|
||||
"factory_reset": true,
|
||||
"id": 0,
|
||||
"invert": false,
|
||||
"name": null,
|
||||
"type": "switch"
|
||||
},
|
||||
"input:1": {
|
||||
"enable": true,
|
||||
"factory_reset": true,
|
||||
"id": 1,
|
||||
"invert": false,
|
||||
"name": null,
|
||||
"type": "switch"
|
||||
},
|
||||
"knx": {
|
||||
"enable": false,
|
||||
"ia": "15.15.255",
|
||||
"routing": {
|
||||
"addr": "224.0.23.12:3671"
|
||||
}
|
||||
},
|
||||
"matter": {
|
||||
"enable": false
|
||||
},
|
||||
"mqtt": {
|
||||
"client_id": "shelly2pmg3-aabbccddeeff",
|
||||
"enable": true,
|
||||
"enable_control": true,
|
||||
"enable_rpc": true,
|
||||
"rpc_ntf": true,
|
||||
"server": "mqtt.test.server",
|
||||
"ssl_ca": null,
|
||||
"status_ntf": true,
|
||||
"topic_prefix": "shellies-gen3/shelly-2pm-gen3-365730",
|
||||
"use_client_cert": false,
|
||||
"user": "iot"
|
||||
},
|
||||
"sys": {
|
||||
"cfg_rev": 171,
|
||||
"debug": {
|
||||
"file_level": null,
|
||||
"level": 2,
|
||||
"mqtt": {
|
||||
"enable": false
|
||||
},
|
||||
"udp": {
|
||||
"addr": null
|
||||
},
|
||||
"websocket": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"addon_type": null,
|
||||
"discoverable": true,
|
||||
"eco_mode": true,
|
||||
"fw_id": "20250508-110823/1.6.1-g8dbd358",
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"name": "Test Name",
|
||||
"profile": "cover"
|
||||
},
|
||||
"location": {
|
||||
"lat": 19.2201,
|
||||
"lon": 34.0121,
|
||||
"tz": "Europe/Warsaw"
|
||||
},
|
||||
"rpc_udp": {
|
||||
"dst_addr": null,
|
||||
"listen_port": null
|
||||
},
|
||||
"sntp": {
|
||||
"server": "sntp.test.server"
|
||||
},
|
||||
"ui_data": {
|
||||
"consumption_types": ["", "light"]
|
||||
}
|
||||
},
|
||||
"wifi": {
|
||||
"sta": {
|
||||
"ssid": "Wifi-Network-Name",
|
||||
"is_open": false,
|
||||
"enable": true,
|
||||
"ipv4mode": "dhcp",
|
||||
"ip": null,
|
||||
"netmask": null,
|
||||
"gw": null,
|
||||
"nameserver": null
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"enable": false,
|
||||
"server": null,
|
||||
"ssl_ca": "ca.pem"
|
||||
}
|
||||
},
|
||||
"shelly": {
|
||||
"app": "S2PMG3",
|
||||
"auth_domain": null,
|
||||
"auth_en": false,
|
||||
"fw_id": "20250508-110823/1.6.1-g8dbd358",
|
||||
"gen": 3,
|
||||
"id": "shelly2pmg3-aabbccddeeff",
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"matter": false,
|
||||
"model": "S3SW-002P16EU",
|
||||
"name": "Test Name",
|
||||
"profile": "cover",
|
||||
"slot": 0,
|
||||
"ver": "1.6.1"
|
||||
},
|
||||
"status": {
|
||||
"ble": {},
|
||||
"bthome": {},
|
||||
"cloud": {
|
||||
"connected": false
|
||||
},
|
||||
"cover:0": {
|
||||
"aenergy": {
|
||||
"by_minute": [0.0, 0.0, 0.0],
|
||||
"minute_ts": 1747492440,
|
||||
"total": 0.0
|
||||
},
|
||||
"apower": 0.0,
|
||||
"current": 0.0,
|
||||
"freq": 50.0,
|
||||
"id": 0,
|
||||
"last_direction": null,
|
||||
"pf": 0.0,
|
||||
"pos_control": false,
|
||||
"source": "init",
|
||||
"state": "stopped",
|
||||
"temperature": {
|
||||
"tC": 36.4,
|
||||
"tF": 97.5
|
||||
},
|
||||
"voltage": 217.7
|
||||
},
|
||||
"input:0": {
|
||||
"id": 0,
|
||||
"state": false
|
||||
},
|
||||
"input:1": {
|
||||
"id": 1,
|
||||
"state": false
|
||||
},
|
||||
"knx": {},
|
||||
"matter": {
|
||||
"commissionable": false,
|
||||
"num_fabrics": 0
|
||||
},
|
||||
"mqtt": {
|
||||
"connected": true
|
||||
},
|
||||
"sys": {
|
||||
"available_updates": {},
|
||||
"btrelay_rev": 0,
|
||||
"cfg_rev": 171,
|
||||
"fs_free": 430080,
|
||||
"fs_size": 917504,
|
||||
"kvs_rev": 0,
|
||||
"last_sync_ts": 1747492085,
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"ram_free": 64632,
|
||||
"ram_min_free": 51660,
|
||||
"ram_size": 245568,
|
||||
"reset_reason": 3,
|
||||
"restart_required": false,
|
||||
"schedule_rev": 23,
|
||||
"time": "16:34",
|
||||
"unixtime": 1747492463,
|
||||
"uptime": 381,
|
||||
"utc_offset": 7200,
|
||||
"webhook_rev": 23
|
||||
},
|
||||
"wifi": {
|
||||
"rssi": -53,
|
||||
"ssid": "Wifi-Network-Name",
|
||||
"sta_ip": "192.168.2.24",
|
||||
"sta_ip6": [],
|
||||
"status": "got ip"
|
||||
},
|
||||
"ws": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
216
tests/components/shelly/fixtures/pro_3em.json
Normal file
216
tests/components/shelly/fixtures/pro_3em.json
Normal file
@ -0,0 +1,216 @@
|
||||
{
|
||||
"config": {
|
||||
"ble": {
|
||||
"enable": false,
|
||||
"rpc": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
"bthome": {},
|
||||
"cloud": {
|
||||
"enable": false,
|
||||
"server": "iot.shelly.cloud:6012/jrpc"
|
||||
},
|
||||
"em:0": {
|
||||
"blink_mode_selector": "active_energy",
|
||||
"ct_type": "120A",
|
||||
"id": 0,
|
||||
"monitor_phase_sequence": false,
|
||||
"name": null,
|
||||
"phase_selector": "all",
|
||||
"reverse": {}
|
||||
},
|
||||
"emdata:0": {},
|
||||
"eth": {
|
||||
"enable": false,
|
||||
"gw": null,
|
||||
"ip": null,
|
||||
"ipv4mode": "dhcp",
|
||||
"nameserver": null,
|
||||
"netmask": null,
|
||||
"server_mode": false
|
||||
},
|
||||
"modbus": {
|
||||
"enable": true
|
||||
},
|
||||
"mqtt": {
|
||||
"client_id": "shellypro3em-aabbccddeeff",
|
||||
"enable": false,
|
||||
"enable_control": true,
|
||||
"enable_rpc": true,
|
||||
"rpc_ntf": true,
|
||||
"server": "mqtt.test.server",
|
||||
"ssl_ca": null,
|
||||
"status_ntf": true,
|
||||
"topic_prefix": "shellypro3em-aabbccddeeff",
|
||||
"use_client_cert": false,
|
||||
"user": "iot"
|
||||
},
|
||||
"sys": {
|
||||
"cfg_rev": 50,
|
||||
"debug": {
|
||||
"file_level": null,
|
||||
"level": 2,
|
||||
"mqtt": {
|
||||
"enable": false
|
||||
},
|
||||
"udp": {
|
||||
"addr": null
|
||||
},
|
||||
"websocket": {
|
||||
"enable": false
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"addon_type": null,
|
||||
"discoverable": true,
|
||||
"eco_mode": false,
|
||||
"fw_id": "20250508-110717/1.6.1-g8dbd358",
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"name": "Test Name",
|
||||
"profile": "triphase",
|
||||
"sys_btn_toggle": true
|
||||
},
|
||||
"location": {
|
||||
"lat": 22.55775,
|
||||
"lon": 54.94637,
|
||||
"tz": "Europe/Warsaw"
|
||||
},
|
||||
"rpc_udp": {
|
||||
"dst_addr": null,
|
||||
"listen_port": null
|
||||
},
|
||||
"sntp": {
|
||||
"server": "sntp.test.server"
|
||||
},
|
||||
"ui_data": {}
|
||||
},
|
||||
"temperature:0": {
|
||||
"id": 0,
|
||||
"name": null,
|
||||
"offset_C": 0.0,
|
||||
"report_thr_C": 5.0
|
||||
},
|
||||
"wifi": {
|
||||
"sta": {
|
||||
"ssid": "Wifi-Network-Name",
|
||||
"is_open": false,
|
||||
"enable": true,
|
||||
"ipv4mode": "dhcp",
|
||||
"ip": null,
|
||||
"netmask": null,
|
||||
"gw": null,
|
||||
"nameserver": null
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"enable": false,
|
||||
"server": null,
|
||||
"ssl_ca": "ca.pem"
|
||||
}
|
||||
},
|
||||
"shelly": {
|
||||
"app": "Pro3EM",
|
||||
"auth_domain": "shellypro3em-aabbccddeeff",
|
||||
"auth_en": true,
|
||||
"fw_id": "20250508-110717/1.6.1-g8dbd358",
|
||||
"gen": 2,
|
||||
"id": "shellypro3em-aabbccddeeff",
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"model": "SPEM-003CEBEU",
|
||||
"name": "Test Name",
|
||||
"profile": "triphase",
|
||||
"slot": 0,
|
||||
"ver": "1.6.1"
|
||||
},
|
||||
"status": {
|
||||
"ble": {},
|
||||
"bthome": {
|
||||
"errors": ["bluetooth_disabled"]
|
||||
},
|
||||
"cloud": {
|
||||
"connected": false
|
||||
},
|
||||
"em:0": {
|
||||
"a_act_power": 2166.2,
|
||||
"a_aprt_power": 2175.9,
|
||||
"a_current": 9.592,
|
||||
"a_freq": 49.9,
|
||||
"a_pf": 0.99,
|
||||
"a_voltage": 227.0,
|
||||
"b_act_power": 3.6,
|
||||
"b_aprt_power": 10.1,
|
||||
"b_current": 0.044,
|
||||
"b_freq": 49.9,
|
||||
"b_pf": 0.36,
|
||||
"b_voltage": 230.0,
|
||||
"c_act_power": 244.0,
|
||||
"c_aprt_power": 339.7,
|
||||
"c_current": 1.479,
|
||||
"c_freq": 49.9,
|
||||
"c_pf": 0.72,
|
||||
"c_voltage": 230.2,
|
||||
"id": 0,
|
||||
"n_current": null,
|
||||
"total_act_power": 2413.825,
|
||||
"total_aprt_power": 2525.779,
|
||||
"total_current": 11.116,
|
||||
"user_calibrated_phase": []
|
||||
},
|
||||
"emdata:0": {
|
||||
"a_total_act_energy": 3105576.42,
|
||||
"a_total_act_ret_energy": 0.0,
|
||||
"b_total_act_energy": 195765.72,
|
||||
"b_total_act_ret_energy": 0.0,
|
||||
"c_total_act_energy": 2114072.05,
|
||||
"c_total_act_ret_energy": 0.0,
|
||||
"id": 0,
|
||||
"total_act": 5415414.19,
|
||||
"total_act_ret": 0.0
|
||||
},
|
||||
"eth": {
|
||||
"ip": null,
|
||||
"ip6": null
|
||||
},
|
||||
"modbus": {},
|
||||
"mqtt": {
|
||||
"connected": false
|
||||
},
|
||||
"sys": {
|
||||
"available_updates": {},
|
||||
"btrelay_rev": 0,
|
||||
"cfg_rev": 50,
|
||||
"fs_free": 180224,
|
||||
"fs_size": 524288,
|
||||
"kvs_rev": 1,
|
||||
"last_sync_ts": 1747561099,
|
||||
"mac": "AABBCCDDEEFF",
|
||||
"ram_free": 113080,
|
||||
"ram_min_free": 97524,
|
||||
"ram_size": 247524,
|
||||
"reset_reason": 3,
|
||||
"restart_required": false,
|
||||
"schedule_rev": 0,
|
||||
"time": "11:38",
|
||||
"unixtime": 1747561101,
|
||||
"uptime": 501683,
|
||||
"utc_offset": 7200,
|
||||
"webhook_rev": 0
|
||||
},
|
||||
"temperature:0": {
|
||||
"id": 0,
|
||||
"tC": 46.3,
|
||||
"tF": 115.4
|
||||
},
|
||||
"wifi": {
|
||||
"rssi": -57,
|
||||
"ssid": "Wifi-Network-Name",
|
||||
"sta_ip": "192.168.2.151",
|
||||
"sta_ip6": [],
|
||||
"status": "got ip"
|
||||
},
|
||||
"ws": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.trv_name_calibration',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -24,7 +24,7 @@
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.PROBLEM: 'problem'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name calibration',
|
||||
'original_name': 'Calibration',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -37,7 +37,7 @@
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'problem',
|
||||
'friendly_name': 'TRV-Name calibration',
|
||||
'friendly_name': 'TRV-Name Calibration',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.trv_name_calibration',
|
||||
@ -47,7 +47,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_flood-entry]
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_kitchen_flood-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -60,8 +60,8 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'binary_sensor.test_name_flood',
|
||||
'has_entity_name': False,
|
||||
'entity_id': 'binary_sensor.test_name_kitchen_flood',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -72,7 +72,7 @@
|
||||
}),
|
||||
'original_device_class': <BinarySensorDeviceClass.MOISTURE: 'moisture'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Test name flood',
|
||||
'original_name': 'Kitchen flood',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -81,21 +81,21 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_flood-state]
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_kitchen_flood-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'moisture',
|
||||
'friendly_name': 'Test name flood',
|
||||
'friendly_name': 'Test name Kitchen flood',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.test_name_flood',
|
||||
'entity_id': 'binary_sensor.test_name_kitchen_flood',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_mute-entry]
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_kitchen_mute-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -108,8 +108,8 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'binary_sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'binary_sensor.test_name_mute',
|
||||
'has_entity_name': False,
|
||||
'entity_id': 'binary_sensor.test_name_kitchen_mute',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -120,7 +120,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Test name mute',
|
||||
'original_name': 'Kitchen mute',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -129,13 +129,13 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_mute-state]
|
||||
# name: test_rpc_flood_entities[binary_sensor.test_name_kitchen_mute-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test name mute',
|
||||
'friendly_name': 'Test name Kitchen mute',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'binary_sensor.test_name_mute',
|
||||
'entity_id': 'binary_sensor.test_name_kitchen_mute',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
|
@ -13,7 +13,7 @@
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'button.trv_name_calibrate',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -24,7 +24,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name Calibrate',
|
||||
'original_name': 'Calibrate',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -60,7 +60,7 @@
|
||||
'domain': 'button',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'button.test_name_reboot',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -71,7 +71,7 @@
|
||||
}),
|
||||
'original_device_class': <ButtonDeviceClass.RESTART: 'restart'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Test name Reboot',
|
||||
'original_name': 'Reboot',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
|
@ -90,7 +90,7 @@
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.test_name',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -101,7 +101,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Test name',
|
||||
'original_name': None,
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 401>,
|
||||
@ -140,7 +140,7 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_climate_hvac_mode[climate.test_name_thermostat_0-entry]
|
||||
# name: test_rpc_climate_hvac_mode[climate.test_name-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -161,8 +161,8 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.test_name_thermostat_0',
|
||||
'has_entity_name': False,
|
||||
'entity_id': 'climate.test_name',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -173,7 +173,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Test name Thermostat 0',
|
||||
'original_name': None,
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
@ -182,12 +182,12 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_climate_hvac_mode[climate.test_name_thermostat_0-state]
|
||||
# name: test_rpc_climate_hvac_mode[climate.test_name-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_humidity': 44.4,
|
||||
'current_temperature': 12.3,
|
||||
'friendly_name': 'Test name Thermostat 0',
|
||||
'friendly_name': 'Test name',
|
||||
'hvac_action': <HVACAction.HEATING: 'heating'>,
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
@ -200,14 +200,14 @@
|
||||
'temperature': 23,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.test_name_thermostat_0',
|
||||
'entity_id': 'climate.test_name',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'heat',
|
||||
})
|
||||
# ---
|
||||
# name: test_wall_display_thermostat_mode[climate.test_name_thermostat_0-entry]
|
||||
# name: test_wall_display_thermostat_mode[climate.test_name-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -228,8 +228,8 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.test_name_thermostat_0',
|
||||
'has_entity_name': False,
|
||||
'entity_id': 'climate.test_name',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -240,7 +240,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Test name Thermostat 0',
|
||||
'original_name': None,
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
@ -249,12 +249,12 @@
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_wall_display_thermostat_mode[climate.test_name_thermostat_0-state]
|
||||
# name: test_wall_display_thermostat_mode[climate.test_name-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_humidity': 44.4,
|
||||
'current_temperature': 12.3,
|
||||
'friendly_name': 'Test name Thermostat 0',
|
||||
'friendly_name': 'Test name',
|
||||
'hvac_action': <HVACAction.HEATING: 'heating'>,
|
||||
'hvac_modes': list([
|
||||
<HVACMode.OFF: 'off'>,
|
||||
@ -267,7 +267,7 @@
|
||||
'temperature': 23,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.test_name_thermostat_0',
|
||||
'entity_id': 'climate.test_name',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
|
@ -18,7 +18,7 @@
|
||||
'domain': 'number',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'number.trv_name_external_temperature',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -29,7 +29,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name external temperature',
|
||||
'original_name': 'External temperature',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -41,7 +41,7 @@
|
||||
# name: test_blu_trv_number_entity[number.trv_name_external_temperature-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'TRV-Name external temperature',
|
||||
'friendly_name': 'TRV-Name External temperature',
|
||||
'max': 50,
|
||||
'min': -50,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
@ -75,7 +75,7 @@
|
||||
'domain': 'number',
|
||||
'entity_category': None,
|
||||
'entity_id': 'number.trv_name_valve_position',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -86,7 +86,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name valve position',
|
||||
'original_name': 'Valve position',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -98,7 +98,7 @@
|
||||
# name: test_blu_trv_number_entity[number.trv_name_valve_position-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'TRV-Name valve position',
|
||||
'friendly_name': 'TRV-Name Valve position',
|
||||
'max': 100,
|
||||
'min': 0,
|
||||
'mode': <NumberMode.SLIDER: 'slider'>,
|
||||
|
@ -15,7 +15,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.trv_name_battery',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -26,7 +26,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name battery',
|
||||
'original_name': 'Battery',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -39,7 +39,7 @@
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'battery',
|
||||
'friendly_name': 'TRV-Name battery',
|
||||
'friendly_name': 'TRV-Name Battery',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
@ -67,7 +67,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.trv_name_signal_strength',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -78,7 +78,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.SIGNAL_STRENGTH: 'signal_strength'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name signal strength',
|
||||
'original_name': 'Signal strength',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -91,7 +91,7 @@
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'signal_strength',
|
||||
'friendly_name': 'TRV-Name signal strength',
|
||||
'friendly_name': 'TRV-Name Signal strength',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'dBm',
|
||||
}),
|
||||
@ -119,7 +119,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.trv_name_valve_position',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -130,7 +130,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'TRV-Name valve position',
|
||||
'original_name': 'Valve position',
|
||||
'platform': 'shelly',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -142,7 +142,7 @@
|
||||
# name: test_blu_trv_sensor_entity[sensor.trv_name_valve_position-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'TRV-Name valve position',
|
||||
'friendly_name': 'TRV-Name Valve position',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': '%',
|
||||
}),
|
||||
@ -154,7 +154,7 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_energy-entry]
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_name_test_switch_0_energy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -169,8 +169,8 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_switch_0_energy',
|
||||
'has_entity_name': False,
|
||||
'entity_id': 'sensor.test_name_test_switch_0_energy',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -196,23 +196,23 @@
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_energy-state]
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_name_test_switch_0_energy-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'test switch_0 energy',
|
||||
'friendly_name': 'Test name test switch_0 energy',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_switch_0_energy',
|
||||
'entity_id': 'sensor.test_name_test_switch_0_energy',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1234.56789',
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_returned_energy-entry]
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_name_test_switch_0_returned_energy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
@ -227,8 +227,8 @@
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_switch_0_returned_energy',
|
||||
'has_entity_name': False,
|
||||
'entity_id': 'sensor.test_name_test_switch_0_returned_energy',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -254,16 +254,16 @@
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_switch_0_returned_energy-state]
|
||||
# name: test_rpc_switch_energy_sensors[sensor.test_name_test_switch_0_returned_energy-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'test switch_0 returned energy',
|
||||
'friendly_name': 'Test name test switch_0 returned energy',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_switch_0_returned_energy',
|
||||
'entity_id': 'sensor.test_name_test_switch_0_returned_energy',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
|
@ -36,7 +36,8 @@ async def test_block_binary_sensor(
|
||||
entity_registry: EntityRegistry,
|
||||
) -> None:
|
||||
"""Test block binary sensor."""
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_channel_1_overpowering"
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_overpowering"
|
||||
await init_integration(hass, 1)
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
@ -239,7 +240,7 @@ async def test_rpc_binary_sensor(
|
||||
entity_registry: EntityRegistry,
|
||||
) -> None:
|
||||
"""Test RPC binary sensor."""
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_cover_0_overpowering"
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_test_cover_0_overpowering"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
@ -521,7 +522,7 @@ async def test_rpc_flood_entities(
|
||||
await init_integration(hass, 4)
|
||||
|
||||
for entity in ("flood", "mute"):
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_{entity}"
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_kitchen_{entity}"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state == snapshot(name=f"{entity_id}-state")
|
||||
|
@ -613,7 +613,7 @@ async def test_rpc_climate_hvac_mode(
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test climate hvac mode service."""
|
||||
entity_id = "climate.test_name_thermostat_0"
|
||||
entity_id = "climate.test_name"
|
||||
|
||||
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
|
||||
|
||||
@ -651,7 +651,7 @@ async def test_rpc_climate_without_humidity(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test climate entity without the humidity value."""
|
||||
entity_id = "climate.test_name_thermostat_0"
|
||||
entity_id = "climate.test_name"
|
||||
new_status = deepcopy(mock_rpc_device.status)
|
||||
new_status.pop("humidity:0")
|
||||
monkeypatch.setattr(mock_rpc_device, "status", new_status)
|
||||
@ -673,7 +673,7 @@ async def test_rpc_climate_set_temperature(
|
||||
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test climate set target temperature."""
|
||||
entity_id = "climate.test_name_thermostat_0"
|
||||
entity_id = "climate.test_name"
|
||||
|
||||
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
|
||||
|
||||
@ -700,7 +700,7 @@ async def test_rpc_climate_hvac_mode_cool(
|
||||
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test climate with hvac mode cooling."""
|
||||
entity_id = "climate.test_name_thermostat_0"
|
||||
entity_id = "climate.test_name"
|
||||
new_config = deepcopy(mock_rpc_device.config)
|
||||
new_config["thermostat:0"]["type"] = "cooling"
|
||||
monkeypatch.setattr(mock_rpc_device, "config", new_config)
|
||||
@ -720,8 +720,8 @@ async def test_wall_display_thermostat_mode(
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test Wall Display in thermostat mode."""
|
||||
climate_entity_id = "climate.test_name_thermostat_0"
|
||||
switch_entity_id = "switch.test_switch_0"
|
||||
climate_entity_id = "climate.test_name"
|
||||
switch_entity_id = "switch.test_name_test_switch_0"
|
||||
|
||||
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
|
||||
|
||||
@ -745,8 +745,8 @@ async def test_wall_display_thermostat_mode_external_actuator(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Wall Display in thermostat mode with an external actuator."""
|
||||
climate_entity_id = "climate.test_name_thermostat_0"
|
||||
switch_entity_id = "switch.test_switch_0"
|
||||
climate_entity_id = "climate.test_name"
|
||||
switch_entity_id = "switch.test_name_test_switch_0"
|
||||
|
||||
new_status = deepcopy(mock_rpc_device.status)
|
||||
new_status["sys"]["relay_in_thermostat"] = False
|
||||
|
@ -56,6 +56,8 @@ async def test_block_reload_on_cfg_change(
|
||||
) -> None:
|
||||
"""Test block reload on config change."""
|
||||
await init_integration(hass, 1)
|
||||
# num_outputs is 2, devicename and channel name is used
|
||||
entity_id = "switch.test_name_channel_1"
|
||||
|
||||
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 1)
|
||||
mock_block_device.mock_update()
|
||||
@ -71,7 +73,7 @@ async def test_block_reload_on_cfg_change(
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
# Generate config change from switch to light
|
||||
monkeypatch.setitem(
|
||||
@ -81,14 +83,14 @@ async def test_block_reload_on_cfg_change(
|
||||
mock_block_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
# Wait for debouncer
|
||||
freezer.tick(timedelta(seconds=ENTRY_RELOAD_COOLDOWN))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1") is None
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
|
||||
async def test_block_no_reload_on_bulb_changes(
|
||||
@ -98,6 +100,9 @@ async def test_block_no_reload_on_bulb_changes(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block no reload on bulb mode/effect change."""
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
# num_outputs is 1, device name is used
|
||||
entity_id = "switch.test_name"
|
||||
await init_integration(hass, 1, model=MODEL_BULB)
|
||||
|
||||
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "cfgChanged", 1)
|
||||
@ -113,14 +118,14 @@ async def test_block_no_reload_on_bulb_changes(
|
||||
mock_block_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
# Wait for debouncer
|
||||
freezer.tick(timedelta(seconds=ENTRY_RELOAD_COOLDOWN))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
# Test no reload on effect change
|
||||
monkeypatch.setattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "effect", 1)
|
||||
@ -128,14 +133,14 @@ async def test_block_no_reload_on_bulb_changes(
|
||||
mock_block_device.mock_update()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
# Wait for debouncer
|
||||
freezer.tick(timedelta(seconds=ENTRY_RELOAD_COOLDOWN))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_name_channel_1")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
|
||||
async def test_block_polling_auth_error(
|
||||
@ -242,9 +247,11 @@ async def test_block_polling_connection_error(
|
||||
"update",
|
||||
AsyncMock(side_effect=DeviceConnectionError),
|
||||
)
|
||||
# num_outputs is 2, device name and channel name is used
|
||||
entity_id = "switch.test_name_channel_1"
|
||||
await init_integration(hass, 1)
|
||||
|
||||
assert (state := hass.states.get("switch.test_name_channel_1"))
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_ON
|
||||
|
||||
# Move time to generate polling
|
||||
@ -252,7 +259,7 @@ async def test_block_polling_connection_error(
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (state := hass.states.get("switch.test_name_channel_1"))
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@ -391,6 +398,7 @@ async def test_rpc_reload_on_cfg_change(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test RPC reload on config change."""
|
||||
entity_id = "switch.test_name_test_switch_0"
|
||||
monkeypatch.delitem(mock_rpc_device.status, "cover:0")
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "relay_in_thermostat", False)
|
||||
await init_integration(hass, 2)
|
||||
@ -421,14 +429,14 @@ async def test_rpc_reload_on_cfg_change(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_switch_0")
|
||||
assert hass.states.get(entity_id)
|
||||
|
||||
# Wait for debouncer
|
||||
freezer.tick(timedelta(seconds=ENTRY_RELOAD_COOLDOWN))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_switch_0") is None
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
|
||||
async def test_rpc_reload_with_invalid_auth(
|
||||
@ -719,11 +727,12 @@ async def test_rpc_reconnect_error(
|
||||
exc: Exception,
|
||||
) -> None:
|
||||
"""Test RPC reconnect error."""
|
||||
entity_id = "switch.test_name_test_switch_0"
|
||||
monkeypatch.delitem(mock_rpc_device.status, "cover:0")
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "relay_in_thermostat", False)
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert (state := hass.states.get("switch.test_switch_0"))
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_ON
|
||||
|
||||
monkeypatch.setattr(mock_rpc_device, "connected", False)
|
||||
@ -734,7 +743,7 @@ async def test_rpc_reconnect_error(
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (state := hass.states.get("switch.test_switch_0"))
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@ -746,6 +755,7 @@ async def test_rpc_error_running_connected_events(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test RPC error while running connected events."""
|
||||
entity_id = "switch.test_name_test_switch_0"
|
||||
monkeypatch.delitem(mock_rpc_device.status, "cover:0")
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "relay_in_thermostat", False)
|
||||
with patch(
|
||||
@ -758,7 +768,7 @@ async def test_rpc_error_running_connected_events(
|
||||
|
||||
assert "Error running connected events for device" in caplog.text
|
||||
|
||||
assert (state := hass.states.get("switch.test_switch_0"))
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Move time to generate reconnect without error
|
||||
@ -766,7 +776,7 @@ async def test_rpc_error_running_connected_events(
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
assert (state := hass.states.get("switch.test_switch_0"))
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
|
@ -116,7 +116,7 @@ async def test_rpc_device_services(
|
||||
entity_registry: EntityRegistry,
|
||||
) -> None:
|
||||
"""Test RPC device cover services."""
|
||||
entity_id = "cover.test_cover_0"
|
||||
entity_id = "cover.test_name_test_cover_0"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
await hass.services.async_call(
|
||||
@ -178,23 +178,24 @@ async def test_rpc_device_no_cover_keys(
|
||||
monkeypatch.delitem(mock_rpc_device.status, "cover:0")
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert hass.states.get("cover.test_cover_0") is None
|
||||
assert hass.states.get("cover.test_name_test_cover_0") is None
|
||||
|
||||
|
||||
async def test_rpc_device_update(
|
||||
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test RPC device update."""
|
||||
entity_id = "cover.test_name_test_cover_0"
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed")
|
||||
await init_integration(hass, 2)
|
||||
|
||||
state = hass.states.get("cover.test_cover_0")
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == CoverState.CLOSED
|
||||
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "open")
|
||||
mock_rpc_device.mock_update()
|
||||
state = hass.states.get("cover.test_cover_0")
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == CoverState.OPEN
|
||||
|
||||
@ -208,7 +209,7 @@ async def test_rpc_device_no_position_control(
|
||||
)
|
||||
await init_integration(hass, 2)
|
||||
|
||||
state = hass.states.get("cover.test_cover_0")
|
||||
state = hass.states.get("cover.test_name_test_cover_0")
|
||||
assert state
|
||||
assert state.state == CoverState.OPEN
|
||||
|
||||
@ -220,7 +221,7 @@ async def test_rpc_cover_tilt(
|
||||
entity_registry: EntityRegistry,
|
||||
) -> None:
|
||||
"""Test RPC cover that supports tilt."""
|
||||
entity_id = "cover.test_cover_0"
|
||||
entity_id = "cover.test_name_test_cover_0"
|
||||
|
||||
config = deepcopy(mock_rpc_device.config)
|
||||
config["cover:0"]["slat"] = {"enable": True}
|
||||
|
479
tests/components/shelly/test_devices.py
Normal file
479
tests/components/shelly/test_devices.py
Normal file
@ -0,0 +1,479 @@
|
||||
"""Test real devices."""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
from aioshelly.const import MODEL_2PM_G3, MODEL_PRO_EM3
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.shelly.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceRegistry
|
||||
from homeassistant.helpers.entity_registry import EntityRegistry
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import load_json_object_fixture
|
||||
|
||||
|
||||
async def test_shelly_2pm_gen3_no_relay_names(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly 2PM Gen3 without relay names.
|
||||
|
||||
This device has two relays/channels,we should get a main device and two sub
|
||||
devices.
|
||||
"""
|
||||
device_fixture = load_json_object_fixture("2pm_gen3.json", DOMAIN)
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=3, model=MODEL_2PM_G3)
|
||||
|
||||
# Relay 0 sub-device
|
||||
entity_id = "switch.test_name_switch_0"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Switch 0"
|
||||
|
||||
entity_id = "sensor.test_name_switch_0_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Switch 0"
|
||||
|
||||
# Relay 1 sub-device
|
||||
entity_id = "switch.test_name_switch_1"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Switch 1"
|
||||
|
||||
entity_id = "sensor.test_name_switch_1_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Switch 1"
|
||||
|
||||
# Main device
|
||||
entity_id = "update.test_name_firmware"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
|
||||
async def test_shelly_2pm_gen3_relay_names(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly 2PM Gen3 with relay names.
|
||||
|
||||
This device has two relays/channels,we should get a main device and two sub
|
||||
devices.
|
||||
"""
|
||||
device_fixture = load_json_object_fixture("2pm_gen3.json", DOMAIN)
|
||||
device_fixture["config"]["switch:0"]["name"] = "Kitchen light"
|
||||
device_fixture["config"]["switch:1"]["name"] = "Living room light"
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=3, model=MODEL_2PM_G3)
|
||||
|
||||
# Relay 0 sub-device
|
||||
entity_id = "switch.kitchen_light"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Kitchen light"
|
||||
|
||||
entity_id = "sensor.kitchen_light_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Kitchen light"
|
||||
|
||||
# Relay 1 sub-device
|
||||
entity_id = "switch.living_room_light"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Living room light"
|
||||
|
||||
entity_id = "sensor.living_room_light_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Living room light"
|
||||
|
||||
# Main device
|
||||
entity_id = "update.test_name_firmware"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
|
||||
async def test_shelly_2pm_gen3_cover(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly 2PM Gen3 with cover profile.
|
||||
|
||||
With the cover profile we should only get the main device and no subdevices.
|
||||
"""
|
||||
device_fixture = load_json_object_fixture("2pm_gen3_cover.json", DOMAIN)
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=3, model=MODEL_2PM_G3)
|
||||
|
||||
entity_id = "cover.test_name"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
entity_id = "sensor.test_name_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
entity_id = "update.test_name_firmware"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
|
||||
async def test_shelly_2pm_gen3_cover_with_name(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly 2PM Gen3 with cover profile and the cover name.
|
||||
|
||||
With the cover profile we should only get the main device and no subdevices.
|
||||
"""
|
||||
device_fixture = load_json_object_fixture("2pm_gen3_cover.json", DOMAIN)
|
||||
device_fixture["config"]["cover:0"]["name"] = "Bedroom blinds"
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=3, model=MODEL_2PM_G3)
|
||||
|
||||
entity_id = "cover.test_name_bedroom_blinds"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
entity_id = "sensor.test_name_bedroom_blinds_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
entity_id = "update.test_name_firmware"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
|
||||
async def test_shelly_pro_3em(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly Pro 3EM.
|
||||
|
||||
We should get the main device and three subdevices, one subdevice per one phase.
|
||||
"""
|
||||
device_fixture = load_json_object_fixture("pro_3em.json", DOMAIN)
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=2, model=MODEL_PRO_EM3)
|
||||
|
||||
# Main device
|
||||
entity_id = "sensor.test_name_total_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
# Phase A sub-device
|
||||
entity_id = "sensor.test_name_phase_a_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Phase A"
|
||||
|
||||
# Phase B sub-device
|
||||
entity_id = "sensor.test_name_phase_b_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Phase B"
|
||||
|
||||
# Phase C sub-device
|
||||
entity_id = "sensor.test_name_phase_c_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Phase C"
|
||||
|
||||
|
||||
async def test_shelly_pro_3em_with_emeter_name(
|
||||
hass: HomeAssistant,
|
||||
mock_rpc_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Shelly Pro 3EM when the name for Emeter is set.
|
||||
|
||||
We should get the main device and three subdevices, one subdevice per one phase.
|
||||
"""
|
||||
device_fixture = load_json_object_fixture("pro_3em.json", DOMAIN)
|
||||
device_fixture["config"]["em:0"]["name"] = "Emeter name"
|
||||
monkeypatch.setattr(mock_rpc_device, "shelly", device_fixture["shelly"])
|
||||
monkeypatch.setattr(mock_rpc_device, "status", device_fixture["status"])
|
||||
monkeypatch.setattr(mock_rpc_device, "config", device_fixture["config"])
|
||||
|
||||
await init_integration(hass, gen=2, model=MODEL_PRO_EM3)
|
||||
|
||||
# Main device
|
||||
entity_id = "sensor.test_name_total_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
||||
|
||||
# Phase A sub-device
|
||||
entity_id = "sensor.test_name_phase_a_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Phase A"
|
||||
|
||||
# Phase B sub-device
|
||||
entity_id = "sensor.test_name_phase_b_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Phase B"
|
||||
|
||||
# Phase C sub-device
|
||||
entity_id = "sensor.test_name_phase_c_active_power"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name Phase C"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_block_channel_with_name(
|
||||
hass: HomeAssistant,
|
||||
mock_block_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
entity_registry: EntityRegistry,
|
||||
device_registry: DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test block channel with name."""
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings["relays"][0], "name", "Kitchen light"
|
||||
)
|
||||
|
||||
await init_integration(hass, 1)
|
||||
|
||||
# channel 1 sub-device; num_outputs is 2 so the name of the channel should be used
|
||||
entity_id = "switch.kitchen_light"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Kitchen light"
|
||||
|
||||
# main device
|
||||
entity_id = "update.test_name_firmware"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry
|
||||
|
||||
device_entry = device_registry.async_get(entry.device_id)
|
||||
assert device_entry
|
||||
assert device_entry.name == "Test name"
|
@ -147,7 +147,7 @@ async def test_rpc_config_entry_diagnostics(
|
||||
],
|
||||
"last_detection": ANY,
|
||||
"monotonic_time": ANY,
|
||||
"name": "Mock Title (12:34:56:78:9A:BE)",
|
||||
"name": "Test name (12:34:56:78:9A:BE)",
|
||||
"scanning": True,
|
||||
"start_time": ANY,
|
||||
"source": "12:34:56:78:9A:BE",
|
||||
|
@ -31,7 +31,7 @@ async def test_rpc_button(
|
||||
) -> None:
|
||||
"""Test RPC device event."""
|
||||
await init_integration(hass, 2)
|
||||
entity_id = "event.test_name_input_0"
|
||||
entity_id = "event.test_name_test_input_0"
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_UNKNOWN
|
||||
@ -176,6 +176,7 @@ async def test_block_event(
|
||||
) -> None:
|
||||
"""Test block device event."""
|
||||
await init_integration(hass, 1)
|
||||
# num_outputs is 2, device name and channel name is used
|
||||
entity_id = "event.test_name_channel_1"
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
@ -201,11 +202,12 @@ async def test_block_event(
|
||||
|
||||
|
||||
async def test_block_event_shix3_1(
|
||||
hass: HomeAssistant, mock_block_device: Mock
|
||||
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test block device event for SHIX3-1."""
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
await init_integration(hass, 1, model=MODEL_I3)
|
||||
entity_id = "event.test_name_channel_1"
|
||||
entity_id = "event.test_name"
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.attributes.get(ATTR_EVENT_TYPES) == unordered(
|
||||
|
@ -346,7 +346,7 @@ async def test_sleeping_rpc_device_offline_during_setup(
|
||||
("gen", "entity_id"),
|
||||
[
|
||||
(1, "switch.test_name_channel_1"),
|
||||
(2, "switch.test_switch_0"),
|
||||
(2, "switch.test_name_test_switch_0"),
|
||||
],
|
||||
)
|
||||
async def test_entry_unload(
|
||||
@ -378,7 +378,7 @@ async def test_entry_unload(
|
||||
("gen", "entity_id"),
|
||||
[
|
||||
(1, "switch.test_name_channel_1"),
|
||||
(2, "switch.test_switch_0"),
|
||||
(2, "switch.test_name_test_switch_0"),
|
||||
],
|
||||
)
|
||||
async def test_entry_unload_device_not_ready(
|
||||
@ -417,7 +417,7 @@ async def test_entry_unload_not_connected(
|
||||
)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
assert (state := hass.states.get("switch.test_switch_0"))
|
||||
assert (state := hass.states.get("switch.test_name_test_switch_0"))
|
||||
assert state.state == STATE_ON
|
||||
assert not mock_stop_scanner.call_count
|
||||
|
||||
@ -448,7 +448,7 @@ async def test_entry_unload_not_connected_but_we_think_we_are(
|
||||
)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
assert (state := hass.states.get("switch.test_switch_0"))
|
||||
assert (state := hass.states.get("switch.test_name_test_switch_0"))
|
||||
assert state.state == STATE_ON
|
||||
assert not mock_stop_scanner.call_count
|
||||
|
||||
@ -483,6 +483,7 @@ async def test_entry_missing_gen(hass: HomeAssistant, mock_block_device: Mock) -
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# num_outputs is 2, channel name is used
|
||||
assert (state := hass.states.get("switch.test_name_channel_1"))
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
@ -58,10 +58,14 @@ SHELLY_PLUS_RGBW_CHANNELS = 4
|
||||
|
||||
|
||||
async def test_block_device_rgbw_bulb(
|
||||
hass: HomeAssistant, mock_block_device: Mock, entity_registry: EntityRegistry
|
||||
hass: HomeAssistant,
|
||||
mock_block_device: Mock,
|
||||
entity_registry: EntityRegistry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block device RGBW bulb."""
|
||||
entity_id = "light.test_name_channel_1"
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
entity_id = "light.test_name"
|
||||
await init_integration(hass, 1, model=MODEL_BULB)
|
||||
|
||||
# Test initial
|
||||
@ -142,7 +146,8 @@ async def test_block_device_rgb_bulb(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test block device RGB bulb."""
|
||||
entity_id = "light.test_name_channel_1"
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
entity_id = "light.test_name"
|
||||
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "mode")
|
||||
monkeypatch.setattr(
|
||||
mock_block_device.blocks[LIGHT_BLOCK_ID], "description", "light_1"
|
||||
@ -246,7 +251,8 @@ async def test_block_device_white_bulb(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block device white bulb."""
|
||||
entity_id = "light.test_name_channel_1"
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
entity_id = "light.test_name"
|
||||
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "red")
|
||||
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "green")
|
||||
monkeypatch.delattr(mock_block_device.blocks[LIGHT_BLOCK_ID], "blue")
|
||||
@ -322,6 +328,7 @@ async def test_block_device_support_transition(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block device supports transition."""
|
||||
# num_outputs is 2, device name and channel name is used
|
||||
entity_id = "light.test_name_channel_1"
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings, "fw", "20220809-122808/v1.12-g99f7e0b"
|
||||
@ -448,7 +455,7 @@ async def test_rpc_device_switch_type_lights_mode(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test RPC device with switch in consumption type lights mode."""
|
||||
entity_id = "light.test_switch_0"
|
||||
entity_id = "light.test_name_test_switch_0"
|
||||
monkeypatch.setitem(
|
||||
mock_rpc_device.config["sys"]["ui_data"], "consumption_types", ["lights"]
|
||||
)
|
||||
@ -595,7 +602,7 @@ async def test_rpc_device_rgb_profile(
|
||||
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
|
||||
monkeypatch.delitem(mock_rpc_device.status, f"light:{i}")
|
||||
monkeypatch.delitem(mock_rpc_device.status, "rgbw:0")
|
||||
entity_id = "light.test_rgb_0"
|
||||
entity_id = "light.test_name_test_rgb_0"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
# Test initial
|
||||
@ -639,7 +646,7 @@ async def test_rpc_device_rgbw_profile(
|
||||
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
|
||||
monkeypatch.delitem(mock_rpc_device.status, f"light:{i}")
|
||||
monkeypatch.delitem(mock_rpc_device.status, "rgb:0")
|
||||
entity_id = "light.test_rgbw_0"
|
||||
entity_id = "light.test_name_test_rgbw_0"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
# Test initial
|
||||
@ -753,7 +760,7 @@ async def test_rpc_rgbw_device_rgb_w_modes_remove_others(
|
||||
# register lights
|
||||
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
|
||||
monkeypatch.delitem(mock_rpc_device.status, f"light:{i}")
|
||||
entity_id = f"light.test_light_{i}"
|
||||
entity_id = f"light.test_name_test_light_{i}"
|
||||
register_entity(
|
||||
hass,
|
||||
LIGHT_DOMAIN,
|
||||
@ -781,7 +788,7 @@ async def test_rpc_rgbw_device_rgb_w_modes_remove_others(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# verify we have RGB/w light
|
||||
entity_id = f"light.test_{active_mode}_0"
|
||||
entity_id = f"light.test_name_test_{active_mode}_0"
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == STATE_ON
|
||||
|
@ -108,7 +108,7 @@ async def test_humanify_shelly_click_event_rpc_device(
|
||||
assert event1["domain"] == DOMAIN
|
||||
assert (
|
||||
event1["message"]
|
||||
== "'single_push' click event for Test name input 0 Input was fired"
|
||||
== "'single_push' click event for Test name Test input 0 Input was fired"
|
||||
)
|
||||
|
||||
assert event2["name"] == "Shelly"
|
||||
|
@ -62,6 +62,7 @@ async def test_block_sensor(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test block sensor."""
|
||||
# num_outputs is 2, channel name is used
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_channel_1_power"
|
||||
await init_integration(hass, 1)
|
||||
|
||||
@ -82,6 +83,7 @@ async def test_energy_sensor(
|
||||
hass: HomeAssistant, mock_block_device: Mock, entity_registry: EntityRegistry
|
||||
) -> None:
|
||||
"""Test energy sensor."""
|
||||
# num_outputs is 2, channel name is used
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_channel_1_energy"
|
||||
await init_integration(hass, 1)
|
||||
|
||||
@ -430,7 +432,9 @@ async def test_block_shelly_air_lamp_life(
|
||||
percentage: float,
|
||||
) -> None:
|
||||
"""Test block Shelly Air lamp life percentage sensor."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.{'test_name_channel_1_lamp_life'}"
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
# num_outputs is 1, device name is used
|
||||
entity_id = f"{SENSOR_DOMAIN}.{'test_name_lamp_life'}"
|
||||
monkeypatch.setattr(
|
||||
mock_block_device.blocks[RELAY_BLOCK_ID], "totalWorkTime", lamp_life_seconds
|
||||
)
|
||||
@ -444,7 +448,7 @@ async def test_rpc_sensor(
|
||||
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test RPC sensor."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_cover_0_power"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_test_cover_0_power"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
@ -673,37 +677,45 @@ async def test_rpc_restored_sleeping_sensor_no_last_state(
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_rpc_em1_sensors(
|
||||
async def test_rpc_energy_meter_1_sensors(
|
||||
hass: HomeAssistant, entity_registry: EntityRegistry, mock_rpc_device: Mock
|
||||
) -> None:
|
||||
"""Test RPC sensors for EM1 component."""
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert (state := hass.states.get("sensor.test_name_em0_power"))
|
||||
assert (state := hass.states.get("sensor.test_name_energy_meter_0_power"))
|
||||
assert state.state == "85.3"
|
||||
|
||||
assert (entry := entity_registry.async_get("sensor.test_name_em0_power"))
|
||||
assert (entry := entity_registry.async_get("sensor.test_name_energy_meter_0_power"))
|
||||
assert entry.unique_id == "123456789ABC-em1:0-power_em1"
|
||||
|
||||
assert (state := hass.states.get("sensor.test_name_em1_power"))
|
||||
assert (state := hass.states.get("sensor.test_name_energy_meter_1_power"))
|
||||
assert state.state == "123.3"
|
||||
|
||||
assert (entry := entity_registry.async_get("sensor.test_name_em1_power"))
|
||||
assert (entry := entity_registry.async_get("sensor.test_name_energy_meter_1_power"))
|
||||
assert entry.unique_id == "123456789ABC-em1:1-power_em1"
|
||||
|
||||
assert (state := hass.states.get("sensor.test_name_em0_total_active_energy"))
|
||||
assert (
|
||||
state := hass.states.get("sensor.test_name_energy_meter_0_total_active_energy")
|
||||
)
|
||||
assert state.state == "123.4564"
|
||||
|
||||
assert (
|
||||
entry := entity_registry.async_get("sensor.test_name_em0_total_active_energy")
|
||||
entry := entity_registry.async_get(
|
||||
"sensor.test_name_energy_meter_0_total_active_energy"
|
||||
)
|
||||
)
|
||||
assert entry.unique_id == "123456789ABC-em1data:0-total_act_energy"
|
||||
|
||||
assert (state := hass.states.get("sensor.test_name_em1_total_active_energy"))
|
||||
assert (
|
||||
state := hass.states.get("sensor.test_name_energy_meter_1_total_active_energy")
|
||||
)
|
||||
assert state.state == "987.6543"
|
||||
|
||||
assert (
|
||||
entry := entity_registry.async_get("sensor.test_name_em1_total_active_energy")
|
||||
entry := entity_registry.async_get(
|
||||
"sensor.test_name_energy_meter_1_total_active_energy"
|
||||
)
|
||||
)
|
||||
assert entry.unique_id == "123456789ABC-em1data:1-total_act_energy"
|
||||
|
||||
@ -901,7 +913,7 @@ async def test_rpc_pulse_counter_sensors(
|
||||
|
||||
await init_integration(hass, 2)
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.gas_pulse_counter"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_gas_pulse_counter"
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == "56174"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "pulse"
|
||||
@ -910,7 +922,7 @@ async def test_rpc_pulse_counter_sensors(
|
||||
assert (entry := entity_registry.async_get(entity_id))
|
||||
assert entry.unique_id == "123456789ABC-input:2-pulse_counter"
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.gas_counter_value"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_gas_counter_value"
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == "561.74"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == expected_unit
|
||||
@ -949,11 +961,11 @@ async def test_rpc_disabled_xtotal_counter(
|
||||
)
|
||||
await init_integration(hass, 2)
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.gas_pulse_counter"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_gas_pulse_counter"
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == "20635"
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.gas_counter_value"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_gas_counter_value"
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
|
||||
@ -980,7 +992,7 @@ async def test_rpc_pulse_counter_frequency_sensors(
|
||||
|
||||
await init_integration(hass, 2)
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.gas_pulse_counter_frequency"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_gas_pulse_counter_frequency"
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == "208.0"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfFrequency.HERTZ
|
||||
@ -989,7 +1001,7 @@ async def test_rpc_pulse_counter_frequency_sensors(
|
||||
assert (entry := entity_registry.async_get(entity_id))
|
||||
assert entry.unique_id == "123456789ABC-input:2-counter_frequency"
|
||||
|
||||
entity_id = f"{SENSOR_DOMAIN}.gas_pulse_counter_frequency_value"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_gas_pulse_counter_frequency_value"
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == "6.11"
|
||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == expected_unit
|
||||
@ -1411,7 +1423,7 @@ async def test_rpc_rgbw_sensors(
|
||||
assert (entry := entity_registry.async_get(entity_id))
|
||||
assert entry.unique_id == f"123456789ABC-{light_type}:0-voltage_{light_type}"
|
||||
|
||||
entity_id = f"sensor.test_name_{light_type}_light_0_device_temperature"
|
||||
entity_id = f"sensor.test_name_{light_type}_light_0_temperature"
|
||||
|
||||
assert (state := hass.states.get(entity_id))
|
||||
assert state.state == "54.3"
|
||||
@ -1544,7 +1556,7 @@ async def test_rpc_switch_energy_sensors(
|
||||
await init_integration(hass, 3)
|
||||
|
||||
for entity in ("energy", "returned_energy"):
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_switch_0_{entity}"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_test_switch_0_{entity}"
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state == snapshot(name=f"{entity_id}-state")
|
||||
@ -1572,4 +1584,4 @@ async def test_rpc_switch_no_returned_energy_sensor(
|
||||
monkeypatch.setattr(mock_rpc_device, "status", status)
|
||||
await init_integration(hass, 3)
|
||||
|
||||
assert hass.states.get("sensor.test_switch_0_returned_energy") is None
|
||||
assert hass.states.get("sensor.test_name_test_switch_0_returned_energy") is None
|
||||
|
@ -42,6 +42,7 @@ async def test_block_device_services(
|
||||
) -> None:
|
||||
"""Test block device turn on/off services."""
|
||||
await init_integration(hass, 1)
|
||||
# num_outputs is 2, device_name and channel name is used
|
||||
entity_id = "switch.test_name_channel_1"
|
||||
|
||||
await hass.services.async_call(
|
||||
@ -192,7 +193,7 @@ async def test_block_restored_motion_switch_no_last_state(
|
||||
@pytest.mark.parametrize(
|
||||
("model", "sleep", "entity", "unique_id"),
|
||||
[
|
||||
(MODEL_1PM, 0, "switch.test_name_channel_1", "123456789ABC-relay_0"),
|
||||
(MODEL_1PM, 0, "switch.test_name", "123456789ABC-relay_0"),
|
||||
(
|
||||
MODEL_MOTION,
|
||||
1000,
|
||||
@ -205,12 +206,15 @@ async def test_block_device_unique_ids(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: EntityRegistry,
|
||||
mock_block_device: Mock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
model: str,
|
||||
sleep: int,
|
||||
entity: str,
|
||||
unique_id: str,
|
||||
) -> None:
|
||||
"""Test block device unique_ids."""
|
||||
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
|
||||
# num_outputs is 1, device name is used
|
||||
await init_integration(hass, 1, model=model, sleep_period=sleep)
|
||||
|
||||
if sleep:
|
||||
@ -332,7 +336,7 @@ async def test_rpc_device_services(
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "relay_in_thermostat", False)
|
||||
await init_integration(hass, 2)
|
||||
|
||||
entity_id = "switch.test_switch_0"
|
||||
entity_id = "switch.test_name_test_switch_0"
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@ -365,7 +369,7 @@ async def test_rpc_device_unique_ids(
|
||||
monkeypatch.setitem(mock_rpc_device.status["sys"], "relay_in_thermostat", False)
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert (entry := entity_registry.async_get("switch.test_switch_0"))
|
||||
assert (entry := entity_registry.async_get("switch.test_name_test_switch_0"))
|
||||
assert entry.unique_id == "123456789ABC-switch:0"
|
||||
|
||||
|
||||
@ -386,11 +390,11 @@ async def test_rpc_device_switch_type_lights_mode(
|
||||
[
|
||||
(
|
||||
DeviceConnectionError,
|
||||
"Device communication error occurred while calling action for switch.test_switch_0 of Test name",
|
||||
"Device communication error occurred while calling action for switch.test_name_test_switch_0 of Test name",
|
||||
),
|
||||
(
|
||||
RpcCallError(-1, "error"),
|
||||
"RPC call error occurred while calling action for switch.test_switch_0 of Test name",
|
||||
"RPC call error occurred while calling action for switch.test_name_test_switch_0 of Test name",
|
||||
),
|
||||
],
|
||||
)
|
||||
@ -411,7 +415,7 @@ async def test_rpc_set_state_errors(
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "switch.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "switch.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
@ -434,7 +438,7 @@ async def test_rpc_auth_error(
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "switch.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "switch.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
@ -476,8 +480,8 @@ async def test_wall_display_relay_mode(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test Wall Display in relay mode."""
|
||||
climate_entity_id = "climate.test_name_thermostat_0"
|
||||
switch_entity_id = "switch.test_switch_0"
|
||||
climate_entity_id = "climate.test_name"
|
||||
switch_entity_id = "switch.test_name_test_switch_0"
|
||||
|
||||
config_entry = await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
|
||||
|
||||
|
@ -79,37 +79,38 @@ async def test_block_get_block_channel_name(
|
||||
mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""Test block get block channel name."""
|
||||
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "relay")
|
||||
|
||||
assert (
|
||||
get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
== "Test name channel 1"
|
||||
result = get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
# when has_entity_name is True the result should be None
|
||||
assert result is None
|
||||
|
||||
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "relay")
|
||||
result = get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
# when has_entity_name is True the result should be None
|
||||
assert result is None
|
||||
|
||||
monkeypatch.setitem(mock_block_device.settings["device"], "type", MODEL_EM3)
|
||||
|
||||
assert (
|
||||
get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
== "Test name channel A"
|
||||
result = get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
# when has_entity_name is True the result should be None
|
||||
assert result is None
|
||||
|
||||
monkeypatch.setitem(
|
||||
mock_block_device.settings, "relays", [{"name": "test-channel"}]
|
||||
)
|
||||
|
||||
assert (
|
||||
get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
== "test-channel"
|
||||
result = get_block_channel_name(
|
||||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
# when has_entity_name is True the result should be None
|
||||
assert result is None
|
||||
|
||||
|
||||
async def test_is_block_momentary_input(
|
||||
@ -241,20 +242,19 @@ async def test_get_block_input_triggers(
|
||||
|
||||
async def test_get_rpc_channel_name(mock_rpc_device: Mock) -> None:
|
||||
"""Test get RPC channel name."""
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:0") == "Test name input 0"
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Test name Input 3"
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:0") == "Test input 0"
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Input 3"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("component", "expected"),
|
||||
[
|
||||
("cover", "Cover"),
|
||||
("input", "Input"),
|
||||
("light", "Light"),
|
||||
("rgb", "RGB light"),
|
||||
("rgbw", "RGBW light"),
|
||||
("switch", "Switch"),
|
||||
("thermostat", "Thermostat"),
|
||||
("cover", None),
|
||||
("light", None),
|
||||
("rgb", None),
|
||||
("rgbw", None),
|
||||
("switch", None),
|
||||
("thermostat", None),
|
||||
],
|
||||
)
|
||||
async def test_get_rpc_channel_name_multiple_components(
|
||||
@ -270,14 +270,9 @@ async def test_get_rpc_channel_name_multiple_components(
|
||||
}
|
||||
monkeypatch.setattr(mock_rpc_device, "config", config)
|
||||
|
||||
assert (
|
||||
get_rpc_channel_name(mock_rpc_device, f"{component}:0")
|
||||
== f"Test name {expected} 0"
|
||||
)
|
||||
assert (
|
||||
get_rpc_channel_name(mock_rpc_device, f"{component}:1")
|
||||
== f"Test name {expected} 1"
|
||||
)
|
||||
# we use sub-devices, so the entity name is not set
|
||||
assert get_rpc_channel_name(mock_rpc_device, f"{component}:0") == expected
|
||||
assert get_rpc_channel_name(mock_rpc_device, f"{component}:1") == expected
|
||||
|
||||
|
||||
async def test_get_rpc_input_triggers(
|
||||
|
Loading…
x
Reference in New Issue
Block a user