Merge branch 'whirlpool_bronze' of github.com:home-assistant/core into whirlpool_bronze

This commit is contained in:
abmantis 2025-04-16 13:06:40 +01:00
commit 1bb74d36e9
44 changed files with 183 additions and 115 deletions

View File

@ -1317,7 +1317,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@v5.4.0
uses: codecov/codecov-action@v5.4.2
with:
fail_ci_if_error: true
flags: full-suite
@ -1459,7 +1459,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@v5.4.0
uses: codecov/codecov-action@v5.4.2
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -31,7 +31,7 @@
"state_attributes": {
"preset_mode": {
"state": {
"auto": "[%key:component::climate::entity_component::_::state_attributes::fan_mode::state::auto%]"
"auto": "[%key:common::state::auto%]"
}
}
}

View File

@ -28,10 +28,10 @@
"name": "Thermostat",
"state": {
"off": "[%key:common::state::off%]",
"auto": "[%key:common::state::auto%]",
"heat": "Heat",
"cool": "Cool",
"heat_cool": "Heat/Cool",
"auto": "Auto",
"dry": "Dry",
"fan_only": "Fan only"
},
@ -50,7 +50,7 @@
"state": {
"off": "[%key:common::state::off%]",
"on": "[%key:common::state::on%]",
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"low": "[%key:common::state::low%]",
"medium": "[%key:common::state::medium%]",
"high": "[%key:common::state::high%]",
@ -69,13 +69,13 @@
"hvac_action": {
"name": "Current action",
"state": {
"off": "[%key:common::state::off%]",
"idle": "[%key:common::state::idle%]",
"cooling": "Cooling",
"defrosting": "Defrosting",
"drying": "Drying",
"fan": "Fan",
"heating": "Heating",
"idle": "[%key:common::state::idle%]",
"off": "[%key:common::state::off%]",
"preheating": "Preheating"
}
},
@ -258,7 +258,7 @@
"hvac_mode": {
"options": {
"off": "[%key:common::state::off%]",
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"cool": "Cool",
"dry": "Dry",
"fan_only": "Fan only",

View File

@ -75,4 +75,7 @@ class ComelitSwitchEntity(ComelitBridgeBaseEntity, SwitchEntity):
@property
def is_on(self) -> bool:
"""Return True if switch is on."""
return self.coordinator.data[OTHER][self._device.index].status == STATE_ON
return (
self.coordinator.data[self._device.type][self._device.index].status
== STATE_ON
)

View File

@ -8,7 +8,7 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["devolo_plc_api"],
"requirements": ["devolo-plc-api==1.4.1"],
"requirements": ["devolo-plc-api==1.5.1"],
"zeroconf": [
{
"type": "_dvl-deviceapi._tcp.local.",

View File

@ -77,12 +77,11 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
self.configuration_url = self.fritz.get_prefixed_host()
await self.async_config_entry_first_refresh()
self.cleanup_removed_devices(
list(self.data.devices) + list(self.data.templates)
)
self.cleanup_removed_devices(self.data)
def cleanup_removed_devices(self, available_ains: list[str]) -> None:
def cleanup_removed_devices(self, data: FritzboxCoordinatorData) -> None:
"""Cleanup entity and device registry from removed devices."""
available_ains = list(data.devices) + list(data.templates)
entity_reg = er.async_get(self.hass)
for entity in er.async_entries_for_config_entry(
entity_reg, self.config_entry.entry_id
@ -91,8 +90,13 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
LOGGER.debug("Removing obsolete entity entry %s", entity.entity_id)
entity_reg.async_remove(entity.entity_id)
available_main_ains = [
ain
for ain, dev in data.devices.items()
if dev.device_and_unit_id[1] is None
]
device_reg = dr.async_get(self.hass)
identifiers = {(DOMAIN, ain) for ain in available_ains}
identifiers = {(DOMAIN, ain) for ain in available_main_ains}
for device in dr.async_entries_for_config_entry(
device_reg, self.config_entry.entry_id
):
@ -165,12 +169,26 @@ class FritzboxDataUpdateCoordinator(DataUpdateCoordinator[FritzboxCoordinatorDat
"""Fetch all device data."""
new_data = await self.hass.async_add_executor_job(self._update_fritz_devices)
for device in new_data.devices.values():
# create device registry entry for new main devices
if (
device.ain not in self.data.devices
and device.device_and_unit_id[1] is None
):
dr.async_get(self.hass).async_get_or_create(
config_entry_id=self.config_entry.entry_id,
name=device.name,
identifiers={(DOMAIN, device.ain)},
manufacturer=device.manufacturer,
model=device.productname,
sw_version=device.fw_version,
configuration_url=self.configuration_url,
)
if (
self.data.devices.keys() - new_data.devices.keys()
or self.data.templates.keys() - new_data.templates.keys()
):
self.cleanup_removed_devices(
list(new_data.devices) + list(new_data.templates)
)
self.cleanup_removed_devices(new_data)
return new_data

View File

@ -58,11 +58,4 @@ class FritzBoxDeviceEntity(FritzBoxEntity):
@property
def device_info(self) -> DeviceInfo:
"""Return device specific attributes."""
return DeviceInfo(
name=self.data.name,
identifiers={(DOMAIN, self.ain)},
manufacturer=self.data.manufacturer,
model=self.data.productname,
sw_version=self.data.fw_version,
configuration_url=self.coordinator.configuration_url,
)
return DeviceInfo(identifiers={(DOMAIN, self.data.device_and_unit_id[0])})

View File

@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/holiday",
"iot_class": "local_polling",
"requirements": ["holidays==0.69", "babel==2.15.0"]
"requirements": ["holidays==0.70", "babel==2.15.0"]
}

View File

@ -37,6 +37,8 @@ class TimePattern:
if isinstance(value, str) and value.startswith("/"):
number = int(value[1:])
if number == 0:
raise vol.Invalid(f"must be a value between 1 and {self.maximum}")
else:
value = number = int(value)

View File

@ -2,7 +2,6 @@
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
from homeassistant.helpers.device_registry import DeviceInfo
@ -25,7 +24,6 @@ class HuaweiLteBaseEntity(Entity):
def __init__(self, router: Router) -> None:
"""Initialize."""
self.router = router
self._unsub_handlers: list[Callable] = []
@property
def _device_unique_id(self) -> str:
@ -48,7 +46,7 @@ class HuaweiLteBaseEntity(Entity):
async def async_added_to_hass(self) -> None:
"""Connect to update signals."""
self._unsub_handlers.append(
self.async_on_remove(
async_dispatcher_connect(self.hass, UPDATE_SIGNAL, self._async_maybe_update)
)
@ -57,12 +55,6 @@ class HuaweiLteBaseEntity(Entity):
if config_entry_unique_id == self.router.config_entry.unique_id:
self.async_schedule_update_ha_state(True)
async def async_will_remove_from_hass(self) -> None:
"""Invoke unsubscription handlers."""
for unsub in self._unsub_handlers:
unsub()
self._unsub_handlers.clear()
class HuaweiLteBaseEntityWithDevice(HuaweiLteBaseEntity):
"""Base entity with device info."""

View File

@ -315,7 +315,7 @@
"preset_mode": {
"name": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::name%]",
"state": {
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"building_protection": "Building protection",
"comfort": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::comfort%]",
"economy": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::eco%]",

View File

@ -123,7 +123,7 @@
"mid": "[%key:common::state::medium%]",
"high": "[%key:common::state::high%]",
"power": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::high%]",
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]"
"auto": "[%key:common::state::auto%]"
}
},
"preset_mode": {
@ -343,7 +343,7 @@
"growth_mode": {
"name": "Mode",
"state": {
"standard": "Auto",
"standard": "[%key:common::state::auto%]",
"ext_leaf": "Vegetables",
"ext_herb": "Herbs",
"ext_flower": "Flowers",
@ -353,7 +353,7 @@
"growth_mode_for_location": {
"name": "{location} mode",
"state": {
"standard": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"standard": "[%key:common::state::auto%]",
"ext_leaf": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::ext_leaf%]",
"ext_herb": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::ext_herb%]",
"ext_flower": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::ext_flower%]",
@ -581,7 +581,7 @@
"name": "[%key:component::lg_thinq::entity::binary_sensor::one_touch_filter::name%]",
"state": {
"off": "[%key:common::state::off%]",
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"auto": "[%key:common::state::auto%]",
"power": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::high%]",
"replace": "Replace filter",
"smart_power": "Smart safe storage",
@ -599,7 +599,7 @@
"name": "Operating mode",
"state": {
"air_clean": "Purify",
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"auto": "[%key:common::state::auto%]",
"clothes_dry": "Laundry",
"edge": "Edge cleaning",
"heat_pump": "Heat pump",
@ -649,7 +649,7 @@
"current_dish_washing_course": {
"name": "Current cycle",
"state": {
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"auto": "[%key:common::state::auto%]",
"heavy": "Intensive",
"delicate": "Delicate",
"turbo": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]",
@ -881,7 +881,7 @@
"high": "[%key:common::state::high%]",
"power": "Turbo",
"turbo": "[%key:component::lg_thinq::entity::select::wind_strength::state::power%]",
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"auto": "[%key:common::state::auto%]",
"wind_1": "Step 1",
"wind_2": "Step 2",
"wind_3": "Step 3",
@ -905,7 +905,7 @@
"name": "Operating mode",
"state": {
"air_clean": "Purifying",
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"auto": "[%key:common::state::auto%]",
"baby_care": "[%key:component::lg_thinq::entity::sensor::personalization_mode::state::baby%]",
"circulator": "Booster",
"clean": "Single",
@ -1016,7 +1016,7 @@
"name": "[%key:component::lg_thinq::entity::binary_sensor::one_touch_filter::name%]",
"state": {
"off": "[%key:common::state::off%]",
"auto": "[%key:component::lg_thinq::entity::sensor::growth_mode::state::standard%]",
"auto": "[%key:common::state::auto%]",
"power": "[%key:component::lg_thinq::entity::sensor::current_job_mode::state::high%]",
"replace": "[%key:component::lg_thinq::entity::sensor::fresh_air_filter::state::replace%]",
"smart_power": "[%key:component::lg_thinq::entity::sensor::fresh_air_filter::state::smart_power%]",

View File

@ -7,6 +7,6 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["linkplay"],
"requirements": ["python-linkplay==0.2.2"],
"requirements": ["python-linkplay==0.2.3"],
"zeroconf": ["_linkplay._tcp.local."]
}

View File

@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/lutron",
"iot_class": "local_polling",
"loggers": ["pylutron"],
"requirements": ["pylutron==0.2.16"],
"requirements": ["pylutron==0.2.18"],
"single_config_entry": true
}

View File

@ -147,7 +147,7 @@
"low": "[%key:common::state::low%]",
"medium": "[%key:common::state::medium%]",
"high": "[%key:common::state::high%]",
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"natural_wind": "Natural wind",
"sleep_wind": "Sleep wind"
}

View File

@ -55,12 +55,12 @@
"heater_mode": {
"name": "Heater mode",
"state": {
"auto": "Auto",
"off": "[%key:common::state::off%]",
"auto": "[%key:common::state::auto%]",
"manual": "[%key:common::state::manual%]",
"extraenergy": "Extra energy",
"ffr": "Fast frequency reserve",
"legionella": "Legionella",
"manual": "Manual",
"off": "[%key:common::state::off%]",
"powersave": "Power save",
"voltage": "Voltage"
}

View File

@ -23,7 +23,7 @@
},
"data_description": {
"password": "The Smile ID printed on the label on the back of your Adam, Smile-T, or P1.",
"host": "The hostname or IP-address of your Smile. You can find it in your router or the Plugwise App.",
"host": "The hostname or IP-address of your Smile. You can find it in your router or the Plugwise app.",
"port": "By default your Smile uses port 80, normally you should not have to change this.",
"username": "Default is `smile`, or `stretch` for the legacy Stretch."
}
@ -113,7 +113,7 @@
"name": "DHW mode",
"state": {
"off": "[%key:common::state::off%]",
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"boost": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::boost%]",
"comfort": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::comfort%]"
}

View File

@ -170,12 +170,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
exclude_event_types=exclude_event_types,
)
get_instance.cache_clear()
entity_registry.async_setup(hass)
instance.async_initialize()
instance.async_register()
instance.start()
async_register_services(hass, instance)
websocket_api.async_setup(hass)
entity_registry.async_setup(hass)
await _async_setup_integration_platform(hass, instance)

View File

@ -4,8 +4,9 @@ import logging
from typing import TYPE_CHECKING
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.start import async_at_start
from homeassistant.helpers.event import async_has_entity_registry_updated_listeners
from .core import Recorder
from .util import filter_unique_constraint_integrity_error, get_instance, session_scope
@ -40,16 +41,17 @@ def async_setup(hass: HomeAssistant) -> None:
"""Handle entity_id changed filter."""
return event_data["action"] == "update" and "old_entity_id" in event_data
@callback
def _setup_entity_registry_event_handler(hass: HomeAssistant) -> None:
"""Subscribe to event registry events."""
hass.bus.async_listen(
er.EVENT_ENTITY_REGISTRY_UPDATED,
_async_entity_id_changed,
event_filter=entity_registry_changed_filter,
if async_has_entity_registry_updated_listeners(hass):
raise HomeAssistantError(
"The recorder entity registry listener must be installed"
" before async_track_entity_registry_updated_event is called"
)
async_at_start(hass, _setup_entity_registry_event_handler)
hass.bus.async_listen(
er.EVENT_ENTITY_REGISTRY_UPDATED,
_async_entity_id_changed,
event_filter=entity_registry_changed_filter,
)
def update_states_metadata(

View File

@ -380,6 +380,9 @@
},
"scene_mode": {
"default": "mdi:view-list"
},
"packing_time": {
"default": "mdi:record-rec"
}
},
"sensor": {

View File

@ -19,5 +19,5 @@
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"quality_scale": "platinum",
"requirements": ["reolink-aio==0.13.1"]
"requirements": ["reolink-aio==0.13.2"]
}

View File

@ -263,6 +263,17 @@ HOST_SELECT_ENTITIES = (
value=lambda api: api.baichuan.active_scene,
method=lambda api, name: api.baichuan.set_scene(scene_name=name),
),
ReolinkHostSelectEntityDescription(
key="packing_time",
cmd_key="GetRec",
translation_key="packing_time",
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
get_options=lambda api: api.recording_packing_time_list,
supported=lambda api: api.supported(None, "pak_time"),
value=lambda api: api.recording_packing_time,
method=lambda api, value: api.set_recording_packing_time(value),
),
)
CHIME_SELECT_ENTITIES = (

View File

@ -652,7 +652,7 @@
"name": "Floodlight mode",
"state": {
"off": "[%key:common::state::off%]",
"auto": "[%key:component::reolink::entity::select::day_night_mode::state::auto%]",
"auto": "[%key:common::state::auto%]",
"onatnight": "On at night",
"schedule": "Schedule",
"adaptive": "Adaptive",
@ -662,7 +662,7 @@
"day_night_mode": {
"name": "Day night mode",
"state": {
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"color": "Color",
"blackwhite": "Black & white"
}
@ -691,7 +691,7 @@
"name": "Doorbell LED",
"state": {
"stayoff": "Stay off",
"auto": "[%key:component::reolink::entity::select::day_night_mode::state::auto%]",
"auto": "[%key:common::state::auto%]",
"alwaysonatnight": "Auto & always on at night",
"always": "Always on",
"alwayson": "Always on"
@ -702,7 +702,7 @@
"state": {
"off": "[%key:common::state::off%]",
"on": "[%key:common::state::on%]",
"auto": "[%key:component::reolink::entity::select::day_night_mode::state::auto%]"
"auto": "[%key:common::state::auto%]"
}
},
"binning_mode": {
@ -710,7 +710,7 @@
"state": {
"off": "[%key:common::state::off%]",
"on": "[%key:common::state::on%]",
"auto": "[%key:component::reolink::entity::select::day_night_mode::state::auto%]"
"auto": "[%key:common::state::auto%]"
}
},
"hub_alarm_ringtone": {
@ -845,6 +845,9 @@
"home": "[%key:common::state::home%]",
"away": "[%key:common::state::not_home%]"
}
},
"packing_time": {
"name": "Recording packing time"
}
},
"sensor": {

View File

@ -426,11 +426,11 @@
"state_attributes": {
"fan_speed": {
"state": {
"auto": "Auto",
"off": "[%key:common::state::off%]",
"auto": "[%key:common::state::auto%]",
"balanced": "Balanced",
"custom": "[%key:component::roborock::entity::select::mop_mode::state::custom%]",
"gentle": "Gentle",
"off": "[%key:common::state::off%]",
"max": "[%key:component::roborock::entity::select::mop_intensity::state::max%]",
"max_plus": "Max plus",
"medium": "[%key:common::state::medium%]",

View File

@ -53,7 +53,7 @@
"state_attributes": {
"preset_mode": {
"state": {
"auto": "Auto"
"auto": "[%key:common::state::auto%]"
}
}
}
@ -139,7 +139,7 @@
"description": "Adds a meter reading to Tado Energy IQ.",
"fields": {
"config_entry": {
"name": "Config Entry",
"name": "Config entry",
"description": "Config entry to add meter reading to."
},
"reading": {

View File

@ -163,7 +163,7 @@ class UkTransportLiveBusTimeSensor(UkTransportSensor):
self._destination_re = re.compile(f"{bus_direction}", re.IGNORECASE)
sensor_name = f"Next bus to {bus_direction}"
stop_url = f"bus/stop/{stop_atcocode}/live.json"
stop_url = f"bus/stop/{stop_atcocode}.json"
UkTransportSensor.__init__(self, sensor_name, api_app_id, api_app_key, stop_url)
self.update = Throttle(interval)(self._update)
@ -226,7 +226,7 @@ class UkTransportLiveTrainTimeSensor(UkTransportSensor):
self._next_trains = []
sensor_name = f"Next train to {calling_at}"
query_url = f"train/station/{station_code}/live.json"
query_url = f"train/station/{station_code}.json"
UkTransportSensor.__init__(
self, sensor_name, api_app_id, api_app_key, query_url

View File

@ -43,4 +43,4 @@ class UptimeRobotBinarySensor(UptimeRobotEntity, BinarySensorEntity):
@property
def is_on(self) -> bool:
"""Return True if the entity is on."""
return self.monitor_available
return bool(self.monitor.status == 2)

View File

@ -59,8 +59,3 @@ class UptimeRobotEntity(CoordinatorEntity[UptimeRobotDataUpdateCoordinator]):
),
self._monitor,
)
@property
def monitor_available(self) -> bool:
"""Returtn if the monitor is available."""
return bool(self.monitor.status == 2)

View File

@ -86,7 +86,7 @@
"state_attributes": {
"preset_mode": {
"state": {
"auto": "Auto",
"auto": "[%key:common::state::auto%]",
"sleep": "Sleep",
"advanced_sleep": "Advanced sleep",
"pet": "Pet",

View File

@ -7,5 +7,5 @@
"iot_class": "local_polling",
"loggers": ["holidays"],
"quality_scale": "internal",
"requirements": ["holidays==0.69"]
"requirements": ["holidays==0.70"]
}

View File

@ -8,5 +8,5 @@
"iot_class": "local_push",
"loggers": ["zeroconf"],
"quality_scale": "internal",
"requirements": ["zeroconf==0.146.0"]
"requirements": ["zeroconf==0.146.5"]
}

View File

@ -75,7 +75,7 @@ voluptuous-serialize==2.6.0
voluptuous==0.15.2
webrtc-models==0.3.0
yarl==1.19.0
zeroconf==0.146.0
zeroconf==0.146.5
# Constrain pycryptodome to avoid vulnerability
# see https://github.com/home-assistant/core/pull/16238

View File

@ -123,7 +123,7 @@ dependencies = [
"voluptuous-openapi==0.0.6",
"yarl==1.19.0",
"webrtc-models==0.3.0",
"zeroconf==0.146.0",
"zeroconf==0.146.5",
]
[project.urls]

2
requirements.txt generated
View File

@ -60,4 +60,4 @@ voluptuous-serialize==2.6.0
voluptuous-openapi==0.0.6
yarl==1.19.0
webrtc-models==0.3.0
zeroconf==0.146.0
zeroconf==0.146.5

12
requirements_all.txt generated
View File

@ -781,7 +781,7 @@ devialet==1.5.7
devolo-home-control-api==0.18.3
# homeassistant.components.devolo_home_network
devolo-plc-api==1.4.1
devolo-plc-api==1.5.1
# homeassistant.components.chacon_dio
dio-chacon-wifi-api==1.2.1
@ -1154,7 +1154,7 @@ hole==0.8.0
# homeassistant.components.holiday
# homeassistant.components.workday
holidays==0.69
holidays==0.70
# homeassistant.components.frontend
home-assistant-frontend==20250411.0
@ -2113,7 +2113,7 @@ pylitterbot==2024.0.0
pylutron-caseta==0.24.0
# homeassistant.components.lutron
pylutron==0.2.16
pylutron==0.2.18
# homeassistant.components.mailgun
pymailgunner==1.4
@ -2436,7 +2436,7 @@ python-juicenet==1.1.0
python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay
python-linkplay==0.2.2
python-linkplay==0.2.3
# homeassistant.components.lirc
# python-lirc==1.2.3
@ -2633,7 +2633,7 @@ renault-api==0.2.9
renson-endura-delta==1.7.2
# homeassistant.components.reolink
reolink-aio==0.13.1
reolink-aio==0.13.2
# homeassistant.components.idteck_prox
rfk101py==0.0.1
@ -3152,7 +3152,7 @@ zabbix-utils==2.0.2
zamg==0.3.6
# homeassistant.components.zeroconf
zeroconf==0.146.0
zeroconf==0.146.5
# homeassistant.components.zeversolar
zeversolar==0.3.2

View File

@ -672,7 +672,7 @@ devialet==1.5.7
devolo-home-control-api==0.18.3
# homeassistant.components.devolo_home_network
devolo-plc-api==1.4.1
devolo-plc-api==1.5.1
# homeassistant.components.chacon_dio
dio-chacon-wifi-api==1.2.1
@ -984,7 +984,7 @@ hole==0.8.0
# homeassistant.components.holiday
# homeassistant.components.workday
holidays==0.69
holidays==0.70
# homeassistant.components.frontend
home-assistant-frontend==20250411.0
@ -1728,7 +1728,7 @@ pylitterbot==2024.0.0
pylutron-caseta==0.24.0
# homeassistant.components.lutron
pylutron==0.2.16
pylutron==0.2.18
# homeassistant.components.mailgun
pymailgunner==1.4
@ -1976,7 +1976,7 @@ python-juicenet==1.1.0
python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay
python-linkplay==0.2.2
python-linkplay==0.2.3
# homeassistant.components.matter
python-matter-server==7.0.0
@ -2137,7 +2137,7 @@ renault-api==0.2.9
renson-endura-delta==1.7.2
# homeassistant.components.reolink
reolink-aio==0.13.1
reolink-aio==0.13.2
# homeassistant.components.rflink
rflink==0.0.66
@ -2548,7 +2548,7 @@ yt-dlp[default]==2025.03.26
zamg==0.3.6
# homeassistant.components.zeroconf
zeroconf==0.146.0
zeroconf==0.146.5
# homeassistant.components.zeversolar
zeversolar==0.3.2

View File

@ -60,6 +60,7 @@ class FritzEntityBaseMock(Mock):
"""base mock of a AVM Fritz!Box binary sensor device."""
ain = CONF_FAKE_AIN
device_and_unit_id = (CONF_FAKE_AIN, None)
manufacturer = CONF_FAKE_MANUFACTURER
name = CONF_FAKE_NAME
productname = CONF_FAKE_PRODUCTNAME

View File

@ -110,6 +110,7 @@ async def test_discover_new_device(hass: HomeAssistant, fritz: Mock) -> None:
new_device = FritzDeviceBinarySensorMock()
new_device.ain = "7890 1234"
new_device.device_and_unit_id = ("7890 1234", None)
new_device.name = "new_device"
set_devices(fritz, devices=[device, new_device])

View File

@ -85,8 +85,16 @@ async def test_coordinator_automatic_registry_cleanup(
) -> None:
"""Test automatic registry cleanup."""
fritz().get_devices.return_value = [
FritzDeviceSwitchMock(ain="fake ain switch", name="fake_switch"),
FritzDeviceCoverMock(ain="fake ain cover", name="fake_cover"),
FritzDeviceSwitchMock(
ain="fake ain switch",
device_and_unit_id=("fake ain switch", None),
name="fake_switch",
),
FritzDeviceCoverMock(
ain="fake ain cover",
device_and_unit_id=("fake ain cover", None),
name="fake_cover",
),
]
entry = MockConfigEntry(
domain=FB_DOMAIN,
@ -101,7 +109,11 @@ async def test_coordinator_automatic_registry_cleanup(
assert len(dr.async_entries_for_config_entry(device_registry, entry.entry_id)) == 2
fritz().get_devices.return_value = [
FritzDeviceSwitchMock(ain="fake ain switch", name="fake_switch")
FritzDeviceSwitchMock(
ain="fake ain switch",
device_and_unit_id=("fake ain switch", None),
name="fake_switch",
)
]
async_fire_time_changed(hass, utcnow() + timedelta(seconds=35))

View File

@ -108,6 +108,7 @@ async def test_discover_new_device(hass: HomeAssistant, fritz: Mock) -> None:
new_device = FritzDeviceSensorMock()
new_device.ain = "7890 1234"
new_device.device_and_unit_id = ("7890 1234", None)
new_device.name = "new_device"
set_devices(fritz, devices=[device, new_device])

View File

@ -365,6 +365,7 @@ async def test_invalid_schemas() -> None:
{"platform": "time_pattern", "minutes": "/"},
{"platform": "time_pattern", "minutes": "*/5"},
{"platform": "time_pattern", "minutes": "/90"},
{"platform": "time_pattern", "hours": "/0", "minutes": 10},
{"platform": "time_pattern", "hours": 12, "minutes": 0, "seconds": 100},
)

View File

@ -129,10 +129,6 @@ async def test_hap_reset_unloads_entry_if_setup(
assert hass.data[HMIPC_DOMAIN] == {}
@patch(
"homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async",
return_value=ConnectionContext(),
)
async def test_hap_create(
hass: HomeAssistant, hmip_config_entry: MockConfigEntry, simple_mock_home
) -> None:
@ -140,15 +136,17 @@ async def test_hap_create(
hass.config.components.add(HMIPC_DOMAIN)
hap = HomematicipHAP(hass, hmip_config_entry)
assert hap
with patch.object(hap, "async_connect"):
with (
patch(
"homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async",
return_value=ConnectionContext(),
),
patch.object(hap, "async_connect"),
):
async with hmip_config_entry.setup_lock:
assert await hap.async_setup()
@patch(
"homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async",
return_value=ConnectionContext(),
)
async def test_hap_create_exception(
hass: HomeAssistant, hmip_config_entry: MockConfigEntry, mock_connection_init
) -> None:
@ -158,13 +156,23 @@ async def test_hap_create_exception(
hap = HomematicipHAP(hass, hmip_config_entry)
assert hap
with patch(
"homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state_async",
side_effect=Exception,
with (
patch(
"homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async",
return_value=ConnectionContext(),
),
patch(
"homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state_async",
side_effect=Exception,
),
):
assert not await hap.async_setup()
with (
patch(
"homeassistant.components.homematicip_cloud.hap.ConnectionContextBuilder.build_context_async",
return_value=ConnectionContext(),
),
patch(
"homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state_async",
side_effect=HmipConnectionError,

View File

@ -77,6 +77,7 @@ from homeassistant.helpers import (
issue_registry as ir,
recorder as recorder_helper,
)
from homeassistant.helpers.event import async_track_entity_registry_updated_event
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
@ -2798,3 +2799,22 @@ async def test_empty_entity_id(
hass.bus.async_fire("hello", {"entity_id": ""})
await async_wait_recording_done(hass)
assert "Invalid entity ID" not in caplog.text
async def test_setting_up_recorder_fails_entity_registry_listener(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test recorder setup fails if an entity registry listener is in place."""
async_track_entity_registry_updated_event(hass, "test.test", lambda x: x)
recorder_helper.async_initialize_recorder(hass)
with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True):
assert not await async_setup_component(
hass,
recorder.DOMAIN,
{recorder.DOMAIN: {recorder.CONF_DB_URL: "sqlite://"}},
)
await hass.async_block_till_done()
assert (
"The recorder entity registry listener must be installed before "
"async_track_entity_registry_updated_event is called" in caplog.text
)

View File

@ -138,6 +138,8 @@ def reolink_connect_class() -> Generator[MagicMock]:
host_mock.daynight_state.return_value = "Black&White"
host_mock.hub_alarm_tone_id.return_value = 1
host_mock.hub_visitor_tone_id.return_value = 1
host_mock.recording_packing_time_list = ["30 Minutes", "60 Minutes"]
host_mock.recording_packing_time = "60 Minutes"
# Baichuan
host_mock.baichuan = create_autospec(Baichuan)