This commit is contained in:
Bram Kragten 2025-07-22 10:30:09 +02:00 committed by GitHub
commit 456f992b7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 284 additions and 75 deletions

View File

@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "bronze",
"requirements": ["aioamazondevices==3.2.10"]
"requirements": ["aioamazondevices==3.5.0"]
}

View File

@ -8,7 +8,7 @@
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
"iot_class": "local_push",
"loggers": ["async_upnp_client"],
"requirements": ["async-upnp-client==0.44.0", "getmac==0.9.5"],
"requirements": ["async-upnp-client==0.45.0", "getmac==0.9.5"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",

View File

@ -7,7 +7,7 @@
"dependencies": ["ssdp"],
"documentation": "https://www.home-assistant.io/integrations/dlna_dms",
"iot_class": "local_polling",
"requirements": ["async-upnp-client==0.44.0"],
"requirements": ["async-upnp-client==0.45.0"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaServer:1",

View File

@ -7,7 +7,7 @@
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"quality_scale": "platinum",
"requirements": ["pyenphase==2.2.1"],
"requirements": ["pyenphase==2.2.2"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."

View File

@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250702.2"]
"requirements": ["home-assistant-frontend==20250702.3"]
}

View File

@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["dacite", "gios"],
"requirements": ["gios==6.1.0"]
"requirements": ["gios==6.1.2"]
}

View File

@ -306,6 +306,11 @@ class WebRTCProvider(CameraWebRTCProvider):
await self.teardown()
raise HomeAssistantError("Camera has no stream source")
if camera.platform.platform_name == "generic":
# This is a workaround to use ffmpeg for generic cameras
# A proper fix will be added in the future together with supporting multiple streams per camera
stream_source = "ffmpeg:" + stream_source
if not self.async_is_supported(stream_source):
await self.teardown()
raise HomeAssistantError("Stream source is not supported by go2rtc")

View File

@ -113,9 +113,7 @@ class HomematicipHAP:
self._ws_close_requested = False
self._ws_connection_closed = asyncio.Event()
self._retry_task: asyncio.Task | None = None
self._tries = 0
self._accesspoint_connected = True
self._get_state_task: asyncio.Task | None = None
self.hmip_device_by_entity_id: dict[str, Any] = {}
self.reset_connection_listener: Callable | None = None
@ -161,17 +159,8 @@ class HomematicipHAP:
"""
if not self.home.connected:
_LOGGER.error("HMIP access point has lost connection with the cloud")
self._accesspoint_connected = False
self._ws_connection_closed.set()
self.set_all_to_unavailable()
elif not self._accesspoint_connected:
# Now the HOME_CHANGED event has fired indicating the access
# point has reconnected to the cloud again.
# Explicitly getting an update as entity states might have
# changed during access point disconnect."""
job = self.hass.async_create_task(self.get_state())
job.add_done_callback(self.get_state_finished)
self._accesspoint_connected = True
@callback
def async_create_entity(self, *args, **kwargs) -> None:
@ -185,20 +174,43 @@ class HomematicipHAP:
await asyncio.sleep(30)
await self.hass.config_entries.async_reload(self.config_entry.entry_id)
async def _try_get_state(self) -> None:
"""Call get_state in a loop until no error occurs, using exponential backoff on error."""
# Wait until WebSocket connection is established.
while not self.home.websocket_is_connected():
await asyncio.sleep(2)
delay = 8
max_delay = 1500
while True:
try:
await self.get_state()
break
except HmipConnectionError as err:
_LOGGER.warning(
"Get_state failed, retrying in %s seconds: %s", delay, err
)
await asyncio.sleep(delay)
delay = min(delay * 2, max_delay)
async def get_state(self) -> None:
"""Update HMIP state and tell Home Assistant."""
await self.home.get_current_state_async()
self.update_all()
def get_state_finished(self, future) -> None:
"""Execute when get_state coroutine has finished."""
"""Execute when try_get_state coroutine has finished."""
try:
future.result()
except HmipConnectionError:
# Somehow connection could not recover. Will disconnect and
# so reconnect loop is taking over.
_LOGGER.error("Updating state after HMIP access point reconnect failed")
self.hass.async_create_task(self.home.disable_events())
except Exception as err: # noqa: BLE001
_LOGGER.error(
"Error updating state after HMIP access point reconnect: %s", err
)
else:
_LOGGER.info(
"Updating state after HMIP access point reconnect finished successfully",
)
def set_all_to_unavailable(self) -> None:
"""Set all devices to unavailable and tell Home Assistant."""
@ -222,8 +234,8 @@ class HomematicipHAP:
async def async_reset(self) -> bool:
"""Close the websocket connection."""
self._ws_close_requested = True
if self._retry_task is not None:
self._retry_task.cancel()
if self._get_state_task is not None:
self._get_state_task.cancel()
await self.home.disable_events_async()
_LOGGER.debug("Closed connection to HomematicIP cloud server")
await self.hass.config_entries.async_unload_platforms(
@ -247,7 +259,9 @@ class HomematicipHAP:
"""Handle websocket connected."""
_LOGGER.info("Websocket connection to HomematicIP Cloud established")
if self._ws_connection_closed.is_set():
await self.get_state()
self._get_state_task = self.hass.async_create_task(self._try_get_state())
self._get_state_task.add_done_callback(self.get_state_finished)
self._ws_connection_closed.clear()
async def ws_disconnected_handler(self) -> None:
@ -256,11 +270,12 @@ class HomematicipHAP:
self._ws_connection_closed.set()
async def ws_reconnected_handler(self, reason: str) -> None:
"""Handle websocket reconnection."""
"""Handle websocket reconnection. Is called when Websocket tries to reconnect."""
_LOGGER.info(
"Websocket connection to HomematicIP Cloud re-established due to reason: %s",
"Websocket connection to HomematicIP Cloud trying to reconnect due to reason: %s",
reason,
)
self._ws_connection_closed.set()
async def get_hap(

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/homematicip_cloud",
"iot_class": "cloud_push",
"loggers": ["homematicip"],
"requirements": ["homematicip==2.0.6"]
"requirements": ["homematicip==2.0.7"]
}

View File

@ -98,6 +98,12 @@ def validate_sensor_state_and_device_class_config(config: ConfigType) -> ConfigT
f"together with state class `{state_class}`"
)
unit_of_measurement: str | None
if (
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
) is not None and not unit_of_measurement.strip():
config.pop(CONF_UNIT_OF_MEASUREMENT)
# Only allow `options` to be set for `enum` sensors
# to limit the possible sensor values
if (options := config.get(CONF_OPTIONS)) is not None:

View File

@ -40,7 +40,7 @@
"samsungctl[websocket]==0.7.1",
"samsungtvws[async,encrypted]==2.7.2",
"wakeonlan==3.1.0",
"async-upnp-client==0.44.0"
"async-upnp-client==0.45.0"
],
"ssdp": [
{

View File

@ -868,8 +868,8 @@ RPC_SENSORS: Final = {
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
available=lambda status: (status and status["n_current"]) is not None,
removal_condition=lambda _config, status, _key: "n_current" not in status,
removal_condition=lambda _config, status, key: status[key].get("n_current")
is None,
entity_registry_enabled_default=False,
),
"total_current": RpcSensorDescription(

View File

@ -30,5 +30,5 @@
"iot_class": "cloud_push",
"loggers": ["pysmartthings"],
"quality_scale": "bronze",
"requirements": ["pysmartthings==3.2.7"]
"requirements": ["pysmartthings==3.2.8"]
}

View File

@ -72,7 +72,7 @@ class SonosFavorites(SonosHouseholdCoordinator):
"""Process the event payload in an async lock and update entities."""
event_id = event.variables["favorites_update_id"]
container_ids = event.variables["container_update_i_ds"]
if not (match := re.search(r"FV:2,(\d+)", container_ids)):
if not container_ids or not (match := re.search(r"FV:2,(\d+)", container_ids)):
return
container_id = int(match.groups()[0])

View File

@ -8,5 +8,5 @@
"iot_class": "local_push",
"loggers": ["async_upnp_client"],
"quality_scale": "internal",
"requirements": ["async-upnp-client==0.44.0"]
"requirements": ["async-upnp-client==0.45.0"]
}

View File

@ -41,5 +41,5 @@
"iot_class": "local_push",
"loggers": ["switchbot"],
"quality_scale": "gold",
"requirements": ["PySwitchbot==0.68.1"]
"requirements": ["PySwitchbot==0.68.2"]
}

View File

@ -28,6 +28,7 @@ class SyncthruCoordinator(DataUpdateCoordinator[SyncThru]):
hass,
_LOGGER,
name=DOMAIN,
config_entry=entry,
update_interval=timedelta(seconds=30),
)
self.syncthru = SyncThru(

View File

@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/tesla_fleet",
"iot_class": "cloud_polling",
"loggers": ["tesla-fleet-api"],
"requirements": ["tesla-fleet-api==1.2.0"]
"requirements": ["tesla-fleet-api==1.2.2"]
}

View File

@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import TeslemetryConfigEntry
from .entity import TeslemetryVehiclePollingEntity
from .entity import TeslemetryVehicleStreamEntity
from .helpers import handle_command, handle_vehicle_command
from .models import TeslemetryVehicleData
@ -74,7 +74,7 @@ async def async_setup_entry(
)
class TeslemetryButtonEntity(TeslemetryVehiclePollingEntity, ButtonEntity):
class TeslemetryButtonEntity(TeslemetryVehicleStreamEntity, ButtonEntity):
"""Base class for Teslemetry buttons."""
api: Vehicle

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/teslemetry",
"iot_class": "cloud_polling",
"loggers": ["tesla-fleet-api"],
"requirements": ["tesla-fleet-api==1.2.0", "teslemetry-stream==0.7.9"]
"requirements": ["tesla-fleet-api==1.2.2", "teslemetry-stream==0.7.9"]
}

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/tessie",
"iot_class": "cloud_polling",
"loggers": ["tessie", "tesla-fleet-api"],
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==1.2.0"]
"requirements": ["tessie-api==0.1.1", "tesla-fleet-api==1.2.2"]
}

View File

@ -8,7 +8,7 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["async_upnp_client"],
"requirements": ["async-upnp-client==0.44.0", "getmac==0.9.5"],
"requirements": ["async-upnp-client==0.45.0", "getmac==0.9.5"],
"ssdp": [
{
"st": "urn:schemas-upnp-org:device:InternetGatewayDevice:1"

View File

@ -16,7 +16,7 @@
},
"iot_class": "local_push",
"loggers": ["async_upnp_client", "yeelight"],
"requirements": ["yeelight==0.7.16", "async-upnp-client==0.44.0"],
"requirements": ["yeelight==0.7.16", "async-upnp-client==0.45.0"],
"zeroconf": [
{
"type": "_miio._udp.local.",

View File

@ -25,7 +25,7 @@ if TYPE_CHECKING:
APPLICATION_NAME: Final = "HomeAssistant"
MAJOR_VERSION: Final = 2025
MINOR_VERSION: Final = 7
PATCH_VERSION: Final = "2"
PATCH_VERSION: Final = "3"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 2)

View File

@ -13,7 +13,7 @@ aiozoneinfo==0.2.3
annotatedyaml==0.4.5
astral==2.2
async-interrupt==1.2.2
async-upnp-client==0.44.0
async-upnp-client==0.45.0
atomicwrites-homeassistant==1.4.1
attrs==25.3.0
audioop-lts==0.2.1
@ -38,7 +38,7 @@ habluetooth==3.49.0
hass-nabucasa==0.106.0
hassil==2.2.3
home-assistant-bluetooth==1.13.1
home-assistant-frontend==20250702.2
home-assistant-frontend==20250702.3
home-assistant-intents==2025.6.23
httpx==0.28.1
ifaddr==0.2.0

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "homeassistant"
version = "2025.7.2"
version = "2025.7.3"
license = "Apache-2.0"
license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"]
description = "Open-source home automation platform running on Python 3."

18
requirements_all.txt generated
View File

@ -84,7 +84,7 @@ PyQRCode==1.2.1
PyRMVtransport==0.3.3
# homeassistant.components.switchbot
PySwitchbot==0.68.1
PySwitchbot==0.68.2
# homeassistant.components.switchmate
PySwitchmate==0.5.1
@ -185,7 +185,7 @@ aioairzone-cloud==0.6.12
aioairzone==1.0.0
# homeassistant.components.alexa_devices
aioamazondevices==3.2.10
aioamazondevices==3.5.0
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station
@ -527,7 +527,7 @@ asmog==0.0.6
# homeassistant.components.ssdp
# homeassistant.components.upnp
# homeassistant.components.yeelight
async-upnp-client==0.44.0
async-upnp-client==0.45.0
# homeassistant.components.arve
asyncarve==0.1.1
@ -1020,7 +1020,7 @@ georss-qld-bushfire-alert-client==0.8
getmac==0.9.5
# homeassistant.components.gios
gios==6.1.0
gios==6.1.2
# homeassistant.components.gitter
gitterpy==0.1.7
@ -1168,13 +1168,13 @@ hole==0.8.0
holidays==0.75
# homeassistant.components.frontend
home-assistant-frontend==20250702.2
home-assistant-frontend==20250702.3
# homeassistant.components.conversation
home-assistant-intents==2025.6.23
# homeassistant.components.homematicip_cloud
homematicip==2.0.6
homematicip==2.0.7
# homeassistant.components.horizon
horimote==0.4.1
@ -1962,7 +1962,7 @@ pyeiscp==0.0.7
pyemoncms==0.1.1
# homeassistant.components.enphase_envoy
pyenphase==2.2.1
pyenphase==2.2.2
# homeassistant.components.envisalink
pyenvisalink==4.7
@ -2348,7 +2348,7 @@ pysmappee==0.2.29
pysmarlaapi==0.9.0
# homeassistant.components.smartthings
pysmartthings==3.2.7
pysmartthings==3.2.8
# homeassistant.components.smarty
pysmarty2==0.10.2
@ -2904,7 +2904,7 @@ temperusb==1.6.1
# homeassistant.components.tesla_fleet
# homeassistant.components.teslemetry
# homeassistant.components.tessie
tesla-fleet-api==1.2.0
tesla-fleet-api==1.2.2
# homeassistant.components.powerwall
tesla-powerwall==0.5.2

View File

@ -81,7 +81,7 @@ PyQRCode==1.2.1
PyRMVtransport==0.3.3
# homeassistant.components.switchbot
PySwitchbot==0.68.1
PySwitchbot==0.68.2
# homeassistant.components.syncthru
PySyncThru==0.8.0
@ -173,7 +173,7 @@ aioairzone-cloud==0.6.12
aioairzone==1.0.0
# homeassistant.components.alexa_devices
aioamazondevices==3.2.10
aioamazondevices==3.5.0
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station
@ -491,7 +491,7 @@ arcam-fmj==1.8.1
# homeassistant.components.ssdp
# homeassistant.components.upnp
# homeassistant.components.yeelight
async-upnp-client==0.44.0
async-upnp-client==0.45.0
# homeassistant.components.arve
asyncarve==0.1.1
@ -890,7 +890,7 @@ georss-qld-bushfire-alert-client==0.8
getmac==0.9.5
# homeassistant.components.gios
gios==6.1.0
gios==6.1.2
# homeassistant.components.glances
glances-api==0.8.0
@ -1017,13 +1017,13 @@ hole==0.8.0
holidays==0.75
# homeassistant.components.frontend
home-assistant-frontend==20250702.2
home-assistant-frontend==20250702.3
# homeassistant.components.conversation
home-assistant-intents==2025.6.23
# homeassistant.components.homematicip_cloud
homematicip==2.0.6
homematicip==2.0.7
# homeassistant.components.remember_the_milk
httplib2==0.20.4
@ -1637,7 +1637,7 @@ pyeiscp==0.0.7
pyemoncms==0.1.1
# homeassistant.components.enphase_envoy
pyenphase==2.2.1
pyenphase==2.2.2
# homeassistant.components.everlights
pyeverlights==0.1.0
@ -1951,7 +1951,7 @@ pysmappee==0.2.29
pysmarlaapi==0.9.0
# homeassistant.components.smartthings
pysmartthings==3.2.7
pysmartthings==3.2.8
# homeassistant.components.smarty
pysmarty2==0.10.2
@ -2390,7 +2390,7 @@ temperusb==1.6.1
# homeassistant.components.tesla_fleet
# homeassistant.components.teslemetry
# homeassistant.components.tessie
tesla-fleet-api==1.2.0
tesla-fleet-api==1.2.2
# homeassistant.components.powerwall
tesla-powerwall==0.5.2

View File

@ -670,3 +670,32 @@ async def test_async_get_image(
HomeAssistantError, match="Stream source is not supported by go2rtc"
):
await async_get_image(hass, camera.entity_id)
@pytest.mark.usefixtures("init_integration")
async def test_generic_workaround(
hass: HomeAssistant,
init_test_integration: MockCamera,
rest_client: AsyncMock,
) -> None:
"""Test workaround for generic integration cameras."""
camera = init_test_integration
assert isinstance(camera._webrtc_provider, WebRTCProvider)
image_bytes = load_fixture_bytes("snapshot.jpg", DOMAIN)
rest_client.get_jpeg_snapshot.return_value = image_bytes
camera.set_stream_source("https://my_stream_url.m3u8")
with patch.object(camera.platform, "platform_name", "generic"):
image = await async_get_image(hass, camera.entity_id)
assert image.content == image_bytes
rest_client.streams.add.assert_called_once_with(
camera.entity_id,
[
"ffmpeg:https://my_stream_url.m3u8",
f"ffmpeg:{camera.entity_id}#audio=opus#query=log_level=debug",
f"ffmpeg:{camera.entity_id}#video=mjpeg",
],
)

View File

@ -195,9 +195,14 @@ async def test_hap_reconnected(
ha_state = hass.states.get(entity_id)
assert ha_state.state == STATE_UNAVAILABLE
mock_hap._accesspoint_connected = False
with patch(
"homeassistant.components.homematicip_cloud.hap.AsyncHome.websocket_is_connected",
return_value=True,
):
await async_manipulate_test_data(hass, mock_hap.home, "connected", True)
await mock_hap.ws_connected_handler()
await hass.async_block_till_done()
ha_state = hass.states.get(entity_id)
assert ha_state.state == STATE_ON

View File

@ -1,6 +1,6 @@
"""Test HomematicIP Cloud accesspoint."""
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from homematicip.auth import Auth
from homematicip.connection.connection_context import ConnectionContext
@ -242,7 +242,14 @@ async def test_get_state_after_disconnect(
hap = HomematicipHAP(hass, hmip_config_entry)
assert hap
with patch.object(hap, "get_state") as mock_get_state:
simple_mock_home = AsyncMock(spec=AsyncHome, autospec=True)
hap.home = simple_mock_home
hap.home.websocket_is_connected = Mock(side_effect=[False, True])
with (
patch("asyncio.sleep", new=AsyncMock()) as mock_sleep,
patch.object(hap, "get_state") as mock_get_state,
):
assert not hap._ws_connection_closed.is_set()
await hap.ws_connected_handler()
@ -250,9 +257,55 @@ async def test_get_state_after_disconnect(
await hap.ws_disconnected_handler()
assert hap._ws_connection_closed.is_set()
with patch(
"homeassistant.components.homematicip_cloud.hap.AsyncHome.websocket_is_connected",
return_value=True,
):
await hap.ws_connected_handler()
mock_get_state.assert_called_once()
assert not hap._ws_connection_closed.is_set()
hap.home.websocket_is_connected.assert_called()
mock_sleep.assert_awaited_with(2)
async def test_try_get_state_exponential_backoff() -> None:
"""Test _try_get_state waits for websocket connection."""
# Arrange: Create instance and mock home
hap = HomematicipHAP(MagicMock(), MagicMock())
hap.home = MagicMock()
hap.home.websocket_is_connected = Mock(return_value=True)
hap.get_state = AsyncMock(
side_effect=[HmipConnectionError, HmipConnectionError, True]
)
with patch("asyncio.sleep", new=AsyncMock()) as mock_sleep:
await hap._try_get_state()
assert mock_sleep.mock_calls[0].args[0] == 8
assert mock_sleep.mock_calls[1].args[0] == 16
assert hap.get_state.call_count == 3
async def test_try_get_state_handle_exception() -> None:
"""Test _try_get_state handles exceptions."""
# Arrange: Create instance and mock home
hap = HomematicipHAP(MagicMock(), MagicMock())
hap.home = MagicMock()
expected_exception = Exception("Connection error")
future = AsyncMock()
future.result = Mock(side_effect=expected_exception)
with patch("homeassistant.components.homematicip_cloud.hap._LOGGER") as mock_logger:
hap.get_state_finished(future)
mock_logger.error.assert_called_once_with(
"Error updating state after HMIP access point reconnect: %s", expected_exception
)
async def test_async_connect(
hass: HomeAssistant, hmip_config_entry: MockConfigEntry, simple_mock_home

View File

@ -924,6 +924,30 @@ async def test_invalid_unit_of_measurement(
"device_class": None,
"unit_of_measurement": None,
},
{
"name": "Test 4",
"state_topic": "test-topic",
"device_class": "ph",
"unit_of_measurement": "",
},
{
"name": "Test 5",
"state_topic": "test-topic",
"device_class": "ph",
"unit_of_measurement": " ",
},
{
"name": "Test 6",
"state_topic": "test-topic",
"device_class": None,
"unit_of_measurement": "",
},
{
"name": "Test 7",
"state_topic": "test-topic",
"device_class": None,
"unit_of_measurement": " ",
},
]
}
}
@ -936,10 +960,25 @@ async def test_valid_device_class_and_uom(
await mqtt_mock_entry()
state = hass.states.get("sensor.test_1")
assert state is not None
assert state.attributes["device_class"] == "temperature"
state = hass.states.get("sensor.test_2")
assert state is not None
assert "device_class" not in state.attributes
state = hass.states.get("sensor.test_3")
assert state is not None
assert "device_class" not in state.attributes
state = hass.states.get("sensor.test_4")
assert state is not None
assert state.attributes["device_class"] == "ph"
state = hass.states.get("sensor.test_5")
assert state is not None
assert state.attributes["device_class"] == "ph"
state = hass.states.get("sensor.test_6")
assert state is not None
assert "device_class" not in state.attributes
state = hass.states.get("sensor.test_7")
assert state is not None
assert "device_class" not in state.attributes

View File

@ -151,7 +151,7 @@
"c_pf": 0.72,
"c_voltage": 230.2,
"id": 0,
"n_current": null,
"n_current": 3.124,
"total_act_power": 2413.825,
"total_aprt_power": 2525.779,
"total_current": 11.116,

View File

@ -4303,6 +4303,62 @@
'state': '230.2',
})
# ---
# name: test_shelly_pro_3em[sensor.test_name_phase_n_current-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.test_name_phase_n_current',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.CURRENT: 'current'>,
'original_icon': None,
'original_name': 'Phase N current',
'platform': 'shelly',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '123456789ABC-em:0-n_current',
'unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
})
# ---
# name: test_shelly_pro_3em[sensor.test_name_phase_n_current-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'current',
'friendly_name': 'Test name Phase N current',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricCurrent.AMPERE: 'A'>,
}),
'context': <ANY>,
'entity_id': 'sensor.test_name_phase_n_current',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '3.124',
})
# ---
# name: test_shelly_pro_3em[sensor.test_name_rssi-entry]
EntityRegistryEntrySnapshot({
'aliases': set({