From ee5e3f7691183025090f711335681d0ee1588146 Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Sat, 5 Apr 2025 18:07:06 +0200 Subject: [PATCH 01/47] Add error details in remote calendar flow (#141753) * Add error details in remote calendar flow * no args * adjust * json * Apply suggestions * remove description placeholder --- homeassistant/components/remote_calendar/config_flow.py | 5 ++++- homeassistant/components/remote_calendar/strings.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/remote_calendar/config_flow.py b/homeassistant/components/remote_calendar/config_flow.py index cc9f45e2767..802a7eb7cea 100644 --- a/homeassistant/components/remote_calendar/config_flow.py +++ b/homeassistant/components/remote_calendar/config_flow.py @@ -69,7 +69,10 @@ class RemoteCalendarConfigFlow(ConfigFlow, domain=DOMAIN): ) except CalendarParseError as err: errors["base"] = "invalid_ics_file" - _LOGGER.debug("Invalid .ics file: %s", err) + _LOGGER.error("Error reading the calendar information: %s", err.message) + _LOGGER.debug( + "Additional calendar error detail: %s", str(err.detailed_error) + ) else: return self.async_create_entry( title=user_input[CONF_CALENDAR_NAME], data=user_input diff --git a/homeassistant/components/remote_calendar/strings.json b/homeassistant/components/remote_calendar/strings.json index fff2d4abbb3..ef7f20d4699 100644 --- a/homeassistant/components/remote_calendar/strings.json +++ b/homeassistant/components/remote_calendar/strings.json @@ -20,7 +20,7 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "forbidden": "The server understood the request but refuses to authorize it.", - "invalid_ics_file": "[%key:component::local_calendar::config::error::invalid_ics_file%]" + "invalid_ics_file": "There was a problem reading the calendar information. See the error log for additional details." } }, "exceptions": { From b38016425f6427d45294326ead7673d81e992cba Mon Sep 17 00:00:00 2001 From: Luke Lashley Date: Fri, 4 Apr 2025 22:58:46 -0400 Subject: [PATCH 02/47] Update Roborock map more consistently on state change (#142228) * update map more consistently on state change * Makecoordinator keep track of last_updated_state --- homeassistant/components/roborock/coordinator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/roborock/coordinator.py b/homeassistant/components/roborock/coordinator.py index cc0bee1cd5f..4e59a092e0a 100644 --- a/homeassistant/components/roborock/coordinator.py +++ b/homeassistant/components/roborock/coordinator.py @@ -153,6 +153,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): ImageConfig(scale=MAP_SCALE), [], ) + self.last_update_state: str | None = None @cached_property def dock_device_info(self) -> DeviceInfo: @@ -291,7 +292,6 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): async def _async_update_data(self) -> DeviceProp: """Update data via library.""" - previous_state = self.roborock_device_info.props.status.state_name try: # Update device props and standard api information await self._update_device_prop() @@ -308,7 +308,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): and (dt_util.utcnow() - self.maps[self.current_map].last_updated) > IMAGE_CACHE_INTERVAL ) - or previous_state != new_status.state_name + or self.last_update_state != new_status.state_name ): try: await self.update_map() @@ -330,6 +330,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): self.update_interval = V1_CLOUD_NOT_CLEANING_INTERVAL else: self.update_interval = V1_LOCAL_NOT_CLEANING_INTERVAL + self.last_update_state = self.roborock_device_info.props.status.state_name return self.roborock_device_info.props def _set_current_map(self) -> None: From e90ba4055385dc5f705d95e25b4836e49ddeb9b4 Mon Sep 17 00:00:00 2001 From: Andre Lengwenus Date: Sat, 5 Apr 2025 20:48:16 +0200 Subject: [PATCH 03/47] Add SensorDeviceClass and unit for LCN CO2 sensor. (#142320) Add SesnorDeviceClass and unit for LCN CO2 sensor. --- homeassistant/components/lcn/sensor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/lcn/sensor.py b/homeassistant/components/lcn/sensor.py index 7783df8679a..0c78ea6637a 100644 --- a/homeassistant/components/lcn/sensor.py +++ b/homeassistant/components/lcn/sensor.py @@ -13,6 +13,7 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + CONCENTRATION_PARTS_PER_MILLION, CONF_DOMAIN, CONF_ENTITIES, CONF_SOURCE, @@ -49,6 +50,7 @@ DEVICE_CLASS_MAPPING = { pypck.lcn_defs.VarUnit.METERPERSECOND: SensorDeviceClass.SPEED, pypck.lcn_defs.VarUnit.VOLT: SensorDeviceClass.VOLTAGE, pypck.lcn_defs.VarUnit.AMPERE: SensorDeviceClass.CURRENT, + pypck.lcn_defs.VarUnit.PPM: SensorDeviceClass.CO2, } UNIT_OF_MEASUREMENT_MAPPING = { @@ -60,6 +62,7 @@ UNIT_OF_MEASUREMENT_MAPPING = { pypck.lcn_defs.VarUnit.METERPERSECOND: UnitOfSpeed.METERS_PER_SECOND, pypck.lcn_defs.VarUnit.VOLT: UnitOfElectricPotential.VOLT, pypck.lcn_defs.VarUnit.AMPERE: UnitOfElectricCurrent.AMPERE, + pypck.lcn_defs.VarUnit.PPM: CONCENTRATION_PARTS_PER_MILLION, } From 8baf61031d7bd1f2c741d48a2dc2e6752be8eeb1 Mon Sep 17 00:00:00 2001 From: tronikos Date: Sat, 5 Apr 2025 02:03:20 -0700 Subject: [PATCH 04/47] Bump opower to 0.10.0 (#142321) --- homeassistant/components/opower/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/opower/manifest.json b/homeassistant/components/opower/manifest.json index 2da4511c0aa..e691d01257a 100644 --- a/homeassistant/components/opower/manifest.json +++ b/homeassistant/components/opower/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/opower", "iot_class": "cloud_polling", "loggers": ["opower"], - "requirements": ["opower==0.9.0"] + "requirements": ["opower==0.10.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index b6ee4cc0c00..3c74f269f59 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1607,7 +1607,7 @@ openwrt-luci-rpc==1.1.17 openwrt-ubus-rpc==0.0.2 # homeassistant.components.opower -opower==0.9.0 +opower==0.10.0 # homeassistant.components.oralb oralb-ble==0.17.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f86b6bba123..bcaf704c4fd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1341,7 +1341,7 @@ openhomedevice==2.2.0 openwebifpy==4.3.1 # homeassistant.components.opower -opower==0.9.0 +opower==0.10.0 # homeassistant.components.oralb oralb-ble==0.17.6 From 5f262267123aa6d9484d360358adbf50a627215e Mon Sep 17 00:00:00 2001 From: tronikos Date: Sat, 5 Apr 2025 11:49:38 -0700 Subject: [PATCH 05/47] Add a description for the enable_google_search_tool option in Google AI (#142322) * Add a description for the enable_google_search_tool option in Google AI * Use quotes --- .../components/google_generative_ai_conversation/strings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/google_generative_ai_conversation/strings.json b/homeassistant/components/google_generative_ai_conversation/strings.json index cd7af711d3a..2697f30eda0 100644 --- a/homeassistant/components/google_generative_ai_conversation/strings.json +++ b/homeassistant/components/google_generative_ai_conversation/strings.json @@ -40,7 +40,8 @@ "enable_google_search_tool": "Enable Google Search tool" }, "data_description": { - "prompt": "Instruct how the LLM should respond. This can be a template." + "prompt": "Instruct how the LLM should respond. This can be a template.", + "enable_google_search_tool": "Only works with \"No control\" in the \"Control Home Assistant\" setting. See docs for a workaround using it with \"Assist\"." } } }, From cdd4894e3059a30ad0ebb456556e6731ec7eb660 Mon Sep 17 00:00:00 2001 From: Luke Lashley Date: Sat, 5 Apr 2025 19:45:00 -0400 Subject: [PATCH 06/47] Check that the current roboorck map exists before updating it. (#142341) * Check that the current map exists * Add a few extra checks * Update coordinator.py Co-authored-by: Allen Porter * fixlint --------- Co-authored-by: Allen Porter --- .../components/roborock/coordinator.py | 18 +++++++++++------- homeassistant/components/roborock/sensor.py | 10 ++++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/roborock/coordinator.py b/homeassistant/components/roborock/coordinator.py index 4e59a092e0a..2439a4f904a 100644 --- a/homeassistant/components/roborock/coordinator.py +++ b/homeassistant/components/roborock/coordinator.py @@ -226,7 +226,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): """Update the currently selected map.""" # The current map was set in the props update, so these can be done without # worry of applying them to the wrong map. - if self.current_map is None: + if self.current_map is None or self.current_map not in self.maps: # This exists as a safeguard/ to keep mypy happy. return try: @@ -302,13 +302,17 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): # If the vacuum is currently cleaning and it has been IMAGE_CACHE_INTERVAL # since the last map update, you can update the map. new_status = self.roborock_device_info.props.status - if self.current_map is not None and ( - ( - new_status.in_cleaning - and (dt_util.utcnow() - self.maps[self.current_map].last_updated) - > IMAGE_CACHE_INTERVAL + if ( + self.current_map is not None + and (current_map := self.maps.get(self.current_map)) + and ( + ( + new_status.in_cleaning + and (dt_util.utcnow() - current_map.last_updated) + > IMAGE_CACHE_INTERVAL + ) + or self.last_update_state != new_status.state_name ) - or self.last_update_state != new_status.state_name ): try: await self.update_map() diff --git a/homeassistant/components/roborock/sensor.py b/homeassistant/components/roborock/sensor.py index 33ecaf74d4f..a007d6fa457 100644 --- a/homeassistant/components/roborock/sensor.py +++ b/homeassistant/components/roborock/sensor.py @@ -381,7 +381,10 @@ class RoborockCurrentRoom(RoborockCoordinatedEntityV1, SensorEntity): @property def options(self) -> list[str]: """Return the currently valid rooms.""" - if self.coordinator.current_map is not None: + if ( + self.coordinator.current_map is not None + and self.coordinator.current_map in self.coordinator.maps + ): return list( self.coordinator.maps[self.coordinator.current_map].rooms.values() ) @@ -390,7 +393,10 @@ class RoborockCurrentRoom(RoborockCoordinatedEntityV1, SensorEntity): @property def native_value(self) -> str | None: """Return the value reported by the sensor.""" - if self.coordinator.current_map is not None: + if ( + self.coordinator.current_map is not None + and self.coordinator.current_map in self.coordinator.maps + ): return self.coordinator.maps[self.coordinator.current_map].current_room return None From 2c855a398616d0f2e6b845ca9e936395e211c872 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 5 Apr 2025 22:38:11 +0200 Subject: [PATCH 07/47] Limit mqtt info logging for discovery of new components (#142344) * Limit mqtt info logging for discovery of new component * Keep in bail out, when debug logging is not enabled --- homeassistant/components/mqtt/discovery.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index a14240ce008..a527e712615 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -154,18 +154,14 @@ def get_origin_support_url(discovery_payload: MQTTDiscoveryPayload) -> str | Non @callback def async_log_discovery_origin_info( - message: str, discovery_payload: MQTTDiscoveryPayload, level: int = logging.INFO + message: str, discovery_payload: MQTTDiscoveryPayload ) -> None: """Log information about the discovery and origin.""" - # We only log origin info once per device discovery - if not _LOGGER.isEnabledFor(level): - # bail out early if logging is disabled + if not _LOGGER.isEnabledFor(logging.DEBUG): + # bail out early if debug logging is disabled return - _LOGGER.log( - level, - "%s%s", - message, - get_origin_log_string(discovery_payload, include_url=True), + _LOGGER.debug( + "%s%s", message, get_origin_log_string(discovery_payload, include_url=True) ) @@ -562,7 +558,7 @@ async def async_start( # noqa: C901 elif already_discovered: # Dispatch update message = f"Component has already been discovered: {component} {discovery_id}, sending update" - async_log_discovery_origin_info(message, payload, logging.DEBUG) + async_log_discovery_origin_info(message, payload) async_dispatcher_send( hass, MQTT_DISCOVERY_UPDATED.format(*discovery_hash), payload ) From 9fb7ee676e0657fce0401c9d8ae42e2be7be281b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 5 Apr 2025 11:07:21 -1000 Subject: [PATCH 08/47] Bump flux_led to 1.2.0 (#142362) changelog: https://github.com/lightinglibs/flux_led/compare/1.1.3...1.2.0 --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index fcb16c9742b..2c5e1b3839e 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -53,5 +53,5 @@ "documentation": "https://www.home-assistant.io/integrations/flux_led", "iot_class": "local_push", "loggers": ["flux_led"], - "requirements": ["flux-led==1.1.3"] + "requirements": ["flux-led==1.2.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 3c74f269f59..4cedaaaa6ca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -944,7 +944,7 @@ flexit_bacnet==2.2.3 flipr-api==1.6.1 # homeassistant.components.flux_led -flux-led==1.1.3 +flux-led==1.2.0 # homeassistant.components.homekit # homeassistant.components.recorder diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bcaf704c4fd..a5a15d6a7b5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -804,7 +804,7 @@ flexit_bacnet==2.2.3 flipr-api==1.6.1 # homeassistant.components.flux_led -flux-led==1.1.3 +flux-led==1.2.0 # homeassistant.components.homekit # homeassistant.components.recorder From 8ff879df22353eb52e65c69e328d50fc61810b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sun, 6 Apr 2025 10:01:26 +0200 Subject: [PATCH 09/47] Update aioairzone to v1.0.0 (#142385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/airzone/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../airzone/snapshots/test_diagnostics.ambr | 16 ++++++++++++++++ tests/components/airzone/util.py | 10 ++++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index 95ed9d200f4..1b636de0a47 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -11,5 +11,5 @@ "documentation": "https://www.home-assistant.io/integrations/airzone", "iot_class": "local_polling", "loggers": ["aioairzone"], - "requirements": ["aioairzone==0.9.9"] + "requirements": ["aioairzone==1.0.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 4cedaaaa6ca..1363bbd3721 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -182,7 +182,7 @@ aioairq==0.4.4 aioairzone-cloud==0.6.11 # homeassistant.components.airzone -aioairzone==0.9.9 +aioairzone==1.0.0 # homeassistant.components.ambient_network # homeassistant.components.ambient_station diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a5a15d6a7b5..4b41b99bd62 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -170,7 +170,7 @@ aioairq==0.4.4 aioairzone-cloud==0.6.11 # homeassistant.components.airzone -aioairzone==0.9.9 +aioairzone==1.0.0 # homeassistant.components.ambient_network # homeassistant.components.ambient_station diff --git a/tests/components/airzone/snapshots/test_diagnostics.ambr b/tests/components/airzone/snapshots/test_diagnostics.ambr index b4976c07e1b..09dea8c354c 100644 --- a/tests/components/airzone/snapshots/test_diagnostics.ambr +++ b/tests/components/airzone/snapshots/test_diagnostics.ambr @@ -44,9 +44,11 @@ }), dict({ 'air_demand': 1, + 'battery': 99, 'coldStage': 1, 'coldStages': 1, 'coldangle': 2, + 'coverage': 72, 'errors': list([ ]), 'floor_demand': 1, @@ -73,9 +75,11 @@ }), dict({ 'air_demand': 0, + 'battery': 35, 'coldStage': 1, 'coldStages': 1, 'coldangle': 0, + 'coverage': 60, 'errors': list([ ]), 'floor_demand': 0, @@ -100,9 +104,11 @@ }), dict({ 'air_demand': 0, + 'battery': 25, 'coldStage': 1, 'coldStages': 1, 'coldangle': 0, + 'coverage': 88, 'errors': list([ dict({ 'Zone': 'Low battery', @@ -130,9 +136,11 @@ }), dict({ 'air_demand': 0, + 'battery': 80, 'coldStage': 1, 'coldStages': 1, 'coldangle': 0, + 'coverage': 66, 'errors': list([ ]), 'floor_demand': 0, @@ -497,9 +505,11 @@ 'temp-set': 19.2, 'temp-step': 0.5, 'temp-unit': 0, + 'thermostat-battery': 99, 'thermostat-fw': '3.33', 'thermostat-model': 'Think (Radio)', 'thermostat-radio': True, + 'thermostat-signal': 72, }), '1:3': dict({ 'absolute-temp-max': 30.0, @@ -546,9 +556,11 @@ 'temp-set': 19.3, 'temp-step': 0.5, 'temp-unit': 0, + 'thermostat-battery': 35, 'thermostat-fw': '3.33', 'thermostat-model': 'Think (Radio)', 'thermostat-radio': True, + 'thermostat-signal': 60, }), '1:4': dict({ 'absolute-temp-max': 86.0, @@ -597,9 +609,11 @@ 'temp-set': 66.9, 'temp-step': 1.0, 'temp-unit': 1, + 'thermostat-battery': 25, 'thermostat-fw': '3.33', 'thermostat-model': 'Think (Radio)', 'thermostat-radio': True, + 'thermostat-signal': 88, }), '1:5': dict({ 'absolute-temp-max': 30.0, @@ -645,9 +659,11 @@ 'temp-set': 19.5, 'temp-step': 0.5, 'temp-unit': 0, + 'thermostat-battery': 80, 'thermostat-fw': '3.33', 'thermostat-model': 'Think (Radio)', 'thermostat-radio': True, + 'thermostat-signal': 66, }), '2:1': dict({ 'absolute-temp-max': 30.0, diff --git a/tests/components/airzone/util.py b/tests/components/airzone/util.py index 50d1964924d..944ca83d053 100644 --- a/tests/components/airzone/util.py +++ b/tests/components/airzone/util.py @@ -11,12 +11,14 @@ from aioairzone.const import ( API_ACS_SET_POINT, API_ACS_TEMP, API_AIR_DEMAND, + API_BATTERY, API_COLD_ANGLE, API_COLD_STAGE, API_COLD_STAGES, API_COOL_MAX_TEMP, API_COOL_MIN_TEMP, API_COOL_SET_POINT, + API_COVERAGE, API_DATA, API_ERRORS, API_FLOOR_DEMAND, @@ -119,6 +121,8 @@ HVAC_MOCK = { API_THERMOS_TYPE: 4, API_THERMOS_FIRMWARE: "3.33", API_THERMOS_RADIO: 1, + API_BATTERY: 99, + API_COVERAGE: 72, API_ON: 1, API_MAX_TEMP: 30, API_MIN_TEMP: 15, @@ -147,6 +151,8 @@ HVAC_MOCK = { API_THERMOS_TYPE: 4, API_THERMOS_FIRMWARE: "3.33", API_THERMOS_RADIO: 1, + API_BATTERY: 35, + API_COVERAGE: 60, API_ON: 1, API_MAX_TEMP: 30, API_MIN_TEMP: 15, @@ -173,6 +179,8 @@ HVAC_MOCK = { API_THERMOS_TYPE: 4, API_THERMOS_FIRMWARE: "3.33", API_THERMOS_RADIO: 1, + API_BATTERY: 25, + API_COVERAGE: 88, API_ON: 0, API_MAX_TEMP: 86, API_MIN_TEMP: 59, @@ -203,6 +211,8 @@ HVAC_MOCK = { API_THERMOS_TYPE: 4, API_THERMOS_FIRMWARE: "3.33", API_THERMOS_RADIO: 1, + API_BATTERY: 80, + API_COVERAGE: 66, API_ON: 0, API_MAX_TEMP: 30, API_MIN_TEMP: 15, From 5d92a047326c86d109d7dd4369785a71d3b1bf73 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Sun, 6 Apr 2025 10:04:18 +0200 Subject: [PATCH 10/47] Only load files ending `.metadata.json` in WebDAV (#142388) --- homeassistant/components/webdav/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/webdav/backup.py b/homeassistant/components/webdav/backup.py index fb2927a58bb..a9afb5fe930 100644 --- a/homeassistant/components/webdav/backup.py +++ b/homeassistant/components/webdav/backup.py @@ -231,7 +231,7 @@ class WebDavBackupAgent(BackupAgent): return { metadata_content.backup_id: metadata_content for file_name in files - if file_name.endswith(".json") + if file_name.endswith(".metadata.json") if (metadata_content := await _download_metadata(file_name)) } From b7f0e877f0b339a073b008d30b93872ea589bec0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Apr 2025 01:00:41 -1000 Subject: [PATCH 11/47] Bump aioesphomeapi to 29.9.0 (#142393) changelog: https://github.com/esphome/aioesphomeapi/compare/v29.8.0...v29.9.0 fixes #142381 --- homeassistant/components/esphome/manager.py | 4 ++-- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/esphome/manager.py b/homeassistant/components/esphome/manager.py index 7ce96a0f510..56c2998a3cc 100644 --- a/homeassistant/components/esphome/manager.py +++ b/homeassistant/components/esphome/manager.py @@ -13,7 +13,7 @@ from aioesphomeapi import ( APIConnectionError, APIVersion, DeviceInfo as EsphomeDeviceInfo, - EncryptionHelloAPIError, + EncryptionPlaintextAPIError, EntityInfo, HomeassistantServiceCall, InvalidAuthAPIError, @@ -571,7 +571,7 @@ class ESPHomeManager: if isinstance( err, ( - EncryptionHelloAPIError, + EncryptionPlaintextAPIError, RequiresEncryptionAPIError, InvalidEncryptionKeyAPIError, InvalidAuthAPIError, diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 954968f5e2c..31ed27c02ae 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -16,7 +16,7 @@ "loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"], "mqtt": ["esphome/discover/#"], "requirements": [ - "aioesphomeapi==29.8.0", + "aioesphomeapi==29.9.0", "esphome-dashboard-api==1.2.3", "bleak-esphome==2.12.0" ], diff --git a/requirements_all.txt b/requirements_all.txt index 1363bbd3721..9c678a8b10b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -243,7 +243,7 @@ aioelectricitymaps==0.4.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==29.8.0 +aioesphomeapi==29.9.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4b41b99bd62..7319b2a3e69 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -231,7 +231,7 @@ aioelectricitymaps==0.4.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==29.8.0 +aioesphomeapi==29.9.0 # homeassistant.components.flo aioflo==2021.11.0 From 87baaf4255c8e2d1b1a52abc662a6740a4c1c36e Mon Sep 17 00:00:00 2001 From: tronikos Date: Mon, 7 Apr 2025 23:58:43 -0700 Subject: [PATCH 12/47] Bump opower to 0.11.1 (#142395) * Bump opower to 0.10.1 * opower==0.11.0 * opower==0.11.1 --------- Co-authored-by: J. Nick Koston --- homeassistant/components/opower/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/opower/manifest.json b/homeassistant/components/opower/manifest.json index e691d01257a..2cc942363cf 100644 --- a/homeassistant/components/opower/manifest.json +++ b/homeassistant/components/opower/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/opower", "iot_class": "cloud_polling", "loggers": ["opower"], - "requirements": ["opower==0.10.0"] + "requirements": ["opower==0.11.1"] } diff --git a/requirements_all.txt b/requirements_all.txt index 9c678a8b10b..69d03f97152 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1607,7 +1607,7 @@ openwrt-luci-rpc==1.1.17 openwrt-ubus-rpc==0.0.2 # homeassistant.components.opower -opower==0.10.0 +opower==0.11.1 # homeassistant.components.oralb oralb-ble==0.17.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7319b2a3e69..be52eef360c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1341,7 +1341,7 @@ openhomedevice==2.2.0 openwebifpy==4.3.1 # homeassistant.components.opower -opower==0.10.0 +opower==0.11.1 # homeassistant.components.oralb oralb-ble==0.17.6 From 2322b0b65fdd12092522ef988a778e2f3ea1cd75 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Fri, 11 Apr 2025 11:32:19 +0200 Subject: [PATCH 13/47] Add exceptions translation to SamsungTV (#142406) * Add exceptions translation to SmasungTV * Update strings.json Co-authored-by: Franck Nijhof --------- Co-authored-by: Franck Nijhof --- homeassistant/components/samsungtv/device_trigger.py | 7 ++++++- homeassistant/components/samsungtv/entity.py | 4 +++- homeassistant/components/samsungtv/strings.json | 8 ++++++++ tests/components/samsungtv/test_remote.py | 7 ++++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/samsungtv/device_trigger.py b/homeassistant/components/samsungtv/device_trigger.py index 2b3d9dbe666..749276b61c4 100644 --- a/homeassistant/components/samsungtv/device_trigger.py +++ b/homeassistant/components/samsungtv/device_trigger.py @@ -15,6 +15,7 @@ from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType from . import trigger +from .const import DOMAIN from .helpers import ( async_get_client_by_device_entry, async_get_device_entry_by_device_id, @@ -75,4 +76,8 @@ async def async_attach_trigger( hass, trigger_config, action, trigger_info ) - raise HomeAssistantError(f"Unhandled trigger type {trigger_type}") + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="unhandled_trigger_type", + translation_placeholders={"trigger_type": trigger_type}, + ) diff --git a/homeassistant/components/samsungtv/entity.py b/homeassistant/components/samsungtv/entity.py index 61aa8abce53..f3ecee373e3 100644 --- a/homeassistant/components/samsungtv/entity.py +++ b/homeassistant/components/samsungtv/entity.py @@ -106,5 +106,7 @@ class SamsungTVEntity(CoordinatorEntity[SamsungTVDataUpdateCoordinator], Entity) self.entity_id, ) raise HomeAssistantError( - f"Entity {self.entity_id} does not support this service." + translation_domain=DOMAIN, + translation_key="service_unsupported", + translation_placeholders={"entity": self.entity_id}, ) diff --git a/homeassistant/components/samsungtv/strings.json b/homeassistant/components/samsungtv/strings.json index c9d08f756d0..65ed7a3fc23 100644 --- a/homeassistant/components/samsungtv/strings.json +++ b/homeassistant/components/samsungtv/strings.json @@ -47,5 +47,13 @@ "trigger_type": { "samsungtv.turn_on": "Device is requested to turn on" } + }, + "exceptions": { + "unhandled_trigger_type": { + "message": "Unhandled trigger type {trigger_type}." + }, + "service_unsupported": { + "message": "Entity {entity} does not support this action." + } } } diff --git a/tests/components/samsungtv/test_remote.py b/tests/components/samsungtv/test_remote.py index 854c92207bf..da7871ca9c5 100644 --- a/tests/components/samsungtv/test_remote.py +++ b/tests/components/samsungtv/test_remote.py @@ -122,9 +122,14 @@ async def test_turn_on_wol(hass: HomeAssistant) -> None: async def test_turn_on_without_turnon(hass: HomeAssistant, remote: Mock) -> None: """Test turn on.""" await setup_samsungtv_entry(hass, MOCK_CONFIG) - with pytest.raises(HomeAssistantError, match="does not support this service"): + with pytest.raises(HomeAssistantError) as exc_info: await hass.services.async_call( REMOTE_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_ID}, True ) # nothing called as not supported feature assert remote.control.call_count == 0 + assert exc_info.value.translation_domain == DOMAIN + assert exc_info.value.translation_key == "service_unsupported" + assert exc_info.value.translation_placeholders == { + "entity": ENTITY_ID, + } From fcb8cdc146ebaefedd9374e79d2d8de3cc4047f9 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Wed, 9 Apr 2025 16:44:36 +0200 Subject: [PATCH 14/47] Add missing strings to Fritz (#142413) * Add missing strings to Fritz * update quality scale * add common section this avoids later re-structuring and re-translating * fix strings * fix strings * apply review comment --------- Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> --- .../components/fritz/quality_scale.yaml | 4 +- homeassistant/components/fritz/strings.json | 40 ++++++++++++++++--- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/fritz/quality_scale.yaml b/homeassistant/components/fritz/quality_scale.yaml index 805705eb4b4..361d61df6a0 100644 --- a/homeassistant/components/fritz/quality_scale.yaml +++ b/homeassistant/components/fritz/quality_scale.yaml @@ -7,9 +7,7 @@ rules: config-flow-test-coverage: status: todo comment: one coverage miss in line 110 - config-flow: - status: todo - comment: data_description are missing + config-flow: done dependency-transparency: done docs-actions: done docs-high-level-description: done diff --git a/homeassistant/components/fritz/strings.json b/homeassistant/components/fritz/strings.json index 06a07cba79e..6191fc524dd 100644 --- a/homeassistant/components/fritz/strings.json +++ b/homeassistant/components/fritz/strings.json @@ -1,4 +1,11 @@ { + "common": { + "data_description_host": "The hostname or IP address of your FRITZ!Box router.", + "data_description_port": "Leave empty to use the default port.", + "data_description_username": "Username for the FRITZ!Box.", + "data_description_password": "Password for the FRITZ!Box.", + "data_description_ssl": "Use SSL to connect to the FRITZ!Box." + }, "config": { "flow_title": "{name}", "step": { @@ -9,6 +16,11 @@ "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", "ssl": "[%key:common::config_flow::data::ssl%]" + }, + "data_description": { + "username": "[%key:component::fritz::common::data_description_username%]", + "password": "[%key:component::fritz::common::data_description_password%]", + "ssl": "[%key:component::fritz::common::data_description_ssl%]" } }, "reauth_confirm": { @@ -17,6 +29,10 @@ "data": { "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]" + }, + "data_description": { + "username": "[%key:component::fritz::common::data_description_username%]", + "password": "[%key:component::fritz::common::data_description_password%]" } }, "reconfigure": { @@ -28,8 +44,9 @@ "ssl": "[%key:common::config_flow::data::ssl%]" }, "data_description": { - "host": "The hostname or IP address of your FRITZ!Box router.", - "port": "Leave it empty to use the default port." + "host": "[%key:component::fritz::common::data_description_host%]", + "port": "[%key:component::fritz::common::data_description_port%]", + "ssl": "[%key:component::fritz::common::data_description_ssl%]" } }, "user": { @@ -43,8 +60,11 @@ "ssl": "[%key:common::config_flow::data::ssl%]" }, "data_description": { - "host": "The hostname or IP address of your FRITZ!Box router.", - "port": "Leave it empty to use the default port." + "host": "[%key:component::fritz::common::data_description_host%]", + "port": "[%key:component::fritz::common::data_description_port%]", + "username": "[%key:component::fritz::common::data_description_username%]", + "password": "[%key:component::fritz::common::data_description_password%]", + "ssl": "[%key:component::fritz::common::data_description_ssl%]" } } }, @@ -70,6 +90,10 @@ "data": { "consider_home": "Seconds to consider a device at 'home'", "old_discovery": "Enable old discovery method" + }, + "data_description": { + "consider_home": "Time in seconds to consider a device at home. Default is 180 seconds.", + "old_discovery": "Enable old discovery method. This is needed for some scenarios." } } } @@ -169,8 +193,12 @@ "config_entry_not_found": { "message": "Failed to perform action \"{service}\". Config entry for target not found" }, - "service_parameter_unknown": { "message": "Action or parameter unknown" }, - "service_not_supported": { "message": "Action not supported" }, + "service_parameter_unknown": { + "message": "Action or parameter unknown" + }, + "service_not_supported": { + "message": "Action not supported" + }, "error_refresh_hosts_info": { "message": "Error refreshing hosts info" }, From 36ec857523ccc14e43dbb08b03cc1f583a07d6af Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:26:58 +0200 Subject: [PATCH 15/47] Fix reload of AVM FRITZ!Tools when new connected device is detected (#142430) --- homeassistant/components/fritz/button.py | 12 +----------- homeassistant/components/fritz/coordinator.py | 12 +++++++++++- homeassistant/components/fritz/entity.py | 3 +++ homeassistant/components/fritz/switch.py | 10 ---------- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/fritz/button.py b/homeassistant/components/fritz/button.py index 74e8ab5e43e..ddc0be3a6d9 100644 --- a/homeassistant/components/fritz/button.py +++ b/homeassistant/components/fritz/button.py @@ -18,7 +18,7 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, Device from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import BUTTON_TYPE_WOL, CONNECTION_TYPE_LAN, DOMAIN, MeshRoles +from .const import BUTTON_TYPE_WOL, CONNECTION_TYPE_LAN, MeshRoles from .coordinator import ( FRITZ_DATA_KEY, AvmWrapper, @@ -175,16 +175,6 @@ class FritzBoxWOLButton(FritzDeviceBase, ButtonEntity): self._name = f"{self.hostname} Wake on LAN" self._attr_unique_id = f"{self._mac}_wake_on_lan" self._is_available = True - self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, self._mac)}, - default_manufacturer="AVM", - default_model="FRITZ!Box Tracked device", - default_name=device.hostname, - via_device=( - DOMAIN, - avm_wrapper.unique_id, - ), - ) async def async_press(self) -> None: """Press the button.""" diff --git a/homeassistant/components/fritz/coordinator.py b/homeassistant/components/fritz/coordinator.py index d60232ec8ad..c0121ed9aa1 100644 --- a/homeassistant/components/fritz/coordinator.py +++ b/homeassistant/components/fritz/coordinator.py @@ -526,7 +526,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]): def manage_device_info( self, dev_info: Device, dev_mac: str, consider_home: bool ) -> bool: - """Update device lists.""" + """Update device lists and return if device is new.""" _LOGGER.debug("Client dev_info: %s", dev_info) if dev_mac in self._devices: @@ -536,6 +536,16 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]): device = FritzDevice(dev_mac, dev_info.name) device.update(dev_info, consider_home) self._devices[dev_mac] = device + + # manually register device entry for new connected device + dr.async_get(self.hass).async_get_or_create( + config_entry_id=self.config_entry.entry_id, + connections={(CONNECTION_NETWORK_MAC, dev_mac)}, + default_manufacturer="AVM", + default_model="FRITZ!Box Tracked device", + default_name=device.hostname, + via_device=(DOMAIN, self.unique_id), + ) return True async def async_send_signal_device_update(self, new_device: bool) -> None: diff --git a/homeassistant/components/fritz/entity.py b/homeassistant/components/fritz/entity.py index 33eb60d72cf..e8b5c49fd43 100644 --- a/homeassistant/components/fritz/entity.py +++ b/homeassistant/components/fritz/entity.py @@ -26,6 +26,9 @@ class FritzDeviceBase(CoordinatorEntity[AvmWrapper]): self._avm_wrapper = avm_wrapper self._mac: str = device.mac_address self._name: str = device.hostname or DEFAULT_DEVICE_NAME + self._attr_device_info = DeviceInfo( + connections={(dr.CONNECTION_NETWORK_MAC, device.mac_address)} + ) @property def name(self) -> str: diff --git a/homeassistant/components/fritz/switch.py b/homeassistant/components/fritz/switch.py index 8b4816f7451..b627e0150a9 100644 --- a/homeassistant/components/fritz/switch.py +++ b/homeassistant/components/fritz/switch.py @@ -511,16 +511,6 @@ class FritzBoxProfileSwitch(FritzDeviceBase, SwitchEntity): self._name = f"{device.hostname} Internet Access" self._attr_unique_id = f"{self._mac}_internet_access" self._attr_entity_category = EntityCategory.CONFIG - self._attr_device_info = DeviceInfo( - connections={(CONNECTION_NETWORK_MAC, self._mac)}, - default_manufacturer="AVM", - default_model="FRITZ!Box Tracked device", - default_name=device.hostname, - via_device=( - DOMAIN, - avm_wrapper.unique_id, - ), - ) @property def is_on(self) -> bool | None: From d39fa39a035ef09d7c5a448363a4ca35d6fc8cb3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Apr 2025 22:11:15 -1000 Subject: [PATCH 16/47] Fix HKC showing hvac_action as idle when fan is active and heat cool target is off (#142443) * Fix HKC showing hvac_action as idle when fan is active and heat cool target is off fixes #142442 * comment relocation --- .../components/homekit_controller/climate.py | 12 +- .../fixtures/ecobee3_lite.json | 3436 ++++++++++++++++ .../snapshots/test_init.ambr | 3458 +++++++++++++++++ .../homekit_controller/test_climate.py | 23 +- 4 files changed, 6921 insertions(+), 8 deletions(-) create mode 100644 tests/components/homekit_controller/fixtures/ecobee3_lite.json diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 7341bbd3a4a..4c8bf8517be 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -659,13 +659,7 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): # e.g. a thermostat is "heating" a room to 75 degrees Fahrenheit. # Can be 0 - 2 (Off, Heat, Cool) - # If the HVAC is switched off, it must be idle - # This works around a bug in some devices (like Eve radiator valves) that - # return they are heating when they are not. target = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if target == HeatingCoolingTargetValues.OFF: - return HVACAction.IDLE - value = self.service.value(CharacteristicsTypes.HEATING_COOLING_CURRENT) current_hass_value = CURRENT_MODE_HOMEKIT_TO_HASS.get(value) @@ -679,6 +673,12 @@ class HomeKitClimateEntity(HomeKitBaseClimateEntity): ): return HVACAction.FAN + # If the HVAC is switched off, it must be idle + # This works around a bug in some devices (like Eve radiator valves) that + # return they are heating when they are not. + if target == HeatingCoolingTargetValues.OFF: + return HVACAction.IDLE + return current_hass_value @property diff --git a/tests/components/homekit_controller/fixtures/ecobee3_lite.json b/tests/components/homekit_controller/fixtures/ecobee3_lite.json new file mode 100644 index 00000000000..0656ed20fdb --- /dev/null +++ b/tests/components/homekit_controller/fixtures/ecobee3_lite.json @@ -0,0 +1,3436 @@ +[ + { + "aid": 1, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "ecobee3 lite", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Thermostat", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "4.8.70226", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "34AB8811-AC7F-4340-BAC3-FD6A85F9943B", + "iid": 11, + "perms": ["pr", "hd"], + "format": "string", + "value": "4.1;3fac0fb4", + "maxLen": 64 + }, + { + "type": "00000220-0000-1000-8000-0026BB765291", + "iid": 10, + "perms": ["pr", "hd"], + "format": "data", + "value": "u4qz9YgSXzQ=" + }, + { + "type": "000000A6-0000-1000-8000-0026BB765291", + "iid": 9, + "perms": ["pr", "ev"], + "format": "uint32", + "value": 0, + "description": "Accessory Flags" + } + ] + }, + { + "iid": 30, + "type": "000000A2-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000037-0000-1000-8000-0026BB765291", + "iid": 31, + "perms": ["pr"], + "format": "string", + "value": "1.1.0", + "description": "Version", + "maxLen": 64 + } + ] + }, + { + "iid": 16, + "type": "0000004A-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000000F-0000-1000-8000-0026BB765291", + "iid": 17, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Current Heating Cooling State", + "minValue": 0, + "maxValue": 2, + "minStep": 1, + "valid-values": [0, 1, 2] + }, + { + "type": "00000033-0000-1000-8000-0026BB765291", + "iid": 18, + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "value": 0, + "description": "Target Heating Cooling State", + "minValue": 0, + "maxValue": 3, + "minStep": 1, + "valid-values": [0, 1, 2, 3] + }, + { + "type": "00000011-0000-1000-8000-0026BB765291", + "iid": 19, + "perms": ["pr", "ev"], + "format": "float", + "value": 21.2, + "description": "Current Temperature", + "unit": "celsius", + "minValue": 0, + "maxValue": 40.0, + "minStep": 0.1 + }, + { + "type": "00000035-0000-1000-8000-0026BB765291", + "iid": 20, + "perms": ["pr", "pw", "ev"], + "format": "float", + "value": 22.2, + "description": "Target Temperature", + "unit": "celsius", + "minValue": 7.2, + "maxValue": 33.3, + "minStep": 0.1 + }, + { + "type": "00000036-0000-1000-8000-0026BB765291", + "iid": 21, + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "value": 1, + "description": "Temperature Display Units", + "minValue": 0, + "maxValue": 1, + "minStep": 1, + "valid-values": [0, 1] + }, + { + "type": "0000000D-0000-1000-8000-0026BB765291", + "iid": 22, + "perms": ["pr", "pw", "ev"], + "format": "float", + "value": 25.0, + "description": "Cooling Threshold Temperature", + "unit": "celsius", + "minValue": 18.3, + "maxValue": 33.3, + "minStep": 0.1 + }, + { + "type": "00000012-0000-1000-8000-0026BB765291", + "iid": 23, + "perms": ["pr", "pw", "ev"], + "format": "float", + "value": 22.2, + "description": "Heating Threshold Temperature", + "unit": "celsius", + "minValue": 7.2, + "maxValue": 26.1, + "minStep": 0.1 + }, + { + "type": "00000010-0000-1000-8000-0026BB765291", + "iid": 24, + "perms": ["pr", "ev"], + "format": "float", + "value": 45.0, + "description": "Current Relative Humidity", + "unit": "percentage", + "minValue": 0, + "maxValue": 100.0, + "minStep": 1.0 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 27, + "perms": ["pr"], + "format": "string", + "value": "Thermostat", + "description": "Name", + "maxLen": 64 + }, + { + "type": "000000BF-0000-1000-8000-0026BB765291", + "iid": 75, + "perms": ["pr", "pw", "ev"], + "format": "uint8", + "value": 0, + "description": "Target Fan State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "000000AF-0000-1000-8000-0026BB765291", + "iid": 76, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Current Fan State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "B7DDB9A3-54BB-4572-91D2-F1F5B0510F8C", + "iid": 33, + "perms": ["pr"], + "format": "uint8", + "value": 3, + "minValue": 0, + "maxValue": 3, + "minStep": 1 + }, + { + "type": "E4489BBC-5227-4569-93E5-B345E3E5508F", + "iid": 34, + "perms": ["pr", "pw"], + "format": "float", + "value": 22.2, + "unit": "celsius", + "minValue": 7.2, + "maxValue": 26.1, + "minStep": 0.1 + }, + { + "type": "7D381BAA-20F9-40E5-9BE9-AEB92D4BECEF", + "iid": 35, + "perms": ["pr", "pw"], + "format": "float", + "value": 25.0, + "unit": "celsius", + "minValue": 18.3, + "maxValue": 33.3, + "minStep": 0.1 + }, + { + "type": "73AAB542-892A-4439-879A-D2A883724B69", + "iid": 36, + "perms": ["pr", "pw"], + "format": "float", + "value": 17.8, + "unit": "celsius", + "minValue": 7.2, + "maxValue": 26.1, + "minStep": 0.1 + }, + { + "type": "5DA985F0-898A-4850-B987-B76C6C78D670", + "iid": 37, + "perms": ["pr", "pw"], + "format": "float", + "value": 25.6, + "unit": "celsius", + "minValue": 18.3, + "maxValue": 33.3, + "minStep": 0.1 + }, + { + "type": "05B97374-6DC0-439B-A0FA-CA33F612D425", + "iid": 38, + "perms": ["pr", "pw"], + "format": "float", + "value": 20.0, + "unit": "celsius", + "minValue": 7.2, + "maxValue": 26.1, + "minStep": 0.1 + }, + { + "type": "A251F6E7-AC46-4190-9C5D-3D06277BDF9F", + "iid": 39, + "perms": ["pr", "pw"], + "format": "float", + "value": 24.4, + "unit": "celsius", + "minValue": 18.3, + "maxValue": 33.3, + "minStep": 0.1 + }, + { + "type": "1B300BC2-CFFC-47FF-89F9-BD6CCF5F2853", + "iid": 40, + "perms": ["pw"], + "format": "uint8", + "minValue": 0, + "maxValue": 3, + "minStep": 1 + }, + { + "type": "1621F556-1367-443C-AF19-82AF018E99DE", + "iid": 41, + "perms": ["pr", "pw"], + "format": "string", + "value": "2025-04-06T23:30:00-05:00R", + "maxLen": 64 + }, + { + "type": "FA128DE6-9D7D-49A4-B6D8-4E4E234DEE38", + "iid": 48, + "perms": ["pw"], + "format": "bool" + }, + { + "type": "4A6AE4F6-036C-495D-87CC-B3702B437741", + "iid": 49, + "perms": ["pr"], + "format": "uint8", + "value": 1, + "minValue": 0, + "maxValue": 4, + "minStep": 1 + }, + { + "type": "DB7BF261-7042-4194-8BD1-3AA22830AEDD", + "iid": 50, + "perms": ["pr"], + "format": "uint8", + "value": 0, + "minValue": 0, + "maxValue": 3, + "minStep": 1 + }, + { + "type": "41935E3E-B54D-42E9-B8B9-D33C6319F0AF", + "iid": 51, + "perms": ["pr"], + "format": "bool", + "value": false + }, + { + "type": "C35DA3C0-E004-40E3-B153-46655CDD9214", + "iid": 52, + "perms": ["pr", "pw"], + "format": "uint8", + "value": 100, + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "48F62AEC-4171-4B4A-8F0E-1EEB6708B3FB", + "iid": 53, + "perms": ["pr"], + "format": "uint8", + "value": 100, + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "1B1515F2-CC45-409F-991F-C480987F92C3", + "iid": 54, + "perms": ["pr"], + "format": "string", + "value": "The Hive is humming along. You have no pending alerts or reminders.", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4295608971, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Master BR", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBRSE4", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Master BR", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 208, + "type": "0000008A-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000011-0000-1000-8000-0026BB765291", + "iid": 209, + "perms": ["pr", "ev"], + "format": "float", + "value": 22.4, + "description": "Current Temperature", + "unit": "celsius", + "minValue": 0, + "maxValue": 100.0, + "minStep": 0.1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 210, + "perms": ["pr"], + "format": "string", + "value": "Master BR Temperature", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 211, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 212, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 1, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Master BR Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 691, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Master BR Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 691, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + } + ] + }, + { + "aid": 4295608960, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Basement", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBRSE4", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Basement", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 208, + "type": "0000008A-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000011-0000-1000-8000-0026BB765291", + "iid": 209, + "perms": ["pr", "ev"], + "format": "float", + "value": 20.3, + "description": "Current Temperature", + "unit": "celsius", + "minValue": 0, + "maxValue": 100.0, + "minStep": 0.1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 210, + "perms": ["pr"], + "format": "string", + "value": "Basement Temperature", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 211, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 212, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Basement Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 9158, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Basement Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 9158, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + } + ] + }, + { + "aid": 4295016858, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Living Room", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBRSE4", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Living Room", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 208, + "type": "0000008A-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000011-0000-1000-8000-0026BB765291", + "iid": 209, + "perms": ["pr", "ev"], + "format": "float", + "value": 21.0, + "description": "Current Temperature", + "unit": "celsius", + "minValue": 0, + "maxValue": 100.0, + "minStep": 0.1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 210, + "perms": ["pr"], + "format": "string", + "value": "Living Room Temperature", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 211, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 212, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Living Room Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": -1, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Living Room Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": -1, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + } + ] + }, + { + "aid": 4295016969, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBRSE4", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 208, + "type": "0000008A-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000011-0000-1000-8000-0026BB765291", + "iid": 209, + "perms": ["pr", "ev"], + "format": "float", + "value": 21.6, + "description": "Current Temperature", + "unit": "celsius", + "minValue": 0, + "maxValue": 100.0, + "minStep": 0.1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 210, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Temperature", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 211, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 212, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": -1, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": -1, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + } + ] + }, + { + "aid": 4298584118, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Master BR Window", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Master BR Window", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Master BR Window Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 1421, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Master BR Window Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 821, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Master BR Window Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298649931, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Loft window", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Loft window", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Loft window Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 327, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Loft window Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 328, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Loft window Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298527970, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Front Door", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Front Door", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 1, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Front Door Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 1473, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Front Door Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 873, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Front Door Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298527962, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Garage Door", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Garage Door", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 1, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Garage Door Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 1189, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Garage Door Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 888, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Garage Door Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298360914, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Basement Window 1", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Basement Window 1", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Basement Window 1 Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": -1, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Basement Window 1 Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": -1, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Basement Window 1 Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298360921, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Deck Door", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Deck Door", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Deck Door Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 944, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Deck Door Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 884, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Deck Door Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298360712, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Living Room Window 1", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Living Room Window 1", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 1, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Living Room Window 1 Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 1923, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Living Room Window 1 Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 625, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Living Room Window 1 Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + }, + { + "aid": 4298568508, + "services": [ + { + "iid": 1, + "type": "0000003E-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 2, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Window", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000020-0000-1000-8000-0026BB765291", + "iid": 3, + "perms": ["pr"], + "format": "string", + "value": "ecobee Inc.", + "description": "Manufacturer", + "maxLen": 64 + }, + { + "type": "00000030-0000-1000-8000-0026BB765291", + "iid": 4, + "perms": ["pr"], + "format": "string", + "value": "**REDACTED**", + "description": "Serial Number", + "maxLen": 64 + }, + { + "type": "00000021-0000-1000-8000-0026BB765291", + "iid": 5, + "perms": ["pr"], + "format": "string", + "value": "EBDWC01", + "description": "Model", + "maxLen": 64 + }, + { + "type": "00000052-0000-1000-8000-0026BB765291", + "iid": 8, + "perms": ["pr"], + "format": "string", + "value": "1.0.0", + "description": "Firmware Revision", + "maxLen": 64 + }, + { + "type": "00000014-0000-1000-8000-0026BB765291", + "iid": 6, + "perms": ["pw"], + "format": "bool", + "description": "Identify" + } + ] + }, + { + "iid": 192, + "type": "00000096-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000008F-0000-1000-8000-0026BB765291", + "iid": 193, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 2, + "description": "Charging State", + "minValue": 0, + "maxValue": 2, + "minStep": 1 + }, + { + "type": "00000068-0000-1000-8000-0026BB765291", + "iid": 194, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 100, + "description": "Battery Level", + "unit": "percentage", + "minValue": 0, + "maxValue": 100, + "minStep": 1 + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 195, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 196, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Window", + "description": "Name", + "maxLen": 64 + } + ] + }, + { + "iid": 57, + "type": "00000086-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000071-0000-1000-8000-0026BB765291", + "iid": 65, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Occupancy Detected", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 29, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Window Occupancy", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 117, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 116, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "A8F798E0-4A40-11E6-BDF4-0800200C9A66", + "iid": 68, + "perms": ["pr"], + "format": "int", + "value": 9060, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 56, + "type": "00000085-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "00000022-0000-1000-8000-0026BB765291", + "iid": 66, + "perms": ["pr", "ev"], + "format": "bool", + "value": false, + "description": "Motion Detected" + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 28, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Window Motion", + "description": "Name", + "maxLen": 64 + }, + { + "type": "00000075-0000-1000-8000-0026BB765291", + "iid": 101, + "perms": ["pr", "ev"], + "format": "bool", + "value": true, + "description": "Status Active" + }, + { + "type": "00000079-0000-1000-8000-0026BB765291", + "iid": 100, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Status Low Battery", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "BFE61C70-4A40-11E6-BDF4-0800200C9A66", + "iid": 67, + "perms": ["pr"], + "format": "int", + "value": 9060, + "unit": "seconds", + "minValue": -1, + "maxValue": 86400, + "minStep": 1 + } + ] + }, + { + "iid": 224, + "type": "00000080-0000-1000-8000-0026BB765291", + "characteristics": [ + { + "type": "0000006A-0000-1000-8000-0026BB765291", + "iid": 225, + "perms": ["pr", "ev"], + "format": "uint8", + "value": 0, + "description": "Contact Sensor State", + "minValue": 0, + "maxValue": 1, + "minStep": 1 + }, + { + "type": "00000023-0000-1000-8000-0026BB765291", + "iid": 226, + "perms": ["pr"], + "format": "string", + "value": "Upstairs BR Window Contact", + "description": "Name", + "maxLen": 64 + } + ] + } + ] + } +] diff --git a/tests/components/homekit_controller/snapshots/test_init.ambr b/tests/components/homekit_controller/snapshots/test_init.ambr index 62b53df33f2..3bb9eb48106 100644 --- a/tests/components/homekit_controller/snapshots/test_init.ambr +++ b/tests/components/homekit_controller/snapshots/test_init.ambr @@ -4195,6 +4195,3464 @@ }), ]) # --- +# name: test_snapshots[ecobee3_lite] + list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4295608960', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBRSE4', + 'model_id': None, + 'name': 'Basement', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.basement_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608960_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Basement Motion', + }), + 'entity_id': 'binary_sensor.basement_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.basement_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608960_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Basement Occupancy', + }), + 'entity_id': 'binary_sensor.basement_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.basement_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608960_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Basement Identify', + }), + 'entity_id': 'button.basement_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.basement_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Basement Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608960_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Basement Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.basement_battery', + 'state': '100', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.basement_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608960_208', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Basement Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.basement_temperature', + 'state': '20.3', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298360914', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Basement Window 1', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.basement_window_1_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Window 1 Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360914_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Basement Window 1 Contact', + }), + 'entity_id': 'binary_sensor.basement_window_1_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.basement_window_1_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Window 1 Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360914_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Basement Window 1 Motion', + }), + 'entity_id': 'binary_sensor.basement_window_1_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.basement_window_1_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Window 1 Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360914_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Basement Window 1 Occupancy', + }), + 'entity_id': 'binary_sensor.basement_window_1_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.basement_window_1_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Basement Window 1 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360914_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Basement Window 1 Identify', + }), + 'entity_id': 'button.basement_window_1_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.basement_window_1_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Basement Window 1 Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360914_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Basement Window 1 Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.basement_window_1_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298360921', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Deck Door', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.deck_door_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Deck Door Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360921_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Deck Door Contact', + }), + 'entity_id': 'binary_sensor.deck_door_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.deck_door_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Deck Door Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360921_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Deck Door Motion', + }), + 'entity_id': 'binary_sensor.deck_door_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.deck_door_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Deck Door Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360921_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Deck Door Occupancy', + }), + 'entity_id': 'binary_sensor.deck_door_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.deck_door_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Deck Door Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360921_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Deck Door Identify', + }), + 'entity_id': 'button.deck_door_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.deck_door_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Deck Door Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360921_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Deck Door Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.deck_door_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298527970', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Front Door', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.front_door_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Front Door Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527970_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Front Door Contact', + }), + 'entity_id': 'binary_sensor.front_door_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.front_door_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Front Door Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527970_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Front Door Motion', + }), + 'entity_id': 'binary_sensor.front_door_motion', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.front_door_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Front Door Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527970_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Front Door Occupancy', + }), + 'entity_id': 'binary_sensor.front_door_occupancy', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.front_door_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Front Door Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527970_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Front Door Identify', + }), + 'entity_id': 'button.front_door_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.front_door_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Front Door Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527970_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Front Door Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.front_door_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298527962', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Garage Door', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.garage_door_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Garage Door Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527962_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Garage Door Contact', + }), + 'entity_id': 'binary_sensor.garage_door_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.garage_door_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Garage Door Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527962_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Garage Door Motion', + }), + 'entity_id': 'binary_sensor.garage_door_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.garage_door_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Garage Door Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527962_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Garage Door Occupancy', + }), + 'entity_id': 'binary_sensor.garage_door_occupancy', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.garage_door_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Garage Door Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527962_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Garage Door Identify', + }), + 'entity_id': 'button.garage_door_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.garage_door_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Garage Door Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298527962_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Garage Door Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.garage_door_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4295016858', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBRSE4', + 'model_id': None, + 'name': 'Living Room', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.living_room_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016858_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Living Room Motion', + }), + 'entity_id': 'binary_sensor.living_room_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.living_room_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016858_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Living Room Occupancy', + }), + 'entity_id': 'binary_sensor.living_room_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.living_room_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016858_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Living Room Identify', + }), + 'entity_id': 'button.living_room_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.living_room_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Living Room Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016858_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Living Room Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.living_room_battery', + 'state': '100', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.living_room_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016858_208', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Living Room Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.living_room_temperature', + 'state': '21.0', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298360712', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Living Room Window 1', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.living_room_window_1_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Window 1 Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360712_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Living Room Window 1 Contact', + }), + 'entity_id': 'binary_sensor.living_room_window_1_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.living_room_window_1_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Window 1 Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360712_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Living Room Window 1 Motion', + }), + 'entity_id': 'binary_sensor.living_room_window_1_motion', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.living_room_window_1_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Window 1 Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360712_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Living Room Window 1 Occupancy', + }), + 'entity_id': 'binary_sensor.living_room_window_1_occupancy', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.living_room_window_1_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Living Room Window 1 Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360712_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Living Room Window 1 Identify', + }), + 'entity_id': 'button.living_room_window_1_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.living_room_window_1_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Living Room Window 1 Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298360712_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Living Room Window 1 Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.living_room_window_1_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298649931', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Loft window', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.loft_window_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Loft window Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298649931_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Loft window Contact', + }), + 'entity_id': 'binary_sensor.loft_window_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.loft_window_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Loft window Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298649931_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Loft window Motion', + }), + 'entity_id': 'binary_sensor.loft_window_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.loft_window_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Loft window Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298649931_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Loft window Occupancy', + }), + 'entity_id': 'binary_sensor.loft_window_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.loft_window_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Loft window Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298649931_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Loft window Identify', + }), + 'entity_id': 'button.loft_window_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.loft_window_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Loft window Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298649931_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Loft window Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.loft_window_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4295608971', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBRSE4', + 'model_id': None, + 'name': 'Master BR', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.master_br_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608971_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Master BR Motion', + }), + 'entity_id': 'binary_sensor.master_br_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.master_br_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608971_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Master BR Occupancy', + }), + 'entity_id': 'binary_sensor.master_br_occupancy', + 'state': 'on', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.master_br_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608971_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Master BR Identify', + }), + 'entity_id': 'button.master_br_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.master_br_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Master BR Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608971_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Master BR Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.master_br_battery', + 'state': '100', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.master_br_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295608971_208', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Master BR Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.master_br_temperature', + 'state': '22.4', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298584118', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Master BR Window', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.master_br_window_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Window Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298584118_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Master BR Window Contact', + }), + 'entity_id': 'binary_sensor.master_br_window_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.master_br_window_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Window Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298584118_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Master BR Window Motion', + }), + 'entity_id': 'binary_sensor.master_br_window_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.master_br_window_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Window Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298584118_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Master BR Window Occupancy', + }), + 'entity_id': 'binary_sensor.master_br_window_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.master_br_window_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Master BR Window Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298584118_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Master BR Window Identify', + }), + 'entity_id': 'button.master_br_window_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.master_br_window_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Master BR Window Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298584118_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Master BR Window Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.master_br_window_battery', + 'state': '100', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:1', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'ecobee3 lite', + 'model_id': None, + 'name': 'Thermostat', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '4.8.70226', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': None, + 'entity_id': 'button.thermostat_clear_hold', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Thermostat Clear Hold', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_16_48', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Thermostat Clear Hold', + }), + 'entity_id': 'button.thermostat_clear_hold', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.thermostat_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Thermostat Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Thermostat Identify', + }), + 'entity_id': 'button.thermostat_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'fan_modes': list([ + 'on', + 'auto', + ]), + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 35, + 'min_temp': 7, + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'climate', + 'entity_category': None, + 'entity_id': 'climate.thermostat', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Thermostat', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_16', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'current_humidity': 45.0, + 'current_temperature': 21.2, + 'fan_mode': 'on', + 'fan_modes': list([ + 'on', + 'auto', + ]), + 'friendly_name': 'Thermostat', + 'hvac_action': , + 'hvac_modes': list([ + , + , + , + , + ]), + 'max_temp': 35, + 'min_temp': 7, + 'supported_features': , + 'target_temp_high': None, + 'target_temp_low': None, + 'temperature': None, + }), + 'entity_id': 'climate.thermostat', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'home', + 'sleep', + 'away', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.thermostat_current_mode', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Thermostat Current Mode', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'ecobee_mode', + 'unique_id': '00:00:00:00:00:00_1_16_33', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Thermostat Current Mode', + 'options': list([ + 'home', + 'sleep', + 'away', + ]), + }), + 'entity_id': 'select.thermostat_current_mode', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'celsius', + 'fahrenheit', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': , + 'entity_id': 'select.thermostat_temperature_display_units', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Thermostat Temperature Display Units', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'temperature_display_units', + 'unique_id': '00:00:00:00:00:00_1_16_21', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Thermostat Temperature Display Units', + 'options': list([ + 'celsius', + 'fahrenheit', + ]), + }), + 'entity_id': 'select.thermostat_temperature_display_units', + 'state': 'fahrenheit', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.thermostat_current_humidity', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Thermostat Current Humidity', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_16_24', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'humidity', + 'friendly_name': 'Thermostat Current Humidity', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.thermostat_current_humidity', + 'state': '45.0', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.thermostat_current_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Thermostat Current Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_1_16_19', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Thermostat Current Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.thermostat_current_temperature', + 'state': '21.2', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4295016969', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBRSE4', + 'model_id': None, + 'name': 'Upstairs BR', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.upstairs_br_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016969_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Upstairs BR Motion', + }), + 'entity_id': 'binary_sensor.upstairs_br_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.upstairs_br_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016969_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Upstairs BR Occupancy', + }), + 'entity_id': 'binary_sensor.upstairs_br_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.upstairs_br_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016969_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Upstairs BR Identify', + }), + 'entity_id': 'button.upstairs_br_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.upstairs_br_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Upstairs BR Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016969_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Upstairs BR Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.upstairs_br_battery', + 'state': '100', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.upstairs_br_temperature', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Temperature', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4295016969_208', + 'unit_of_measurement': , + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Upstairs BR Temperature', + 'state_class': , + 'unit_of_measurement': , + }), + 'entity_id': 'sensor.upstairs_br_temperature', + 'state': '21.6', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + 'TestData', + ]), + 'config_entries_subentries': dict({ + 'TestData': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '', + 'identifiers': list([ + list([ + 'homekit_controller:accessory-id', + '00:00:00:00:00:00:aid:4298568508', + ]), + ]), + 'is_new': False, + 'labels': list([ + ]), + 'manufacturer': 'ecobee Inc.', + 'model': 'EBDWC01', + 'model_id': None, + 'name': 'Upstairs BR Window', + 'name_by_user': None, + 'primary_config_entry': 'TestData', + 'serial_number': '**REDACTED**', + 'suggested_area': None, + 'sw_version': '1.0.0', + }), + 'entities': list([ + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.upstairs_br_window_contact', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Window Contact', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298568508_224', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'opening', + 'friendly_name': 'Upstairs BR Window Contact', + }), + 'entity_id': 'binary_sensor.upstairs_br_window_contact', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.upstairs_br_window_motion', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Window Motion', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298568508_56', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'motion', + 'friendly_name': 'Upstairs BR Window Motion', + }), + 'entity_id': 'binary_sensor.upstairs_br_window_motion', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.upstairs_br_window_occupancy', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Window Occupancy', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298568508_57', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'occupancy', + 'friendly_name': 'Upstairs BR Window Occupancy', + }), + 'entity_id': 'binary_sensor.upstairs_br_window_occupancy', + 'state': 'off', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.upstairs_br_window_identify', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Upstairs BR Window Identify', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298568508_1_6', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'identify', + 'friendly_name': 'Upstairs BR Window Identify', + }), + 'entity_id': 'button.upstairs_br_window_identify', + 'state': 'unknown', + }), + }), + dict({ + 'entry': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'categories': dict({ + }), + 'config_entry_id': 'TestData', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.upstairs_br_window_battery', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': 'mdi:battery-unknown', + 'original_name': 'Upstairs BR Window Battery', + 'platform': 'homekit_controller', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '00:00:00:00:00:00_4298568508_192', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Upstairs BR Window Battery', + 'icon': 'mdi:battery', + 'state_class': , + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.upstairs_br_window_battery', + 'state': '100', + }), + }), + ]), + }), + ]) +# --- # name: test_snapshots[ecobee3_no_sensors] list([ dict({ diff --git a/tests/components/homekit_controller/test_climate.py b/tests/components/homekit_controller/test_climate.py index 62c73af9977..b119b5f7b80 100644 --- a/tests/components/homekit_controller/test_climate.py +++ b/tests/components/homekit_controller/test_climate.py @@ -8,6 +8,8 @@ from aiohomekit.model.characteristics import ( CharacteristicsTypes, CurrentFanStateValues, CurrentHeaterCoolerStateValues, + HeatingCoolingCurrentValues, + HeatingCoolingTargetValues, SwingModeValues, TargetHeaterCoolerStateValues, ) @@ -20,6 +22,7 @@ from homeassistant.components.climate import ( SERVICE_SET_HVAC_MODE, SERVICE_SET_SWING_MODE, SERVICE_SET_TEMPERATURE, + HVACAction, HVACMode, ) from homeassistant.core import HomeAssistant @@ -662,7 +665,7 @@ async def test_hvac_mode_vs_hvac_action( state = await helper.poll_and_get_state() assert state.state == "heat" - assert state.attributes["hvac_action"] == "fan" + assert state.attributes["hvac_action"] == HVACAction.FAN # Simulate that current temperature is below target temp # Heating might be on and hvac_action currently 'heat' @@ -676,7 +679,23 @@ async def test_hvac_mode_vs_hvac_action( state = await helper.poll_and_get_state() assert state.state == "heat" - assert state.attributes["hvac_action"] == "heating" + assert state.attributes["hvac_action"] == HVACAction.HEATING + + # If the fan is active, and the heating is off, the hvac_action should be 'fan' + # and not 'idle' or 'heating' + await helper.async_update( + ServicesTypes.THERMOSTAT, + { + CharacteristicsTypes.FAN_STATE_CURRENT: CurrentFanStateValues.ACTIVE, + CharacteristicsTypes.HEATING_COOLING_CURRENT: HeatingCoolingCurrentValues.IDLE, + CharacteristicsTypes.HEATING_COOLING_TARGET: HeatingCoolingTargetValues.OFF, + CharacteristicsTypes.FAN_STATE_CURRENT: CurrentFanStateValues.ACTIVE, + }, + ) + + state = await helper.poll_and_get_state() + assert state.state == HVACMode.OFF + assert state.attributes["hvac_action"] == HVACAction.FAN async def test_hvac_mode_vs_hvac_action_current_mode_wrong( From 44a92ca81c2fc2b55fbfd673527c98778d224972 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 7 Apr 2025 09:35:44 +0200 Subject: [PATCH 17/47] Fix Reolink smart AI sensors (#142454) --- homeassistant/components/reolink/binary_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/reolink/binary_sensor.py b/homeassistant/components/reolink/binary_sensor.py index 39910bbc52a..95c5f1982c3 100644 --- a/homeassistant/components/reolink/binary_sensor.py +++ b/homeassistant/components/reolink/binary_sensor.py @@ -301,7 +301,7 @@ async def async_setup_entry( ) for entity_description in BINARY_SMART_AI_SENSORS for location in api.baichuan.smart_location_list( - channel, entity_description.key + channel, entity_description.smart_type ) if entity_description.supported(api, channel, location) ) From d59200a9f59bcb6a58664855ad5d99b3e17537d4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 7 Apr 2025 22:56:21 +0200 Subject: [PATCH 18/47] Fix kelvin parameter in light action specifications (#142456) --- homeassistant/components/light/services.yaml | 4 ++-- homeassistant/components/light/strings.json | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index 2a1fbd11afd..77c5df88dd3 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -199,7 +199,7 @@ turn_on: example: "[255, 100, 100]" selector: color_rgb: - kelvin: &kelvin + color_temp_kelvin: &color_temp_kelvin filter: *color_temp_support selector: color_temp: @@ -317,7 +317,7 @@ toggle: fields: transition: *transition rgb_color: *rgb_color - kelvin: *kelvin + color_temp_kelvin: *color_temp_kelvin brightness_pct: *brightness_pct effect: *effect advanced_fields: diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index 4a3b98ded46..ecacb3c2daa 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -19,8 +19,8 @@ "field_flash_name": "Flash", "field_hs_color_description": "Color in hue/sat format. A list of two integers. Hue is 0-360 and Sat is 0-100.", "field_hs_color_name": "Hue/Sat color", - "field_kelvin_description": "Color temperature in Kelvin.", - "field_kelvin_name": "Color temperature", + "field_color_temp_kelvin_description": "Color temperature in Kelvin.", + "field_color_temp_kelvin_name": "Color temperature", "field_profile_description": "Name of a light profile to use.", "field_profile_name": "Profile", "field_rgb_color_description": "The color in RGB format. A list of three integers between 0 and 255 representing the values of red, green, and blue.", @@ -322,9 +322,9 @@ "name": "[%key:component::light::common::field_color_temp_name%]", "description": "[%key:component::light::common::field_color_temp_description%]" }, - "kelvin": { - "name": "[%key:component::light::common::field_kelvin_name%]", - "description": "[%key:component::light::common::field_kelvin_description%]" + "color_temp_kelvin": { + "name": "[%key:component::light::common::field_color_temp_kelvin_name%]", + "description": "[%key:component::light::common::field_color_temp_kelvin_description%]" }, "brightness": { "name": "[%key:component::light::common::field_brightness_name%]", @@ -420,9 +420,9 @@ "name": "[%key:component::light::common::field_color_temp_name%]", "description": "[%key:component::light::common::field_color_temp_description%]" }, - "kelvin": { - "name": "[%key:component::light::common::field_kelvin_name%]", - "description": "[%key:component::light::common::field_kelvin_description%]" + "color_temp_kelvin": { + "name": "[%key:component::light::common::field_color_temp_kelvin_name%]", + "description": "[%key:component::light::common::field_color_temp_kelvin_description%]" }, "brightness": { "name": "[%key:component::light::common::field_brightness_name%]", From 485b28d9eaf2e91eaf8b82c43f4206b414d46804 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Tue, 8 Apr 2025 08:58:08 +0200 Subject: [PATCH 19/47] Bump aioshelly to version 13.4.1 (#142477) * Bymp aioshelly to 13.4.1 * Catch InvalidHostError --------- Co-authored-by: J. Nick Koston --- homeassistant/components/shelly/config_flow.py | 3 +++ homeassistant/components/shelly/manifest.json | 2 +- homeassistant/components/shelly/strings.json | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/shelly/test_config_flow.py | 2 ++ 6 files changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index 200a88ea24c..6e41df282ef 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -12,6 +12,7 @@ from aioshelly.exceptions import ( CustomPortNotSupported, DeviceConnectionError, InvalidAuthError, + InvalidHostError, MacAddressMismatchError, ) from aioshelly.rpc_device import RpcDevice @@ -157,6 +158,8 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN): self.info = await self._async_get_info(host, port) except DeviceConnectionError: errors["base"] = "cannot_connect" + except InvalidHostError: + errors["base"] = "invalid_host" except Exception: # noqa: BLE001 LOGGER.exception("Unexpected exception") errors["base"] = "unknown" diff --git a/homeassistant/components/shelly/manifest.json b/homeassistant/components/shelly/manifest.json index e863720e476..19ccd1354a7 100644 --- a/homeassistant/components/shelly/manifest.json +++ b/homeassistant/components/shelly/manifest.json @@ -8,7 +8,7 @@ "integration_type": "device", "iot_class": "local_push", "loggers": ["aioshelly"], - "requirements": ["aioshelly==13.4.0"], + "requirements": ["aioshelly==13.4.1"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/homeassistant/components/shelly/strings.json b/homeassistant/components/shelly/strings.json index afc3f92a3ce..8c8c1b94c48 100644 --- a/homeassistant/components/shelly/strings.json +++ b/homeassistant/components/shelly/strings.json @@ -51,6 +51,7 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "invalid_host": "[%key:common::config_flow::error::invalid_host%]", "unknown": "[%key:common::config_flow::error::unknown%]", "firmware_not_fully_provisioned": "Device not fully provisioned. Please contact Shelly support", "custom_port_not_supported": "Gen1 device does not support custom port.", diff --git a/requirements_all.txt b/requirements_all.txt index 69d03f97152..d835408a0a6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -371,7 +371,7 @@ aioruuvigateway==0.1.0 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==13.4.0 +aioshelly==13.4.1 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index be52eef360c..752e9d08a61 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -353,7 +353,7 @@ aioruuvigateway==0.1.0 aiosenz==1.0.0 # homeassistant.components.shelly -aioshelly==13.4.0 +aioshelly==13.4.1 # homeassistant.components.skybell aioskybell==22.7.0 diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index fffffc21cae..60883ebf5bd 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -11,6 +11,7 @@ from aioshelly.exceptions import ( CustomPortNotSupported, DeviceConnectionError, InvalidAuthError, + InvalidHostError, ) import pytest @@ -308,6 +309,7 @@ async def test_form_auth( ("exc", "base_error"), [ (DeviceConnectionError, "cannot_connect"), + (InvalidHostError, "invalid_host"), (ValueError, "unknown"), ], ) From 9410061405073b3abd024f32e8ef62bb93cf3164 Mon Sep 17 00:00:00 2001 From: Wilfred Ketelaar Date: Mon, 7 Apr 2025 15:54:08 +0200 Subject: [PATCH 20/47] Fixed Renault charge state icon (#142478) Fixed charge state icon (duplicate mdi prefix) --- homeassistant/components/renault/icons.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/renault/icons.json b/homeassistant/components/renault/icons.json index 8b9c4885eaa..aa9175052fb 100644 --- a/homeassistant/components/renault/icons.json +++ b/homeassistant/components/renault/icons.json @@ -35,7 +35,7 @@ }, "sensor": { "charge_state": { - "default": "mdi:mdi:flash-off", + "default": "mdi:flash-off", "state": { "charge_in_progress": "mdi:flash" } From f702f3efcdc0ac2da9dc7017083ed95a20e028ff Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Wed, 9 Apr 2025 08:53:44 +0200 Subject: [PATCH 21/47] Fix Shelly initialization if device runs large script (#142487) * Don't check the whole script to see if it generates events * Fix tests --------- Co-authored-by: Shay Levy --- homeassistant/components/shelly/const.py | 4 ++++ homeassistant/components/shelly/utils.py | 3 ++- tests/components/shelly/conftest.py | 8 ++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/shelly/const.py b/homeassistant/components/shelly/const.py index 43fb6df18d0..0c64df52409 100644 --- a/homeassistant/components/shelly/const.py +++ b/homeassistant/components/shelly/const.py @@ -277,3 +277,7 @@ ROLE_TO_DEVICE_CLASS_MAP = { "current_humidity": SensorDeviceClass.HUMIDITY, "current_temperature": SensorDeviceClass.TEMPERATURE, } + +# We want to check only the first 5 KB of the script if it contains emitEvent() +# so that the integration startup remains fast. +MAX_SCRIPT_SIZE = 5120 diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 474e2bb9410..a5e08faf0e0 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -58,6 +58,7 @@ from .const import ( GEN2_BETA_RELEASE_URL, GEN2_RELEASE_URL, LOGGER, + MAX_SCRIPT_SIZE, RPC_INPUTS_EVENTS_TYPES, SHAIR_MAX_WORK_HOURS, SHBTN_INPUTS_EVENTS_TYPES, @@ -642,7 +643,7 @@ def get_rpc_ws_url(hass: HomeAssistant) -> str | None: async def get_rpc_script_event_types(device: RpcDevice, id: int) -> list[str]: """Return a list of event types for a specific script.""" - code_response = await device.script_getcode(id) + code_response = await device.script_getcode(id, bytes_to_read=MAX_SCRIPT_SIZE) matches = SHELLY_EMIT_EVENT_PATTERN.finditer(code_response["data"]) return sorted([*{str(event_type.group(1)) for event_type in matches}]) diff --git a/tests/components/shelly/conftest.py b/tests/components/shelly/conftest.py index 8f8255235be..2a386a1628c 100644 --- a/tests/components/shelly/conftest.py +++ b/tests/components/shelly/conftest.py @@ -492,7 +492,9 @@ def _mock_rpc_device(version: str | None = None): initialized=True, connected=True, script_getcode=AsyncMock( - side_effect=lambda script_id: {"data": MOCK_SCRIPTS[script_id - 1]} + side_effect=lambda script_id, bytes_to_read: { + "data": MOCK_SCRIPTS[script_id - 1] + } ), xmod_info={}, ) @@ -514,7 +516,9 @@ def _mock_blu_rtv_device(version: str | None = None): initialized=True, connected=True, script_getcode=AsyncMock( - side_effect=lambda script_id: {"data": MOCK_SCRIPTS[script_id - 1]} + side_effect=lambda script_id, bytes_to_read: { + "data": MOCK_SCRIPTS[script_id - 1] + } ), xmod_info={}, ) From c540acf2bd40cd6c3925f869a57594cd2942f7fe Mon Sep 17 00:00:00 2001 From: Ivan Lopez Hernandez Date: Mon, 7 Apr 2025 21:20:54 -0700 Subject: [PATCH 22/47] Handle None on the response candidates in Google Generative AI (#142497) * Added type checking on the candidates list * Made error message a constant --- .../conversation.py | 14 +++++++-- .../test_conversation.py | 29 ++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/google_generative_ai_conversation/conversation.py b/homeassistant/components/google_generative_ai_conversation/conversation.py index 7c19c5445a7..73a82b98664 100644 --- a/homeassistant/components/google_generative_ai_conversation/conversation.py +++ b/homeassistant/components/google_generative_ai_conversation/conversation.py @@ -55,6 +55,10 @@ from .const import ( # Max number of back and forth with the LLM to generate a response MAX_TOOL_ITERATIONS = 10 +ERROR_GETTING_RESPONSE = ( + "Sorry, I had a problem getting a response from Google Generative AI." +) + async def async_setup_entry( hass: HomeAssistant, @@ -429,6 +433,12 @@ class GoogleGenerativeAIConversationEntity( raise HomeAssistantError( f"The message got blocked due to content violations, reason: {chat_response.prompt_feedback.block_reason_message}" ) + if not chat_response.candidates: + LOGGER.error( + "No candidates found in the response: %s", + chat_response, + ) + raise HomeAssistantError(ERROR_GETTING_RESPONSE) except ( APIError, @@ -452,9 +462,7 @@ class GoogleGenerativeAIConversationEntity( response_parts = chat_response.candidates[0].content.parts if not response_parts: - raise HomeAssistantError( - "Sorry, I had a problem getting a response from Google Generative AI." - ) + raise HomeAssistantError(ERROR_GETTING_RESPONSE) content = " ".join( [part.text.strip() for part in response_parts if part.text] ) diff --git a/tests/components/google_generative_ai_conversation/test_conversation.py b/tests/components/google_generative_ai_conversation/test_conversation.py index 9c4ecc4f9a4..75cb308d5de 100644 --- a/tests/components/google_generative_ai_conversation/test_conversation.py +++ b/tests/components/google_generative_ai_conversation/test_conversation.py @@ -12,6 +12,7 @@ import voluptuous as vol from homeassistant.components import conversation from homeassistant.components.conversation import UserContent, async_get_chat_log, trace from homeassistant.components.google_generative_ai_conversation.conversation import ( + ERROR_GETTING_RESPONSE, _escape_decode, _format_schema, ) @@ -492,7 +493,33 @@ async def test_empty_response( assert result.response.response_type == intent.IntentResponseType.ERROR, result assert result.response.error_code == "unknown", result assert result.response.as_dict()["speech"]["plain"]["speech"] == ( - "Sorry, I had a problem getting a response from Google Generative AI." + ERROR_GETTING_RESPONSE + ) + + +@pytest.mark.usefixtures("mock_init_component") +async def test_none_response( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +) -> None: + """Test empty response.""" + with patch("google.genai.chats.AsyncChats.create") as mock_create: + mock_chat = AsyncMock() + mock_create.return_value.send_message = mock_chat + chat_response = Mock(prompt_feedback=None) + mock_chat.return_value = chat_response + chat_response.candidates = None + result = await conversation.async_converse( + hass, + "hello", + None, + Context(), + agent_id="conversation.google_generative_ai_conversation", + ) + + assert result.response.response_type == intent.IntentResponseType.ERROR, result + assert result.response.error_code == "unknown", result + assert result.response.as_dict()["speech"]["plain"]["speech"] == ( + ERROR_GETTING_RESPONSE ) From d9f91598a59f8e3f9b830b06506e96f1bf33c622 Mon Sep 17 00:00:00 2001 From: tronikos Date: Tue, 8 Apr 2025 11:46:09 -0700 Subject: [PATCH 23/47] Fix range of Google Generative AI temperature (#142513) --- .../components/google_generative_ai_conversation/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/google_generative_ai_conversation/config_flow.py b/homeassistant/components/google_generative_ai_conversation/config_flow.py index ac6cb696a7d..ee980c9bf48 100644 --- a/homeassistant/components/google_generative_ai_conversation/config_flow.py +++ b/homeassistant/components/google_generative_ai_conversation/config_flow.py @@ -303,7 +303,7 @@ async def google_generative_ai_config_option_schema( CONF_TEMPERATURE, description={"suggested_value": options.get(CONF_TEMPERATURE)}, default=RECOMMENDED_TEMPERATURE, - ): NumberSelector(NumberSelectorConfig(min=0, max=1, step=0.05)), + ): NumberSelector(NumberSelectorConfig(min=0, max=2, step=0.05)), vol.Optional( CONF_TOP_P, description={"suggested_value": options.get(CONF_TOP_P)}, From 8dc21ef619fed5359d81f75cd3cf5ce1a9e47d5d Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Tue, 8 Apr 2025 12:00:05 +0200 Subject: [PATCH 24/47] Allow max to be equal with min for mqtt number config validation (#142522) --- homeassistant/components/mqtt/number.py | 4 +- tests/components/mqtt/test_number.py | 59 ++++++++++++++++++------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index 5ee93cfba07..c3cc31bf04f 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -70,8 +70,8 @@ MQTT_NUMBER_ATTRIBUTES_BLOCKED = frozenset( def validate_config(config: ConfigType) -> ConfigType: """Validate that the configuration is valid, throws if it isn't.""" - if config[CONF_MIN] >= config[CONF_MAX]: - raise vol.Invalid(f"'{CONF_MAX}' must be > '{CONF_MIN}'") + if config[CONF_MIN] > config[CONF_MAX]: + raise vol.Invalid(f"{CONF_MAX} must be >= {CONF_MIN}") return config diff --git a/tests/components/mqtt/test_number.py b/tests/components/mqtt/test_number.py index f391236aca4..fd54e5f0643 100644 --- a/tests/components/mqtt/test_number.py +++ b/tests/components/mqtt/test_number.py @@ -835,32 +835,57 @@ async def test_entity_debug_info_message( @pytest.mark.parametrize( - "hass_config", + ("hass_config", "min_number", "max_number", "step"), [ - { - mqtt.DOMAIN: { - number.DOMAIN: { - "state_topic": "test/state_number", - "command_topic": "test/cmd_number", - "name": "Test Number", - "min": 5, - "max": 110, - "step": 20, + ( + { + mqtt.DOMAIN: { + number.DOMAIN: { + "state_topic": "test/state_number", + "command_topic": "test/cmd_number", + "name": "Test Number", + "min": 5, + "max": 110, + "step": 20, + } } - } - } + }, + 5, + 110, + 20, + ), + ( + { + mqtt.DOMAIN: { + number.DOMAIN: { + "state_topic": "test/state_number", + "command_topic": "test/cmd_number", + "name": "Test Number", + "min": 100, + "max": 100, + } + } + }, + 100, + 100, + 1, + ), ], ) async def test_min_max_step_attributes( - hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator + hass: HomeAssistant, + mqtt_mock_entry: MqttMockHAClientGenerator, + min_number: float, + max_number: float, + step: float, ) -> None: """Test min/max/step attributes.""" await mqtt_mock_entry() state = hass.states.get("number.test_number") - assert state.attributes.get(ATTR_MIN) == 5 - assert state.attributes.get(ATTR_MAX) == 110 - assert state.attributes.get(ATTR_STEP) == 20 + assert state.attributes.get(ATTR_MIN) == min_number + assert state.attributes.get(ATTR_MAX) == max_number + assert state.attributes.get(ATTR_STEP) == step @pytest.mark.parametrize( @@ -885,7 +910,7 @@ async def test_invalid_min_max_attributes( ) -> None: """Test invalid min/max attributes.""" assert await mqtt_mock_entry() - assert f"'{CONF_MAX}' must be > '{CONF_MIN}'" in caplog.text + assert f"{CONF_MAX} must be >= {CONF_MIN}" in caplog.text @pytest.mark.parametrize( From d1b78982192d0522a729a6d73ecb6a4e3f7781a2 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 8 Apr 2025 15:44:35 +0200 Subject: [PATCH 25/47] Fix small typo in Music Assistant integration causing unavailable players (#142535) Fix small typo in Music Assistant integration causing issues with adding players --- homeassistant/components/music_assistant/media_player.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/music_assistant/media_player.py b/homeassistant/components/music_assistant/media_player.py index 005e5006b52..05fdbe85777 100644 --- a/homeassistant/components/music_assistant/media_player.py +++ b/homeassistant/components/music_assistant/media_player.py @@ -151,6 +151,9 @@ async def async_setup_entry( assert event.object_id is not None if event.object_id in added_ids: return + player = mass.players.get(event.object_id) + if TYPE_CHECKING: + assert player is not None if not player.expose_to_ha: return added_ids.add(event.object_id) From e9f02edd8b3901f20190bd5f873bf190d8eec668 Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Tue, 8 Apr 2025 20:40:13 +0200 Subject: [PATCH 26/47] Fix adding devices in Husqvarna Automower (#142549) --- homeassistant/components/husqvarna_automower/coordinator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/husqvarna_automower/coordinator.py b/homeassistant/components/husqvarna_automower/coordinator.py index 9456074596a..c23ca508916 100644 --- a/homeassistant/components/husqvarna_automower/coordinator.py +++ b/homeassistant/components/husqvarna_automower/coordinator.py @@ -136,6 +136,7 @@ class AutomowerDataUpdateCoordinator(DataUpdateCoordinator[MowerDictionary]): # Process new device new_devices = current_devices - self._devices_last_update if new_devices: + self.data = data _LOGGER.debug("New devices found: %s", ", ".join(map(str, new_devices))) self._add_new_devices(new_devices) From ec53b61f9e78bb3b6a58b3772907c27248ff579b Mon Sep 17 00:00:00 2001 From: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:38:48 -0500 Subject: [PATCH 27/47] Bump pyheos to v1.0.5 (#142554) Update pyheos --- homeassistant/components/heos/manifest.json | 2 +- homeassistant/components/heos/media_player.py | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/heos/manifest.json b/homeassistant/components/heos/manifest.json index cbac9f20574..8a88913456d 100644 --- a/homeassistant/components/heos/manifest.json +++ b/homeassistant/components/heos/manifest.json @@ -8,7 +8,7 @@ "iot_class": "local_push", "loggers": ["pyheos"], "quality_scale": "platinum", - "requirements": ["pyheos==1.0.4"], + "requirements": ["pyheos==1.0.5"], "ssdp": [ { "st": "urn:schemas-denon-com:device:ACT-Denon:1" diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 9cd01051b95..6ba672a57ad 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -87,6 +87,7 @@ BASE_SUPPORTED_FEATURES = ( PLAY_STATE_TO_STATE = { None: MediaPlayerState.IDLE, + PlayState.UNKNOWN: MediaPlayerState.IDLE, PlayState.PLAY: MediaPlayerState.PLAYING, PlayState.STOP: MediaPlayerState.IDLE, PlayState.PAUSE: MediaPlayerState.PAUSED, diff --git a/requirements_all.txt b/requirements_all.txt index d835408a0a6..4be2b5f028a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2005,7 +2005,7 @@ pygti==0.9.4 pyhaversion==22.8.0 # homeassistant.components.heos -pyheos==1.0.4 +pyheos==1.0.5 # homeassistant.components.hive pyhive-integration==1.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 752e9d08a61..aec2bfe732e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1632,7 +1632,7 @@ pygti==0.9.4 pyhaversion==22.8.0 # homeassistant.components.heos -pyheos==1.0.4 +pyheos==1.0.5 # homeassistant.components.hive pyhive-integration==1.0.2 From a9f85294608bf0b6025585e5b9c3bdf504ba6b82 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <9592452+CFenner@users.noreply.github.com> Date: Wed, 9 Apr 2025 20:36:41 +0200 Subject: [PATCH 28/47] Fix Quickmode handling in ViCare integration (#142561) * only check quickmode if supported * update snapshot * revert --- homeassistant/components/vicare/fan.py | 27 +++++++++++-------- .../components/vicare/snapshots/test_fan.ambr | 19 +++++++++++-- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/vicare/fan.py b/homeassistant/components/vicare/fan.py index d84b2038dde..88d42503a03 100644 --- a/homeassistant/components/vicare/fan.py +++ b/homeassistant/components/vicare/fan.py @@ -127,6 +127,7 @@ class ViCareFan(ViCareEntity, FanEntity): _attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS) _attr_translation_key = "ventilation" + _attributes: dict[str, Any] = {} def __init__( self, @@ -155,7 +156,7 @@ class ViCareFan(ViCareEntity, FanEntity): self._attr_supported_features |= FanEntityFeature.SET_SPEED # evaluate quickmodes - quickmodes: list[str] = ( + self._attributes["vicare_quickmodes"] = quickmodes = list[str]( device.getVentilationQuickmodes() if is_supported( "getVentilationQuickmodes", @@ -196,26 +197,23 @@ class ViCareFan(ViCareEntity, FanEntity): @property def is_on(self) -> bool | None: """Return true if the entity is on.""" - if ( - self._attr_supported_features & FanEntityFeature.TURN_OFF - and self._api.getVentilationQuickmode(VentilationQuickmode.STANDBY) - ): + if VentilationQuickmode.STANDBY in self._attributes[ + "vicare_quickmodes" + ] and self._api.getVentilationQuickmode(VentilationQuickmode.STANDBY): return False return self.percentage is not None and self.percentage > 0 def turn_off(self, **kwargs: Any) -> None: """Turn the entity off.""" - self._api.activateVentilationQuickmode(str(VentilationQuickmode.STANDBY)) @property def icon(self) -> str | None: """Return the icon to use in the frontend.""" - if ( - self._attr_supported_features & FanEntityFeature.TURN_OFF - and self._api.getVentilationQuickmode(VentilationQuickmode.STANDBY) - ): + if VentilationQuickmode.STANDBY in self._attributes[ + "vicare_quickmodes" + ] and self._api.getVentilationQuickmode(VentilationQuickmode.STANDBY): return "mdi:fan-off" if hasattr(self, "_attr_preset_mode"): if self._attr_preset_mode == VentilationMode.VENTILATION: @@ -242,7 +240,9 @@ class ViCareFan(ViCareEntity, FanEntity): """Set the speed of the fan, as a percentage.""" if self._attr_preset_mode != str(VentilationMode.PERMANENT): self.set_preset_mode(VentilationMode.PERMANENT) - elif self._api.getVentilationQuickmode(VentilationQuickmode.STANDBY): + elif VentilationQuickmode.STANDBY in self._attributes[ + "vicare_quickmodes" + ] and self._api.getVentilationQuickmode(VentilationQuickmode.STANDBY): self._api.deactivateVentilationQuickmode(str(VentilationQuickmode.STANDBY)) level = percentage_to_ordered_list_item(ORDERED_NAMED_FAN_SPEEDS, percentage) @@ -254,3 +254,8 @@ class ViCareFan(ViCareEntity, FanEntity): target_mode = VentilationMode.to_vicare_mode(preset_mode) _LOGGER.debug("changing ventilation mode to %s", target_mode) self._api.activateVentilationMode(target_mode) + + @property + def extra_state_attributes(self) -> dict[str, Any]: + """Show Device Attributes.""" + return self._attributes diff --git a/tests/components/vicare/snapshots/test_fan.ambr b/tests/components/vicare/snapshots/test_fan.ambr index 2c9e815f7bf..2a44fb87b65 100644 --- a/tests/components/vicare/snapshots/test_fan.ambr +++ b/tests/components/vicare/snapshots/test_fan.ambr @@ -55,6 +55,11 @@ , ]), 'supported_features': , + 'vicare_quickmodes': list([ + 'comfort', + 'eco', + 'holiday', + ]), }), 'context': , 'entity_id': 'fan.model0_ventilation', @@ -94,7 +99,7 @@ 'options': dict({ }), 'original_device_class': None, - 'original_icon': 'mdi:fan-off', + 'original_icon': 'mdi:fan', 'original_name': 'Ventilation', 'platform': 'vicare', 'previous_unique_id': None, @@ -108,7 +113,7 @@ StateSnapshot({ 'attributes': ReadOnlyDict({ 'friendly_name': 'model1 Ventilation', - 'icon': 'mdi:fan-off', + 'icon': 'mdi:fan', 'percentage': 0, 'percentage_step': 25.0, 'preset_mode': None, @@ -118,6 +123,11 @@ , ]), 'supported_features': , + 'vicare_quickmodes': list([ + 'comfort', + 'eco', + 'holiday', + ]), }), 'context': , 'entity_id': 'fan.model1_ventilation', @@ -179,6 +189,11 @@ , ]), 'supported_features': , + 'vicare_quickmodes': list([ + 'comfort', + 'eco', + 'holiday', + ]), }), 'context': , 'entity_id': 'fan.model2_ventilation', From 60ece3e1c9f6b14a33657ebbb660ec7d510107bb Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 8 Apr 2025 22:02:51 -0400 Subject: [PATCH 29/47] Fix Core deadlock by ensuring only one ZHA log queue handler thread is running at a time (#142568) Ensure only one log queue handler is running at a time --- homeassistant/components/zha/helpers.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zha/helpers.py b/homeassistant/components/zha/helpers.py index 700e2833705..c819f94ceba 100644 --- a/homeassistant/components/zha/helpers.py +++ b/homeassistant/components/zha/helpers.py @@ -514,6 +514,7 @@ class ZHAGatewayProxy(EventBase): self._log_queue_handler.listener = logging.handlers.QueueListener( log_simple_queue, log_relay_handler ) + self._log_queue_handler_count: int = 0 self._unsubs: list[Callable[[], None]] = [] self._unsubs.append(self.gateway.on_all_events(self._handle_event_protocol)) @@ -747,7 +748,10 @@ class ZHAGatewayProxy(EventBase): if filterer: self._log_queue_handler.addFilter(filterer) - if self._log_queue_handler.listener: + # Only start a new log queue handler if the old one is no longer running + self._log_queue_handler_count += 1 + + if self._log_queue_handler.listener and self._log_queue_handler_count == 1: self._log_queue_handler.listener.start() for logger_name in DEBUG_RELAY_LOGGERS: @@ -763,7 +767,10 @@ class ZHAGatewayProxy(EventBase): for logger_name in DEBUG_RELAY_LOGGERS: logging.getLogger(logger_name).removeHandler(self._log_queue_handler) - if self._log_queue_handler.listener: + # Only stop the log queue handler if nothing else is using it + self._log_queue_handler_count -= 1 + + if self._log_queue_handler.listener and self._log_queue_handler_count == 0: self._log_queue_handler.listener.stop() if filterer: From d3d78898838c1d69a90cd7312fae3408a99bc285 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 9 Apr 2025 08:51:44 +0200 Subject: [PATCH 30/47] Fix ssl_cert load from config_flow (#142570) fix ssl_cert load from config_flow --- homeassistant/components/daikin/config_flow.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/daikin/config_flow.py b/homeassistant/components/daikin/config_flow.py index cc25a88ae39..f5febafc4dc 100644 --- a/homeassistant/components/daikin/config_flow.py +++ b/homeassistant/components/daikin/config_flow.py @@ -18,6 +18,7 @@ from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PASSWORD, CONF_UUID from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo +from homeassistant.util.ssl import client_context_no_verify from .const import DOMAIN, KEY_MAC, TIMEOUT @@ -90,6 +91,7 @@ class FlowHandler(ConfigFlow, domain=DOMAIN): key=key, uuid=uuid, password=password, + ssl_context=client_context_no_verify(), ) except (TimeoutError, ClientError): self.host = None From acdac6d5e82fd687552073123ae6a3b0f4b004d7 Mon Sep 17 00:00:00 2001 From: Thimo Seitz Date: Thu, 10 Apr 2025 20:14:30 +0200 Subject: [PATCH 31/47] Update growatt server dependency to 1.6.0 (#142606) * Update GrowattServer Dependency * Update requirements_test_all.txt --- homeassistant/components/growatt_server/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/growatt_server/manifest.json b/homeassistant/components/growatt_server/manifest.json index 98ceb35ee17..7b3e67228b1 100644 --- a/homeassistant/components/growatt_server/manifest.json +++ b/homeassistant/components/growatt_server/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/growatt_server", "iot_class": "cloud_polling", "loggers": ["growattServer"], - "requirements": ["growattServer==1.5.0"] + "requirements": ["growattServer==1.6.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 4be2b5f028a..28520b20056 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1084,7 +1084,7 @@ greenwavereality==0.5.1 gridnet==5.0.1 # homeassistant.components.growatt_server -growattServer==1.5.0 +growattServer==1.6.0 # homeassistant.components.google_sheets gspread==5.5.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index aec2bfe732e..93129fd44a7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -929,7 +929,7 @@ greeneye_monitor==3.0.3 gridnet==5.0.1 # homeassistant.components.growatt_server -growattServer==1.5.0 +growattServer==1.6.0 # homeassistant.components.google_sheets gspread==5.5.0 From 82959081de3acb7e1a25b80459edbaa0883f6ad9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Apr 2025 15:48:29 -1000 Subject: [PATCH 32/47] Pin multidict to >= 6.4.2 to resolve memory leaks (#142614) * Pin multidict to >= 6.4.1 to resolve memory leaks https://github.com/aio-libs/multidict/issues/1134 https://github.com/aio-libs/multidict/issues/1131 https://github.com/aio-libs/multidict/releases/tag/v6.4.1 https://github.com/aio-libs/multidict/releases/tag/v6.4.0 * Apply suggestions from code review --- homeassistant/package_constraints.txt | 5 +++++ script/gen_requirements_all.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b6dec576591..8eecf0e6f80 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -212,3 +212,8 @@ async-timeout==4.0.3 # https://github.com/home-assistant/core/issues/122508 # https://github.com/home-assistant/core/issues/118004 aiofiles>=24.1.0 + +# multidict < 6.4.0 has memory leaks +# https://github.com/aio-libs/multidict/issues/1134 +# https://github.com/aio-libs/multidict/issues/1131 +multidict>=6.4.2 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 1be6286d30c..4f8db92b1ba 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -241,6 +241,11 @@ async-timeout==4.0.3 # https://github.com/home-assistant/core/issues/122508 # https://github.com/home-assistant/core/issues/118004 aiofiles>=24.1.0 + +# multidict < 6.4.0 has memory leaks +# https://github.com/aio-libs/multidict/issues/1134 +# https://github.com/aio-libs/multidict/issues/1131 +multidict>=6.4.2 """ GENERATED_MESSAGE = ( From 2c4461457a29fd9df8db9ae1943a23ff5bae9191 Mon Sep 17 00:00:00 2001 From: henryptung Date: Wed, 9 Apr 2025 19:52:10 -0700 Subject: [PATCH 33/47] Bump led_ble to 1.1.7 (#142629) changelog: https://github.com/Bluetooth-Devices/led-ble/compare/v1.1.6...v1.1.7 --- homeassistant/components/led_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/led_ble/manifest.json b/homeassistant/components/led_ble/manifest.json index b88ef3f029a..14983e5fdfe 100644 --- a/homeassistant/components/led_ble/manifest.json +++ b/homeassistant/components/led_ble/manifest.json @@ -35,5 +35,5 @@ "dependencies": ["bluetooth_adapters"], "documentation": "https://www.home-assistant.io/integrations/led_ble", "iot_class": "local_polling", - "requirements": ["bluetooth-data-tools==1.26.5", "led-ble==1.1.6"] + "requirements": ["bluetooth-data-tools==1.26.5", "led-ble==1.1.7"] } diff --git a/requirements_all.txt b/requirements_all.txt index 28520b20056..47eda9ba837 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1314,7 +1314,7 @@ ld2410-ble==0.1.1 leaone-ble==0.1.0 # homeassistant.components.led_ble -led-ble==1.1.6 +led-ble==1.1.7 # homeassistant.components.lektrico lektricowifi==0.0.43 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 93129fd44a7..ee4a39dc7d5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1111,7 +1111,7 @@ ld2410-ble==0.1.1 leaone-ble==0.1.0 # homeassistant.components.led_ble -led-ble==1.1.6 +led-ble==1.1.7 # homeassistant.components.lektrico lektricowifi==0.0.43 From d1781f57661b5a39b9c5a0065d89627df72c04db Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:05:38 +0200 Subject: [PATCH 34/47] Bump livisi to 0.0.25 (#142638) --- homeassistant/components/livisi/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/livisi/manifest.json b/homeassistant/components/livisi/manifest.json index 1077cacf2c4..46ffad162f3 100644 --- a/homeassistant/components/livisi/manifest.json +++ b/homeassistant/components/livisi/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/livisi", "iot_class": "local_polling", - "requirements": ["livisi==0.0.24"] + "requirements": ["livisi==0.0.25"] } diff --git a/requirements_all.txt b/requirements_all.txt index 47eda9ba837..686117734be 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1350,7 +1350,7 @@ linear-garage-door==0.2.9 linode-api==4.1.9b1 # homeassistant.components.livisi -livisi==0.0.24 +livisi==0.0.25 # homeassistant.components.google_maps locationsharinglib==5.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ee4a39dc7d5..3ac97971908 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1132,7 +1132,7 @@ libsoundtouch==0.8 linear-garage-door==0.2.9 # homeassistant.components.livisi -livisi==0.0.24 +livisi==0.0.25 # homeassistant.components.london_underground london-tube-status==0.5 From d192aecd3b072b8dd59b518604a80e7d83175cb3 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Thu, 10 Apr 2025 21:16:53 +0200 Subject: [PATCH 35/47] Comelit config flow timeout error (#142667) --- homeassistant/components/comelit/config_flow.py | 15 ++++++++++++--- homeassistant/components/comelit/strings.json | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/comelit/config_flow.py b/homeassistant/components/comelit/config_flow.py index f29cc62136b..5854bc1e324 100644 --- a/homeassistant/components/comelit/config_flow.py +++ b/homeassistant/components/comelit/config_flow.py @@ -2,6 +2,7 @@ from __future__ import annotations +from asyncio.exceptions import TimeoutError from collections.abc import Mapping from typing import Any @@ -53,10 +54,18 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, try: await api.login() - except aiocomelit_exceptions.CannotConnect as err: - raise CannotConnect from err + except (aiocomelit_exceptions.CannotConnect, TimeoutError) as err: + raise CannotConnect( + translation_domain=DOMAIN, + translation_key="cannot_connect", + translation_placeholders={"error": repr(err)}, + ) from err except aiocomelit_exceptions.CannotAuthenticate as err: - raise InvalidAuth from err + raise InvalidAuth( + translation_domain=DOMAIN, + translation_key="cannot_authenticate", + translation_placeholders={"error": repr(err)}, + ) from err finally: await api.logout() await api.close() diff --git a/homeassistant/components/comelit/strings.json b/homeassistant/components/comelit/strings.json index f9771e58a40..21f2b89db98 100644 --- a/homeassistant/components/comelit/strings.json +++ b/homeassistant/components/comelit/strings.json @@ -69,6 +69,12 @@ }, "invalid_clima_data": { "message": "Invalid 'clima' data" + }, + "cannot_connect": { + "message": "Error connecting: {error}" + }, + "cannot_authenticate": { + "message": "Error authenticating: {error}" } } } From b40bdab0aefe0785365343e9e94ad23ce1cbc5e8 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 10 Apr 2025 23:11:35 +0200 Subject: [PATCH 36/47] Fix EC certificate key not allowed in MQTT client setup (#142698) --- homeassistant/components/mqtt/config_flow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 7fe01e9a890..5947bfb3da9 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -1611,6 +1611,7 @@ def async_is_pem_data(data: bytes) -> bool: return ( b"-----BEGIN CERTIFICATE-----" in data or b"-----BEGIN PRIVATE KEY-----" in data + or b"-----BEGIN EC PRIVATE KEY-----" in data or b"-----BEGIN RSA PRIVATE KEY-----" in data or b"-----BEGIN ENCRYPTED PRIVATE KEY-----" in data ) From 4c1d32020a1d3f400acbf158d2284892789f44c8 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <9592452+CFenner@users.noreply.github.com> Date: Fri, 11 Apr 2025 10:32:54 +0200 Subject: [PATCH 37/47] Bump PyViCare to 2.44.0 (#142701) bump vicare to v2.44.0 --- homeassistant/components/vicare/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/vicare/manifest.json b/homeassistant/components/vicare/manifest.json index e39adaf6c4c..fed777e6435 100644 --- a/homeassistant/components/vicare/manifest.json +++ b/homeassistant/components/vicare/manifest.json @@ -11,5 +11,5 @@ "documentation": "https://www.home-assistant.io/integrations/vicare", "iot_class": "cloud_polling", "loggers": ["PyViCare"], - "requirements": ["PyViCare==2.43.1"] + "requirements": ["PyViCare==2.44.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 686117734be..9413259c7c4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -100,7 +100,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.7.5 # homeassistant.components.vicare -PyViCare==2.43.1 +PyViCare==2.44.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.14.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3ac97971908..0b9629299d9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -94,7 +94,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.7.5 # homeassistant.components.vicare -PyViCare==2.43.1 +PyViCare==2.44.0 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.14.3 From fd2987a9fd883c33fa54c44fd66fc1875e1be1fa Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 11 Apr 2025 10:22:30 +0200 Subject: [PATCH 38/47] Bump reolink-aio 0.13.1 (#142719) --- homeassistant/components/reolink/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/reolink/manifest.json b/homeassistant/components/reolink/manifest.json index 82b9586cccc..9105dfda66f 100644 --- a/homeassistant/components/reolink/manifest.json +++ b/homeassistant/components/reolink/manifest.json @@ -19,5 +19,5 @@ "iot_class": "local_push", "loggers": ["reolink_aio"], "quality_scale": "platinum", - "requirements": ["reolink-aio==0.13.0"] + "requirements": ["reolink-aio==0.13.1"] } diff --git a/requirements_all.txt b/requirements_all.txt index 9413259c7c4..86fc127a23c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2627,7 +2627,7 @@ renault-api==0.2.9 renson-endura-delta==1.7.2 # homeassistant.components.reolink -reolink-aio==0.13.0 +reolink-aio==0.13.1 # homeassistant.components.idteck_prox rfk101py==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0b9629299d9..e27c99280ad 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2128,7 +2128,7 @@ renault-api==0.2.9 renson-endura-delta==1.7.2 # homeassistant.components.reolink -reolink-aio==0.13.0 +reolink-aio==0.13.1 # homeassistant.components.rflink rflink==0.0.66 From 7696b101f6856e908e80ad96a45e5d30bb8b982b Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 11 Apr 2025 11:42:18 +0200 Subject: [PATCH 39/47] Reolink migrate unique ID debugging (#142723) * Filter out unexpected unique_ids * correct * Add test * fix styling --- homeassistant/components/reolink/__init__.py | 8 ++++++++ tests/components/reolink/test_init.py | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index 99ca91c5bdf..c326f1120c9 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -420,6 +420,14 @@ def migrate_entity_ids( if entity.device_id in ch_device_ids: ch = ch_device_ids[entity.device_id] id_parts = entity.unique_id.split("_", 2) + if len(id_parts) < 3: + _LOGGER.warning( + "Reolink channel %s entity has unexpected unique_id format %s, with device id %s", + ch, + entity.unique_id, + entity.device_id, + ) + continue if host.api.supported(ch, "UID") and id_parts[1] != host.api.camera_uid(ch): new_id = f"{host.unique_id}_{host.api.camera_uid(ch)}_{id_parts[2]}" existing_entity = entity_reg.async_get_entity_id( diff --git a/tests/components/reolink/test_init.py b/tests/components/reolink/test_init.py index 4c4908dca6f..5915bd06608 100644 --- a/tests/components/reolink/test_init.py +++ b/tests/components/reolink/test_init.py @@ -424,6 +424,15 @@ async def test_removing_chime( True, True, ), + ( + f"{TEST_UID}_unexpected", + f"{TEST_UID}_unexpected", + f"{TEST_UID}_{TEST_UID_CAM}", + f"{TEST_UID}_{TEST_UID_CAM}", + Platform.SWITCH, + True, + True, + ), ], ) async def test_migrate_entity_ids( @@ -469,7 +478,8 @@ async def test_migrate_entity_ids( ) assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id) - assert entity_registry.async_get_entity_id(domain, DOMAIN, new_id) is None + if original_id != new_id: + assert entity_registry.async_get_entity_id(domain, DOMAIN, new_id) is None assert device_registry.async_get_device(identifiers={(DOMAIN, original_dev_id)}) if new_dev_id != original_dev_id: @@ -482,7 +492,8 @@ async def test_migrate_entity_ids( assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id) is None + if original_id != new_id: + assert entity_registry.async_get_entity_id(domain, DOMAIN, original_id) is None assert entity_registry.async_get_entity_id(domain, DOMAIN, new_id) if new_dev_id != original_dev_id: From 494849988976464a8ef864d4a8ff215375b02865 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Apr 2025 17:25:26 +0200 Subject: [PATCH 40/47] Update frontend to 20250411.0 (#142736) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 140d90c5dbe..64b49588ba1 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -20,5 +20,5 @@ "documentation": "https://www.home-assistant.io/integrations/frontend", "integration_type": "system", "quality_scale": "internal", - "requirements": ["home-assistant-frontend==20250404.0"] + "requirements": ["home-assistant-frontend==20250411.0"] } diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 8eecf0e6f80..95502bf257c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -38,7 +38,7 @@ habluetooth==3.37.0 hass-nabucasa==0.94.0 hassil==2.2.3 home-assistant-bluetooth==1.13.1 -home-assistant-frontend==20250404.0 +home-assistant-frontend==20250411.0 home-assistant-intents==2025.3.28 httpx==0.28.1 ifaddr==0.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 86fc127a23c..207a95a6a95 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1157,7 +1157,7 @@ hole==0.8.0 holidays==0.69 # homeassistant.components.frontend -home-assistant-frontend==20250404.0 +home-assistant-frontend==20250411.0 # homeassistant.components.conversation home-assistant-intents==2025.3.28 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e27c99280ad..81358b073b7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -984,7 +984,7 @@ hole==0.8.0 holidays==0.69 # homeassistant.components.frontend -home-assistant-frontend==20250404.0 +home-assistant-frontend==20250411.0 # homeassistant.components.conversation home-assistant-intents==2025.3.28 From b5d541b5963dfae805abc78a40f94087b1c22531 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 11 Apr 2025 16:51:32 +0200 Subject: [PATCH 41/47] Bump pySmartThings to 3.0.4 (#142739) --- homeassistant/components/smartthings/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/smartthings/manifest.json b/homeassistant/components/smartthings/manifest.json index dda7ef53cf5..4cd27e49664 100644 --- a/homeassistant/components/smartthings/manifest.json +++ b/homeassistant/components/smartthings/manifest.json @@ -30,5 +30,5 @@ "iot_class": "cloud_push", "loggers": ["pysmartthings"], "quality_scale": "bronze", - "requirements": ["pysmartthings==3.0.2"] + "requirements": ["pysmartthings==3.0.4"] } diff --git a/requirements_all.txt b/requirements_all.txt index 207a95a6a95..962c426cfc0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2319,7 +2319,7 @@ pysma==0.7.5 pysmappee==0.2.29 # homeassistant.components.smartthings -pysmartthings==3.0.2 +pysmartthings==3.0.4 # homeassistant.components.smarty pysmarty2==0.10.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 81358b073b7..99a77f19a46 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1889,7 +1889,7 @@ pysma==0.7.5 pysmappee==0.2.29 # homeassistant.components.smartthings -pysmartthings==3.0.2 +pysmartthings==3.0.4 # homeassistant.components.smarty pysmarty2==0.10.2 From 28d776a0b02404418c0a0832aa80d96f57852385 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Fri, 11 Apr 2025 17:24:39 +0200 Subject: [PATCH 42/47] Fix SmartThings gas meter (#142741) --- .../components/smartthings/sensor.py | 6 +- tests/components/smartthings/conftest.py | 1 + .../fixtures/device_status/gas_meter.json | 61 ++++++ .../fixtures/devices/gas_meter.json | 56 +++++ .../smartthings/snapshots/test_init.ambr | 33 +++ .../smartthings/snapshots/test_sensor.ambr | 202 ++++++++++++++++++ 6 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 tests/components/smartthings/fixtures/device_status/gas_meter.json create mode 100644 tests/components/smartthings/fixtures/devices/gas_meter.json diff --git a/homeassistant/components/smartthings/sensor.py b/homeassistant/components/smartthings/sensor.py index 346516be480..e081f35d0e0 100644 --- a/homeassistant/components/smartthings/sensor.py +++ b/homeassistant/components/smartthings/sensor.py @@ -413,7 +413,6 @@ CAPABILITY_TO_SENSORS: dict[ ) ] }, - # Haven't seen at devices yet Capability.GAS_METER: { Attribute.GAS_METER: [ SmartThingsSensorEntityDescription( @@ -421,7 +420,7 @@ CAPABILITY_TO_SENSORS: dict[ translation_key="gas_meter", native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, device_class=SensorDeviceClass.ENERGY, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, ) ], Attribute.GAS_METER_CALORIFIC: [ @@ -443,7 +442,7 @@ CAPABILITY_TO_SENSORS: dict[ key=Attribute.GAS_METER_VOLUME, native_unit_of_measurement=UnitOfVolume.CUBIC_METERS, device_class=SensorDeviceClass.GAS, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, ) ], }, @@ -1003,6 +1002,7 @@ CAPABILITY_TO_SENSORS: dict[ UNITS = { "C": UnitOfTemperature.CELSIUS, "F": UnitOfTemperature.FAHRENHEIT, + "ccf": UnitOfVolume.CENTUM_CUBIC_FEET, "lux": LIGHT_LUX, "mG": None, "μg/m^3": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, diff --git a/tests/components/smartthings/conftest.py b/tests/components/smartthings/conftest.py index 277c327744f..26af812fe1f 100644 --- a/tests/components/smartthings/conftest.py +++ b/tests/components/smartthings/conftest.py @@ -146,6 +146,7 @@ def mock_smartthings() -> Generator[AsyncMock]: "ikea_kadrilj", "aux_ac", "hw_q80r_soundbar", + "gas_meter", ] ) def device_fixture( diff --git a/tests/components/smartthings/fixtures/device_status/gas_meter.json b/tests/components/smartthings/fixtures/device_status/gas_meter.json new file mode 100644 index 00000000000..dc7f9b2e0c3 --- /dev/null +++ b/tests/components/smartthings/fixtures/device_status/gas_meter.json @@ -0,0 +1,61 @@ +{ + "components": { + "main": { + "healthCheck": { + "checkInterval": { + "value": 60, + "unit": "s", + "data": { + "deviceScheme": "UNTRACKED", + "protocol": "cloud" + }, + "timestamp": "2025-02-27T14:06:11.704Z" + }, + "healthStatus": { + "value": null + }, + "DeviceWatch-Enroll": { + "value": null + }, + "DeviceWatch-DeviceStatus": { + "value": "online", + "data": {}, + "timestamp": "2025-04-11T13:00:00.444Z" + } + }, + "refresh": {}, + "gasMeter": { + "gasMeterPrecision": { + "value": { + "volume": 5, + "calorific": 1, + "conversion": 1 + }, + "timestamp": "2025-04-11T13:00:00.444Z" + }, + "gasMeterCalorific": { + "value": 40, + "timestamp": "2025-04-11T13:00:00.444Z" + }, + "gasMeterTime": { + "value": "2025-04-11T13:30:00.028Z", + "timestamp": "2025-04-11T13:30:00.532Z" + }, + "gasMeterVolume": { + "value": 14, + "unit": "ccf", + "timestamp": "2025-04-11T13:00:00.444Z" + }, + "gasMeterConversion": { + "value": 3.6, + "timestamp": "2025-04-11T13:00:00.444Z" + }, + "gasMeter": { + "value": 450.5, + "unit": "kWh", + "timestamp": "2025-04-11T13:00:00.444Z" + } + } + } + } +} diff --git a/tests/components/smartthings/fixtures/devices/gas_meter.json b/tests/components/smartthings/fixtures/devices/gas_meter.json new file mode 100644 index 00000000000..9bf8af654c7 --- /dev/null +++ b/tests/components/smartthings/fixtures/devices/gas_meter.json @@ -0,0 +1,56 @@ +{ + "items": [ + { + "deviceId": "3b57dca3-9a90-4f27-ba80-f947b1e60d58", + "name": "copper_gas_meter_v04", + "label": "Gas Meter", + "manufacturerName": "0A6v", + "presentationId": "ST_176e9efa-01d2-4d1b-8130-d37a4ef1b413", + "deviceManufacturerCode": "CopperLabs", + "locationId": "4e88bf74-3bed-4e6d-9fa7-6acb776a4df9", + "ownerId": "6fc21de5-123e-2f8c-2cc6-311635aeaaef", + "roomId": "fafae9db-a2b5-480f-8ff5-df8f913356df", + "components": [ + { + "id": "main", + "label": "main", + "capabilities": [ + { + "id": "healthCheck", + "version": 1 + }, + { + "id": "refresh", + "version": 1 + }, + { + "id": "gasMeter", + "version": 1 + } + ], + "categories": [ + { + "name": "GasMeter", + "categoryType": "manufacturer" + } + ] + } + ], + "createTime": "2025-02-27T14:06:11.522Z", + "profile": { + "id": "5cca2553-23d6-43c4-81ad-a1c6c43efa00" + }, + "viper": { + "manufacturerName": "CopperLabs", + "modelName": "Virtual Gas Meter", + "endpointAppId": "viper_1d5767a0-af08-11ed-a999-9f1f172a27ff" + }, + "type": "VIPER", + "restrictionTier": 0, + "allowed": null, + "executionContext": "CLOUD", + "relationships": [] + } + ], + "_links": {} +} diff --git a/tests/components/smartthings/snapshots/test_init.ambr b/tests/components/smartthings/snapshots/test_init.ambr index 8ec97af7d84..db8c3a6ccc5 100644 --- a/tests/components/smartthings/snapshots/test_init.ambr +++ b/tests/components/smartthings/snapshots/test_init.ambr @@ -1058,6 +1058,39 @@ 'via_device_id': None, }) # --- +# name: test_devices[gas_meter] + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': 'https://account.smartthings.com', + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'smartthings', + '3b57dca3-9a90-4f27-ba80-f947b1e60d58', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'CopperLabs', + 'model': 'Virtual Gas Meter', + 'model_id': None, + 'name': 'Gas Meter', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'suggested_area': None, + 'sw_version': None, + 'via_device_id': None, + }) +# --- # name: test_devices[ge_in_wall_smart_dimmer] DeviceRegistryEntrySnapshot({ 'area_id': 'theater', diff --git a/tests/components/smartthings/snapshots/test_sensor.ambr b/tests/components/smartthings/snapshots/test_sensor.ambr index 8ace345be18..e9441f2e408 100644 --- a/tests/components/smartthings/snapshots/test_sensor.ambr +++ b/tests/components/smartthings/snapshots/test_sensor.ambr @@ -8007,6 +8007,208 @@ 'state': 'unknown', }) # --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas_meter_gas', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': , + }), + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Gas', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '3b57dca3-9a90-4f27-ba80-f947b1e60d58_main_gasMeter_gasMeterVolume_gasMeterVolume', + 'unit_of_measurement': , + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'gas', + 'friendly_name': 'Gas Meter Gas', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.gas_meter_gas', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '40', + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas_meter-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas_meter_gas_meter', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Gas meter', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'gas_meter', + 'unique_id': '3b57dca3-9a90-4f27-ba80-f947b1e60d58_main_gasMeter_gasMeter_gasMeter', + 'unit_of_measurement': 'kWh', + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas_meter-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'energy', + 'friendly_name': 'Gas Meter Gas meter', + 'state_class': , + 'unit_of_measurement': 'kWh', + }), + 'context': , + 'entity_id': 'sensor.gas_meter_gas_meter', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '450.5', + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas_meter_calorific-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas_meter_gas_meter_calorific', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Gas meter calorific', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'gas_meter_calorific', + 'unique_id': '3b57dca3-9a90-4f27-ba80-f947b1e60d58_main_gasMeter_gasMeterCalorific_gasMeterCalorific', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas_meter_calorific-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Gas Meter Gas meter calorific', + }), + 'context': , + 'entity_id': 'sensor.gas_meter_gas_meter_calorific', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '40', + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas_meter_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.gas_meter_gas_meter_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Gas meter time', + 'platform': 'smartthings', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'gas_meter_time', + 'unique_id': '3b57dca3-9a90-4f27-ba80-f947b1e60d58_main_gasMeter_gasMeterTime_gasMeterTime', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[gas_meter][sensor.gas_meter_gas_meter_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'Gas Meter Gas meter time', + }), + 'context': , + 'entity_id': 'sensor.gas_meter_gas_meter_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2025-04-11T13:30:00+00:00', + }) +# --- # name: test_all_entities[generic_ef00_v1][sensor.thermostat_kuche_link_quality-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ From d868f39aea0df06bbf3bd1e94e218c8abe5850f4 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 11 Apr 2025 08:30:12 -0700 Subject: [PATCH 43/47] Fix Anthropic bug parsing a streaming response with no json (#142745) --- .../components/anthropic/conversation.py | 2 +- .../components/anthropic/test_conversation.py | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/anthropic/conversation.py b/homeassistant/components/anthropic/conversation.py index 5e5ad464eaa..288ec63509e 100644 --- a/homeassistant/components/anthropic/conversation.py +++ b/homeassistant/components/anthropic/conversation.py @@ -266,7 +266,7 @@ async def _transform_stream( raise ValueError("Unexpected stop event without a current block") if current_block["type"] == "tool_use": tool_block = cast(ToolUseBlockParam, current_block) - tool_args = json.loads(current_tool_args) + tool_args = json.loads(current_tool_args) if current_tool_args else {} tool_block["input"] = tool_args yield { "tool_calls": [ diff --git a/tests/components/anthropic/test_conversation.py b/tests/components/anthropic/test_conversation.py index 67a4434a664..caaef43e931 100644 --- a/tests/components/anthropic/test_conversation.py +++ b/tests/components/anthropic/test_conversation.py @@ -303,11 +303,27 @@ async def test_conversation_agent( @patch("homeassistant.components.anthropic.conversation.llm.AssistAPI._async_get_tools") +@pytest.mark.parametrize( + ("tool_call_json_parts", "expected_call_tool_args"), + [ + ( + ['{"param1": "test_value"}'], + {"param1": "test_value"}, + ), + ( + ['{"para', 'm1": "test_valu', 'e"}'], + {"param1": "test_value"}, + ), + ([""], {}), + ], +) async def test_function_call( mock_get_tools, hass: HomeAssistant, mock_config_entry_with_assist: MockConfigEntry, mock_init_component, + tool_call_json_parts: list[str], + expected_call_tool_args: dict[str, Any], ) -> None: """Test function call from the assistant.""" agent_id = "conversation.claude" @@ -343,7 +359,7 @@ async def test_function_call( 1, "toolu_0123456789AbCdEfGhIjKlM", "test_tool", - ['{"para', 'm1": "test_valu', 'e"}'], + tool_call_json_parts, ), ] ) @@ -387,7 +403,7 @@ async def test_function_call( llm.ToolInput( id="toolu_0123456789AbCdEfGhIjKlM", tool_name="test_tool", - tool_args={"param1": "test_value"}, + tool_args=expected_call_tool_args, ), llm.LLMContext( platform="anthropic", From 933d008e52635cf3309986a0371735f2ba9d3926 Mon Sep 17 00:00:00 2001 From: Jeff Rescignano Date: Fri, 11 Apr 2025 12:07:27 -0400 Subject: [PATCH 44/47] Upgrade sharkiq depedency to 1.1.0 (#142746) --- homeassistant/components/sharkiq/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sharkiq/manifest.json b/homeassistant/components/sharkiq/manifest.json index 0e07dd96902..9f9009693e5 100644 --- a/homeassistant/components/sharkiq/manifest.json +++ b/homeassistant/components/sharkiq/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/sharkiq", "iot_class": "cloud_polling", "loggers": ["sharkiq"], - "requirements": ["sharkiq==1.0.2"] + "requirements": ["sharkiq==1.1.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 962c426cfc0..d689114d187 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2730,7 +2730,7 @@ sentry-sdk==1.45.1 sfrbox-api==0.0.11 # homeassistant.components.sharkiq -sharkiq==1.0.2 +sharkiq==1.1.0 # homeassistant.components.aquostv sharp_aquos_rc==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 99a77f19a46..c6df7040f83 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2207,7 +2207,7 @@ sentry-sdk==1.45.1 sfrbox-api==0.0.11 # homeassistant.components.sharkiq -sharkiq==1.0.2 +sharkiq==1.1.0 # homeassistant.components.simplefin simplefin4py==0.0.18 From 667cb772e9e862a1009586ddbd2257255a209162 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 11 Apr 2025 16:12:45 +0000 Subject: [PATCH 45/47] Bump version to 2025.4.2 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 12925dea143..ec6a6313542 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2025 MINOR_VERSION: Final = 4 -PATCH_VERSION: Final = "1" +PATCH_VERSION: Final = "2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 0) diff --git a/pyproject.toml b/pyproject.toml index 0c5a974a2aa..901ad944bec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2025.4.1" +version = "2025.4.2" license = "Apache-2.0" license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"] description = "Open-source home automation platform running on Python 3." From c7c645776d518af6cf60531b8a09ecce965379c3 Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Fri, 4 Apr 2025 07:21:40 +0200 Subject: [PATCH 46/47] Bump ical to 9.1.0 (#142197) --- homeassistant/components/google/manifest.json | 2 +- homeassistant/components/local_calendar/manifest.json | 2 +- homeassistant/components/local_todo/manifest.json | 2 +- homeassistant/components/remote_calendar/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index efce97a0d6f..2bedc7a3163 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/google", "iot_class": "cloud_polling", "loggers": ["googleapiclient"], - "requirements": ["gcal-sync==7.0.0", "oauth2client==4.1.3", "ical==9.0.3"] + "requirements": ["gcal-sync==7.0.0", "oauth2client==4.1.3", "ical==9.1.0"] } diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index 528552aaa57..90cd5a6d2ac 100644 --- a/homeassistant/components/local_calendar/manifest.json +++ b/homeassistant/components/local_calendar/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/local_calendar", "iot_class": "local_polling", "loggers": ["ical"], - "requirements": ["ical==9.0.3"] + "requirements": ["ical==9.1.0"] } diff --git a/homeassistant/components/local_todo/manifest.json b/homeassistant/components/local_todo/manifest.json index 6f117131c20..a630c18c669 100644 --- a/homeassistant/components/local_todo/manifest.json +++ b/homeassistant/components/local_todo/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/local_todo", "iot_class": "local_polling", - "requirements": ["ical==9.0.3"] + "requirements": ["ical==9.1.0"] } diff --git a/homeassistant/components/remote_calendar/manifest.json b/homeassistant/components/remote_calendar/manifest.json index 256f5baf0ff..da078395484 100644 --- a/homeassistant/components/remote_calendar/manifest.json +++ b/homeassistant/components/remote_calendar/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["ical"], "quality_scale": "silver", - "requirements": ["ical==9.0.3"] + "requirements": ["ical==9.1.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index d689114d187..ef0d477eda3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1196,7 +1196,7 @@ ibmiotf==0.3.4 # homeassistant.components.local_calendar # homeassistant.components.local_todo # homeassistant.components.remote_calendar -ical==9.0.3 +ical==9.1.0 # homeassistant.components.caldav icalendar==6.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c6df7040f83..ac142674d20 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1014,7 +1014,7 @@ ibeacon-ble==1.2.0 # homeassistant.components.local_calendar # homeassistant.components.local_todo # homeassistant.components.remote_calendar -ical==9.0.3 +ical==9.1.0 # homeassistant.components.caldav icalendar==6.1.0 From 7a1bea7ff5c6d79bb3f054f954f7ef08e5242123 Mon Sep 17 00:00:00 2001 From: Sanjay Govind Date: Tue, 8 Apr 2025 17:50:36 +1200 Subject: [PATCH 47/47] Add jaraco.itertools license exception as the classifier was removed but no SPDX expression was added (#142439) --- script/licenses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/script/licenses.py b/script/licenses.py index 448e9dd2a67..62e1845b911 100644 --- a/script/licenses.py +++ b/script/licenses.py @@ -190,6 +190,7 @@ EXCEPTIONS = { "enocean", # https://github.com/kipe/enocean/pull/142 "imutils", # https://github.com/PyImageSearch/imutils/pull/292 "iso4217", # Public domain + "jaraco.itertools", # MIT - https://github.com/jaraco/jaraco.itertools/issues/21 "kiwiki_client", # https://github.com/c7h/kiwiki_client/pull/6 "ld2410-ble", # https://github.com/930913/ld2410-ble/pull/7 "maxcube-api", # https://github.com/uebelack/python-maxcube-api/pull/48