mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Merge branch 'whirlpool_bronze' of github.com:home-assistant/core into whirlpool_bronze
This commit is contained in:
commit
1bb74d36e9
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -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 }}
|
||||
|
@ -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%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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.",
|
||||
|
@ -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
|
||||
|
@ -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])})
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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."""
|
||||
|
@ -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%]",
|
||||
|
@ -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%]",
|
||||
|
@ -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."]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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%]"
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(
|
||||
|
@ -380,6 +380,9 @@
|
||||
},
|
||||
"scene_mode": {
|
||||
"default": "mdi:view-list"
|
||||
},
|
||||
"packing_time": {
|
||||
"default": "mdi:record-rec"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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 = (
|
||||
|
@ -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": {
|
||||
|
@ -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%]",
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -86,7 +86,7 @@
|
||||
"state_attributes": {
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"auto": "Auto",
|
||||
"auto": "[%key:common::state::auto%]",
|
||||
"sleep": "Sleep",
|
||||
"advanced_sleep": "Advanced sleep",
|
||||
"pet": "Pet",
|
||||
|
@ -7,5 +7,5 @@
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["holidays"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["holidays==0.69"]
|
||||
"requirements": ["holidays==0.70"]
|
||||
}
|
||||
|
@ -8,5 +8,5 @@
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["zeroconf"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["zeroconf==0.146.0"]
|
||||
"requirements": ["zeroconf==0.146.5"]
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
2
requirements.txt
generated
@ -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
12
requirements_all.txt
generated
@ -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
|
||||
|
12
requirements_test_all.txt
generated
12
requirements_test_all.txt
generated
@ -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
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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])
|
||||
|
||||
|
@ -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},
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user