Compare commits

..

73 Commits

Author SHA1 Message Date
Franck Nijhof c576d5267a Bump steamloop to 1.2.1 (#173809) 2026-06-14 19:45:34 -05:00
Franck Nijhof 1241a11c9d Bump bleak-esphome to 3.9.4 (#173825) 2026-06-14 17:56:47 -05:00
Franck Nijhof 8f7447de58 Bump aiocomelit to 2.0.5 (#173800) 2026-06-15 00:00:16 +02:00
Franck Nijhof 1e54dba835 Bump pyiskra to 0.1.29 (#173820) 2026-06-15 00:00:07 +02:00
Manu 20583d6d1b Bump pyrate-limiter to 4.4.0 (#173819) 2026-06-14 23:59:57 +02:00
Franck Nijhof b94370ee51 Bump devolo-home-control-api to 0.19.1 (#173806) 2026-06-14 23:59:51 +02:00
Franck Nijhof d068a2aa11 Bump blinkpy to 0.25.6 (#173811) 2026-06-14 23:59:30 +02:00
Franck Nijhof 5d53a1f204 Bump anthemav to 1.4.2 (#173812) 2026-06-14 23:59:08 +02:00
Franck Nijhof 2908f37130 Bump pyimouapi to 1.2.8 (#173813) 2026-06-14 23:58:45 +02:00
Franck Nijhof a88a795ad3 Bump bluecurrent-api to 1.3.3 (#173815) 2026-06-14 23:58:24 +02:00
Franck Nijhof 73a36a2c47 Bump denonavr to 1.3.3 (#173814) 2026-06-14 23:58:05 +02:00
Franck Nijhof 054494181e Bump geniushub-client to 0.7.4 (#173818) 2026-06-14 23:57:38 +02:00
Franck Nijhof e4e8f901ab Bump yalexs-ble to 3.3.1 (#173792) 2026-06-14 14:58:12 -05:00
Franck Nijhof b7a29bfa2f Bump pyrainbird to 6.3.1 (#173786) 2026-06-14 21:23:59 +02:00
Franck Nijhof 26b0079945 Bump pyenphase to 2.4.9 (#173785) 2026-06-14 14:13:54 -05:00
Raphael Hehl 7454f40dd8 Bump uiprotect to 13.1.2 (#173728) 2026-06-14 19:12:07 +02:00
Franck Nijhof 26b7d1e32c Slugify OwnTracks beacon name in entity ID (#173629) 2026-06-14 19:12:05 +02:00
Franck Nijhof f7342ea9b0 Add missing translation_domain to nasweb exception raises (#173732) 2026-06-14 19:11:42 +02:00
Franck Nijhof 825d99ddaf Bump uv to 0.11.21 (#173768) 2026-06-14 19:09:43 +02:00
Michael 401fae6bdd Bump py-synologydsm-api to 2.10.0 (#173774) 2026-06-14 19:08:49 +02:00
Franck Nijhof 5433beeec1 Skip Miele fan set_percentage when already at the target step (#173725) 2026-06-14 19:06:54 +02:00
G Johansson af60e248d3 Remove listener from holiday calendar when entity is disabled (#173759) 2026-06-14 19:05:33 +02:00
Franck Nijhof 8c452c280f Add missing flow form field translation in hue (#173747) 2026-06-14 19:02:44 +02:00
Franck Nijhof 3aec970321 Add missing flow form field translations in islamic_prayer_times (#173749) 2026-06-14 19:02:03 +02:00
Franck Nijhof 687c91d5f4 Add missing flow form field translation in lacrosse_view (#173750) 2026-06-14 19:01:36 +02:00
Franck Nijhof 377fdceb6c Add missing flow form field translation in melnor (#173752) 2026-06-14 19:00:56 +02:00
Franck Nijhof 11a4533ccc Fix flow form field translation key in meteoclimatic (#173754) 2026-06-14 19:00:12 +02:00
Franck Nijhof 52b2738b2a Add missing flow form field translation in motionblinds_ble (#173758) 2026-06-14 18:59:23 +02:00
Franck Nijhof 3fda722dbb Add missing flow form field translation in blink (#173756) 2026-06-14 18:58:52 +02:00
Franck Nijhof e01215da0e Fix exception translation placeholder mismatch in Swiss Public Transport (#173735) 2026-06-14 18:58:05 +02:00
starkillerOG 6c116cf3e4 Bump reolink_aio to 0.21.1 (#173772) 2026-06-14 18:56:55 +02:00
Franck Nijhof 8017e802dd Bump aiopulse to 0.4.7 (#173763) 2026-06-14 18:56:12 +02:00
Franck Nijhof 501d956b1b Bump pylint to 4.0.6 (#173769) 2026-06-14 18:52:24 +02:00
Franck Nijhof 8aca342a78 Bump snapcast to 2.3.8 (#173765) 2026-06-14 18:51:48 +02:00
Franck Nijhof bd68e9fbe3 Bump syrupy to 5.3.2 (#173767) 2026-06-14 18:51:12 +02:00
Franck Nijhof b75c839868 Bump python-linkplay to 0.2.14 (#173770) 2026-06-14 18:49:59 +02:00
Franck Nijhof 742bfb00ff Bump anova-wifi to 0.17.1 (#173764) 2026-06-14 18:49:24 +02:00
Franck Nijhof 987c19d991 Replace duplicate SERVICE_RELOAD constant with homeassistant.const import in conversation (#173741) 2026-06-14 18:48:55 +02:00
Franck Nijhof b4319c4d0c Bump aioacaia to 0.1.18 (#173762) 2026-06-14 18:35:35 +02:00
Franck Nijhof 0fdb3ebed7 Bump aioamazondevices to 14.0.4 (#173761) 2026-06-14 18:16:06 +02:00
G Johansson efa3334616 Bump lxml to 6.1.1 (#173748) 2026-06-14 18:00:43 +02:00
Franck Nijhof 9ec0f2fe4f Add missing flow form field translation in gogogate2 (#173753) 2026-06-14 17:30:29 +02:00
Franck Nijhof 9bc5e2b06b Fix flow form field translation key in lookin (#173751) 2026-06-14 17:30:21 +02:00
Franck Nijhof 46a38cc481 Add missing flow form field translation in flux_led (#173746) 2026-06-14 17:30:13 +02:00
Franck Nijhof a63f2f1d20 Replace duplicate constants with homeassistant.const imports in Teslemetry (#173744) 2026-06-14 17:26:39 +02:00
Franck Nijhof 744bb6a068 Add missing flow form field translation in tuya (#173745) 2026-06-14 17:26:16 +02:00
Franck Nijhof d449e3e97b Replace duplicate constants with homeassistant.const imports in select (#173743) 2026-06-14 17:25:53 +02:00
Franck Nijhof 0df379704f Replace duplicate CONF_OPTIONS constant with homeassistant.const import in input_select (#173742) 2026-06-14 17:25:24 +02:00
Franck Nijhof 4ab7ce04a8 Add missing exception translation key in Israel Rail (#173738) 2026-06-14 17:24:54 +02:00
Franck Nijhof 210b08b637 Fix exception translation placeholder mismatch in Homevolt (#173737) 2026-06-14 17:24:31 +02:00
Franck Nijhof f0b448dc6e Fix exception translation placeholder mismatch in Snoo (#173736) 2026-06-14 17:24:04 +02:00
Franck Nijhof b5a314bf60 Add missing exception translation keys in VeSync (#173739) 2026-06-14 17:23:38 +02:00
Franck Nijhof 741c342749 Replace duplicate CONF_EVENT constant with homeassistant.const import in calendar (#173740) 2026-06-14 17:23:11 +02:00
Franck Nijhof f4d4df9c35 Fix options flow form field translation key in plaato (#173755) 2026-06-14 17:22:07 +02:00
Franck Nijhof bcbdf7b2bb Add missing flow form field translation in snooz (#173760) 2026-06-14 17:21:15 +02:00
iluebbe b3309ef169 Add Powerline hint to username field description (#167473)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-14 16:57:21 +02:00
epenet caaf5f9715 Adjust pylint checker to prevent invalid use of Platform enum (#173374)
Co-authored-by: Markus Tuominen <3738613+Markus98@users.noreply.github.com>
2026-06-14 16:17:00 +02:00
BrettLynch123 7ce7de3650 Fix tessie setup_error on transient aiohttp.ClientError during startup (#173659) 2026-06-14 15:49:22 +02:00
fdebrus 2c14c6be75 Optimistic UI updates for Vistapool write entities (#173373)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-14 15:41:57 +02:00
Christian Lackas e020f338ab Add window state sensor for HomematicIP rotary handle (HmIP-SRH) (#173423) 2026-06-14 15:36:15 +02:00
jasonjhofmann c85c2c4cd3 Add network MAC connection to JVC Projector device (#173683)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:56:29 +02:00
Raman Gupta c4e618e990 Stop validating # of slots in zwave_js.set_credential action (#173644) 2026-06-14 14:18:32 +02:00
Åke Strandberg 5efde60d21 Remove unnecessary #pylint disable..." (#173726) 2026-06-14 14:17:16 +02:00
jasonjhofmann d9dc10ed81 Add network MAC connection to myStrom bulb devices (#173707)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:16:50 +02:00
Onero-testdev cb6ae03d21 Register SwitchBot Standing Fan device (#173577)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:41:17 +02:00
Allen Porter 915b78473c Improve Rainbird config flow test coverage (#173703) 2026-06-14 13:35:54 +02:00
Vincent Wolsink 559006ba19 Adjust humidity attributes to (mandatory) new controller firmware in Huum (#173702) 2026-06-14 13:22:35 +02:00
Thomas D bad2eed9fe Bump qbusmqttapi to v1.5.1 for the Qbus integration (#173714) 2026-06-14 13:14:58 +02:00
Sid 9f1a079688 Bump eheimdigital to 1.7.0 (#173716) 2026-06-14 13:00:52 +02:00
Bipin Kumar 965a96b957 Add Dry and Fan-Only modes to Panasonic CS-CU-EZ18CKYXFM AC in Matter (#173709) 2026-06-14 12:40:37 +02:00
Manu d5791ae8b4 Remove cleanup code for removed entities from Xbox integration (#173688) 2026-06-14 10:51:33 +02:00
J. Nick Koston 7b561934ea Bump aiodiscover to 3.3.2 (#173705) 2026-06-13 22:44:47 -05:00
Franck Nijhof cf60690fb7 Skip building ZHA entity log messages when the level is disabled (#173695) 2026-06-13 22:15:47 +02:00
128 changed files with 989 additions and 850 deletions
+1 -1
View File
@@ -26,5 +26,5 @@
"iot_class": "local_push",
"loggers": ["aioacaia"],
"quality_scale": "platinum",
"requirements": ["aioacaia==0.1.17"]
"requirements": ["aioacaia==0.1.18"]
}
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/acmeda",
"iot_class": "local_push",
"loggers": ["aiopulse"],
"requirements": ["aiopulse==0.4.6"]
"requirements": ["aiopulse==0.4.7"]
}
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==14.0.3"]
"requirements": ["aioamazondevices==14.0.4"]
}
+1 -1
View File
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["anova_wifi"],
"requirements": ["anova-wifi==0.17.0"]
"requirements": ["anova-wifi==0.17.1"]
}
@@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["anthemav"],
"requirements": ["anthemav==1.4.1"]
"requirements": ["anthemav==1.4.2"]
}
@@ -30,5 +30,5 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==9.2.7", "yalexs-ble==3.3.0"]
"requirements": ["yalexs==9.2.7", "yalexs-ble==3.3.1"]
}
+1 -1
View File
@@ -21,5 +21,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["blinkpy"],
"requirements": ["blinkpy==0.25.2"]
"requirements": ["blinkpy==0.25.6"]
}
@@ -26,6 +26,12 @@
"description": "The credentials for {username} need to be updated",
"title": "Re-authenticate Blink"
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]"
}
},
"user": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["bluecurrent_api"],
"requirements": ["bluecurrent-api==1.3.2"]
"requirements": ["bluecurrent-api==1.3.3"]
}
@@ -24,7 +24,7 @@ from homeassistant.components.websocket_api import (
ActiveConnection,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.const import CONF_EVENT, STATE_OFF, STATE_ON
from homeassistant.core import (
CALLBACK_TYPE,
HomeAssistant,
@@ -45,7 +45,6 @@ from homeassistant.util import dt as dt_util
from homeassistant.util.json import JsonValueType
from .const import (
CONF_EVENT,
DATA_COMPONENT,
DOMAIN,
EVENT_DESCRIPTION,
@@ -13,9 +13,6 @@ if TYPE_CHECKING:
DOMAIN = "calendar"
DATA_COMPONENT: HassKey[EntityComponent[CalendarEntity]] = HassKey(DOMAIN)
# pylint: disable-next=home-assistant-duplicate-const
CONF_EVENT = "event"
class CalendarEntityFeature(IntFlag):
"""Supported features of the calendar entity."""
@@ -8,5 +8,5 @@
"iot_class": "local_polling",
"loggers": ["aiocomelit"],
"quality_scale": "platinum",
"requirements": ["aiocomelit==2.0.3"]
"requirements": ["aiocomelit==2.0.5"]
}
@@ -8,7 +8,7 @@ from hassil.recognize import RecognizeResult
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import MATCH_ALL
from homeassistant.const import MATCH_ALL, SERVICE_RELOAD
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@@ -53,7 +53,6 @@ from .const import (
METADATA_CUSTOM_FILE,
METADATA_CUSTOM_SENTENCE,
SERVICE_PROCESS,
SERVICE_RELOAD,
ConversationEntityFeature,
)
from .default_agent import async_setup_default_agent
@@ -19,8 +19,6 @@ ATTR_AGENT_ID = "agent_id"
ATTR_CONVERSATION_ID = "conversation_id"
SERVICE_PROCESS = "process"
# pylint: disable-next=home-assistant-duplicate-const
SERVICE_RELOAD = "reload"
DATA_COMPONENT: HassKey[EntityComponent[ConversationEntity]] = HassKey(DOMAIN)
@@ -7,7 +7,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["denonavr"],
"requirements": ["denonavr==1.3.2"],
"requirements": ["denonavr==1.3.3"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",
@@ -9,6 +9,6 @@
"iot_class": "local_push",
"loggers": ["HomeControl", "Mydevolo", "MprmRest", "MprmWebsocket", "Mprm"],
"quality_scale": "silver",
"requirements": ["devolo-home-control-api==0.19.0"],
"requirements": ["devolo-home-control-api==0.19.1"],
"zeroconf": ["_dvl-deviceapi._tcp.local."]
}
+1 -1
View File
@@ -16,7 +16,7 @@
"quality_scale": "internal",
"requirements": [
"aiodhcpwatcher==1.2.7",
"aiodiscover==3.3.1",
"aiodiscover==3.3.2",
"cached-ipaddress==1.1.2"
]
}
@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"loggers": ["eheimdigital"],
"quality_scale": "platinum",
"requirements": ["eheimdigital==1.6.0"],
"requirements": ["eheimdigital==1.7.0"],
"zeroconf": [
{ "name": "eheimdigital._http._tcp.local.", "type": "_http._tcp.local." }
]
@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"loggers": ["pyenphase"],
"quality_scale": "platinum",
"requirements": ["pyenphase==2.4.8"],
"requirements": ["pyenphase==2.4.9"],
"zeroconf": [
{
"type": "_enphase-envoy._tcp.local."
@@ -19,7 +19,7 @@
"requirements": [
"aioesphomeapi==45.3.1",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.9.1"
"bleak-esphome==3.9.4"
],
"zeroconf": ["_esphomelib._tcp.local."]
}
@@ -13,6 +13,11 @@
"discovery_confirm": {
"description": "Do you want to set up {model} {id} ({ipaddr})?"
},
"pick_device": {
"data": {
"device": "[%key:common::config_flow::data::device%]"
}
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
+1 -1
View File
@@ -5,7 +5,7 @@
"data_description_password": "Password for the FRITZ!Box.",
"data_description_port": "Leave empty to use the default port.",
"data_description_ssl": "Use SSL to connect to the FRITZ!Box.",
"data_description_username": "Username for the FRITZ!Box.",
"data_description_username": "Username for the FRITZ!Box. FRITZ!Powerline devices ignore this information and accept any value.",
"data_feature_device_tracking": "Enable network device tracking"
},
"config": {
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/geniushub",
"iot_class": "local_polling",
"loggers": ["geniushubclient"],
"requirements": ["geniushub-client==0.7.1"]
"requirements": ["geniushub-client==0.7.4"]
}
@@ -11,6 +11,7 @@
"step": {
"user": {
"data": {
"device": "[%key:common::config_flow::data::device%]",
"ip_address": "[%key:common::config_flow::data::ip%]",
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]"
@@ -151,6 +151,13 @@ class HolidayCalendarEntity(CalendarEntity):
"""Set up first update."""
self._update_state_and_setup_listener()
async def async_will_remove_from_hass(self) -> None:
"""Cancel listener when removing."""
await super().async_will_remove_from_hass()
if self.unsub:
self.unsub()
self.unsub = None
def update_event(self, now: datetime) -> CalendarEvent | None:
"""Return the next upcoming event."""
next_holiday = None
@@ -27,6 +27,7 @@ from homematicip.device import (
PassageDetector,
PresenceDetectorIndoor,
RoomControlDeviceAnalog,
RotaryHandleSensor,
SmokeDetector,
SoilMoistureSensorInterface,
SwitchMeasuring,
@@ -166,6 +167,7 @@ ILLUMINATION_DEVICE_ATTRIBUTES = {
}
TILT_STATE_VALUES = ["neutral", "tilted", "non_neutral"]
WINDOW_STATE_VALUES = ["open", "closed", "tilted"]
def get_device_handlers(hap: HomematicipHAP) -> dict[type, Callable]:
@@ -204,6 +206,9 @@ def get_device_handlers(hap: HomematicipHAP) -> dict[type, Callable]:
RoomControlDeviceAnalog: lambda device: [
HomematicipTemperatureSensor(hap, device),
],
RotaryHandleSensor: lambda device: [
HomematicipWindowStateSensor(hap, device),
],
LightSensor: lambda device: [
HomematicipIlluminanceSensor(hap, device),
],
@@ -498,6 +503,24 @@ class HomematicipTiltStateSensor(HomematicipGenericEntity, SensorEntity):
return state_attr
class HomematicipWindowStateSensor(HomematicipGenericEntity, SensorEntity):
"""Representation of the HomematicIP rotary handle window state sensor."""
_attr_device_class = SensorDeviceClass.ENUM
_attr_options = WINDOW_STATE_VALUES
_attr_translation_key = "window_state"
def __init__(self, hap: HomematicipHAP, device: RotaryHandleSensor) -> None:
"""Initialize the window state sensor."""
super().__init__(hap, device, feature_id="window_state")
@property
def native_value(self) -> str | None:
"""Return the state."""
window_state = getattr(self._device, "windowState", None)
return window_state.lower() if window_state is not None else None
class HomematicipFloorTerminalBlockMechanicChannelValve(
HomematicipGenericEntity, SensorEntity
):
@@ -98,6 +98,14 @@
"non_neutral": "Non-neutral",
"tilted": "Tilted"
}
},
"window_state": {
"name": "Window state",
"state": {
"closed": "[%key:common::state::closed%]",
"open": "[%key:common::state::open%]",
"tilted": "Tilted"
}
}
}
},
@@ -50,14 +50,12 @@ def homevolt_exception_handler[_HomevoltEntityT: HomevoltEntity, **_P](
translation_key="auth_failed",
) from error
except HomevoltConnectionError as error:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="communication_error",
translation_placeholders={"error": str(error)},
) from error
except HomevoltError as error:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unknown_error",
@@ -172,10 +172,10 @@
"message": "[%key:common::config_flow::error::invalid_auth%]"
},
"communication_error": {
"message": "[%key:common::config_flow::error::cannot_connect%]"
"message": "Error communicating with the Homevolt battery: {error}"
},
"unknown_error": {
"message": "[%key:common::config_flow::error::unknown%]"
"message": "Unknown error from the Homevolt battery: {error}"
}
}
}
+2 -1
View File
@@ -18,7 +18,8 @@
"step": {
"init": {
"data": {
"host": "[%key:common::config_flow::data::host%]"
"host": "[%key:common::config_flow::data::host%]",
"id": "Hue bridge"
},
"data_description": {
"host": "The hostname or IP address of your Hue bridge."
+5 -3
View File
@@ -4,7 +4,7 @@ import logging
from huum.const import SaunaStatus
from homeassistant.components.number import NumberEntity
from homeassistant.components.number import NumberDeviceClass, NumberEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -34,7 +34,9 @@ class HuumSteamer(HuumBaseEntity, NumberEntity):
"""Representation of a steamer."""
_attr_translation_key = "humidity"
_attr_native_max_value = 10
_attr_device_class = NumberDeviceClass.HUMIDITY
_attr_native_unit_of_measurement = "%"
_attr_native_max_value = 40
_attr_native_min_value = 0
_attr_native_step = 1
@@ -47,7 +49,7 @@ class HuumSteamer(HuumBaseEntity, NumberEntity):
@property
def native_value(self) -> float | None:
"""Return the current value."""
return self.coordinator.data.target_humidity
return self.coordinator.data.humidity
async def async_set_native_value(self, value: float) -> None:
"""Update the current value."""
+1 -1
View File
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"quality_scale": "bronze",
"requirements": ["pyimouapi==1.2.7"]
"requirements": ["pyimouapi==1.2.8"]
}
@@ -21,6 +21,7 @@ from homeassistant.const import (
CONF_ICON,
CONF_ID,
CONF_NAME,
CONF_OPTIONS,
SERVICE_RELOAD,
)
from homeassistant.core import HomeAssistant, ServiceCall, callback
@@ -37,8 +38,6 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "input_select"
CONF_INITIAL = "initial"
# pylint: disable-next=home-assistant-duplicate-const
CONF_OPTIONS = "options"
SERVICE_SET_OPTIONS = "set_options"
STORAGE_KEY = DOMAIN
+1 -1
View File
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["pyiskra"],
"requirements": ["pyiskra==0.1.27"]
"requirements": ["pyiskra==0.1.29"]
}
@@ -5,6 +5,10 @@
},
"step": {
"user": {
"data": {
"location": "[%key:common::config_flow::data::location%]",
"name": "[%key:common::config_flow::data::name%]"
},
"description": "Do you want to set up Islamic Prayer Times?",
"title": "Set up Islamic Prayer Times"
}
@@ -29,7 +29,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: IsraelRailConfigEntry) -
try:
await hass.async_add_executor_job(train_schedule.query, start, destination)
except Exception as e:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="request_timeout",
@@ -65,5 +65,10 @@
"name": "Trains +2"
}
}
},
"exceptions": {
"request_timeout": {
"message": "Timeout connecting to the Israel Rail API for {config_title}: {error}"
}
}
}
@@ -4,7 +4,7 @@ import logging
from jvcprojector import Command, JvcProjector
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER, NAME
@@ -27,8 +27,12 @@ class JvcProjectorEntity(CoordinatorEntity[JvcProjectorDataUpdateCoordinator]):
super().__init__(coordinator, command)
self._attr_unique_id = coordinator.unique_id
# The config entry unique id is the device's formatted MAC address (set
# from the projector's MAC in the config flow), so it doubles as the
# network MAC connection.
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._attr_unique_id)},
connections={(CONNECTION_NETWORK_MAC, self._attr_unique_id)},
name=NAME,
model=self.device.model,
manufacturer=MANUFACTURER,
@@ -10,6 +10,11 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"location": {
"data": {
"location": "[%key:common::config_flow::data::location%]"
}
},
"user": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
@@ -7,6 +7,6 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["linkplay"],
"requirements": ["python-linkplay==0.2.12"],
"requirements": ["python-linkplay==0.2.14"],
"zeroconf": ["_linkplay._tcp.local."]
}
+1 -1
View File
@@ -23,7 +23,7 @@
},
"user": {
"data": {
"ip_address": "[%key:common::config_flow::data::ip%]"
"host": "[%key:common::config_flow::data::host%]"
}
}
}
@@ -99,6 +99,7 @@ SUPPORT_DRY_MODE_DEVICES: set[tuple[int, int]] = {
# support dry mode.
(0x0001, 0x0108),
(0x0001, 0x010A),
(0x0001, 0x013F),
(0x1209, 0x8000),
(0x1209, 0x8001),
(0x1209, 0x8002),
@@ -138,6 +139,7 @@ SUPPORT_FAN_MODE_DEVICES: set[tuple[int, int]] = {
# support fan-only mode.
(0x0001, 0x0108),
(0x0001, 0x010A),
(0x0001, 0x013F),
(0x118C, 0x2022),
(0x1209, 0x8000),
(0x1209, 0x8001),
@@ -8,6 +8,11 @@
"bluetooth_confirm": {
"description": "Do you want to add the Melnor Bluetooth valve `{name}` to Home Assistant?",
"title": "Discovered Melnor Bluetooth valve"
},
"pick_device": {
"data": {
"address": "[%key:common::config_flow::data::device%]"
}
}
}
},
@@ -10,10 +10,10 @@
"step": {
"user": {
"data": {
"code": "Station code"
"station_code": "Station code"
},
"data_description": {
"code": "Looks like ESCAT4300000043206B"
"station_code": "Looks like ESCAT4300000043206B"
}
}
}
+2 -2
View File
@@ -135,6 +135,8 @@ class MieleFan(MieleEntity, FanEntity):
_LOGGER.debug("Calc ventilation_step: %s", ventilation_step)
if ventilation_step == 0:
await self.async_turn_off()
elif ventilation_step == self.device.state_ventilation_step:
return
else:
try:
await self.api.send_action(
@@ -165,7 +167,6 @@ class MieleFan(MieleEntity, FanEntity):
try:
await self.api.send_action(self._device_id, {POWER_ON: True})
except ClientResponseError as ex:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="set_state_error",
@@ -183,7 +184,6 @@ class MieleFan(MieleEntity, FanEntity):
try:
await self.api.send_action(self._device_id, {POWER_OFF: True})
except ClientResponseError as ex:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="set_state_error",
@@ -10,6 +10,9 @@
},
"step": {
"confirm": {
"data": {
"blind_type": "Blind type"
},
"description": "What kind of blind is {display_name}?"
},
"user": {
+2 -1
View File
@@ -14,7 +14,7 @@ from homeassistant.components.light import (
LightEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN, MANUFACTURER
@@ -57,6 +57,7 @@ class MyStromLight(LightEntity):
self._attr_hs_color = 0, 0
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, mac)},
connections={(CONNECTION_NETWORK_MAC, mac)},
name=name,
manufacturer=MANUFACTURER,
sw_version=self._bulb.firmware,
+12 -10
View File
@@ -50,16 +50,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
)
if not await webio_api.refresh_device_info():
_LOGGER.error("[%s] Refresh device info failed", entry.data[CONF_HOST])
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="config_entry_error_internal_error",
translation_placeholders={"support_email": SUPPORT_EMAIL},
)
webio_serial = webio_api.get_serial_number()
if webio_serial is None:
_LOGGER.error("[%s] Serial number not available", entry.data[CONF_HOST])
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="config_entry_error_internal_error",
translation_placeholders={"support_email": SUPPORT_EMAIL},
)
@@ -67,8 +67,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
_LOGGER.error(
"[%s] Serial number doesn't match config entry", entry.data[CONF_HOST]
)
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(translation_key="config_entry_error_serial_mismatch")
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="config_entry_error_serial_mismatch",
)
coordinator = NASwebCoordinator(
hass, webio_api, name=f"NASweb[{webio_api.get_name()}]"
@@ -79,15 +81,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
webhook_url = nasweb_data.get_webhook_url(hass)
if not await webio_api.status_subscription(webhook_url, True):
_LOGGER.error("Failed to subscribe for status updates from webio")
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="config_entry_error_internal_error",
translation_placeholders={"support_email": SUPPORT_EMAIL},
)
if not await nasweb_data.notify_coordinator.check_connection(webio_serial):
_LOGGER.error("Did not receive status from device")
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="config_entry_error_no_status_update",
translation_placeholders={"support_email": SUPPORT_EMAIL},
)
@@ -96,14 +98,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: NASwebConfigEntry) -> bo
f"[{entry.data[CONF_HOST]}] Check connection reached timeout"
) from error
except AuthError as error:
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_invalid_authentication"
translation_domain=DOMAIN,
translation_key="config_entry_error_invalid_authentication",
) from error
except NoURLAvailableError as error:
# pylint: disable-next=home-assistant-exception-translation-key-domain-mismatch
raise ConfigEntryError(
translation_key="config_entry_error_missing_internal_url"
translation_domain=DOMAIN,
translation_key="config_entry_error_missing_internal_url",
) from error
device_registry = dr.async_get(hass)
+1 -1
View File
@@ -65,7 +65,7 @@
"config_entry_error_no_status_update": {
"message": "Did not receive any status updates within the expected time window. Make sure the Home Assistant internal URL is reachable from the NASweb device. If the issue persists contact support at {support_email}"
},
"serial_mismatch": {
"config_entry_error_serial_mismatch": {
"message": "Connected to different NASweb device (serial number mismatch)."
}
}
@@ -26,6 +26,7 @@ from homeassistant.helpers.dispatcher import (
)
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_when_setup
from homeassistant.util import slugify
from homeassistant.util.json import json_loads
from .config_flow import CONF_SECRET
@@ -311,6 +312,6 @@ class OwnTracksContext:
# kwargs location is the beacon's configured lat/lon
kwargs.pop("battery", None)
for beacon in self.mobile_beacons_active[dev_id]:
kwargs["dev_id"] = f"{BEACON_DEV_ID}_{beacon}"
kwargs["dev_id"] = slugify(f"{BEACON_DEV_ID}_{beacon}")
kwargs["host_name"] = beacon
self.async_see(**kwargs)
+1 -1
View File
@@ -41,7 +41,7 @@
"step": {
"user": {
"data": {
"update_interval": "Update interval (minutes)"
"scan_interval": "Update interval (minutes)"
},
"description": "Set the update interval (minutes)",
"title": "Options for Plaato"
@@ -90,5 +90,5 @@
"integration_type": "hub",
"iot_class": "cloud_polling",
"quality_scale": "bronze",
"requirements": ["PSNAWP==3.0.3", "pyrate-limiter==4.2.0"]
"requirements": ["PSNAWP==3.0.3", "pyrate-limiter==4.4.0"]
}
+1 -1
View File
@@ -14,5 +14,5 @@
"cloudapp/QBUSMQTTGW/+/state"
],
"quality_scale": "bronze",
"requirements": ["qbusmqttapi==1.5.0"]
"requirements": ["qbusmqttapi==1.5.1"]
}
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["pyrainbird"],
"requirements": ["pyrainbird==6.3.0"]
"requirements": ["pyrainbird==6.3.1"]
}
@@ -34,11 +34,7 @@ rules:
docs-removal-instructions: todo
test-before-setup: done
docs-high-level-description: done
config-flow-test-coverage:
status: todo
comment: |
All config flow tests should finish with CREATE_ENTRY and ABORT to
test they are able to recover from errors
config-flow-test-coverage: done
docs-actions: done
runtime-data: done
@@ -20,5 +20,5 @@
"iot_class": "local_push",
"loggers": ["reolink_aio"],
"quality_scale": "platinum",
"requirements": ["reolink-aio==0.21.0"]
"requirements": ["reolink-aio==0.21.1"]
}
@@ -6,5 +6,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/scrape",
"iot_class": "cloud_polling",
"requirements": ["beautifulsoup4==4.13.3", "lxml==6.0.1"]
"requirements": ["beautifulsoup4==4.13.3", "lxml==6.1.1"]
}
+1 -2
View File
@@ -8,6 +8,7 @@ from propcache.api import cached_property
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_OPTION, SERVICE_SELECT_OPTION
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
@@ -18,13 +19,11 @@ from homeassistant.util.hass_dict import HassKey
from .const import (
ATTR_CYCLE,
ATTR_OPTION,
ATTR_OPTIONS,
DOMAIN,
SERVICE_SELECT_FIRST,
SERVICE_SELECT_LAST,
SERVICE_SELECT_NEXT,
SERVICE_SELECT_OPTION,
SERVICE_SELECT_PREVIOUS,
)
-4
View File
@@ -4,8 +4,6 @@ DOMAIN = "select"
ATTR_CYCLE = "cycle"
ATTR_OPTIONS = "options"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_OPTION = "option"
CONF_CYCLE = "cycle"
CONF_OPTION = "option"
@@ -13,6 +11,4 @@ CONF_OPTION = "option"
SERVICE_SELECT_FIRST = "select_first"
SERVICE_SELECT_LAST = "select_last"
SERVICE_SELECT_NEXT = "select_next"
# pylint: disable-next=home-assistant-duplicate-const
SERVICE_SELECT_OPTION = "select_option"
SERVICE_SELECT_PREVIOUS = "select_previous"
@@ -10,10 +10,12 @@ from homeassistant.components.device_automation import (
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_OPTION,
CONF_DEVICE_ID,
CONF_DOMAIN,
CONF_ENTITY_ID,
CONF_TYPE,
SERVICE_SELECT_OPTION,
)
from homeassistant.core import Context, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
@@ -23,7 +25,6 @@ from homeassistant.helpers.typing import ConfigType, TemplateVarsType
from .const import (
ATTR_CYCLE,
ATTR_OPTION,
ATTR_OPTIONS,
CONF_CYCLE,
CONF_OPTION,
@@ -31,7 +32,6 @@ from .const import (
SERVICE_SELECT_FIRST,
SERVICE_SELECT_LAST,
SERVICE_SELECT_NEXT,
SERVICE_SELECT_OPTION,
SERVICE_SELECT_PREVIOUS,
)
@@ -5,10 +5,10 @@ from collections.abc import Iterable
import logging
from typing import Any
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, SERVICE_SELECT_OPTION
from homeassistant.core import Context, HomeAssistant, State
from .const import ATTR_OPTION, ATTR_OPTIONS, DOMAIN, SERVICE_SELECT_OPTION
from .const import ATTR_OPTIONS, DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["construct", "snapcast"],
"requirements": ["snapcast==2.3.7"]
"requirements": ["snapcast==2.3.8"]
}
+2 -4
View File
@@ -80,11 +80,10 @@ class SnooSwitch(SnooDescriptionEntity, SwitchEntity):
True,
)
except SnooCommandException as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_on_failed",
translation_placeholders={"name": str(self.name), "status": "on"},
translation_placeholders={"name": str(self.name)},
) from err
async def async_turn_off(self, **kwargs: Any) -> None:
@@ -97,9 +96,8 @@ class SnooSwitch(SnooDescriptionEntity, SwitchEntity):
False,
)
except SnooCommandException as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_off_failed",
translation_placeholders={"name": str(self.name), "status": "off"},
translation_placeholders={"name": str(self.name)},
) from err
+2 -1
View File
@@ -18,7 +18,8 @@
},
"user": {
"data": {
"address": "[%key:common::config_flow::data::device%]"
"address": "[%key:common::config_flow::data::device%]",
"name": "[%key:common::config_flow::data::name%]"
},
"description": "[%key:component::bluetooth::config::step::user::description%]"
}
@@ -86,12 +86,11 @@ async def async_setup_entry(
},
) from e
except OpendataTransportError as e:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise ConfigEntryError(
translation_domain=DOMAIN,
translation_key="invalid_data",
translation_placeholders={
**PLACEHOLDERS,
"stationboard_url": PLACEHOLDERS["stationboard_url"],
"config_title": entry.title,
"error": str(e),
},
@@ -96,6 +96,7 @@ PLATFORMS_BY_TYPE = {
],
SupportedModels.HUBMINI_MATTER.value: [Platform.SENSOR],
SupportedModels.CIRCULATOR_FAN.value: [Platform.FAN, Platform.SENSOR],
SupportedModels.STANDING_FAN.value: [Platform.SENSOR],
SupportedModels.S10_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
SupportedModels.S20_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
SupportedModels.K10_VACUUM.value: [Platform.VACUUM, Platform.SENSOR],
@@ -207,6 +208,7 @@ CLASS_BY_DEVICE = {
SupportedModels.RELAY_SWITCH_1.value: switchbot.SwitchbotRelaySwitch,
SupportedModels.ROLLER_SHADE.value: switchbot.SwitchbotRollerShade,
SupportedModels.CIRCULATOR_FAN.value: switchbot.SwitchbotFan,
SupportedModels.STANDING_FAN.value: switchbot.SwitchbotStandingFan,
SupportedModels.S10_VACUUM.value: switchbot.SwitchbotVacuum,
SupportedModels.S20_VACUUM.value: switchbot.SwitchbotVacuum,
SupportedModels.K10_VACUUM.value: switchbot.SwitchbotVacuum,
@@ -71,6 +71,7 @@ class SupportedModels(StrEnum):
LOCK_VISION = "lock_vision"
LOCK_PRO_WIFI = "lock_pro_wifi"
WEATHER_STATION = "weather_station"
STANDING_FAN = "standing_fan"
CONNECTABLE_SUPPORTED_MODEL_TYPES = {
@@ -120,6 +121,7 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
SwitchbotModel.LOCK_VISION_PRO: SupportedModels.LOCK_VISION_PRO,
SwitchbotModel.LOCK_VISION: SupportedModels.LOCK_VISION,
SwitchbotModel.LOCK_PRO_WIFI: SupportedModels.LOCK_PRO_WIFI,
SwitchbotModel.STANDING_FAN: SupportedModels.STANDING_FAN,
}
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
@@ -8,7 +8,7 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["synology_dsm"],
"requirements": ["py-synologydsm-api==2.9.0"],
"requirements": ["py-synologydsm-api==2.10.0"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:Basic:1",
@@ -6,7 +6,15 @@ import voluptuous as vol
from voluptuous import All, Range
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE_ID, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.const import (
ATTR_ID,
ATTR_LOCATION,
ATTR_NAME,
ATTR_TIME,
CONF_DEVICE_ID,
CONF_LATITUDE,
CONF_LONGITUDE,
)
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
@@ -18,20 +26,14 @@ from .models import TeslemetryEnergyData, TeslemetryVehicleData
_LOGGER = logging.getLogger(__name__)
# Attributes
# pylint: disable-next=home-assistant-duplicate-const
ATTR_ID = "id"
ATTR_GPS = "gps"
ATTR_TYPE = "type"
ATTR_VALUE = "value"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_LOCATION = "location"
ATTR_LOCALE = "locale"
ATTR_ORDER = "order"
ATTR_TIMESTAMP = "timestamp"
ATTR_FIELDS = "fields"
ATTR_ENABLE = "enable"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_TIME = "time"
ATTR_PIN = "pin"
ATTR_TOU_SETTINGS = "tou_settings"
ATTR_PRECONDITIONING_ENABLED = "preconditioning_enabled"
@@ -44,8 +46,6 @@ ATTR_DAYS_OF_WEEK = "days_of_week"
ATTR_START_TIME = "start_time"
ATTR_END_TIME = "end_time"
ATTR_ONE_TIME = "one_time"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_NAME = "name"
ATTR_PRECONDITION_TIME = "precondition_time"
# Services
+8 -3
View File
@@ -3,6 +3,7 @@
import asyncio
import logging
from aiohttp import ClientError
from tesla_fleet_api.const import Scope
from tesla_fleet_api.exceptions import (
Forbidden,
@@ -81,6 +82,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo
translation_domain=DOMAIN,
translation_key="cannot_connect",
) from e
except ClientError as e:
raise ConfigEntryNotReady from e
vehicles: list[TessieVehicleData] = []
for vehicle in state_of_all_vehicles["results"]:
@@ -124,13 +127,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo
try:
scopes = await tessie.scopes()
except TeslaFleetError as e:
except (TeslaFleetError, ClientError) as e:
raise ConfigEntryNotReady from e
if Scope.ENERGY_DEVICE_DATA in scopes:
try:
products = (await tessie.products())["response"]
except TeslaFleetError as e:
except (TeslaFleetError, ClientError) as e:
raise ConfigEntryNotReady from e
for product in products:
@@ -154,7 +157,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: TessieConfigEntry) -> bo
except (InvalidToken, Forbidden, SubscriptionRequired) as e:
raise ConfigEntryAuthFailed from e
except TeslaFleetError as e:
raise ConfigEntryNotReady(e.message) from e
raise ConfigEntryNotReady(getattr(e, "message", str(e))) from e
except ClientError as e:
raise ConfigEntryNotReady from e
powerwall = (
product["components"]["battery"] or product["components"]["solar"]
+1 -1
View File
@@ -8,5 +8,5 @@
"iot_class": "local_push",
"loggers": ["steamloop"],
"quality_scale": "bronze",
"requirements": ["steamloop==1.2.0"]
"requirements": ["steamloop==1.2.1"]
}
@@ -15,6 +15,9 @@
"description": "The Tuya integration now uses an improved login method. To reauthenticate with your Smart Life or Tuya Smart account, you need to enter your user code.\n\nYou can find this code in the Smart Life app or Tuya Smart app in **Settings** > **Account and Security** screen, and enter the code shown on the **User Code** field. The user code is case-sensitive, please be sure to enter it exactly as shown in the app."
},
"scan": {
"data": {
"QR": "QR code"
},
"description": "Use the Smart Life app or Tuya Smart app to scan the following QR code to complete the login.\n\nContinue to the next step once you have completed this step in the app."
},
"user": {
@@ -9,5 +9,5 @@
"iot_class": "local_push",
"loggers": ["uiprotect"],
"quality_scale": "platinum",
"requirements": ["uiprotect==13.1.1"]
"requirements": ["uiprotect==13.1.2"]
}
@@ -64,17 +64,14 @@ async def async_setup_entry(
try:
await manager.login()
except VeSyncLoginError as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN, translation_key="invalid_auth"
) from err
except VeSyncServerError as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryNotReady(
translation_domain=DOMAIN, translation_key="server_error"
) from err
except VeSyncAPIResponseError as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryNotReady(
translation_domain=DOMAIN, translation_key="api_response_error"
) from err
@@ -154,6 +154,17 @@
}
}
},
"exceptions": {
"api_response_error": {
"message": "Invalid response from the VeSync API"
},
"invalid_auth": {
"message": "[%key:common::config_flow::error::invalid_auth%]"
},
"server_error": {
"message": "Server error occurred while connecting to VeSync"
}
},
"services": {
"update_devices": {
"description": "Adds new VeSync devices to Home Assistant.",
+1 -4
View File
@@ -67,7 +67,4 @@ class VistapoolLEDPulseButton(VistapoolEntity, ButtonEntity):
translation_key="set_failed",
translation_placeholders={"entity": self.entity_id},
) from err
# Optimistically reflect the just-written value so a rapid second press
# doesn't read the stale off-state before the Firestore push round-trips.
self.coordinator.data.setdefault("light", {})["status"] = 1
self.coordinator.async_set_updated_data(self.coordinator.data)
self.coordinator.apply_optimistic(_LIGHT_STATUS_PATH, 1)
@@ -81,3 +81,22 @@ class VistapoolDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
def get_value(self, path: str, default: Any = None) -> Any:
"""Get nested data using dot-notation path."""
return AquariteClient.get_value(self.data, path, default)
def apply_optimistic(self, value_path: str, value: Any) -> None:
"""Reflect a just-written value before the Firestore push round-trips.
Hayward's cloud takes several seconds to acknowledge a write back
through Firestore, which would make the UI feel laggy. Writing into
coordinator.data after a successful REST call gives entities instant
feedback; the next snapshot from Firestore overwrites it harmlessly.
"""
keys = value_path.split(".")
target: dict[str, Any] = self.data
for key in keys[:-1]:
child = target.get(key)
if not isinstance(child, dict):
child = {}
target[key] = child
target = child
target[keys[-1]] = value
self.async_set_updated_data(self.data)
@@ -71,3 +71,4 @@ class VistapoolLight(VistapoolEntity, LightEntity):
translation_key="set_failed",
translation_placeholders={"entity": self.entity_id},
) from err
self.coordinator.apply_optimistic(_VALUE_PATH, value)
@@ -233,3 +233,4 @@ class VistapoolNumber(VistapoolEntity, NumberEntity):
translation_key="set_failed",
translation_placeholders={"entity": self.entity_id},
) from err
self.coordinator.apply_optimistic(self.entity_description.value_path, raw)
+1 -25
View File
@@ -9,7 +9,6 @@ from pythonxbox.api.provider.people.models import Person
from pythonxbox.api.provider.titlehub.models import Title
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorEntity,
BinarySensorEntityDescription,
)
@@ -17,12 +16,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import XboxConfigEntry
from .entity import (
XboxBaseEntity,
XboxBaseEntityDescription,
check_deprecated_entity,
profile_pic,
)
from .entity import XboxBaseEntity, XboxBaseEntityDescription, profile_pic
PARALLEL_UPDATES = 0
@@ -31,9 +25,7 @@ class XboxBinarySensor(StrEnum):
"""Xbox binary sensor."""
ONLINE = "online"
IN_PARTY = "in_party"
IN_GAME = "in_game"
IN_MULTIPLAYER = "in_multiplayer"
HAS_GAME_PASS = "has_game_pass"
@@ -81,21 +73,11 @@ SENSOR_DESCRIPTIONS: tuple[XboxBinarySensorEntityDescription, ...] = (
entity_picture_fn=profile_pic,
attributes_fn=profile_attributes,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.IN_PARTY,
is_on_fn=lambda _: None,
deprecated=True,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.IN_GAME,
translation_key=XboxBinarySensor.IN_GAME,
is_on_fn=in_game,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.IN_MULTIPLAYER,
is_on_fn=lambda _: None,
deprecated=True,
),
XboxBinarySensorEntityDescription(
key=XboxBinarySensor.HAS_GAME_PASS,
translation_key=XboxBinarySensor.HAS_GAME_PASS,
@@ -118,9 +100,6 @@ async def async_setup_entry(
[
XboxBinarySensorEntity(coordinator, entry.unique_id, description)
for description in SENSOR_DESCRIPTIONS
if check_deprecated_entity(
hass, entry.unique_id, description, BINARY_SENSOR_DOMAIN
)
]
)
@@ -130,9 +109,6 @@ async def async_setup_entry(
XboxBinarySensorEntity(coordinator, subentry.unique_id, description)
for description in SENSOR_DESCRIPTIONS
if subentry.unique_id
and check_deprecated_entity(
hass, subentry.unique_id, description, BINARY_SENSOR_DOMAIN
)
and subentry.unique_id in coordinator.data.presence
and subentry.subentry_type == "friend"
],
-23
View File
@@ -9,8 +9,6 @@ from pythonxbox.api.provider.smartglass.models import ConsoleType, SmartglassCon
from pythonxbox.api.provider.titlehub.models import Title
from yarl import URL
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -40,7 +38,6 @@ class XboxBaseEntityDescription(EntityDescription):
attributes_fn: Callable[[Person, Title | None], Mapping[str, Any] | None] | None = (
None
)
deprecated: bool | None = None
class XboxBaseEntity(CoordinatorEntity[XboxPresenceCoordinator]):
@@ -145,26 +142,6 @@ class XboxConsoleBaseEntity(CoordinatorEntity[XboxConsoleStatusCoordinator]):
return self.coordinator.data.get(self._console.id) is not None
def check_deprecated_entity(
hass: HomeAssistant,
xuid: str,
entity_description: XboxBaseEntityDescription,
entity_domain: str,
) -> bool:
"""Check for deprecated entity and remove it."""
if not entity_description.deprecated:
return True
ent_reg = er.async_get(hass)
if entity_id := ent_reg.async_get_entity_id(
entity_domain,
DOMAIN,
f"{xuid}_{entity_description.key}",
):
ent_reg.async_remove(entity_id)
return False
def to_https(image_url: str) -> str:
"""Convert image URLs to secure URLs."""
+1 -26
View File
@@ -11,7 +11,6 @@ from pythonxbox.api.provider.smartglass.models import SmartglassConsole, Storage
from pythonxbox.api.provider.titlehub.models import Title
from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN,
EntityCategory,
SensorDeviceClass,
SensorEntity,
@@ -27,13 +26,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import XboxConfigEntry, XboxConsolesCoordinator
from .entity import (
MAP_MODEL,
XboxBaseEntity,
XboxBaseEntityDescription,
check_deprecated_entity,
to_https,
)
from .entity import MAP_MODEL, XboxBaseEntity, XboxBaseEntityDescription, to_https
PARALLEL_UPDATES = 0
@@ -58,8 +51,6 @@ class XboxSensor(StrEnum):
STATUS = "status"
GAMER_SCORE = "gamer_score"
ACCOUNT_TIER = "account_tier"
GOLD_TENURE = "gold_tenure"
LAST_ONLINE = "last_online"
FOLLOWING = "following"
FOLLOWER = "follower"
@@ -200,16 +191,6 @@ SENSOR_DESCRIPTIONS: tuple[XboxSensorEntityDescription, ...] = (
value_fn=lambda x, _: x.gamer_score,
state_class=SensorStateClass.MEASUREMENT,
),
XboxSensorEntityDescription(
key=XboxSensor.ACCOUNT_TIER,
value_fn=lambda _, __: None,
deprecated=True,
),
XboxSensorEntityDescription(
key=XboxSensor.GOLD_TENURE,
value_fn=lambda _, __: None,
deprecated=True,
),
XboxSensorEntityDescription(
key=XboxSensor.LAST_ONLINE,
translation_key=XboxSensor.LAST_ONLINE,
@@ -304,9 +285,6 @@ async def async_setup_entry(
[
XboxSensorEntity(presence, config_entry.unique_id, description)
for description in SENSOR_DESCRIPTIONS
if check_deprecated_entity(
hass, config_entry.unique_id, description, SENSOR_DOMAIN
)
]
)
for subentry_id, subentry in config_entry.subentries.items():
@@ -315,9 +293,6 @@ async def async_setup_entry(
XboxSensorEntity(presence, subentry.unique_id, description)
for description in SENSOR_DESCRIPTIONS
if subentry.unique_id
and check_deprecated_entity(
hass, subentry.unique_id, description, SENSOR_DOMAIN
)
and subentry.unique_id in presence.data.presence
and subentry.subentry_type == "friend"
],
+1 -1
View File
@@ -14,5 +14,5 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["socketio", "engineio", "yalexs"],
"requirements": ["yalexs==9.2.7", "yalexs-ble==3.3.0"]
"requirements": ["yalexs==9.2.7", "yalexs-ble==3.3.1"]
}
@@ -13,5 +13,5 @@
"documentation": "https://www.home-assistant.io/integrations/yalexs_ble",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["yalexs-ble==3.3.0"]
"requirements": ["yalexs-ble==3.3.1"]
}
+1 -5
View File
@@ -19,11 +19,7 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
from .const import DOMAIN
from .coordinator import YotoConfigEntry, YotoDataUpdateCoordinator
PLATFORMS: list[Platform] = [
Platform.MEDIA_PLAYER,
Platform.SENSOR,
Platform.TIME,
]
PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER, Platform.TIME]
async def async_setup_entry(hass: HomeAssistant, entry: YotoConfigEntry) -> bool:
@@ -148,6 +148,8 @@ class YotoDataUpdateCoordinator(DataUpdateCoordinator[dict[str, YotoPlayer]]):
"""Ask each player to push a fresh status snapshot over MQTT."""
if not self.client.is_mqtt_connected:
return
# Fire-and-forget: the data/status response lands via the on_update
# callback later, which already triggers async_set_updated_data.
for device_id in list(self.client.players):
await self.client.request_player_status(device_id)
-18
View File
@@ -1,23 +1,5 @@
{
"entity": {
"sensor": {
"card_insertion_state": {
"default": "mdi:card-bulleted-outline",
"state": {
"none": "mdi:card-bulleted-off-outline",
"physical": "mdi:card-bulleted",
"remote": "mdi:cast-audio",
"streaming": "mdi:radio-tower"
}
},
"day_mode": {
"default": "mdi:theme-light-dark",
"state": {
"day": "mdi:weather-sunny",
"night": "mdi:weather-night"
}
}
},
"time": {
"day_mode_start": {
"default": "mdi:weather-sunny"
-103
View File
@@ -1,103 +0,0 @@
"""Sensor platform for the Yoto integration."""
from collections.abc import Callable
from dataclasses import dataclass
from yoto_api import CardInsertionState, DayMode, YotoPlayer
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .coordinator import YotoConfigEntry, YotoDataUpdateCoordinator
from .entity import YotoPlayerEntity
PARALLEL_UPDATES = 0
def _enum_state(value: CardInsertionState | None) -> str | None:
"""Return an enum member as a lowercase string, or None if unset."""
return value.name.lower() if value is not None else None
def _day_mode_state(value: DayMode | None) -> str | None:
"""Return day/night, treating the firmware's UNKNOWN as unset."""
if value is None or value is DayMode.UNKNOWN:
return None
return value.name.lower()
@dataclass(frozen=True, kw_only=True)
class YotoSensorEntityDescription(SensorEntityDescription):
"""Describes a Yoto sensor entity."""
value_fn: Callable[[YotoPlayer], StateType]
SENSORS: tuple[YotoSensorEntityDescription, ...] = (
YotoSensorEntityDescription(
key="battery_level",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda player: player.status.battery_level_percentage,
),
YotoSensorEntityDescription(
key="card_insertion_state",
translation_key="card_insertion_state",
device_class=SensorDeviceClass.ENUM,
options=[state.name.lower() for state in CardInsertionState],
value_fn=lambda player: _enum_state(player.status.card_insertion_state),
),
YotoSensorEntityDescription(
key="day_mode",
translation_key="day_mode",
device_class=SensorDeviceClass.ENUM,
options=["day", "night"],
value_fn=lambda player: _day_mode_state(player.status.day_mode),
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: YotoConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Yoto sensor platform."""
coordinator = entry.runtime_data
async_add_entities(
YotoSensor(coordinator, player, description)
for player in coordinator.client.players.values()
for description in SENSORS
)
class YotoSensor(YotoPlayerEntity, SensorEntity):
"""Representation of a Yoto player sensor."""
entity_description: YotoSensorEntityDescription
def __init__(
self,
coordinator: YotoDataUpdateCoordinator,
player: YotoPlayer,
description: YotoSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator, player)
self.entity_description = description
self._attr_unique_id = f"{player.id}_{description.key}"
@property
def native_value(self) -> StateType:
"""Return the sensor value."""
return self.entity_description.value_fn(self.player)
@@ -37,24 +37,6 @@
}
},
"entity": {
"sensor": {
"card_insertion_state": {
"name": "Card slot",
"state": {
"none": "Empty",
"physical": "Physical card",
"remote": "Remote",
"streaming": "Streaming"
}
},
"day_mode": {
"name": "Day mode",
"state": {
"day": "Day",
"night": "Night"
}
}
},
"time": {
"day_mode_start": {
"name": "Day mode start"
+4
View File
@@ -214,6 +214,10 @@ class ZHAEntity(LogMixin, RestoreEntity, Entity):
def log(self, level: int, msg: str, *args, **kwargs):
"""Log a message."""
if not _LOGGER.isEnabledFor(level):
# Avoid building the prefixed message and args tuple for disabled
# levels; this runs for every entity event via _handle_entity_events.
return
msg = f"%s: {msg}"
args = (self.entity_id, *args)
_LOGGER.log(level, msg, *args, **kwargs)
@@ -432,15 +432,6 @@ async def async_set_credential(
translation_key="no_available_credential_slots",
translation_placeholders={"credential_type": cred_type_str},
)
elif not 1 <= credential_slot <= type_cap.number_of_credential_slots:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="credential_slot_out_of_range",
translation_placeholders={
"credential_type": cred_type_str,
"max_slot": str(type_cap.number_of_credential_slots),
},
)
status = await node.access_control.set_credential(
user_id, credential_type, credential_slot, credential_data
@@ -322,9 +322,6 @@
"credential_rejected_wrong_uuid": {
"message": "The device rejected the credential because the user unique identifier does not match."
},
"credential_slot_out_of_range": {
"message": "Credential slot for {credential_type} must be between 1 and {max_slot}."
},
"credential_type_not_supported": {
"message": "Credential type {credential_type} is not supported on this device"
},
+2 -2
View File
@@ -1,7 +1,7 @@
# Automatically generated by gen_requirements_all.py, do not edit
aiodhcpwatcher==1.2.7
aiodiscover==3.3.1
aiodiscover==3.3.2
aiodns==4.0.4
aiogithubapi==26.0.0
aiohttp-asyncmdnsresolver==0.2.0
@@ -70,7 +70,7 @@ standard-telnetlib==3.13.0
typing-extensions>=4.15.0,<5.0
ulid-transform==2.2.9
urllib3>=2.0
uv==0.11.19
uv==0.11.21
voluptuous-openapi==0.3.0
voluptuous-serialize==2.7.0
voluptuous==0.15.2
@@ -1,4 +1,8 @@
"""Plugin to encourage correct use of DOMAIN constants in tests."""
"""Plugin to prevent incorrect use of Platform enum in tests.
This plugin checks for common test helper functions and methods
where a domain is expected, and ensures the argument is not a Platform.
"""
from dataclasses import dataclass
@@ -30,9 +34,6 @@ _METHOD_CHECKS: list[tuple[str, str, ArgumentCheckInfo]] = [
("hass.states", "async_entity_ids", ArgumentCheckInfo(0, "domain_filter", True)),
]
_DOMAIN_CONSTANTS: set[str] = {"DOMAIN", "domain"}
_DOMAIN_SUFFIXES: tuple[str, ...] = ("_DOMAIN", "_domain")
def _check_call_node_domain_arguments(node: nodes.Call) -> nodes.NodeNG | None:
"""Ensure the call node arguments are valid domain constant or variable.
@@ -81,10 +82,14 @@ def _check_call_node_domain_argument(
return None
def _check_domain_argument(arg_node: nodes.NodeNG, allow_iterable: bool) -> bool:
def _check_domain_argument(arg_node: nodes.NodeNG, allow_iterable: bool = True) -> bool:
"""Ensure the argument node is a domain constant or variable.
We allow:
We currently only disallow `Platform.Xyz`
The plugin can be extended in the future (see #173374) to improve
consistency and only allow certain patterns for domain arguments,
such as:
- x.DOMAIN/x.domain attribute (including *_DOMAIN/*_domain)
- DOMAIN/domain name (including *_DOMAIN/*_domain)
- string literals
@@ -94,21 +99,8 @@ def _check_domain_argument(arg_node: nodes.NodeNG, allow_iterable: bool) -> bool
"""
match arg_node:
case nodes.Attribute():
if (
attrname := arg_node.attrname
) in _DOMAIN_CONSTANTS or attrname.endswith(_DOMAIN_SUFFIXES):
return True
case nodes.Const():
if isinstance(arg_node.value, str):
return True
case nodes.Name():
if (node_name := arg_node.name) in _DOMAIN_CONSTANTS or node_name.endswith(
_DOMAIN_SUFFIXES
):
return True
case nodes.Subscript():
# Ignore subscripts like dict["key"]
return True
if arg_node.expr.as_string() == "Platform":
return False
case nodes.Tuple():
if allow_iterable:
return all(
@@ -116,7 +108,7 @@ def _check_domain_argument(arg_node: nodes.NodeNG, allow_iterable: bool) -> bool
for element in arg_node.elts
)
return False
return True
class DomainConstantChecker(BaseChecker):
+1 -1
View File
@@ -74,7 +74,7 @@ dependencies = [
"typing-extensions>=4.15.0,<5.0",
"ulid-transform==2.2.9",
"urllib3>=2.0",
"uv==0.11.19",
"uv==0.11.21",
"voluptuous==0.15.2",
"voluptuous-serialize==2.7.0",
"voluptuous-openapi==0.3.0",
+1 -1
View File
@@ -55,7 +55,7 @@ standard-telnetlib==3.13.0
typing-extensions>=4.15.0,<5.0
ulid-transform==2.2.9
urllib3>=2.0
uv==0.11.19
uv==0.11.21
voluptuous-openapi==0.3.0
voluptuous-serialize==2.7.0
voluptuous==0.15.2
+28 -28
View File
@@ -178,7 +178,7 @@ aio-georss-gdacs==0.10
aio-ownet==0.0.5
# homeassistant.components.acaia
aioacaia==0.1.17
aioacaia==0.1.18
# homeassistant.components.airq
aioairq==0.4.7
@@ -190,7 +190,7 @@ aioairzone-cloud==0.7.2
aioairzone==1.0.5
# homeassistant.components.alexa_devices
aioamazondevices==14.0.3
aioamazondevices==14.0.4
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station
@@ -230,13 +230,13 @@ aiobotocore==2.21.1
aiocentriconnect==0.2.3
# homeassistant.components.comelit
aiocomelit==2.0.3
aiocomelit==2.0.5
# homeassistant.components.dhcp
aiodhcpwatcher==1.2.7
# homeassistant.components.dhcp
aiodiscover==3.3.1
aiodiscover==3.3.2
# homeassistant.components.dnsip
aiodns==4.0.4
@@ -372,7 +372,7 @@ aiopnsense==1.0.8
aioptdevices==2026.03.2
# homeassistant.components.acmeda
aiopulse==0.4.6
aiopulse==0.4.7
# homeassistant.components.purpleair
aiopurpleair==2025.08.1
@@ -516,10 +516,10 @@ androidtvremote2==0.3.1
anel-pwrctrl-homeassistant==0.0.1.dev2
# homeassistant.components.anova
anova-wifi==0.17.0
anova-wifi==0.17.1
# homeassistant.components.anthemav
anthemav==1.4.1
anthemav==1.4.2
# homeassistant.components.anthropic
anthropic==0.108.0
@@ -657,7 +657,7 @@ beautifulsoup4==4.13.3
bizkaibus==0.1.1
# homeassistant.components.esphome
bleak-esphome==3.9.1
bleak-esphome==3.9.4
# homeassistant.components.bluetooth
bleak-retry-connector==4.6.1
@@ -669,13 +669,13 @@ bleak==3.0.2
blebox-uniapi==2.5.5
# homeassistant.components.blink
blinkpy==0.25.2
blinkpy==0.25.6
# homeassistant.components.bitcoin
blockchain==1.4.4
# homeassistant.components.blue_current
bluecurrent-api==1.3.2
bluecurrent-api==1.3.3
# homeassistant.components.bluemaestro
bluemaestro-ble==0.4.1
@@ -829,13 +829,13 @@ demetriek==1.3.0
denon-rs232==4.1.0
# homeassistant.components.denonavr
denonavr==1.3.2
denonavr==1.3.3
# homeassistant.components.devialet
devialet==1.5.7
# homeassistant.components.devolo_home_control
devolo-home-control-api==0.19.0
devolo-home-control-api==0.19.1
# homeassistant.components.devolo_home_network
devolo-plc-api==1.5.1
@@ -889,7 +889,7 @@ ecoaliface==0.4.0
egauge-async==0.4.0
# homeassistant.components.eheimdigital
eheimdigital==1.6.0
eheimdigital==1.7.0
# homeassistant.components.ekeybionyx
ekey-bionyxpy==1.0.1
@@ -1081,7 +1081,7 @@ gcal-sync==8.0.0
genie-partner-sdk==1.0.11
# homeassistant.components.geniushub
geniushub-client==0.7.1
geniushub-client==0.7.4
# homeassistant.components.geocaching
geocachingapi==0.3.0
@@ -1522,7 +1522,7 @@ lupupy==0.3.2
lw12==0.9.2
# homeassistant.components.scrape
lxml==6.0.1
lxml==6.1.1
# homeassistant.components.matrix
matrix-nio==0.25.2
@@ -1948,7 +1948,7 @@ py-schluter==0.1.7
py-sucks==0.9.11
# homeassistant.components.synology_dsm
py-synologydsm-api==2.9.0
py-synologydsm-api==2.10.0
# homeassistant.components.unifi_access
py-unifi-access==1.3.0
@@ -2151,7 +2151,7 @@ pyegps==0.2.5
pyemoncms==0.1.3
# homeassistant.components.enphase_envoy
pyenphase==2.4.8
pyenphase==2.4.9
# homeassistant.components.envertech_evt800
pyenvertechevt800==0.2.4
@@ -2238,7 +2238,7 @@ pyialarm==2.2.0
pyicloud==2.4.1
# homeassistant.components.imou
pyimouapi==1.2.7
pyimouapi==1.2.8
# homeassistant.components.insteon
pyinsteon==1.6.4
@@ -2262,7 +2262,7 @@ pyiqvia==2022.04.0
pyirishrail==0.0.2
# homeassistant.components.iskra
pyiskra==0.1.27
pyiskra==0.1.29
# homeassistant.components.iss
pyiss==1.0.1
@@ -2495,10 +2495,10 @@ pyqwikswitch==0.93
pyrail==0.4.1
# homeassistant.components.rainbird
pyrainbird==6.3.0
pyrainbird==6.3.1
# homeassistant.components.playstation_network
pyrate-limiter==4.2.0
pyrate-limiter==4.4.0
# homeassistant.components.recswitch
pyrecswitch==1.0.2
@@ -2684,7 +2684,7 @@ python-join-api==0.1.1
python-kasa[speedups]==0.10.2
# homeassistant.components.linkplay
python-linkplay==0.2.12
python-linkplay==0.2.14
# homeassistant.components.melcloud
python-melcloud==0.1.3
@@ -2860,7 +2860,7 @@ pyzerproc==0.4.8
qbittorrent-api==2026.5.1
# homeassistant.components.qbus
qbusmqttapi==1.5.0
qbusmqttapi==1.5.1
# homeassistant.components.qingping
qingping-ble==1.1.5
@@ -2902,7 +2902,7 @@ renault-api==0.5.12
renson-endura-delta==1.7.2
# homeassistant.components.reolink
reolink-aio==0.21.0
reolink-aio==0.21.1
# homeassistant.components.radio_frequency
rf-protocols==4.2.0
@@ -3046,7 +3046,7 @@ slixmpp==1.13.2
smart-meter-texas==0.5.5
# homeassistant.components.snapcast
snapcast==2.3.7
snapcast==2.3.8
# homeassistant.components.sonos
soco==0.31.1
@@ -3100,7 +3100,7 @@ starlink-grpc-core==1.2.5
statsd==3.2.1
# homeassistant.components.trane
steamloop==1.2.0
steamloop==1.2.1
# homeassistant.components.steam_online
steamodd==4.21
@@ -3255,7 +3255,7 @@ uasiren==0.0.1
uhooapi==1.2.8
# homeassistant.components.unifiprotect
uiprotect==13.1.1
uiprotect==13.1.2
# homeassistant.components.landisgyr_heat_meter
ultraheat-api==0.6.1
@@ -3423,7 +3423,7 @@ yalesmartalarmclient==0.4.3
# homeassistant.components.august
# homeassistant.components.yale
# homeassistant.components.yalexs_ble
yalexs-ble==3.3.0
yalexs-ble==3.3.1
# homeassistant.components.august
# homeassistant.components.yale
+2 -2
View File
@@ -19,7 +19,7 @@ mock-open==1.4.0
mypy==2.1.0
prek==0.2.28
pydantic==2.13.4
pylint==4.0.5
pylint==4.0.6
pylint-per-file-ignores==3.2.1
pipdeptree==2.26.1
pytest-asyncio==1.4.0
@@ -37,7 +37,7 @@ pytest==9.0.3
requests==2.34.2
requests-mock==1.12.1
respx==0.23.1
syrupy==5.3.1
syrupy==5.3.2
tqdm==4.67.1
types-aiofiles==24.1.0.20250822
types-atomicwrites==1.4.5.1
+44
View File
@@ -1,6 +1,7 @@
"""Tests for calendar platform of Holiday integration."""
from datetime import datetime, timedelta
import logging
from freezegun.api import FrozenDateTimeFactory
from holidays import CATHOLIC
@@ -17,6 +18,7 @@ from homeassistant.components.holiday.const import (
)
from homeassistant.const import CONF_COUNTRY
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
@@ -431,3 +433,45 @@ async def test_categories(
]
}
}
async def test_no_update_when_disabled(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
entity_registry: er.EntityRegistry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that a disabled calendar entity does not trigger updates."""
zone = await dt_util.async_get_time_zone("US/Hawaii")
freezer.move_to(datetime(2023, 1, 1, 0, 1, 1, tzinfo=zone))
config_entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_COUNTRY: "US", CONF_PROVINCE: "AK"},
title="United States, AK",
)
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
await async_setup_component(hass, "calendar", {})
await hass.async_block_till_done()
entity_id = "calendar.united_states_ak"
state = hass.states.get(entity_id)
assert state is not None
entity_registry.async_update_entity(
entity_id, disabled_by=er.RegistryEntryDisabler.USER
)
await hass.async_block_till_done()
with caplog.at_level(logging.WARNING, logger="homeassistant.helpers.entity"):
freezer.move_to(datetime(2023, 1, 2, 0, 1, 1, tzinfo=zone))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (
"incorrectly being triggered for updates while it is disabled"
not in caplog.text
)
@@ -22,7 +22,7 @@ async def test_hmip_load_all_supported_devices(
test_devices=None, test_groups=None
)
assert len(mock_hap.hmip_device_by_entity_id) == 384
assert len(mock_hap.hmip_device_by_entity_id) == 385
async def test_hmip_remove_device(

Some files were not shown because too many files have changed in this diff Show More