mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
2025.7.3 (#149024)
This commit is contained in:
commit
456f992b7e
@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioamazondevices"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["aioamazondevices==3.2.10"]
|
||||
"requirements": ["aioamazondevices==3.5.0"]
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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."
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["dacite", "gios"],
|
||||
"requirements": ["gios==6.1.0"]
|
||||
"requirements": ["gios==6.1.2"]
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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(
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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": [
|
||||
{
|
||||
|
@ -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(
|
||||
|
@ -30,5 +30,5 @@
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["pysmartthings"],
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pysmartthings==3.2.7"]
|
||||
"requirements": ["pysmartthings==3.2.8"]
|
||||
}
|
||||
|
@ -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])
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -41,5 +41,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["switchbot"],
|
||||
"quality_scale": "gold",
|
||||
"requirements": ["PySwitchbot==0.68.1"]
|
||||
"requirements": ["PySwitchbot==0.68.2"]
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ class SyncthruCoordinator(DataUpdateCoordinator[SyncThru]):
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
config_entry=entry,
|
||||
update_interval=timedelta(seconds=30),
|
||||
)
|
||||
self.syncthru = SyncThru(
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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.",
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
18
requirements_all.txt
generated
@ -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
|
||||
|
18
requirements_test_all.txt
generated
18
requirements_test_all.txt
generated
@ -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
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -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
|
||||
await async_manipulate_test_data(hass, mock_hap.home, "connected", True)
|
||||
await hass.async_block_till_done()
|
||||
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
|
||||
|
||||
|
@ -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,8 +257,54 @@ async def test_get_state_after_disconnect(
|
||||
|
||||
await hap.ws_disconnected_handler()
|
||||
assert hap._ws_connection_closed.is_set()
|
||||
await hap.ws_connected_handler()
|
||||
mock_get_state.assert_called_once()
|
||||
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(
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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({
|
||||
|
Loading…
x
Reference in New Issue
Block a user