diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index af0bdc5c2df..bde1635f692 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,7 +37,7 @@ on: type: boolean env: - CACHE_VERSION: 2 + CACHE_VERSION: 3 UV_CACHE_VERSION: 1 MYPY_CACHE_VERSION: 1 HA_SHORT_VERSION: "2025.6" diff --git a/homeassistant/brands/switchbot.json b/homeassistant/brands/switchbot.json index 0909b24a146..43963109ee7 100644 --- a/homeassistant/brands/switchbot.json +++ b/homeassistant/brands/switchbot.json @@ -1,5 +1,6 @@ { "domain": "switchbot", "name": "SwitchBot", - "integrations": ["switchbot", "switchbot_cloud"] + "integrations": ["switchbot", "switchbot_cloud"], + "iot_standards": ["matter"] } diff --git a/homeassistant/components/alexa_devices/manifest.json b/homeassistant/components/alexa_devices/manifest.json index 96f17d541fc..a2bb423860b 100644 --- a/homeassistant/components/alexa_devices/manifest.json +++ b/homeassistant/components/alexa_devices/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["aioamazondevices"], "quality_scale": "bronze", - "requirements": ["aioamazondevices==3.1.2"] + "requirements": ["aioamazondevices==3.1.14"] } diff --git a/homeassistant/components/bthome/manifest.json b/homeassistant/components/bthome/manifest.json index 4130606ff5c..0bbdfae50e4 100644 --- a/homeassistant/components/bthome/manifest.json +++ b/homeassistant/components/bthome/manifest.json @@ -20,5 +20,5 @@ "dependencies": ["bluetooth_adapters"], "documentation": "https://www.home-assistant.io/integrations/bthome", "iot_class": "local_push", - "requirements": ["bthome-ble==3.12.4"] + "requirements": ["bthome-ble==3.13.1"] } diff --git a/homeassistant/components/ecovacs/manifest.json b/homeassistant/components/ecovacs/manifest.json index 8a7388da735..97739f698d9 100644 --- a/homeassistant/components/ecovacs/manifest.json +++ b/homeassistant/components/ecovacs/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/ecovacs", "iot_class": "cloud_push", "loggers": ["sleekxmppfs", "sucks", "deebot_client"], - "requirements": ["py-sucks==0.9.11", "deebot-client==13.3.0"] + "requirements": ["py-sucks==0.9.11", "deebot-client==13.4.0"] } diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 9b70aba4de1..0577ed10c19 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -17,7 +17,7 @@ "mqtt": ["esphome/discover/#"], "quality_scale": "platinum", "requirements": [ - "aioesphomeapi==32.2.1", + "aioesphomeapi==33.0.0", "esphome-dashboard-api==1.3.0", "bleak-esphome==2.16.0" ], diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index fecd245869a..1acfa3a2ad1 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.1.0", "oauth2client==4.1.3", "ical==10.0.0"] + "requirements": ["gcal-sync==7.1.0", "oauth2client==4.1.3", "ical==10.0.4"] } diff --git a/homeassistant/components/holiday/manifest.json b/homeassistant/components/holiday/manifest.json index 5a5f1daf967..c76d6638730 100644 --- a/homeassistant/components/holiday/manifest.json +++ b/homeassistant/components/holiday/manifest.json @@ -5,5 +5,5 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/holiday", "iot_class": "local_polling", - "requirements": ["holidays==0.74", "babel==2.15.0"] + "requirements": ["holidays==0.75", "babel==2.15.0"] } diff --git a/homeassistant/components/home_connect/manifest.json b/homeassistant/components/home_connect/manifest.json index d4b37552fb7..34a3b756119 100644 --- a/homeassistant/components/home_connect/manifest.json +++ b/homeassistant/components/home_connect/manifest.json @@ -21,6 +21,6 @@ "documentation": "https://www.home-assistant.io/integrations/home_connect", "iot_class": "cloud_push", "loggers": ["aiohomeconnect"], - "requirements": ["aiohomeconnect==0.17.1"], + "requirements": ["aiohomeconnect==0.18.0"], "zeroconf": ["_homeconnect._tcp.local."] } diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index 163f3c402dc..d5af2859873 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", "iot_class": "cloud_push", "loggers": ["homematicip"], - "requirements": ["homematicip==2.0.5"] + "requirements": ["homematicip==2.0.6"] } diff --git a/homeassistant/components/lamarzocco/manifest.json b/homeassistant/components/lamarzocco/manifest.json index 46a29427264..7fdafc4dda1 100644 --- a/homeassistant/components/lamarzocco/manifest.json +++ b/homeassistant/components/lamarzocco/manifest.json @@ -37,5 +37,5 @@ "iot_class": "cloud_push", "loggers": ["pylamarzocco"], "quality_scale": "platinum", - "requirements": ["pylamarzocco==2.0.8"] + "requirements": ["pylamarzocco==2.0.9"] } diff --git a/homeassistant/components/lamarzocco/number.py b/homeassistant/components/lamarzocco/number.py index 980a08c09ae..f8cb8b1d6fe 100644 --- a/homeassistant/components/lamarzocco/number.py +++ b/homeassistant/components/lamarzocco/number.py @@ -58,6 +58,10 @@ ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = ( CoffeeBoiler, machine.dashboard.config[WidgetType.CM_COFFEE_BOILER] ).target_temperature ), + available_fn=( + lambda coordinator: WidgetType.CM_COFFEE_BOILER + in coordinator.device.dashboard.config + ), ), LaMarzoccoNumberEntityDescription( key="smart_standby_time", @@ -221,7 +225,7 @@ class LaMarzoccoNumberEntity(LaMarzoccoEntity, NumberEntity): entity_description: LaMarzoccoNumberEntityDescription @property - def native_value(self) -> float: + def native_value(self) -> float | int: """Return the current value.""" return self.entity_description.native_value_fn(self.coordinator.device) diff --git a/homeassistant/components/lamarzocco/sensor.py b/homeassistant/components/lamarzocco/sensor.py index 29f1c6209ec..c76f51c3488 100644 --- a/homeassistant/components/lamarzocco/sensor.py +++ b/homeassistant/components/lamarzocco/sensor.py @@ -57,6 +57,10 @@ ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = ( ).ready_start_time ), entity_category=EntityCategory.DIAGNOSTIC, + available_fn=( + lambda coordinator: WidgetType.CM_COFFEE_BOILER + in coordinator.device.dashboard.config + ), ), LaMarzoccoSensorEntityDescription( key="steam_boiler_ready_time", diff --git a/homeassistant/components/lcn/manifest.json b/homeassistant/components/lcn/manifest.json index be5d6299f09..30584bc33f6 100644 --- a/homeassistant/components/lcn/manifest.json +++ b/homeassistant/components/lcn/manifest.json @@ -8,5 +8,5 @@ "documentation": "https://www.home-assistant.io/integrations/lcn", "iot_class": "local_push", "loggers": ["pypck"], - "requirements": ["pypck==0.8.6", "lcn-frontend==0.2.5"] + "requirements": ["pypck==0.8.8", "lcn-frontend==0.2.5"] } diff --git a/homeassistant/components/local_calendar/manifest.json b/homeassistant/components/local_calendar/manifest.json index e0b08313d63..3bf00f30624 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==10.0.0"] + "requirements": ["ical==10.0.4"] } diff --git a/homeassistant/components/local_todo/manifest.json b/homeassistant/components/local_todo/manifest.json index c8e80e4f91b..134cea5293b 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==10.0.0"] + "requirements": ["ical==10.0.4"] } diff --git a/homeassistant/components/minecraft_server/__init__.py b/homeassistant/components/minecraft_server/__init__.py index d8f60380a6c..e74b78446e5 100644 --- a/homeassistant/components/minecraft_server/__init__.py +++ b/homeassistant/components/minecraft_server/__init__.py @@ -5,6 +5,7 @@ from __future__ import annotations import logging from typing import Any +import dns.asyncresolver import dns.rdata import dns.rdataclass import dns.rdatatype @@ -22,20 +23,23 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] _LOGGER = logging.getLogger(__name__) -def load_dnspython_rdata_classes() -> None: - """Load dnspython rdata classes used by mcstatus.""" +def prevent_dnspython_blocking_operations() -> None: + """Prevent dnspython blocking operations by pre-loading required data.""" + + # Blocking import: https://github.com/rthalley/dnspython/issues/1083 for rdtype in dns.rdatatype.RdataType: if not dns.rdatatype.is_metatype(rdtype) or rdtype == dns.rdatatype.OPT: dns.rdata.get_rdata_class(dns.rdataclass.IN, rdtype) # type: ignore[no-untyped-call] + # Blocking open: https://github.com/rthalley/dnspython/issues/1200 + dns.asyncresolver.get_default_resolver() + async def async_setup_entry( hass: HomeAssistant, entry: MinecraftServerConfigEntry ) -> bool: """Set up Minecraft Server from a config entry.""" - - # Workaround to avoid blocking imports from dnspython (https://github.com/rthalley/dnspython/issues/1083) - await hass.async_add_executor_job(load_dnspython_rdata_classes) + await hass.async_add_executor_job(prevent_dnspython_blocking_operations) # Create coordinator instance and store it. coordinator = MinecraftServerCoordinator(hass, entry) diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index 165c4c19675..9cff2956a5f 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -62,6 +62,7 @@ TILT_DEVICE_MAP = { BlindType.VerticalBlind: CoverDeviceClass.BLIND, BlindType.VerticalBlindLeft: CoverDeviceClass.BLIND, BlindType.VerticalBlindRight: CoverDeviceClass.BLIND, + BlindType.RollerTiltMotor: CoverDeviceClass.BLIND, } TILT_ONLY_DEVICE_MAP = { diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 1a6c9c5f82f..a82da20396f 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -21,5 +21,5 @@ "documentation": "https://www.home-assistant.io/integrations/motion_blinds", "iot_class": "local_push", "loggers": ["motionblinds"], - "requirements": ["motionblinds==0.6.27"] + "requirements": ["motionblinds==0.6.28"] } diff --git a/homeassistant/components/onedrive/coordinator.py b/homeassistant/components/onedrive/coordinator.py index 3eb7d762712..07a8dbd203b 100644 --- a/homeassistant/components/onedrive/coordinator.py +++ b/homeassistant/components/onedrive/coordinator.py @@ -66,6 +66,7 @@ class OneDriveUpdateCoordinator(DataUpdateCoordinator[Drive]): translation_domain=DOMAIN, translation_key="authentication_failed" ) from err except OneDriveException as err: + _LOGGER.debug("Failed to fetch drive data: %s", err, exc_info=True) raise UpdateFailed( translation_domain=DOMAIN, translation_key="update_failed" ) from err diff --git a/homeassistant/components/rachio/calendar.py b/homeassistant/components/rachio/calendar.py index 18b1b6a4d8f..a8b593e1138 100644 --- a/homeassistant/components/rachio/calendar.py +++ b/homeassistant/components/rachio/calendar.py @@ -16,10 +16,8 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util from .const import ( - KEY_ADDRESS, KEY_DURATION_SECONDS, KEY_ID, - KEY_LOCALITY, KEY_PROGRAM_ID, KEY_PROGRAM_NAME, KEY_RUN_SUMMARIES, @@ -65,7 +63,6 @@ class RachioCalendarEntity( super().__init__(coordinator) self.base_station = base_station self._event: CalendarEvent | None = None - self._location = coordinator.base_station[KEY_ADDRESS][KEY_LOCALITY] self._attr_translation_placeholders = { "base": coordinator.base_station[KEY_SERIAL_NUMBER] } @@ -87,7 +84,6 @@ class RachioCalendarEntity( end=dt_util.as_local(start_time) + timedelta(seconds=int(event[KEY_TOTAL_RUN_DURATION])), description=valves, - location=self._location, ) def _handle_upcoming_event(self) -> dict[str, Any] | None: @@ -155,7 +151,6 @@ class RachioCalendarEntity( start=event_start, end=event_end, description=valves, - location=self._location, uid=f"{run[KEY_PROGRAM_ID]}/{run[KEY_START_TIME]}", ) event_list.append(event) diff --git a/homeassistant/components/rachio/const.py b/homeassistant/components/rachio/const.py index 08a09f309f6..64b26526f57 100644 --- a/homeassistant/components/rachio/const.py +++ b/homeassistant/components/rachio/const.py @@ -75,8 +75,6 @@ KEY_PROGRAM_ID = "programId" KEY_PROGRAM_NAME = "programName" KEY_PROGRAM_RUN_SUMMARIES = "valveProgramRunSummaries" KEY_TOTAL_RUN_DURATION = "totalRunDurationSeconds" -KEY_ADDRESS = "address" -KEY_LOCALITY = "locality" KEY_SKIP = "skip" KEY_SKIPPABLE = "skippable" diff --git a/homeassistant/components/remote_calendar/manifest.json b/homeassistant/components/remote_calendar/manifest.json index 7bdc5362ae7..6da9ce4bb07 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==10.0.0"] + "requirements": ["ical==10.0.4"] } diff --git a/homeassistant/components/reolink/entity.py b/homeassistant/components/reolink/entity.py index d7e8817b1b7..0d91670fc84 100644 --- a/homeassistant/components/reolink/entity.py +++ b/homeassistant/components/reolink/entity.py @@ -82,7 +82,7 @@ class ReolinkHostCoordinatorEntity(CoordinatorEntity[DataUpdateCoordinator[None] connections={(CONNECTION_NETWORK_MAC, self._host.api.mac_address)}, name=self._host.api.nvr_name, model=self._host.api.model, - model_id=self._host.api.item_number, + model_id=self._host.api.item_number(), manufacturer=self._host.api.manufacturer, hw_version=self._host.api.hardware_version, sw_version=self._host.api.sw_version, diff --git a/homeassistant/components/reolink/manifest.json b/homeassistant/components/reolink/manifest.json index 5ae8b0305e4..04996689bf7 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.5"] + "requirements": ["reolink-aio==0.14.1"] } diff --git a/homeassistant/components/roku/manifest.json b/homeassistant/components/roku/manifest.json index 7fe2fb3b686..d5e2e2e5224 100644 --- a/homeassistant/components/roku/manifest.json +++ b/homeassistant/components/roku/manifest.json @@ -10,7 +10,7 @@ "integration_type": "device", "iot_class": "local_polling", "loggers": ["rokuecp"], - "requirements": ["rokuecp==0.19.3"], + "requirements": ["rokuecp==0.19.5"], "ssdp": [ { "st": "roku:ecp", diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index eab7514514d..ad03a373dba 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -235,11 +235,15 @@ class ShellyButton(ShellyBaseButton): self._attr_unique_id = f"{coordinator.mac}_{description.key}" if isinstance(coordinator, ShellyBlockCoordinator): self._attr_device_info = get_block_device_info( - coordinator.device, coordinator.mac + coordinator.device, + coordinator.mac, + suggested_area=coordinator.suggested_area, ) else: self._attr_device_info = get_rpc_device_info( - coordinator.device, coordinator.mac + coordinator.device, + coordinator.mac, + suggested_area=coordinator.suggested_area, ) self._attr_device_info = DeviceInfo( connections={(CONNECTION_NETWORK_MAC, coordinator.mac)} diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index 26fabe7e8b5..abc387f3efd 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -211,7 +211,10 @@ class BlockSleepingClimate( elif entry is not None: self._unique_id = entry.unique_id self._attr_device_info = get_block_device_info( - coordinator.device, coordinator.mac, sensor_block + coordinator.device, + coordinator.mac, + sensor_block, + suggested_area=coordinator.suggested_area, ) self._attr_name = get_block_entity_name( self.coordinator.device, sensor_block, None diff --git a/homeassistant/components/shelly/coordinator.py b/homeassistant/components/shelly/coordinator.py index f980ba8f914..fa434588b34 100644 --- a/homeassistant/components/shelly/coordinator.py +++ b/homeassistant/components/shelly/coordinator.py @@ -31,7 +31,11 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback -from homeassistant.helpers import device_registry as dr, issue_registry as ir +from homeassistant.helpers import ( + area_registry as ar, + device_registry as dr, + issue_registry as ir, +) from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, format_mac from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -114,6 +118,7 @@ class ShellyCoordinatorBase[_DeviceT: BlockDevice | RpcDevice]( self.device = device self.device_id: str | None = None self._pending_platforms: list[Platform] | None = None + self.suggested_area: str | None = None device_name = device.name if device.initialized else entry.title interval_td = timedelta(seconds=update_interval) # The device has come online at least once. In the case of a sleeping RPC @@ -176,6 +181,11 @@ class ShellyCoordinatorBase[_DeviceT: BlockDevice | RpcDevice]( hw_version=f"gen{get_device_entry_gen(self.config_entry)}", configuration_url=f"http://{get_host(self.config_entry.data[CONF_HOST])}:{get_http_port(self.config_entry.data)}", ) + # We want to use the main device area as the suggested area for sub-devices. + if (area_id := device_entry.area_id) is not None: + area_registry = ar.async_get(self.hass) + if (area := area_registry.async_get_area(area_id)) is not None: + self.suggested_area = area.name self.device_id = device_entry.id async def shutdown(self) -> None: @@ -825,6 +835,15 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]): except InvalidAuthError: self.config_entry.async_start_reauth(self.hass) return + except RpcCallError as err: + # Ignore 404 (No handler for) error + if err.code != 404: + LOGGER.debug( + "Error during shutdown for device %s: %s", + self.name, + err.message, + ) + return except DeviceConnectionError as err: # If the device is restarting or has gone offline before # the ping/pong timeout happens, the shutdown command diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index 1b0078890af..5a420a4543b 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -362,7 +362,10 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]): self.block = block self._attr_name = get_block_entity_name(coordinator.device, block) self._attr_device_info = get_block_device_info( - coordinator.device, coordinator.mac, block + coordinator.device, + coordinator.mac, + block, + suggested_area=coordinator.suggested_area, ) self._attr_unique_id = f"{coordinator.mac}-{block.description}" @@ -405,7 +408,10 @@ class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]): super().__init__(coordinator) self.key = key self._attr_device_info = get_rpc_device_info( - coordinator.device, coordinator.mac, key + coordinator.device, + coordinator.mac, + key, + suggested_area=coordinator.suggested_area, ) self._attr_unique_id = f"{coordinator.mac}-{key}" self._attr_name = get_rpc_entity_name(coordinator.device, key) @@ -521,7 +527,9 @@ class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]): ) self._attr_unique_id = f"{coordinator.mac}-{attribute}" self._attr_device_info = get_block_device_info( - coordinator.device, coordinator.mac + coordinator.device, + coordinator.mac, + suggested_area=coordinator.suggested_area, ) self._last_value = None @@ -630,7 +638,10 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity): self.entity_description = description self._attr_device_info = get_block_device_info( - coordinator.device, coordinator.mac, block + coordinator.device, + coordinator.mac, + block, + suggested_area=coordinator.suggested_area, ) if block is not None: @@ -642,7 +653,6 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity): ) elif entry is not None: self._attr_unique_id = entry.unique_id - self._attr_name = cast(str, entry.original_name) @callback def _update_callback(self) -> None: @@ -698,7 +708,10 @@ class ShellySleepingRpcAttributeEntity(ShellyRpcAttributeEntity): self.entity_description = description self._attr_device_info = get_rpc_device_info( - coordinator.device, coordinator.mac, key + coordinator.device, + coordinator.mac, + key, + suggested_area=coordinator.suggested_area, ) self._attr_unique_id = self._attr_unique_id = ( f"{coordinator.mac}-{key}-{attribute}" diff --git a/homeassistant/components/shelly/event.py b/homeassistant/components/shelly/event.py index 677ea1f6138..2eb9ff00964 100644 --- a/homeassistant/components/shelly/event.py +++ b/homeassistant/components/shelly/event.py @@ -207,7 +207,10 @@ class ShellyRpcEvent(CoordinatorEntity[ShellyRpcCoordinator], EventEntity): super().__init__(coordinator) self.event_id = int(key.split(":")[-1]) self._attr_device_info = get_rpc_device_info( - coordinator.device, coordinator.mac, key + coordinator.device, + coordinator.mac, + key, + suggested_area=coordinator.suggested_area, ) self._attr_unique_id = f"{coordinator.mac}-{key}" self._attr_name = get_rpc_entity_name(coordinator.device, key) diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 0ea246c7734..3a6f5f221c5 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -139,7 +139,11 @@ class RpcEmeterPhaseSensor(RpcSensor): super().__init__(coordinator, key, attribute, description) self._attr_device_info = get_rpc_device_info( - coordinator.device, coordinator.mac, key, description.emeter_phase + coordinator.device, + coordinator.mac, + key, + emeter_phase=description.emeter_phase, + suggested_area=coordinator.suggested_area, ) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index cc0f2cf75d5..953fcbace06 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -751,6 +751,7 @@ def get_rpc_device_info( mac: str, key: str | None = None, emeter_phase: str | None = None, + suggested_area: str | None = None, ) -> DeviceInfo: """Return device info for RPC device.""" if key is None: @@ -770,6 +771,7 @@ def get_rpc_device_info( identifiers={(DOMAIN, f"{mac}-{key}-{emeter_phase.lower()}")}, name=get_rpc_sub_device_name(device, key, emeter_phase), manufacturer="Shelly", + suggested_area=suggested_area, via_device=(DOMAIN, mac), ) @@ -784,6 +786,7 @@ def get_rpc_device_info( identifiers={(DOMAIN, f"{mac}-{key}")}, name=get_rpc_sub_device_name(device, key), manufacturer="Shelly", + suggested_area=suggested_area, via_device=(DOMAIN, mac), ) @@ -805,7 +808,10 @@ def get_blu_trv_device_info( def get_block_device_info( - device: BlockDevice, mac: str, block: Block | None = None + device: BlockDevice, + mac: str, + block: Block | None = None, + suggested_area: str | None = None, ) -> DeviceInfo: """Return device info for Block device.""" if ( @@ -820,6 +826,7 @@ def get_block_device_info( identifiers={(DOMAIN, f"{mac}-{block.description}")}, name=get_block_sub_device_name(device, block), manufacturer="Shelly", + suggested_area=suggested_area, via_device=(DOMAIN, mac), ) diff --git a/homeassistant/components/smartthings/manifest.json b/homeassistant/components/smartthings/manifest.json index 481048c3bdb..7c3fc47e512 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.2.4"] + "requirements": ["pysmartthings==3.2.5"] } diff --git a/homeassistant/components/switchbot_cloud/binary_sensor.py b/homeassistant/components/switchbot_cloud/binary_sensor.py index 14278072c83..752c428fa6c 100644 --- a/homeassistant/components/switchbot_cloud/binary_sensor.py +++ b/homeassistant/components/switchbot_cloud/binary_sensor.py @@ -69,6 +69,7 @@ async def async_setup_entry( for description in BINARY_SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES[ device.device_type ] + if device.device_type in BINARY_SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES ) diff --git a/homeassistant/components/switchbot_cloud/sensor.py b/homeassistant/components/switchbot_cloud/sensor.py index 9975bd49186..9920717a8d7 100644 --- a/homeassistant/components/switchbot_cloud/sensor.py +++ b/homeassistant/components/switchbot_cloud/sensor.py @@ -151,6 +151,7 @@ async def async_setup_entry( SwitchBotCloudSensor(data.api, device, coordinator, description) for device, coordinator in data.devices.sensors for description in SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES[device.device_type] + if device.device_type in SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES ) diff --git a/homeassistant/components/teslemetry/binary_sensor.py b/homeassistant/components/teslemetry/binary_sensor.py index a32c5fea40e..439df76c838 100644 --- a/homeassistant/components/teslemetry/binary_sensor.py +++ b/homeassistant/components/teslemetry/binary_sensor.py @@ -126,7 +126,7 @@ VEHICLE_DESCRIPTIONS: tuple[TeslemetryBinarySensorEntityDescription, ...] = ( polling=True, polling_value_fn=lambda x: x != "", streaming_listener=lambda vehicle, callback: vehicle.listen_ChargingCableType( - lambda value: callback(value != "Unknown") + lambda value: callback(value is not None and value != "Unknown") ), entity_category=EntityCategory.DIAGNOSTIC, device_class=BinarySensorDeviceClass.CONNECTIVITY, diff --git a/homeassistant/components/traccar/__init__.py b/homeassistant/components/traccar/__init__.py index 5b9bc2551b7..e8c151179ce 100644 --- a/homeassistant/components/traccar/__init__.py +++ b/homeassistant/components/traccar/__init__.py @@ -1,9 +1,12 @@ """Support for Traccar Client.""" from http import HTTPStatus +from json import JSONDecodeError +import logging from aiohttp import web import voluptuous as vol +from voluptuous.humanize import humanize_error from homeassistant.components import webhook from homeassistant.config_entries import ConfigEntry @@ -20,7 +23,6 @@ from .const import ( ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_SPEED, - ATTR_TIMESTAMP, DOMAIN, ) @@ -29,6 +31,7 @@ PLATFORMS = [Platform.DEVICE_TRACKER] TRACKER_UPDATE = f"{DOMAIN}_tracker_update" +LOGGER = logging.getLogger(__name__) DEFAULT_ACCURACY = 200 DEFAULT_BATTERY = -1 @@ -49,21 +52,50 @@ WEBHOOK_SCHEMA = vol.Schema( vol.Optional(ATTR_BATTERY, default=DEFAULT_BATTERY): vol.Coerce(float), vol.Optional(ATTR_BEARING): vol.Coerce(float), vol.Optional(ATTR_SPEED): vol.Coerce(float), - vol.Optional(ATTR_TIMESTAMP): vol.Coerce(int), }, extra=vol.REMOVE_EXTRA, ) +def _parse_json_body(json_body: dict) -> dict: + """Parse JSON body from request.""" + location = json_body.get("location", {}) + coords = location.get("coords", {}) + battery_level = location.get("battery", {}).get("level") + return { + "id": json_body.get("device_id"), + "lat": coords.get("latitude"), + "lon": coords.get("longitude"), + "accuracy": coords.get("accuracy"), + "altitude": coords.get("altitude"), + "batt": battery_level * 100 if battery_level is not None else DEFAULT_BATTERY, + "bearing": coords.get("heading"), + "speed": coords.get("speed"), + } + + async def handle_webhook( - hass: HomeAssistant, webhook_id: str, request: web.Request + hass: HomeAssistant, + webhook_id: str, + request: web.Request, ) -> web.Response: """Handle incoming webhook with Traccar Client request.""" + if not (requestdata := dict(request.query)): + try: + requestdata = _parse_json_body(await request.json()) + except JSONDecodeError as error: + LOGGER.error("Error parsing JSON body: %s", error) + return web.Response( + text="Invalid JSON", + status=HTTPStatus.UNPROCESSABLE_ENTITY, + ) try: - data = WEBHOOK_SCHEMA(dict(request.query)) + data = WEBHOOK_SCHEMA(requestdata) except vol.MultipleInvalid as error: + LOGGER.warning(humanize_error(requestdata, error)) return web.Response( - text=error.error_message, status=HTTPStatus.UNPROCESSABLE_ENTITY + text=error.error_message, + status=HTTPStatus.UNPROCESSABLE_ENTITY, ) attrs = { diff --git a/homeassistant/components/traccar/const.py b/homeassistant/components/traccar/const.py index df4bfa8ec99..f6928cc9ee9 100644 --- a/homeassistant/components/traccar/const.py +++ b/homeassistant/components/traccar/const.py @@ -17,7 +17,6 @@ ATTR_LONGITUDE = "lon" ATTR_MOTION = "motion" ATTR_SPEED = "speed" ATTR_STATUS = "status" -ATTR_TIMESTAMP = "timestamp" ATTR_TRACKER = "tracker" ATTR_TRACCAR_ID = "traccar_id" diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 64bb278a8e2..47e2a01e798 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -40,7 +40,7 @@ "integration_type": "hub", "iot_class": "local_push", "loggers": ["uiprotect", "unifi_discovery"], - "requirements": ["uiprotect==7.11.0", "unifi-discovery==1.2.0"], + "requirements": ["uiprotect==7.14.1", "unifi-discovery==1.2.0"], "ssdp": [ { "manufacturer": "Ubiquiti Networks", diff --git a/homeassistant/components/wallbox/const.py b/homeassistant/components/wallbox/const.py index d978e1ec7c9..34d17e52275 100644 --- a/homeassistant/components/wallbox/const.py +++ b/homeassistant/components/wallbox/const.py @@ -3,7 +3,7 @@ from enum import StrEnum DOMAIN = "wallbox" -UPDATE_INTERVAL = 30 +UPDATE_INTERVAL = 60 BIDIRECTIONAL_MODEL_PREFIXES = ["QS"] @@ -74,3 +74,4 @@ class EcoSmartMode(StrEnum): OFF = "off" ECO_MODE = "eco_mode" FULL_SOLAR = "full_solar" + DISABLED = "disabled" diff --git a/homeassistant/components/wallbox/coordinator.py b/homeassistant/components/wallbox/coordinator.py index 60f062e57cc..598bfa7429a 100644 --- a/homeassistant/components/wallbox/coordinator.py +++ b/homeassistant/components/wallbox/coordinator.py @@ -90,7 +90,9 @@ def _require_authentication[_WallboxCoordinatorT: WallboxCoordinator, **_P]( except requests.exceptions.HTTPError as wallbox_connection_error: if wallbox_connection_error.response.status_code == HTTPStatus.FORBIDDEN: raise ConfigEntryAuthFailed from wallbox_connection_error - raise ConnectionError from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error return require_authentication @@ -137,49 +139,65 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): @_require_authentication def _get_data(self) -> dict[str, Any]: """Get new sensor data for Wallbox component.""" - data: dict[str, Any] = self._wallbox.getChargerStatus(self._station) - data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][ - CHARGER_MAX_CHARGING_CURRENT_KEY - ] - data[CHARGER_LOCKED_UNLOCKED_KEY] = data[CHARGER_DATA_KEY][ - CHARGER_LOCKED_UNLOCKED_KEY - ] - data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][ - CHARGER_ENERGY_PRICE_KEY - ] - # Only show max_icp_current if power_boost is available in the wallbox unit: - if ( - data[CHARGER_DATA_KEY].get(CHARGER_MAX_ICP_CURRENT_KEY, 0) > 0 - and CHARGER_POWER_BOOST_KEY - in data[CHARGER_DATA_KEY][CHARGER_PLAN_KEY][CHARGER_FEATURES_KEY] - ): - data[CHARGER_MAX_ICP_CURRENT_KEY] = data[CHARGER_DATA_KEY][ - CHARGER_MAX_ICP_CURRENT_KEY + try: + data: dict[str, Any] = self._wallbox.getChargerStatus(self._station) + data[CHARGER_MAX_CHARGING_CURRENT_KEY] = data[CHARGER_DATA_KEY][ + CHARGER_MAX_CHARGING_CURRENT_KEY ] + data[CHARGER_LOCKED_UNLOCKED_KEY] = data[CHARGER_DATA_KEY][ + CHARGER_LOCKED_UNLOCKED_KEY + ] + data[CHARGER_ENERGY_PRICE_KEY] = data[CHARGER_DATA_KEY][ + CHARGER_ENERGY_PRICE_KEY + ] + # Only show max_icp_current if power_boost is available in the wallbox unit: + if ( + data[CHARGER_DATA_KEY].get(CHARGER_MAX_ICP_CURRENT_KEY, 0) > 0 + and CHARGER_POWER_BOOST_KEY + in data[CHARGER_DATA_KEY][CHARGER_PLAN_KEY][CHARGER_FEATURES_KEY] + ): + data[CHARGER_MAX_ICP_CURRENT_KEY] = data[CHARGER_DATA_KEY][ + CHARGER_MAX_ICP_CURRENT_KEY + ] - data[CHARGER_CURRENCY_KEY] = ( - f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh" - ) + data[CHARGER_CURRENCY_KEY] = ( + f"{data[CHARGER_DATA_KEY][CHARGER_CURRENCY_KEY][CODE_KEY]}/kWh" + ) - data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get( - data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN - ) + data[CHARGER_STATUS_DESCRIPTION_KEY] = CHARGER_STATUS.get( + data[CHARGER_STATUS_ID_KEY], ChargerStatus.UNKNOWN + ) - # Set current solar charging mode - eco_smart_enabled = data[CHARGER_DATA_KEY][CHARGER_ECO_SMART_KEY][ - CHARGER_ECO_SMART_STATUS_KEY - ] - eco_smart_mode = data[CHARGER_DATA_KEY][CHARGER_ECO_SMART_KEY][ - CHARGER_ECO_SMART_MODE_KEY - ] - if eco_smart_enabled is False: - data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.OFF - elif eco_smart_mode == 0: - data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.ECO_MODE - elif eco_smart_mode == 1: - data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.FULL_SOLAR + # Set current solar charging mode + eco_smart_enabled = ( + data[CHARGER_DATA_KEY] + .get(CHARGER_ECO_SMART_KEY, {}) + .get(CHARGER_ECO_SMART_STATUS_KEY) + ) - return data + eco_smart_mode = ( + data[CHARGER_DATA_KEY] + .get(CHARGER_ECO_SMART_KEY, {}) + .get(CHARGER_ECO_SMART_MODE_KEY) + ) + if eco_smart_mode is None: + data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.DISABLED + elif eco_smart_enabled is False: + data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.OFF + elif eco_smart_mode == 0: + data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.ECO_MODE + elif eco_smart_mode == 1: + data[CHARGER_ECO_SMART_KEY] = EcoSmartMode.FULL_SOLAR + + return data # noqa: TRY300 + except requests.exceptions.HTTPError as wallbox_connection_error: + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def _async_update_data(self) -> dict[str, Any]: """Get new sensor data for Wallbox component.""" @@ -193,7 +211,13 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): except requests.exceptions.HTTPError as wallbox_connection_error: if wallbox_connection_error.response.status_code == 403: raise InvalidAuth from wallbox_connection_error - raise + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def async_set_charging_current(self, charging_current: float) -> None: """Set maximum charging current for Wallbox.""" @@ -210,7 +234,13 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): except requests.exceptions.HTTPError as wallbox_connection_error: if wallbox_connection_error.response.status_code == 403: raise InvalidAuth from wallbox_connection_error - raise + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def async_set_icp_current(self, icp_current: float) -> None: """Set maximum icp current for Wallbox.""" @@ -220,8 +250,16 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): @_require_authentication def _set_energy_cost(self, energy_cost: float) -> None: """Set energy cost for Wallbox.""" - - self._wallbox.setEnergyCost(self._station, energy_cost) + try: + self._wallbox.setEnergyCost(self._station, energy_cost) + except requests.exceptions.HTTPError as wallbox_connection_error: + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def async_set_energy_cost(self, energy_cost: float) -> None: """Set energy cost for Wallbox.""" @@ -239,7 +277,13 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): except requests.exceptions.HTTPError as wallbox_connection_error: if wallbox_connection_error.response.status_code == 403: raise InvalidAuth from wallbox_connection_error - raise + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def async_set_lock_unlock(self, lock: bool) -> None: """Set wallbox to locked or unlocked.""" @@ -249,11 +293,19 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): @_require_authentication def _pause_charger(self, pause: bool) -> None: """Set wallbox to pause or resume.""" - - if pause: - self._wallbox.pauseChargingSession(self._station) - else: - self._wallbox.resumeChargingSession(self._station) + try: + if pause: + self._wallbox.pauseChargingSession(self._station) + else: + self._wallbox.resumeChargingSession(self._station) + except requests.exceptions.HTTPError as wallbox_connection_error: + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def async_pause_charger(self, pause: bool) -> None: """Set wallbox to pause or resume.""" @@ -263,13 +315,21 @@ class WallboxCoordinator(DataUpdateCoordinator[dict[str, Any]]): @_require_authentication def _set_eco_smart(self, option: str) -> None: """Set wallbox solar charging mode.""" - - if option == EcoSmartMode.ECO_MODE: - self._wallbox.enableEcoSmart(self._station, 0) - elif option == EcoSmartMode.FULL_SOLAR: - self._wallbox.enableEcoSmart(self._station, 1) - else: - self._wallbox.disableEcoSmart(self._station) + try: + if option == EcoSmartMode.ECO_MODE: + self._wallbox.enableEcoSmart(self._station, 0) + elif option == EcoSmartMode.FULL_SOLAR: + self._wallbox.enableEcoSmart(self._station, 1) + else: + self._wallbox.disableEcoSmart(self._station) + except requests.exceptions.HTTPError as wallbox_connection_error: + if wallbox_connection_error.response.status_code == 429: + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="too_many_requests" + ) from wallbox_connection_error + raise HomeAssistantError( + translation_domain=DOMAIN, translation_key="api_failed" + ) from wallbox_connection_error async def async_set_eco_smart(self, option: str) -> None: """Set wallbox solar charging mode.""" diff --git a/homeassistant/components/wallbox/lock.py b/homeassistant/components/wallbox/lock.py index ef35734ed7e..7acc56f67f2 100644 --- a/homeassistant/components/wallbox/lock.py +++ b/homeassistant/components/wallbox/lock.py @@ -7,7 +7,7 @@ from typing import Any from homeassistant.components.lock import LockEntity, LockEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import PlatformNotReady +from homeassistant.exceptions import HomeAssistantError, PlatformNotReady from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( @@ -41,7 +41,7 @@ async def async_setup_entry( ) except InvalidAuth: return - except ConnectionError as exc: + except HomeAssistantError as exc: raise PlatformNotReady from exc async_add_entities( diff --git a/homeassistant/components/wallbox/number.py b/homeassistant/components/wallbox/number.py index a5880f6e0f7..80773478582 100644 --- a/homeassistant/components/wallbox/number.py +++ b/homeassistant/components/wallbox/number.py @@ -12,7 +12,7 @@ from typing import cast from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import PlatformNotReady +from homeassistant.exceptions import HomeAssistantError, PlatformNotReady from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( @@ -93,7 +93,7 @@ async def async_setup_entry( ) except InvalidAuth: return - except ConnectionError as exc: + except HomeAssistantError as exc: raise PlatformNotReady from exc async_add_entities( diff --git a/homeassistant/components/wallbox/select.py b/homeassistant/components/wallbox/select.py index 7ad7a135bc8..0048aa35c7c 100644 --- a/homeassistant/components/wallbox/select.py +++ b/homeassistant/components/wallbox/select.py @@ -63,15 +63,15 @@ async def async_setup_entry( ) -> None: """Create wallbox select entities in HASS.""" coordinator: WallboxCoordinator = hass.data[DOMAIN][entry.entry_id] - - async_add_entities( - WallboxSelect(coordinator, description) - for ent in coordinator.data - if ( - (description := SELECT_TYPES.get(ent)) - and description.supported_fn(coordinator) + if coordinator.data[CHARGER_ECO_SMART_KEY] != EcoSmartMode.DISABLED: + async_add_entities( + WallboxSelect(coordinator, description) + for ent in coordinator.data + if ( + (description := SELECT_TYPES.get(ent)) + and description.supported_fn(coordinator) + ) ) - ) class WallboxSelect(WallboxEntity, SelectEntity): diff --git a/homeassistant/components/wallbox/sensor.py b/homeassistant/components/wallbox/sensor.py index 4b0ec8175e3..e19fc2b936a 100644 --- a/homeassistant/components/wallbox/sensor.py +++ b/homeassistant/components/wallbox/sensor.py @@ -3,7 +3,6 @@ from __future__ import annotations from dataclasses import dataclass -import logging from typing import cast from homeassistant.components.sensor import ( @@ -49,11 +48,6 @@ from .const import ( from .coordinator import WallboxCoordinator from .entity import WallboxEntity -CHARGER_STATION = "station" -UPDATE_INTERVAL = 30 - -_LOGGER = logging.getLogger(__name__) - @dataclass(frozen=True) class WallboxSensorEntityDescription(SensorEntityDescription): diff --git a/homeassistant/components/wallbox/strings.json b/homeassistant/components/wallbox/strings.json index 68602a960c2..ee98a4855e3 100644 --- a/homeassistant/components/wallbox/strings.json +++ b/homeassistant/components/wallbox/strings.json @@ -112,6 +112,9 @@ "exceptions": { "api_failed": { "message": "Error communicating with Wallbox API" + }, + "too_many_requests": { + "message": "Error communicating with Wallbox API, too many requests" } } } diff --git a/homeassistant/components/workday/manifest.json b/homeassistant/components/workday/manifest.json index 9091dd131dd..f9fae38f1f5 100644 --- a/homeassistant/components/workday/manifest.json +++ b/homeassistant/components/workday/manifest.json @@ -7,5 +7,5 @@ "iot_class": "local_polling", "loggers": ["holidays"], "quality_scale": "internal", - "requirements": ["holidays==0.74"] + "requirements": ["holidays==0.75"] } diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 4a5ec7be1dc..4908298847b 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -21,7 +21,7 @@ "zha", "universal_silabs_flasher" ], - "requirements": ["zha==0.0.59"], + "requirements": ["zha==0.0.60"], "usb": [ { "vid": "10C4", diff --git a/homeassistant/components/zwave_js/binary_sensor.py b/homeassistant/components/zwave_js/binary_sensor.py index 1439aa0ca0f..d70690ace31 100644 --- a/homeassistant/components/zwave_js/binary_sensor.py +++ b/homeassistant/components/zwave_js/binary_sensor.py @@ -318,12 +318,37 @@ PROPERTY_SENSOR_MAPPINGS: dict[str, PropertyZWaveJSEntityDescription] = { # Mappings for boolean sensors -BOOLEAN_SENSOR_MAPPINGS: dict[int, BinarySensorEntityDescription] = { - CommandClass.BATTERY: BinarySensorEntityDescription( - key=str(CommandClass.BATTERY), +BOOLEAN_SENSOR_MAPPINGS: dict[tuple[int, int | str], BinarySensorEntityDescription] = { + (CommandClass.BATTERY, "backup"): BinarySensorEntityDescription( + key="battery_backup", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + (CommandClass.BATTERY, "disconnected"): BinarySensorEntityDescription( + key="battery_disconnected", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + (CommandClass.BATTERY, "isLow"): BinarySensorEntityDescription( + key="battery_is_low", device_class=BinarySensorDeviceClass.BATTERY, entity_category=EntityCategory.DIAGNOSTIC, ), + (CommandClass.BATTERY, "lowFluid"): BinarySensorEntityDescription( + key="battery_low_fluid", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + (CommandClass.BATTERY, "overheating"): BinarySensorEntityDescription( + key="battery_overheating", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), + (CommandClass.BATTERY, "rechargeable"): BinarySensorEntityDescription( + key="battery_rechargeable", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), } @@ -432,8 +457,9 @@ class ZWaveBooleanBinarySensor(ZWaveBaseEntity, BinarySensorEntity): # Entity class attributes self._attr_name = self.generate_name(include_value_name=True) + primary_value = self.info.primary_value if description := BOOLEAN_SENSOR_MAPPINGS.get( - self.info.primary_value.command_class + (primary_value.command_class, primary_value.property_) ): self.entity_description = description diff --git a/homeassistant/components/zwave_js/const.py b/homeassistant/components/zwave_js/const.py index 3d626710d52..a99e9fd0113 100644 --- a/homeassistant/components/zwave_js/const.py +++ b/homeassistant/components/zwave_js/const.py @@ -139,7 +139,10 @@ ATTR_TWIST_ASSIST = "twist_assist" ADDON_SLUG = "core_zwave_js" # Sensor entity description constants -ENTITY_DESC_KEY_BATTERY = "battery" +ENTITY_DESC_KEY_BATTERY_LEVEL = "battery_level" +ENTITY_DESC_KEY_BATTERY_LIST_STATE = "battery_list_state" +ENTITY_DESC_KEY_BATTERY_MAXIMUM_CAPACITY = "battery_maximum_capacity" +ENTITY_DESC_KEY_BATTERY_TEMPERATURE = "battery_temperature" ENTITY_DESC_KEY_CURRENT = "current" ENTITY_DESC_KEY_VOLTAGE = "voltage" ENTITY_DESC_KEY_ENERGY_MEASUREMENT = "energy_measurement" diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py index b46735e4040..92233dd2e77 100644 --- a/homeassistant/components/zwave_js/discovery.py +++ b/homeassistant/components/zwave_js/discovery.py @@ -896,6 +896,7 @@ DISCOVERY_SCHEMAS = [ writeable=False, ), entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, ), # generic text sensors ZWaveDiscoverySchema( @@ -912,7 +913,6 @@ DISCOVERY_SCHEMAS = [ hint="numeric_sensor", primary_value=ZWaveValueDiscoverySchema( command_class={ - CommandClass.BATTERY, CommandClass.ENERGY_PRODUCTION, CommandClass.SENSOR_ALARM, CommandClass.SENSOR_MULTILEVEL, @@ -921,6 +921,36 @@ DISCOVERY_SCHEMAS = [ ), data_template=NumericSensorDataTemplate(), ), + ZWaveDiscoverySchema( + platform=Platform.SENSOR, + hint="numeric_sensor", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.BATTERY}, + type={ValueType.NUMBER}, + property={"level", "maximumCapacity"}, + ), + data_template=NumericSensorDataTemplate(), + ), + ZWaveDiscoverySchema( + platform=Platform.SENSOR, + hint="numeric_sensor", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.BATTERY}, + type={ValueType.NUMBER}, + property={"temperature"}, + ), + data_template=NumericSensorDataTemplate(), + ), + ZWaveDiscoverySchema( + platform=Platform.SENSOR, + hint="list", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.BATTERY}, + type={ValueType.NUMBER}, + property={"chargingStatus", "rechargeOrReplace"}, + ), + data_template=NumericSensorDataTemplate(), + ), ZWaveDiscoverySchema( platform=Platform.SENSOR, hint="numeric_sensor", @@ -932,6 +962,7 @@ DISCOVERY_SCHEMAS = [ ), data_template=NumericSensorDataTemplate(), entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, ), # Meter sensors for Meter CC ZWaveDiscoverySchema( @@ -957,6 +988,7 @@ DISCOVERY_SCHEMAS = [ writeable=True, ), entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, ), # button for Indicator CC ZWaveDiscoverySchema( @@ -980,6 +1012,7 @@ DISCOVERY_SCHEMAS = [ writeable=True, ), entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, ), # binary switch # barrier operator signaling states @@ -1184,6 +1217,7 @@ DISCOVERY_SCHEMAS = [ any_available_states={(0, "idle")}, ), allow_multi=True, + entity_registry_enabled_default=False, ), # event # stateful = False diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index e619c6afc7c..731a786d226 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -133,7 +133,10 @@ from homeassistant.const import ( ) from .const import ( - ENTITY_DESC_KEY_BATTERY, + ENTITY_DESC_KEY_BATTERY_LEVEL, + ENTITY_DESC_KEY_BATTERY_LIST_STATE, + ENTITY_DESC_KEY_BATTERY_MAXIMUM_CAPACITY, + ENTITY_DESC_KEY_BATTERY_TEMPERATURE, ENTITY_DESC_KEY_CO, ENTITY_DESC_KEY_CO2, ENTITY_DESC_KEY_CURRENT, @@ -380,8 +383,31 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): def resolve_data(self, value: ZwaveValue) -> NumericSensorDataTemplateData: """Resolve helper class data for a discovered value.""" - if value.command_class == CommandClass.BATTERY: - return NumericSensorDataTemplateData(ENTITY_DESC_KEY_BATTERY, PERCENTAGE) + if value.command_class == CommandClass.BATTERY and value.property_ == "level": + return NumericSensorDataTemplateData( + ENTITY_DESC_KEY_BATTERY_LEVEL, PERCENTAGE + ) + if value.command_class == CommandClass.BATTERY and value.property_ in ( + "chargingStatus", + "rechargeOrReplace", + ): + return NumericSensorDataTemplateData( + ENTITY_DESC_KEY_BATTERY_LIST_STATE, None + ) + if ( + value.command_class == CommandClass.BATTERY + and value.property_ == "maximumCapacity" + ): + return NumericSensorDataTemplateData( + ENTITY_DESC_KEY_BATTERY_MAXIMUM_CAPACITY, PERCENTAGE + ) + if ( + value.command_class == CommandClass.BATTERY + and value.property_ == "temperature" + ): + return NumericSensorDataTemplateData( + ENTITY_DESC_KEY_BATTERY_TEMPERATURE, UnitOfTemperature.CELSIUS + ) if value.command_class == CommandClass.METER: try: diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index 4db14d003b1..05fa785760b 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -58,7 +58,10 @@ from .const import ( ATTR_VALUE, DATA_CLIENT, DOMAIN, - ENTITY_DESC_KEY_BATTERY, + ENTITY_DESC_KEY_BATTERY_LEVEL, + ENTITY_DESC_KEY_BATTERY_LIST_STATE, + ENTITY_DESC_KEY_BATTERY_MAXIMUM_CAPACITY, + ENTITY_DESC_KEY_BATTERY_TEMPERATURE, ENTITY_DESC_KEY_CO, ENTITY_DESC_KEY_CO2, ENTITY_DESC_KEY_CURRENT, @@ -95,17 +98,33 @@ from .migrate import async_migrate_statistics_sensors PARALLEL_UPDATES = 0 -# These descriptions should include device class. -ENTITY_DESCRIPTION_KEY_DEVICE_CLASS_MAP: dict[ - tuple[str, str], SensorEntityDescription -] = { - (ENTITY_DESC_KEY_BATTERY, PERCENTAGE): SensorEntityDescription( - key=ENTITY_DESC_KEY_BATTERY, +# These descriptions should have a non None unit of measurement. +ENTITY_DESCRIPTION_KEY_UNIT_MAP: dict[tuple[str, str], SensorEntityDescription] = { + (ENTITY_DESC_KEY_BATTERY_LEVEL, PERCENTAGE): SensorEntityDescription( + key=ENTITY_DESC_KEY_BATTERY_LEVEL, device_class=SensorDeviceClass.BATTERY, entity_category=EntityCategory.DIAGNOSTIC, state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=PERCENTAGE, ), + (ENTITY_DESC_KEY_BATTERY_MAXIMUM_CAPACITY, PERCENTAGE): SensorEntityDescription( + key=ENTITY_DESC_KEY_BATTERY_MAXIMUM_CAPACITY, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + entity_registry_enabled_default=False, + ), + ( + ENTITY_DESC_KEY_BATTERY_TEMPERATURE, + UnitOfTemperature.CELSIUS, + ): SensorEntityDescription( + key=ENTITY_DESC_KEY_BATTERY_TEMPERATURE, + device_class=SensorDeviceClass.TEMPERATURE, + entity_category=EntityCategory.DIAGNOSTIC, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + entity_registry_enabled_default=False, + ), (ENTITY_DESC_KEY_CURRENT, UnitOfElectricCurrent.AMPERE): SensorEntityDescription( key=ENTITY_DESC_KEY_CURRENT, device_class=SensorDeviceClass.CURRENT, @@ -285,8 +304,14 @@ ENTITY_DESCRIPTION_KEY_DEVICE_CLASS_MAP: dict[ ), } -# These descriptions are without device class. +# These descriptions are without unit of measurement. ENTITY_DESCRIPTION_KEY_MAP = { + ENTITY_DESC_KEY_BATTERY_LIST_STATE: SensorEntityDescription( + key=ENTITY_DESC_KEY_BATTERY_LIST_STATE, + device_class=SensorDeviceClass.ENUM, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + ), ENTITY_DESC_KEY_CO: SensorEntityDescription( key=ENTITY_DESC_KEY_CO, state_class=SensorStateClass.MEASUREMENT, @@ -538,7 +563,7 @@ def get_entity_description( """Return the entity description for the given data.""" data_description_key = data.entity_description_key or "" data_unit = data.unit_of_measurement or "" - return ENTITY_DESCRIPTION_KEY_DEVICE_CLASS_MAP.get( + return ENTITY_DESCRIPTION_KEY_UNIT_MAP.get( (data_description_key, data_unit), ENTITY_DESCRIPTION_KEY_MAP.get( data_description_key, @@ -588,6 +613,10 @@ async def async_setup_entry( entities.append( ZWaveListSensor(config_entry, driver, info, entity_description) ) + elif info.platform_hint == "list": + entities.append( + ZWaveListSensor(config_entry, driver, info, entity_description) + ) elif info.platform_hint == "config_parameter": entities.append( ZWaveConfigParameterSensor( diff --git a/homeassistant/const.py b/homeassistant/const.py index 9d5ed69f8d7..6b51f735e97 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 = 6 -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, 2) diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 846a5c74ddb..40c1f873382 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -6426,7 +6426,10 @@ "iot_class": "cloud_polling", "name": "SwitchBot Cloud" } - } + }, + "iot_standards": [ + "matter" + ] }, "switcher_kis": { "name": "Switcher", diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index f157e82bc53..6e1988fe4cd 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -682,9 +682,12 @@ def _load_services_file(hass: HomeAssistant, integration: Integration) -> JSON_T def _load_services_files( hass: HomeAssistant, integrations: Iterable[Integration] -) -> list[JSON_TYPE]: +) -> dict[str, JSON_TYPE]: """Load service files for multiple integrations.""" - return [_load_services_file(hass, integration) for integration in integrations] + return { + integration.domain: _load_services_file(hass, integration) + for integration in integrations + } @callback @@ -744,10 +747,9 @@ async def async_get_all_descriptions( _LOGGER.error("Failed to load integration: %s", domain, exc_info=int_or_exc) if integrations: - contents = await hass.async_add_executor_job( + loaded = await hass.async_add_executor_job( _load_services_files, hass, integrations ) - loaded = dict(zip(domains_with_missing_services, contents, strict=False)) # Load translations for all service domains translations = await translation.async_get_translations( diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index ac33b8fe9f1..706218022b3 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -7,7 +7,7 @@ aiofiles==24.1.0 aiohasupervisor==0.3.1 aiohttp-asyncmdnsresolver==0.1.1 aiohttp-fast-zlib==0.3.0 -aiohttp==3.12.12 +aiohttp==3.12.13 aiohttp_cors==0.7.0 aiousbwatcher==1.1.1 aiozoneinfo==0.2.3 diff --git a/pyproject.toml b/pyproject.toml index 067b310f371..fc66f3f8d8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2025.6.1" +version = "2025.6.2" license = "Apache-2.0" license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"] description = "Open-source home automation platform running on Python 3." @@ -29,7 +29,7 @@ dependencies = [ # change behavior based on presence of supervisor. Deprecated with #127228 # Lib can be removed with 2025.11 "aiohasupervisor==0.3.1", - "aiohttp==3.12.12", + "aiohttp==3.12.13", "aiohttp_cors==0.7.0", "aiohttp-fast-zlib==0.3.0", "aiohttp-asyncmdnsresolver==0.1.1", diff --git a/requirements.txt b/requirements.txt index 333198a5346..70f214bbfc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ aiodns==3.5.0 aiofiles==24.1.0 aiohasupervisor==0.3.1 -aiohttp==3.12.12 +aiohttp==3.12.13 aiohttp_cors==0.7.0 aiohttp-fast-zlib==0.3.0 aiohttp-asyncmdnsresolver==0.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index d0e8b10ce42..2ce35c3fd8d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -182,7 +182,7 @@ aioairzone-cloud==0.6.12 aioairzone==1.0.0 # homeassistant.components.alexa_devices -aioamazondevices==3.1.2 +aioamazondevices==3.1.14 # homeassistant.components.ambient_network # homeassistant.components.ambient_station @@ -244,7 +244,7 @@ aioelectricitymaps==0.4.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==32.2.1 +aioesphomeapi==33.0.0 # homeassistant.components.flo aioflo==2021.11.0 @@ -265,7 +265,7 @@ aioharmony==0.5.2 aiohasupervisor==0.3.1 # homeassistant.components.home_connect -aiohomeconnect==0.17.1 +aiohomeconnect==0.18.0 # homeassistant.components.homekit_controller aiohomekit==3.2.14 @@ -683,7 +683,7 @@ brunt==1.2.0 bt-proximity==0.2.1 # homeassistant.components.bthome -bthome-ble==3.12.4 +bthome-ble==3.13.1 # homeassistant.components.bt_home_hub_5 bthomehub5-devicelist==0.1.1 @@ -765,7 +765,7 @@ debugpy==1.8.14 # decora==0.6 # homeassistant.components.ecovacs -deebot-client==13.3.0 +deebot-client==13.4.0 # homeassistant.components.ihc # homeassistant.components.namecheapdns @@ -1161,7 +1161,7 @@ hole==0.8.0 # homeassistant.components.holiday # homeassistant.components.workday -holidays==0.74 +holidays==0.75 # homeassistant.components.frontend home-assistant-frontend==20250531.3 @@ -1170,7 +1170,7 @@ home-assistant-frontend==20250531.3 home-assistant-intents==2025.6.10 # homeassistant.components.homematicip_cloud -homematicip==2.0.5 +homematicip==2.0.6 # homeassistant.components.horizon horimote==0.4.1 @@ -1203,7 +1203,7 @@ ibmiotf==0.3.4 # homeassistant.components.local_calendar # homeassistant.components.local_todo # homeassistant.components.remote_calendar -ical==10.0.0 +ical==10.0.4 # homeassistant.components.caldav icalendar==6.1.0 @@ -1448,7 +1448,7 @@ monzopy==1.4.2 mopeka-iot-ble==0.8.0 # homeassistant.components.motion_blinds -motionblinds==0.6.27 +motionblinds==0.6.28 # homeassistant.components.motionblinds_ble motionblindsble==0.1.3 @@ -2096,7 +2096,7 @@ pykwb==0.0.8 pylacrosse==0.4 # homeassistant.components.lamarzocco -pylamarzocco==2.0.8 +pylamarzocco==2.0.9 # homeassistant.components.lastfm pylast==5.1.0 @@ -2236,7 +2236,7 @@ pypaperless==4.1.0 pypca==0.0.7 # homeassistant.components.lcn -pypck==0.8.6 +pypck==0.8.8 # homeassistant.components.pglab pypglab==0.0.5 @@ -2341,7 +2341,7 @@ pysmappee==0.2.29 pysmarlaapi==0.8.2 # homeassistant.components.smartthings -pysmartthings==3.2.4 +pysmartthings==3.2.5 # homeassistant.components.smarty pysmarty2==0.10.2 @@ -2652,7 +2652,7 @@ renault-api==0.3.1 renson-endura-delta==1.7.2 # homeassistant.components.reolink -reolink-aio==0.13.5 +reolink-aio==0.14.1 # homeassistant.components.idteck_prox rfk101py==0.0.1 @@ -2673,7 +2673,7 @@ rjpl==0.3.6 rocketchat-API==0.6.1 # homeassistant.components.roku -rokuecp==0.19.3 +rokuecp==0.19.5 # homeassistant.components.romy romy==0.0.10 @@ -2987,7 +2987,7 @@ typedmonarchmoney==0.4.4 uasiren==0.0.1 # homeassistant.components.unifiprotect -uiprotect==7.11.0 +uiprotect==7.14.1 # homeassistant.components.landisgyr_heat_meter ultraheat-api==0.5.7 @@ -3180,7 +3180,7 @@ zeroconf==0.147.0 zeversolar==0.3.2 # homeassistant.components.zha -zha==0.0.59 +zha==0.0.60 # homeassistant.components.zhong_hong zhong-hong-hvac==1.0.13 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 861c8097470..8ca017a72c2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -170,7 +170,7 @@ aioairzone-cloud==0.6.12 aioairzone==1.0.0 # homeassistant.components.alexa_devices -aioamazondevices==3.1.2 +aioamazondevices==3.1.14 # homeassistant.components.ambient_network # homeassistant.components.ambient_station @@ -232,7 +232,7 @@ aioelectricitymaps==0.4.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==32.2.1 +aioesphomeapi==33.0.0 # homeassistant.components.flo aioflo==2021.11.0 @@ -250,7 +250,7 @@ aioharmony==0.5.2 aiohasupervisor==0.3.1 # homeassistant.components.home_connect -aiohomeconnect==0.17.1 +aiohomeconnect==0.18.0 # homeassistant.components.homekit_controller aiohomekit==3.2.14 @@ -607,7 +607,7 @@ brottsplatskartan==1.0.5 brunt==1.2.0 # homeassistant.components.bthome -bthome-ble==3.12.4 +bthome-ble==3.13.1 # homeassistant.components.buienradar buienradar==1.0.6 @@ -665,7 +665,7 @@ debugpy==1.8.14 # decora==0.6 # homeassistant.components.ecovacs -deebot-client==13.3.0 +deebot-client==13.4.0 # homeassistant.components.ihc # homeassistant.components.namecheapdns @@ -1007,7 +1007,7 @@ hole==0.8.0 # homeassistant.components.holiday # homeassistant.components.workday -holidays==0.74 +holidays==0.75 # homeassistant.components.frontend home-assistant-frontend==20250531.3 @@ -1016,7 +1016,7 @@ home-assistant-frontend==20250531.3 home-assistant-intents==2025.6.10 # homeassistant.components.homematicip_cloud -homematicip==2.0.5 +homematicip==2.0.6 # homeassistant.components.remember_the_milk httplib2==0.20.4 @@ -1040,7 +1040,7 @@ ibeacon-ble==1.2.0 # homeassistant.components.local_calendar # homeassistant.components.local_todo # homeassistant.components.remote_calendar -ical==10.0.0 +ical==10.0.4 # homeassistant.components.caldav icalendar==6.1.0 @@ -1237,7 +1237,7 @@ monzopy==1.4.2 mopeka-iot-ble==0.8.0 # homeassistant.components.motion_blinds -motionblinds==0.6.27 +motionblinds==0.6.28 # homeassistant.components.motionblinds_ble motionblindsble==0.1.3 @@ -1738,7 +1738,7 @@ pykrakenapi==0.1.8 pykulersky==0.5.8 # homeassistant.components.lamarzocco -pylamarzocco==2.0.8 +pylamarzocco==2.0.9 # homeassistant.components.lastfm pylast==5.1.0 @@ -1857,7 +1857,7 @@ pypalazzetti==0.1.19 pypaperless==4.1.0 # homeassistant.components.lcn -pypck==0.8.6 +pypck==0.8.8 # homeassistant.components.pglab pypglab==0.0.5 @@ -1941,7 +1941,7 @@ pysmappee==0.2.29 pysmarlaapi==0.8.2 # homeassistant.components.smartthings -pysmartthings==3.2.4 +pysmartthings==3.2.5 # homeassistant.components.smarty pysmarty2==0.10.2 @@ -2195,7 +2195,7 @@ renault-api==0.3.1 renson-endura-delta==1.7.2 # homeassistant.components.reolink -reolink-aio==0.13.5 +reolink-aio==0.14.1 # homeassistant.components.rflink rflink==0.0.66 @@ -2204,7 +2204,7 @@ rflink==0.0.66 ring-doorbell==0.9.13 # homeassistant.components.roku -rokuecp==0.19.3 +rokuecp==0.19.5 # homeassistant.components.romy romy==0.0.10 @@ -2458,7 +2458,7 @@ typedmonarchmoney==0.4.4 uasiren==0.0.1 # homeassistant.components.unifiprotect -uiprotect==7.11.0 +uiprotect==7.14.1 # homeassistant.components.landisgyr_heat_meter ultraheat-api==0.5.7 @@ -2621,7 +2621,7 @@ zeroconf==0.147.0 zeversolar==0.3.2 # homeassistant.components.zha -zha==0.0.59 +zha==0.0.60 # homeassistant.components.zwave_js zwave-js-server-python==0.63.0 diff --git a/tests/components/reolink/conftest.py b/tests/components/reolink/conftest.py index a2155ba00eb..1d8244a890a 100644 --- a/tests/components/reolink/conftest.py +++ b/tests/components/reolink/conftest.py @@ -99,7 +99,7 @@ def reolink_connect_class() -> Generator[MagicMock]: host_mock.sw_upload_progress.return_value = 100 host_mock.manufacturer = "Reolink" host_mock.model = TEST_HOST_MODEL - host_mock.item_number = TEST_ITEM_NUMBER + host_mock.item_number.return_value = TEST_ITEM_NUMBER host_mock.camera_model.return_value = TEST_CAM_MODEL host_mock.camera_name.return_value = TEST_NVR_NAME host_mock.camera_hardware_version.return_value = "IPC_00001" diff --git a/tests/components/shelly/test_sensor.py b/tests/components/shelly/test_sensor.py index e95d4cfaeb2..8f021c2d58a 100644 --- a/tests/components/shelly/test_sensor.py +++ b/tests/components/shelly/test_sensor.py @@ -23,6 +23,7 @@ from homeassistant.components.shelly.const import DOMAIN from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, PERCENTAGE, STATE_UNAVAILABLE, @@ -40,6 +41,7 @@ from homeassistant.helpers.entity_registry import EntityRegistry from homeassistant.setup import async_setup_component from . import ( + MOCK_MAC, init_integration, mock_polling_rpc_update, mock_rest_update, @@ -1585,3 +1587,45 @@ async def test_rpc_switch_no_returned_energy_sensor( await init_integration(hass, 3) assert hass.states.get("sensor.test_name_test_switch_0_returned_energy") is None + + +async def test_block_friendly_name_sleeping_sensor( + hass: HomeAssistant, + mock_block_device: Mock, + device_registry: DeviceRegistry, + entity_registry: EntityRegistry, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test friendly name for restored sleeping sensor.""" + entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True) + device = register_device(device_registry, entry) + + entity = entity_registry.async_get_or_create( + SENSOR_DOMAIN, + DOMAIN, + f"{MOCK_MAC}-sensor_0-temp", + suggested_object_id="test_name_temperature", + original_name="Test name temperature", + disabled_by=None, + config_entry=entry, + device_id=device.id, + ) + + # Old name, the word "temperature" starts with a lower case letter + assert entity.original_name == "Test name temperature" + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert (state := hass.states.get(entity.entity_id)) + + # New name, the word "temperature" starts with a capital letter + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test name Temperature" + + # Make device online + monkeypatch.setattr(mock_block_device, "initialized", True) + mock_block_device.mock_online() + await hass.async_block_till_done(wait_background_tasks=True) + + assert (state := hass.states.get(entity.entity_id)) + assert state.attributes[ATTR_FRIENDLY_NAME] == "Test name Temperature" diff --git a/tests/components/switchbot_cloud/test_binary_sensor.py b/tests/components/switchbot_cloud/test_binary_sensor.py new file mode 100644 index 00000000000..753653af9a8 --- /dev/null +++ b/tests/components/switchbot_cloud/test_binary_sensor.py @@ -0,0 +1,39 @@ +"""Test for the switchbot_cloud binary sensors.""" + +from unittest.mock import patch + +from switchbot_api import Device + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import configure_integration + + +async def test_unsupported_device_type( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_list_devices, + mock_get_status, +) -> None: + """Test that unsupported device types do not create sensors.""" + mock_list_devices.return_value = [ + Device( + version="V1.0", + deviceId="unsupported-id-1", + deviceName="unsupported-device", + deviceType="UnsupportedDevice", + hubDeviceId="test-hub-id", + ), + ] + mock_get_status.return_value = {} + + with patch( + "homeassistant.components.switchbot_cloud.PLATFORMS", [Platform.BINARY_SENSOR] + ): + entry = await configure_integration(hass) + + # Assert no binary sensor entities were created for unsupported device type + entities = er.async_entries_for_config_entry(entity_registry, entry.entry_id) + assert len([e for e in entities if e.domain == "binary_sensor"]) == 0 diff --git a/tests/components/switchbot_cloud/test_sensor.py b/tests/components/switchbot_cloud/test_sensor.py index 0927e3cf1ea..ccad2f1ad6c 100644 --- a/tests/components/switchbot_cloud/test_sensor.py +++ b/tests/components/switchbot_cloud/test_sensor.py @@ -65,3 +65,29 @@ async def test_meter_no_coordinator_data( entry = await configure_integration(hass) await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id) + + +async def test_unsupported_device_type( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_list_devices, + mock_get_status, +) -> None: + """Test that unsupported device types do not create sensors.""" + mock_list_devices.return_value = [ + Device( + version="V1.0", + deviceId="unsupported-id-1", + deviceName="unsupported-device", + deviceType="UnsupportedDevice", + hubDeviceId="test-hub-id", + ), + ] + mock_get_status.return_value = {} + + with patch("homeassistant.components.switchbot_cloud.PLATFORMS", [Platform.SENSOR]): + entry = await configure_integration(hass) + + # Assert no sensor entities were created for unsupported device type + entities = er.async_entries_for_config_entry(entity_registry, entry.entry_id) + assert len([e for e in entities if e.domain == "sensor"]) == 0 diff --git a/tests/components/traccar/test_init.py b/tests/components/traccar/test_init.py index fb90262a084..eb864cadd87 100644 --- a/tests/components/traccar/test_init.py +++ b/tests/components/traccar/test_init.py @@ -146,8 +146,12 @@ async def test_enter_and_exit( assert len(entity_registry.entities) == 1 -async def test_enter_with_attrs(hass: HomeAssistant, client, webhook_id) -> None: - """Test when additional attributes are present.""" +async def test_enter_with_attrs_as_query( + hass: HomeAssistant, + client, + webhook_id, +) -> None: + """Test when additional attributes are present URL query.""" url = f"/api/webhook/{webhook_id}" data = { "timestamp": 123456789, @@ -197,6 +201,45 @@ async def test_enter_with_attrs(hass: HomeAssistant, client, webhook_id) -> None assert state.attributes["altitude"] == 123 +async def test_enter_with_attrs_as_payload( + hass: HomeAssistant, client, webhook_id +) -> None: + """Test when additional attributes are present in JSON payload.""" + url = f"/api/webhook/{webhook_id}" + data = { + "location": { + "coords": { + "heading": "105.32", + "latitude": "1.0", + "longitude": "1.1", + "accuracy": 10.5, + "altitude": 102.0, + "speed": 100.0, + }, + "extras": {}, + "manual": True, + "is_moving": False, + "_": "&id=123&lat=1.0&lon=1.1×tamp=2013-09-17T07:32:51Z&", + "odometer": 0, + "activity": {"type": "still"}, + "timestamp": "2013-09-17T07:32:51Z", + "battery": {"level": 0.1, "is_charging": False}, + }, + "device_id": "123", + } + + req = await client.post(url, json=data) + await hass.async_block_till_done() + assert req.status == HTTPStatus.OK + state = hass.states.get(f"{DEVICE_TRACKER_DOMAIN}.{data['device_id']}") + assert state.state == STATE_NOT_HOME + assert state.attributes["gps_accuracy"] == 10.5 + assert state.attributes["battery_level"] == 10.0 + assert state.attributes["speed"] == 100.0 + assert state.attributes["bearing"] == 105.32 + assert state.attributes["altitude"] == 102.0 + + async def test_two_devices(hass: HomeAssistant, client, webhook_id) -> None: """Test updating two different devices.""" url = f"/api/webhook/{webhook_id}" diff --git a/tests/components/wallbox/__init__.py b/tests/components/wallbox/__init__.py index d347777f7e8..37e7d5059f0 100644 --- a/tests/components/wallbox/__init__.py +++ b/tests/components/wallbox/__init__.py @@ -162,6 +162,9 @@ test_response_no_power_boost = { http_404_error = requests.exceptions.HTTPError() http_404_error.response = requests.Response() http_404_error.response.status_code = HTTPStatus.NOT_FOUND +http_429_error = requests.exceptions.HTTPError() +http_429_error.response = requests.Response() +http_429_error.response.status_code = HTTPStatus.TOO_MANY_REQUESTS authorisation_response = { "data": { @@ -192,6 +195,24 @@ authorisation_response_unauthorised = { } } +invalid_reauth_response = { + "jwt": "fakekeyhere", + "refresh_token": "refresh_fakekeyhere", + "user_id": 12345, + "ttl": 145656758, + "refresh_token_ttl": 145756758, + "error": False, + "status": 200, +} + +http_403_error = requests.exceptions.HTTPError() +http_403_error.response = requests.Response() +http_403_error.response.status_code = HTTPStatus.FORBIDDEN + +http_404_error = requests.exceptions.HTTPError() +http_404_error.response = requests.Response() +http_404_error.response.status_code = HTTPStatus.NOT_FOUND + async def setup_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None: """Test wallbox sensor class setup.""" @@ -216,6 +237,31 @@ async def setup_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None await hass.async_block_till_done() +async def setup_integration_no_eco_mode( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test wallbox sensor class setup.""" + with requests_mock.Mocker() as mock_request: + mock_request.get( + "https://user-api.wall-box.com/users/signin", + json=authorisation_response, + status_code=HTTPStatus.OK, + ) + mock_request.get( + "https://api.wall-box.com/chargers/status/12345", + json=test_response_no_power_boost, + status_code=HTTPStatus.OK, + ) + mock_request.put( + "https://api.wall-box.com/v2/charger/12345", + json={CHARGER_MAX_CHARGING_CURRENT_KEY: 20}, + status_code=HTTPStatus.OK, + ) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + async def setup_integration_select( hass: HomeAssistant, entry: MockConfigEntry, response ) -> None: diff --git a/tests/components/wallbox/test_config_flow.py b/tests/components/wallbox/test_config_flow.py index 467e20c51c1..bdfb4cad18d 100644 --- a/tests/components/wallbox/test_config_flow.py +++ b/tests/components/wallbox/test_config_flow.py @@ -1,9 +1,6 @@ """Test the Wallbox config flow.""" -from http import HTTPStatus -import json - -import requests_mock +from unittest.mock import Mock, patch from homeassistant import config_entries from homeassistant.components.wallbox import config_flow @@ -24,23 +21,21 @@ from homeassistant.data_entry_flow import FlowResultType from . import ( authorisation_response, authorisation_response_unauthorised, + http_403_error, + http_404_error, setup_integration, ) from tests.common import MockConfigEntry -test_response = json.loads( - json.dumps( - { - CHARGER_CHARGING_POWER_KEY: 0, - CHARGER_MAX_AVAILABLE_POWER_KEY: "xx", - CHARGER_CHARGING_SPEED_KEY: 0, - CHARGER_ADDED_RANGE_KEY: "xx", - CHARGER_ADDED_ENERGY_KEY: "44.697", - CHARGER_DATA_KEY: {CHARGER_MAX_CHARGING_CURRENT_KEY: 24}, - } - ) -) +test_response = { + CHARGER_CHARGING_POWER_KEY: 0, + CHARGER_MAX_AVAILABLE_POWER_KEY: "xx", + CHARGER_CHARGING_SPEED_KEY: 0, + CHARGER_ADDED_RANGE_KEY: "xx", + CHARGER_ADDED_ENERGY_KEY: "44.697", + CHARGER_DATA_KEY: {CHARGER_MAX_CHARGING_CURRENT_KEY: 24}, +} async def test_show_set_form(hass: HomeAssistant) -> None: @@ -59,17 +54,16 @@ async def test_form_cannot_authenticate(hass: HomeAssistant) -> None: DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=HTTPStatus.FORBIDDEN, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=HTTPStatus.FORBIDDEN, - ) + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(side_effect=http_403_error), + ), + patch( + "homeassistant.components.wallbox.Wallbox.pauseChargingSession", + new=Mock(side_effect=http_403_error), + ), + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -89,17 +83,16 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None: DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response_unauthorised, - status_code=HTTPStatus.NOT_FOUND, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=HTTPStatus.NOT_FOUND, - ) + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(side_effect=http_404_error), + ), + patch( + "homeassistant.components.wallbox.Wallbox.pauseChargingSession", + new=Mock(side_effect=http_404_error), + ), + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -119,17 +112,16 @@ async def test_form_validate_input(hass: HomeAssistant) -> None: DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=HTTPStatus.OK, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=HTTPStatus.OK, - ) + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.getChargerStatus", + new=Mock(return_value=test_response), + ), + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -148,18 +140,16 @@ async def test_form_reauth(hass: HomeAssistant, entry: MockConfigEntry) -> None: await setup_integration(hass, entry) assert entry.state is ConfigEntryState.LOADED - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response_unauthorised), + ), + patch( + "homeassistant.components.wallbox.Wallbox.getChargerStatus", + new=Mock(return_value=test_response), + ), + ): result = await entry.start_reauth_flow(hass) result2 = await hass.config_entries.flow.async_configure( @@ -183,26 +173,16 @@ async def test_form_reauth_invalid(hass: HomeAssistant, entry: MockConfigEntry) await setup_integration(hass, entry) assert entry.state is ConfigEntryState.LOADED - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json={ - "jwt": "fakekeyhere", - "refresh_token": "refresh_fakekeyhere", - "user_id": 12345, - "ttl": 145656758, - "refresh_token_ttl": 145756758, - "error": False, - "status": 200, - }, - status_code=200, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response_unauthorised), + ), + patch( + "homeassistant.components.wallbox.Wallbox.getChargerStatus", + new=Mock(return_value=test_response), + ), + ): result = await entry.start_reauth_flow(hass) result2 = await hass.config_entries.flow.async_configure( diff --git a/tests/components/wallbox/test_init.py b/tests/components/wallbox/test_init.py index b4b5a199243..5048385aaf6 100644 --- a/tests/components/wallbox/test_init.py +++ b/tests/components/wallbox/test_init.py @@ -1,18 +1,18 @@ """Test Wallbox Init Component.""" -import requests_mock +from unittest.mock import Mock, patch -from homeassistant.components.wallbox.const import ( - CHARGER_MAX_CHARGING_CURRENT_KEY, - DOMAIN, -) +from homeassistant.components.wallbox.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from . import ( authorisation_response, + http_403_error, + http_429_error, setup_integration, setup_integration_connection_error, + setup_integration_no_eco_mode, setup_integration_read_only, test_response, ) @@ -52,18 +52,16 @@ async def test_wallbox_refresh_failed_connection_error_auth( await setup_integration(hass, entry) assert entry.state is ConfigEntryState.LOADED - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=404, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(side_effect=http_429_error), + ), + patch( + "homeassistant.components.wallbox.Wallbox.pauseChargingSession", + new=Mock(return_value=test_response), + ), + ): wallbox = hass.data[DOMAIN][entry.entry_id] await wallbox.async_refresh() @@ -80,18 +78,68 @@ async def test_wallbox_refresh_failed_invalid_auth( await setup_integration(hass, entry) assert entry.state is ConfigEntryState.LOADED - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=403, - ) - mock_request.put( - "https://api.wall-box.com/v2/charger/12345", - json={CHARGER_MAX_CHARGING_CURRENT_KEY: 20}, - status_code=403, - ) + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(side_effect=http_403_error), + ), + patch( + "homeassistant.components.wallbox.Wallbox.pauseChargingSession", + new=Mock(side_effect=http_403_error), + ), + ): + wallbox = hass.data[DOMAIN][entry.entry_id] + await wallbox.async_refresh() + + assert await hass.config_entries.async_unload(entry.entry_id) + assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_wallbox_refresh_failed_http_error( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test Wallbox setup with authentication error.""" + + await setup_integration(hass, entry) + assert entry.state is ConfigEntryState.LOADED + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.getChargerStatus", + new=Mock(side_effect=http_403_error), + ), + ): + wallbox = hass.data[DOMAIN][entry.entry_id] + + await wallbox.async_refresh() + + assert await hass.config_entries.async_unload(entry.entry_id) + assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_wallbox_refresh_failed_too_many_requests( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test Wallbox setup with authentication error.""" + + await setup_integration(hass, entry) + assert entry.state is ConfigEntryState.LOADED + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.getChargerStatus", + new=Mock(side_effect=http_429_error), + ), + ): wallbox = hass.data[DOMAIN][entry.entry_id] await wallbox.async_refresh() @@ -108,18 +156,16 @@ async def test_wallbox_refresh_failed_connection_error( await setup_integration(hass, entry) assert entry.state is ConfigEntryState.LOADED - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.get( - "https://api.wall-box.com/chargers/status/12345", - json=test_response, - status_code=403, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.pauseChargingSession", + new=Mock(side_effect=http_403_error), + ), + ): wallbox = hass.data[DOMAIN][entry.entry_id] await wallbox.async_refresh() @@ -138,3 +184,15 @@ async def test_wallbox_refresh_failed_read_only( assert await hass.config_entries.async_unload(entry.entry_id) assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_wallbox_setup_load_entry_no_eco_mode( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test Wallbox Unload.""" + + await setup_integration_no_eco_mode(hass, entry) + assert entry.state is ConfigEntryState.LOADED + + assert await hass.config_entries.async_unload(entry.entry_id) + assert entry.state is ConfigEntryState.NOT_LOADED diff --git a/tests/components/wallbox/test_lock.py b/tests/components/wallbox/test_lock.py index 1d48e53b515..5842d708f11 100644 --- a/tests/components/wallbox/test_lock.py +++ b/tests/components/wallbox/test_lock.py @@ -1,15 +1,18 @@ """Test Wallbox Lock component.""" +from unittest.mock import Mock, patch + import pytest -import requests_mock from homeassistant.components.lock import SERVICE_LOCK, SERVICE_UNLOCK from homeassistant.components.wallbox.const import CHARGER_LOCKED_UNLOCKED_KEY from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from . import ( authorisation_response, + http_429_error, setup_integration, setup_integration_platform_not_ready, setup_integration_read_only, @@ -28,18 +31,20 @@ async def test_wallbox_lock_class(hass: HomeAssistant, entry: MockConfigEntry) - assert state assert state.state == "unlocked" - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.put( - "https://api.wall-box.com/v2/charger/12345", - json={CHARGER_LOCKED_UNLOCKED_KEY: False}, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.lockCharger", + new=Mock(return_value={CHARGER_LOCKED_UNLOCKED_KEY: False}), + ), + patch( + "homeassistant.components.wallbox.Wallbox.unlockCharger", + new=Mock(return_value={CHARGER_LOCKED_UNLOCKED_KEY: False}), + ), + ): await hass.services.async_call( "lock", SERVICE_LOCK, @@ -66,36 +71,73 @@ async def test_wallbox_lock_class_connection_error( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.put( - "https://api.wall-box.com/v2/charger/12345", - json={CHARGER_LOCKED_UNLOCKED_KEY: False}, - status_code=404, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.lockCharger", + new=Mock(side_effect=ConnectionError), + ), + pytest.raises(ConnectionError), + ): + await hass.services.async_call( + "lock", + SERVICE_LOCK, + { + ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID, + }, + blocking=True, ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - "lock", - SERVICE_LOCK, - { - ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID, - }, - blocking=True, - ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - "lock", - SERVICE_UNLOCK, - { - ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID, - }, - blocking=True, - ) + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.lockCharger", + new=Mock(side_effect=ConnectionError), + ), + patch( + "homeassistant.components.wallbox.Wallbox.unlockCharger", + new=Mock(side_effect=ConnectionError), + ), + pytest.raises(ConnectionError), + ): + await hass.services.async_call( + "lock", + SERVICE_UNLOCK, + { + ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID, + }, + blocking=True, + ) + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.lockCharger", + new=Mock(side_effect=http_429_error), + ), + patch( + "homeassistant.components.wallbox.Wallbox.unlockCharger", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + "lock", + SERVICE_UNLOCK, + { + ATTR_ENTITY_ID: MOCK_LOCK_ENTITY_ID, + }, + blocking=True, + ) async def test_wallbox_lock_class_authentication_error( diff --git a/tests/components/wallbox/test_number.py b/tests/components/wallbox/test_number.py index c319668c161..c603ae24106 100644 --- a/tests/components/wallbox/test_number.py +++ b/tests/components/wallbox/test_number.py @@ -1,22 +1,26 @@ """Test Wallbox Switch component.""" +from unittest.mock import Mock, patch + import pytest -import requests_mock from homeassistant.components.input_number import ATTR_VALUE, SERVICE_SET_VALUE from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN -from homeassistant.components.wallbox import InvalidAuth from homeassistant.components.wallbox.const import ( CHARGER_ENERGY_PRICE_KEY, CHARGER_MAX_CHARGING_CURRENT_KEY, CHARGER_MAX_ICP_CURRENT_KEY, ) +from homeassistant.components.wallbox.coordinator import InvalidAuth from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryAuthFailed +from homeassistant.exceptions import HomeAssistantError from . import ( authorisation_response, + http_403_error, + http_404_error, + http_429_error, setup_integration, setup_integration_bidir, setup_integration_platform_not_ready, @@ -29,6 +33,14 @@ from .const import ( from tests.common import MockConfigEntry +mock_wallbox = Mock() +mock_wallbox.authenticate = Mock(return_value=authorisation_response) +mock_wallbox.setEnergyCost = Mock(return_value={CHARGER_ENERGY_PRICE_KEY: 1.1}) +mock_wallbox.setMaxChargingCurrent = Mock( + return_value={CHARGER_MAX_CHARGING_CURRENT_KEY: 20} +) +mock_wallbox.setIcpMaxCurrent = Mock(return_value={CHARGER_MAX_ICP_CURRENT_KEY: 10}) + async def test_wallbox_number_class( hass: HomeAssistant, entry: MockConfigEntry @@ -37,17 +49,16 @@ async def test_wallbox_number_class( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.put( - "https://api.wall-box.com/v2/charger/12345", - json={CHARGER_MAX_CHARGING_CURRENT_KEY: 20}, - status_code=200, - ) + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setMaxChargingCurrent", + new=Mock(return_value={CHARGER_MAX_CHARGING_CURRENT_KEY: 20}), + ), + ): state = hass.states.get(MOCK_NUMBER_ENTITY_ID) assert state.attributes["min"] == 6 assert state.attributes["max"] == 25 @@ -82,19 +93,16 @@ async def test_wallbox_number_energy_class( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - - mock_request.post( - "https://api.wall-box.com/chargers/config/12345", - json={CHARGER_ENERGY_PRICE_KEY: 1.1}, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setEnergyCost", + new=Mock(return_value={CHARGER_ENERGY_PRICE_KEY: 1.1}), + ), + ): await hass.services.async_call( "number", SERVICE_SET_VALUE, @@ -113,59 +121,113 @@ async def test_wallbox_number_class_connection_error( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.put( - "https://api.wall-box.com/v2/charger/12345", - json={CHARGER_MAX_CHARGING_CURRENT_KEY: 20}, - status_code=404, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setMaxChargingCurrent", + new=Mock(side_effect=http_404_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + "number", + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ID, + ATTR_VALUE: 20, + }, + blocking=True, ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - "number", - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ID, - ATTR_VALUE: 20, - }, - blocking=True, - ) - -async def test_wallbox_number_class_energy_price_connection_error( +async def test_wallbox_number_class_too_many_requests( hass: HomeAssistant, entry: MockConfigEntry ) -> None: """Test wallbox sensor class.""" await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.post( - "https://api.wall-box.com/chargers/config/12345", - json={CHARGER_ENERGY_PRICE_KEY: 1.1}, - status_code=404, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setMaxChargingCurrent", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + "number", + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ID, + ATTR_VALUE: 20, + }, + blocking=True, ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - "number", - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID, - ATTR_VALUE: 1.1, - }, - blocking=True, - ) + +async def test_wallbox_number_class_energy_price_update_failed( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test wallbox sensor class.""" + + await setup_integration(hass, entry) + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setEnergyCost", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + "number", + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID, + ATTR_VALUE: 1.1, + }, + blocking=True, + ) + + +async def test_wallbox_number_class_energy_price_update_connection_error( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test wallbox sensor class.""" + + await setup_integration(hass, entry) + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setEnergyCost", + new=Mock(side_effect=http_404_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + "number", + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID, + ATTR_VALUE: 1.1, + }, + blocking=True, + ) async def test_wallbox_number_class_energy_price_auth_error( @@ -175,28 +237,26 @@ async def test_wallbox_number_class_energy_price_auth_error( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setEnergyCost", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + "number", + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID, + ATTR_VALUE: 1.1, + }, + blocking=True, ) - mock_request.post( - "https://api.wall-box.com/chargers/config/12345", - json={CHARGER_ENERGY_PRICE_KEY: 1.1}, - status_code=403, - ) - - with pytest.raises(ConfigEntryAuthFailed): - await hass.services.async_call( - "number", - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ENERGY_PRICE_ID, - ATTR_VALUE: 1.1, - }, - blocking=True, - ) async def test_wallbox_number_class_platform_not_ready( @@ -218,19 +278,16 @@ async def test_wallbox_number_class_icp_energy( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - - mock_request.post( - "https://api.wall-box.com/chargers/config/12345", - json={CHARGER_MAX_ICP_CURRENT_KEY: 10}, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setIcpMaxCurrent", + new=Mock(return_value={CHARGER_MAX_ICP_CURRENT_KEY: 10}), + ), + ): await hass.services.async_call( NUMBER_DOMAIN, SERVICE_SET_VALUE, @@ -249,28 +306,26 @@ async def test_wallbox_number_class_icp_energy_auth_error( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setIcpMaxCurrent", + new=Mock(side_effect=http_403_error), + ), + pytest.raises(InvalidAuth), + ): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID, + ATTR_VALUE: 10, + }, + blocking=True, ) - mock_request.post( - "https://api.wall-box.com/chargers/config/12345", - json={CHARGER_MAX_ICP_CURRENT_KEY: 10}, - status_code=403, - ) - - with pytest.raises(InvalidAuth): - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID, - ATTR_VALUE: 10, - }, - blocking=True, - ) async def test_wallbox_number_class_icp_energy_connection_error( @@ -280,25 +335,52 @@ async def test_wallbox_number_class_icp_energy_connection_error( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.post( - "https://api.wall-box.com/chargers/config/12345", - json={CHARGER_MAX_ICP_CURRENT_KEY: 10}, - status_code=404, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setIcpMaxCurrent", + new=Mock(side_effect=http_404_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID, + ATTR_VALUE: 10, + }, + blocking=True, ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID, - ATTR_VALUE: 10, - }, - blocking=True, - ) + +async def test_wallbox_number_class_icp_energy_too_many_request( + hass: HomeAssistant, entry: MockConfigEntry +) -> None: + """Test wallbox sensor class.""" + + await setup_integration(hass, entry) + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.setIcpMaxCurrent", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: MOCK_NUMBER_ENTITY_ICP_CURRENT_ID, + ATTR_VALUE: 10, + }, + blocking=True, + ) diff --git a/tests/components/wallbox/test_select.py b/tests/components/wallbox/test_select.py index 516b1e87c27..f59a8367b41 100644 --- a/tests/components/wallbox/test_select.py +++ b/tests/components/wallbox/test_select.py @@ -16,6 +16,7 @@ from homeassistant.core import HomeAssistant, HomeAssistantError from . import ( authorisation_response, http_404_error, + http_429_error, setup_integration_select, test_response, test_response_eco_mode, @@ -109,7 +110,41 @@ async def test_wallbox_select_class_error( "homeassistant.components.wallbox.Wallbox.enableEcoSmart", new=Mock(side_effect=error), ), - pytest.raises(HomeAssistantError, match="Error communicating with Wallbox API"), + pytest.raises(HomeAssistantError), + ): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: MOCK_SELECT_ENTITY_ID, + ATTR_OPTION: mode, + }, + blocking=True, + ) + + +@pytest.mark.parametrize(("mode", "response"), TEST_OPTIONS) +async def test_wallbox_select_too_many_requests_error( + hass: HomeAssistant, + entry: MockConfigEntry, + mode, + response, + mock_authenticate, +) -> None: + """Test wallbox select class connection error.""" + + await setup_integration_select(hass, entry, response) + + with ( + patch( + "homeassistant.components.wallbox.Wallbox.disableEcoSmart", + new=Mock(side_effect=http_429_error), + ), + patch( + "homeassistant.components.wallbox.Wallbox.enableEcoSmart", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), ): await hass.services.async_call( SELECT_DOMAIN, diff --git a/tests/components/wallbox/test_switch.py b/tests/components/wallbox/test_switch.py index b7c3a81dc73..eb983ca44ce 100644 --- a/tests/components/wallbox/test_switch.py +++ b/tests/components/wallbox/test_switch.py @@ -1,15 +1,16 @@ """Test Wallbox Lock component.""" +from unittest.mock import Mock, patch + import pytest -import requests_mock from homeassistant.components.switch import SERVICE_TURN_OFF, SERVICE_TURN_ON from homeassistant.components.wallbox.const import CHARGER_STATUS_ID_KEY from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryAuthFailed +from homeassistant.exceptions import HomeAssistantError -from . import authorisation_response, setup_integration +from . import authorisation_response, http_404_error, http_429_error, setup_integration from .const import MOCK_SWITCH_ENTITY_ID from tests.common import MockConfigEntry @@ -26,18 +27,20 @@ async def test_wallbox_switch_class( assert state assert state.state == "on" - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.post( - "https://api.wall-box.com/v3/chargers/12345/remote-action", - json={CHARGER_STATUS_ID_KEY: 193}, - status_code=200, - ) - + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.pauseChargingSession", + new=Mock(return_value={CHARGER_STATUS_ID_KEY: 193}), + ), + patch( + "homeassistant.components.wallbox.Wallbox.resumeChargingSession", + new=Mock(return_value={CHARGER_STATUS_ID_KEY: 193}), + ), + ): await hass.services.async_call( "switch", SERVICE_TURN_ON, @@ -64,72 +67,52 @@ async def test_wallbox_switch_class_connection_error( await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, - ) - mock_request.post( - "https://api.wall-box.com/v3/chargers/12345/remote-action", - json={CHARGER_STATUS_ID_KEY: 193}, - status_code=404, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.resumeChargingSession", + new=Mock(side_effect=http_404_error), + ), + pytest.raises(HomeAssistantError), + ): + # Test behavior when a connection error occurs + await hass.services.async_call( + "switch", + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: MOCK_SWITCH_ENTITY_ID, + }, + blocking=True, ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - "switch", - SERVICE_TURN_ON, - { - ATTR_ENTITY_ID: MOCK_SWITCH_ENTITY_ID, - }, - blocking=True, - ) - with pytest.raises(ConnectionError): - await hass.services.async_call( - "switch", - SERVICE_TURN_OFF, - { - ATTR_ENTITY_ID: MOCK_SWITCH_ENTITY_ID, - }, - blocking=True, - ) - -async def test_wallbox_switch_class_authentication_error( +async def test_wallbox_switch_class_too_many_requests( hass: HomeAssistant, entry: MockConfigEntry ) -> None: """Test wallbox switch class connection error.""" await setup_integration(hass, entry) - with requests_mock.Mocker() as mock_request: - mock_request.get( - "https://user-api.wall-box.com/users/signin", - json=authorisation_response, - status_code=200, + with ( + patch( + "homeassistant.components.wallbox.Wallbox.authenticate", + new=Mock(return_value=authorisation_response), + ), + patch( + "homeassistant.components.wallbox.Wallbox.resumeChargingSession", + new=Mock(side_effect=http_429_error), + ), + pytest.raises(HomeAssistantError), + ): + # Test behavior when a connection error occurs + await hass.services.async_call( + "switch", + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: MOCK_SWITCH_ENTITY_ID, + }, + blocking=True, ) - mock_request.post( - "https://api.wall-box.com/v3/chargers/12345/remote-action", - json={CHARGER_STATUS_ID_KEY: 193}, - status_code=403, - ) - - with pytest.raises(ConfigEntryAuthFailed): - await hass.services.async_call( - "switch", - SERVICE_TURN_ON, - { - ATTR_ENTITY_ID: MOCK_SWITCH_ENTITY_ID, - }, - blocking=True, - ) - with pytest.raises(ConfigEntryAuthFailed): - await hass.services.async_call( - "switch", - SERVICE_TURN_OFF, - { - ATTR_ENTITY_ID: MOCK_SWITCH_ENTITY_ID, - }, - blocking=True, - ) diff --git a/tests/components/zwave_js/common.py b/tests/components/zwave_js/common.py index 64bc981de11..578eeab5ec7 100644 --- a/tests/components/zwave_js/common.py +++ b/tests/components/zwave_js/common.py @@ -21,7 +21,6 @@ ENERGY_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed_2" VOLTAGE_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed_3" CURRENT_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed_4" SWITCH_ENTITY = "switch.smart_plug_with_two_usb_ports" -LOW_BATTERY_BINARY_SENSOR = "binary_sensor.multisensor_6_low_battery_level" ENABLED_LEGACY_BINARY_SENSOR = "binary_sensor.z_wave_door_window_sensor_any" DISABLED_LEGACY_BINARY_SENSOR = "binary_sensor.multisensor_6_any" NOTIFICATION_MOTION_BINARY_SENSOR = "binary_sensor.multisensor_6_motion_detection" diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index e0485ced091..25f40e4418d 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -199,6 +199,12 @@ def climate_heatit_z_trm3_no_value_state_fixture() -> dict[str, Any]: return load_json_object_fixture("climate_heatit_z_trm3_no_value_state.json", DOMAIN) +@pytest.fixture(name="ring_keypad_state", scope="package") +def ring_keypad_state_fixture() -> dict[str, Any]: + """Load the Ring keypad state fixture data.""" + return load_json_object_fixture("ring_keypad_state.json", DOMAIN) + + @pytest.fixture(name="nortek_thermostat_state", scope="package") def nortek_thermostat_state_fixture() -> dict[str, Any]: """Load the nortek thermostat node state fixture data.""" @@ -876,6 +882,14 @@ def nortek_thermostat_removed_event_fixture(client) -> Node: return Event("node removed", event_data) +@pytest.fixture(name="ring_keypad") +def ring_keypad_fixture(client: MagicMock, ring_keypad_state: NodeDataType) -> Node: + """Mock a Ring keypad node.""" + node = Node(client, copy.deepcopy(ring_keypad_state)) + client.driver.controller.nodes[node.node_id] = node + return node + + @pytest.fixture(name="integration") async def integration_fixture( hass: HomeAssistant, diff --git a/tests/components/zwave_js/fixtures/ring_keypad_state.json b/tests/components/zwave_js/fixtures/ring_keypad_state.json new file mode 100644 index 00000000000..3d003518b6e --- /dev/null +++ b/tests/components/zwave_js/fixtures/ring_keypad_state.json @@ -0,0 +1,7543 @@ +{ + "nodeId": 4, + "index": 0, + "installerIcon": 8193, + "userIcon": 8193, + "status": 4, + "ready": true, + "isListening": false, + "isRouting": true, + "isSecure": true, + "manufacturerId": 838, + "productId": 1025, + "productType": 257, + "firmwareVersion": "1.18.0", + "zwavePlusVersion": 2, + "deviceConfig": { + "filename": "/home/dominic/Repositories/zwavejs2mqtt/store/.config-db/devices/0x0346/keypad_v2.json", + "isEmbedded": true, + "manufacturer": "Ring", + "manufacturerId": 838, + "label": "4AK1SZ", + "description": "Keypad v2", + "devices": [ + { + "productType": 257, + "productId": 769 + }, + { + "productType": 257, + "productId": 1025 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "preferred": false, + "paramInformation": { + "_map": {} + }, + "compat": { + "disableStrictEntryControlDataValidation": true + }, + "metadata": { + "inclusion": "Classic Inclusion should be used if the controller does not support SmartStart.\n1. Initiate add flow for Security Devices in the Ring mobile application \u2013 Follow the guided add flow instructions provided in the Ring mobile application.\n2. Select add manually and enter the 5-digit DSK PIN found on the package of the Ring Alarm Keypad or the 5-digit DSK PIN found under the QR code on the device.\n3. After powering on the device, press and hold the #1 button for ~3 seconds. Release the button and the device will enter Classic inclusion mode which implements both classic inclusion with a Node Information Frame, and Network Wide Inclusion. During Classic Inclusion mode, the green Connection LED will blink three times followed by a brief pause, repeatedly. When Classic inclusion times-out, the device will blink alternating red and green a few times", + "exclusion": "1. Initiate remove 'Ring Alarm Keypad' flow in the Ring Alarm mobile application \u2013 Select the settings icon from device details page and choose 'Remove Device' to remove the device. This will place the controller into Remove or 'Z-Wave Exclusion' mode.\n2. Locate the pinhole reset button on the back of the device.\n3. With the controller in Remove (Z-Wave Exclusion) mode, use a paper clip or similar object and tap the pinhole button. The device's Connection LED turns on solid red to indicate the device was removed from the network.", + "reset": "Factory Default Instructions\n1. To restore Ring Alarm Keypad to factory default settings, locate the pinhole reset button on the device. This is found on the back of the device after removing the back bracket.\n2. Using a paperclip or similar object, insert it into the pinhole, press and hold the button down for 10 seconds.\n3. The device's Connection icon LED will rapidly blink green continuously for 10 seconds. After about 10 seconds, when the green blinking stops, release the button. The red LED will turn on solid to indicate the device was removed from the network.\nNote\nUse this procedure only in the event that the network primary controller is missing or otherwise inoperable", + "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=product_documents/4150/Ring%20Alarm%20Keypad%20Zwave.pdf" + } + }, + "label": "4AK1SZ", + "interviewAttempts": 0, + "isFrequentListening": "250ms", + "maxDataRate": 100000, + "supportedDataRates": [40000, 100000], + "protocolVersion": 3, + "supportsBeaming": true, + "supportsSecurity": false, + "nodeType": 1, + "zwavePlusNodeType": 0, + "zwavePlusRoleType": 7, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing End Node" + }, + "generic": { + "key": 64, + "label": "Entry Control" + }, + "specific": { + "key": 11, + "label": "Secure Keypad" + } + }, + "interviewStage": "Complete", + "deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x0346:0x0101:0x0401:1.18.0", + "statistics": { + "commandsTX": 1, + "commandsRX": 0, + "commandsDroppedRX": 0, + "commandsDroppedTX": 0, + "timeoutResponse": 0, + "rtt": 27.5, + "lastSeen": "2025-06-18T11:17:39.315Z", + "rssi": -54, + "lwr": { + "protocolDataRate": 2, + "repeaters": [], + "rssi": -54, + "repeaterRSSI": [] + } + }, + "highestSecurityClass": 2, + "isControllerNode": false, + "keepAwake": false, + "lastSeen": "2025-06-18T11:17:39.315Z", + "protocol": 0, + "values": [ + { + "endpoint": 0, + "commandClass": 111, + "commandClassName": "Entry Control", + "property": "keyCacheTimeout", + "propertyName": "keyCacheTimeout", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "How long the key cache must wait for additional characters", + "label": "Key cache timeout", + "min": 1, + "max": 30, + "unit": "seconds", + "stateful": true, + "secret": false + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 111, + "commandClassName": "Entry Control", + "property": "keyCacheSize", + "propertyName": "keyCacheSize", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Number of character that must be stored before sending", + "label": "Key cache size", + "min": 4, + "max": 10, + "stateful": true, + "secret": false + }, + "value": 8 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 1, + "propertyName": "Heartbeat Interval", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Heartbeat Interval", + "default": 70, + "min": 1, + "max": 70, + "unit": "minutes", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 70 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 2, + "propertyName": "Message Retry Attempt Limit", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Message Retry Attempt Limit", + "default": 1, + "min": 0, + "max": 5, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 3, + "propertyName": "Delay Between Retry Attempts", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Delay Between Retry Attempts", + "default": 5, + "min": 1, + "max": 60, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 4, + "propertyName": "Announcement Audio Volume", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Announcement Audio Volume", + "default": 7, + "min": 0, + "max": 10, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 7 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 5, + "propertyName": "Key Tone Volume", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Key Tone Volume", + "default": 6, + "min": 0, + "max": 10, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 6 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 6, + "propertyName": "Siren Volume", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Siren Volume", + "default": 10, + "min": 0, + "max": 10, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 10 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 7, + "propertyName": "Long Press Duration: Emergency Buttons", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Hold time required to capture a long press", + "label": "Long Press Duration: Emergency Buttons", + "default": 3, + "min": 2, + "max": 5, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 8, + "propertyName": "Long Press Duration: Number Pad", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Hold time required to capture a long press", + "label": "Long Press Duration: Number Pad", + "default": 3, + "min": 2, + "max": 5, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 9, + "propertyName": "Timeout: Proximity Display", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Timeout: Proximity Display", + "default": 5, + "min": 0, + "max": 30, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 10, + "propertyName": "Timeout: Display on Button Press", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Timeout: Display on Button Press", + "default": 5, + "min": 0, + "max": 30, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 11, + "propertyName": "Timeout: Display on Status Change", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Timeout: Display on Status Change", + "default": 5, + "min": 1, + "max": 30, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 12, + "propertyName": "Brightness: Security Mode", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Brightness: Security Mode", + "default": 100, + "min": 0, + "max": 100, + "unit": "%", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 100 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 13, + "propertyName": "Brightness: Key Backlight", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Brightness: Key Backlight", + "default": 100, + "min": 0, + "max": 100, + "unit": "%", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 100 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 14, + "propertyName": "Key Backlight Ambient Light Sensor Level", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Key Backlight Ambient Light Sensor Level", + "default": 20, + "min": 0, + "max": 100, + "unit": "%", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 20 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 15, + "propertyName": "Proximity Detection", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Proximity Detection", + "default": 1, + "min": 0, + "max": 1, + "states": { + "0": "Disable", + "1": "Enable" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 16, + "propertyName": "LED Ramp Time", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "LED Ramp Time", + "default": 50, + "min": 0, + "max": 255, + "unit": "seconds", + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 50 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 17, + "propertyName": "Battery Low Threshold", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Battery Low Threshold", + "default": 15, + "min": 0, + "max": 100, + "unit": "%", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 30 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 19, + "propertyName": "Battery Warning Threshold", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Battery Warning Threshold", + "default": 5, + "min": 0, + "max": 100, + "unit": "%", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 10 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 18, + "propertyName": "Keypad Language", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Keypad Language", + "default": 30, + "min": 0, + "max": 31, + "states": { + "0": "English", + "2": "French", + "5": "Spanish" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 20, + "propertyName": "System Security Mode Blink Duration", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "System Security Mode Blink Duration", + "default": 2, + "min": 1, + "max": 60, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 21, + "propertyName": "Supervision Report Timeout", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Supervision Report Timeout", + "default": 10000, + "min": 500, + "max": 30000, + "unit": "ms", + "valueSize": 2, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 10000 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 22, + "propertyName": "System Security Mode Display", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Allowable range: 1-600", + "label": "System Security Mode Display", + "default": 0, + "min": 0, + "max": 601, + "states": { + "0": "Always off", + "601": "Always on" + }, + "valueSize": 2, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 1, + "propertyName": "param023_1", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 1, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 2, + "propertyName": "param023_2", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 4, + "propertyName": "param023_4", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 1, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 8, + "propertyName": "param023_8", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 16, + "propertyName": "param023_16", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 32, + "propertyName": "param023_32", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 1, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 64, + "propertyName": "param023_64", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 128, + "propertyName": "param023_128", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 256, + "propertyName": "param023_256", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 512, + "propertyName": "param023_512", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 1024, + "propertyName": "param023_1024", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 2048, + "propertyName": "param023_2048", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 4096, + "propertyName": "param023_4096", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 8192, + "propertyName": "param023_8192", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 16384, + "propertyName": "param023_16384", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 32768, + "propertyName": "param023_32768", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 65536, + "propertyName": "param023_65536", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 131072, + "propertyName": "param023_131072", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 262144, + "propertyName": "param023_262144", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 524288, + "propertyName": "param023_524288", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 1048576, + "propertyName": "param023_1048576", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 2097152, + "propertyName": "param023_2097152", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 4194304, + "propertyName": "param023_4194304", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 8388608, + "propertyName": "param023_8388608", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 16777216, + "propertyName": "param023_16777216", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 33554432, + "propertyName": "param023_33554432", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 67108864, + "propertyName": "param023_67108864", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 134217728, + "propertyName": "param023_134217728", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 268435456, + "propertyName": "param023_268435456", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 536870912, + "propertyName": "param023_536870912", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 1073741824, + "propertyName": "param023_1073741824", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyKey": 2147483648, + "propertyName": "param023_2147483648", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "default": 0, + "min": 0, + "max": 1, + "valueSize": 4, + "format": 3, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 24, + "propertyName": "Calibrate Speaker", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Calibrate Speaker", + "default": 0, + "min": 0, + "max": 1, + "states": { + "0": "Disable", + "1": "Enable" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 26, + "propertyName": "Motion Sensor Timeout", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Motion Sensor Timeout", + "default": 3, + "min": 0, + "max": 60, + "unit": "seconds", + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 25, + "propertyName": "Z-Wave Sleep Timeout", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "", + "label": "Z-Wave Sleep Timeout", + "default": 10, + "min": 0, + "max": 15, + "valueSize": 1, + "format": 1, + "isAdvanced": true, + "requiresReInclusion": false, + "allowManualEntry": true, + "isFromConfig": false + }, + "value": 10 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyName": "Languages Supported Report", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "description": "This parameter reports a bitmask of supported languages", + "label": "Languages Supported Report", + "default": 37, + "min": 0, + "max": 4294967295, + "valueSize": 4, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + } + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "Home Security", + "propertyKey": "Motion sensor status", + "propertyName": "Home Security", + "propertyKeyName": "Motion sensor status", + "ccVersion": 8, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Motion sensor status", + "ccSpecific": { + "notificationType": 7 + }, + "min": 0, + "max": 255, + "states": { + "0": "idle", + "8": "Motion detection" + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "Power Management", + "propertyKey": "Power status", + "propertyName": "Power Management", + "propertyKeyName": "Power status", + "ccVersion": 8, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Power status", + "ccSpecific": { + "notificationType": 8 + }, + "min": 0, + "max": 255, + "states": { + "0": "idle", + "1": "Power has been applied" + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "System", + "propertyKey": "Software status", + "propertyName": "System", + "propertyKeyName": "Software status", + "ccVersion": 8, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Software status", + "ccSpecific": { + "notificationType": 9 + }, + "min": 0, + "max": 255, + "states": { + "0": "idle", + "4": "System software failure (with failure code)" + }, + "stateful": true, + "secret": false + }, + "value": 4 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "alarmType", + "propertyName": "alarmType", + "ccVersion": 8, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Alarm Type", + "min": 0, + "max": 255, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "alarmLevel", + "propertyName": "alarmLevel", + "ccVersion": 8, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Alarm Level", + "min": 0, + "max": 255, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "Power Management", + "propertyKey": "Mains status", + "propertyName": "Power Management", + "propertyKeyName": "Mains status", + "ccVersion": 8, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Mains status", + "ccSpecific": { + "notificationType": 8 + }, + "min": 0, + "max": 255, + "states": { + "2": "AC mains disconnected", + "3": "AC mains re-connected" + }, + "stateful": true, + "secret": false + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productId", + "propertyName": "productId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Product ID", + "min": 0, + "max": 65535, + "stateful": true, + "secret": false + }, + "value": 1025 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productType", + "propertyName": "productType", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Product type", + "min": 0, + "max": 65535, + "stateful": true, + "secret": false + }, + "value": 257 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "manufacturerId", + "propertyName": "manufacturerId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Manufacturer ID", + "min": 0, + "max": 65535, + "stateful": true, + "secret": false + }, + "value": 838 + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "disconnected", + "propertyName": "disconnected", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Battery is disconnected", + "stateful": true, + "secret": false + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "rechargeOrReplace", + "propertyName": "rechargeOrReplace", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Recharge or replace", + "min": 0, + "max": 255, + "states": { + "0": "No", + "1": "Soon", + "2": "Now" + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "lowFluid", + "propertyName": "lowFluid", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Fluid is low", + "stateful": true, + "secret": false + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "overheating", + "propertyName": "overheating", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Overheating", + "stateful": true, + "secret": false + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "backup", + "propertyName": "backup", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Used as backup", + "stateful": true, + "secret": false + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "rechargeable", + "propertyName": "rechargeable", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Rechargeable", + "stateful": true, + "secret": false + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "chargingStatus", + "propertyName": "chargingStatus", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Charging status", + "min": 0, + "max": 255, + "states": { + "0": "Discharging", + "1": "Charging", + "2": "Maintaining" + }, + "stateful": true, + "secret": false + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "isLow", + "propertyName": "isLow", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Low battery level", + "stateful": true, + "secret": false + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "level", + "propertyName": "level", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Battery level", + "min": 0, + "max": 100, + "unit": "%", + "stateful": true, + "secret": false + }, + "value": 100 + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "temperature", + "propertyName": "temperature", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Temperature", + "min": -128, + "max": 127, + "unit": "\u00b0C", + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "maximumCapacity", + "propertyName": "maximumCapacity", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Maximum capacity", + "min": 0, + "max": 100, + "unit": "%", + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hardwareVersion", + "propertyName": "hardwareVersion", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Z-Wave chip hardware version", + "stateful": true, + "secret": false + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "firmwareVersions", + "propertyName": "firmwareVersions", + "ccVersion": 3, + "metadata": { + "type": "string[]", + "readable": true, + "writeable": false, + "label": "Z-Wave chip firmware versions", + "stateful": true, + "secret": false + }, + "value": ["1.18", "1.1"] + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "protocolVersion", + "propertyName": "protocolVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol version", + "stateful": true, + "secret": false + }, + "value": "7.12" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "libraryType", + "propertyName": "libraryType", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Library type", + "states": { + "0": "Unknown", + "1": "Static Controller", + "2": "Controller", + "3": "Enhanced Slave", + "4": "Slave", + "5": "Installer", + "6": "Routing Slave", + "7": "Bridge Controller", + "8": "Device under Test", + "9": "N/A", + "10": "AV Remote", + "11": "AV Device" + }, + "stateful": true, + "secret": false + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationBuildNumber", + "propertyName": "applicationBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Application build number", + "stateful": true, + "secret": false + }, + "value": 28 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationVersion", + "propertyName": "applicationVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Application version", + "stateful": true, + "secret": false + }, + "value": "1.18.0" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "zWaveProtocolBuildNumber", + "propertyName": "zWaveProtocolBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol build number", + "stateful": true, + "secret": false + }, + "value": 48 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "zWaveProtocolVersion", + "propertyName": "zWaveProtocolVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol version", + "stateful": true, + "secret": false + }, + "value": "7.12.4" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hostInterfaceBuildNumber", + "propertyName": "hostInterfaceBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Serial API build number", + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hostInterfaceVersion", + "propertyName": "hostInterfaceVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Serial API version", + "stateful": true, + "secret": false + }, + "value": "unused" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationFrameworkBuildNumber", + "propertyName": "applicationFrameworkBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave application framework API build number", + "stateful": true, + "secret": false + }, + "value": 48 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationFrameworkAPIVersion", + "propertyName": "applicationFrameworkAPIVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave application framework API version", + "stateful": true, + "secret": false + }, + "value": "1.18.0" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "sdkVersion", + "propertyName": "sdkVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "SDK version", + "stateful": true, + "secret": false + }, + "value": "7.12.4" + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 1, + "propertyName": "0", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0 (default) - Multilevel", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 3, + "propertyName": "0", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0 (default) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 4, + "propertyName": "0", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0 (default) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 5, + "propertyName": "0", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0 (default) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 7, + "propertyName": "0", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0 (default) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 9, + "propertyName": "0", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0 (default) - Sound level", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 8, + "propertyName": "0", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0 (default) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 0, + "propertyKey": 6, + "propertyName": "0", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0 (default) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 0, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "timeout", + "propertyName": "timeout", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": true, + "label": "Timeout", + "stateful": true, + "secret": false + }, + "value": "" + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 1, + "propertyName": "Ready", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x03 (Ready) - Multilevel", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 6 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 3, + "propertyName": "Ready", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x03 (Ready) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 4, + "propertyName": "Ready", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x03 (Ready) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 5, + "propertyName": "Ready", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x03 (Ready) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 7, + "propertyName": "Ready", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x03 (Ready) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 4 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 9, + "propertyName": "Ready", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x03 (Ready) - Sound level", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 8, + "propertyName": "Ready", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x03 (Ready) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 3, + "propertyKey": 6, + "propertyName": "Ready", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x03 (Ready) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 3, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 1, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x02 (Not armed / disarmed) - Multilevel", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 3, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x02 (Not armed / disarmed) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 4, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x02 (Not armed / disarmed) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 5, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x02 (Not armed / disarmed) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 7, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x02 (Not armed / disarmed) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 9, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x02 (Not armed / disarmed) - Sound level", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 8, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x02 (Not armed / disarmed) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 2, + "propertyKey": 6, + "propertyName": "Not armed / disarmed", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x02 (Not armed / disarmed) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 2, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 1, + "propertyName": "Code not accepted", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x09 (Code not accepted) - Multilevel", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 3, + "propertyName": "Code not accepted", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x09 (Code not accepted) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 4, + "propertyName": "Code not accepted", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x09 (Code not accepted) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 5, + "propertyName": "Code not accepted", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x09 (Code not accepted) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 7, + "propertyName": "Code not accepted", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x09 (Code not accepted) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 9, + "propertyName": "Code not accepted", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x09 (Code not accepted) - Sound level", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 8, + "propertyName": "Code not accepted", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x09 (Code not accepted) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 9, + "propertyKey": 6, + "propertyName": "Code not accepted", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x09 (Code not accepted) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 9, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 1, + "propertyName": "Armed Stay", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x0a (Armed Stay) - Multilevel", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 3, + "propertyName": "Armed Stay", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x0a (Armed Stay) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 4, + "propertyName": "Armed Stay", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x0a (Armed Stay) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 5, + "propertyName": "Armed Stay", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x0a (Armed Stay) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 7, + "propertyName": "Armed Stay", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x0a (Armed Stay) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 9, + "propertyName": "Armed Stay", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x0a (Armed Stay) - Sound level", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 8, + "propertyName": "Armed Stay", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x0a (Armed Stay) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 10, + "propertyKey": 6, + "propertyName": "Armed Stay", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x0a (Armed Stay) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 10, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 1, + "propertyName": "Armed Away", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x0b (Armed Away) - Multilevel", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 3, + "propertyName": "Armed Away", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x0b (Armed Away) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 4, + "propertyName": "Armed Away", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x0b (Armed Away) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 5, + "propertyName": "Armed Away", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x0b (Armed Away) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 7, + "propertyName": "Armed Away", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x0b (Armed Away) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 9, + "propertyName": "Armed Away", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x0b (Armed Away) - Sound level", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 8, + "propertyName": "Armed Away", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x0b (Armed Away) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 11, + "propertyKey": 6, + "propertyName": "Armed Away", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x0b (Armed Away) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 11, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 1, + "propertyName": "Alarming", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x0c (Alarming) - Multilevel", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 3, + "propertyName": "Alarming", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x0c (Alarming) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 4, + "propertyName": "Alarming", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x0c (Alarming) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 5, + "propertyName": "Alarming", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x0c (Alarming) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 7, + "propertyName": "Alarming", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x0c (Alarming) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 9, + "propertyName": "Alarming", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x0c (Alarming) - Sound level", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 8, + "propertyName": "Alarming", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x0c (Alarming) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 12, + "propertyKey": 6, + "propertyName": "Alarming", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x0c (Alarming) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 12, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 1, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x0d (Alarming: Burglar) - Multilevel", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 3, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x0d (Alarming: Burglar) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 4, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x0d (Alarming: Burglar) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 5, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x0d (Alarming: Burglar) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 7, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x0d (Alarming: Burglar) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 9, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x0d (Alarming: Burglar) - Sound level", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 8, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x0d (Alarming: Burglar) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 13, + "propertyKey": 6, + "propertyName": "Alarming: Burglar", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x0d (Alarming: Burglar) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 13, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 1, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x0e (Alarming: Smoke / Fire) - Multilevel", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 3, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x0e (Alarming: Smoke / Fire) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 4, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x0e (Alarming: Smoke / Fire) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 5, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x0e (Alarming: Smoke / Fire) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 7, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x0e (Alarming: Smoke / Fire) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 9, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x0e (Alarming: Smoke / Fire) - Sound level", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 8, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x0e (Alarming: Smoke / Fire) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 14, + "propertyKey": 6, + "propertyName": "Alarming: Smoke / Fire", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x0e (Alarming: Smoke / Fire) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 14, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 1, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x0f (Alarming: Carbon Monoxide) - Multilevel", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 3, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x0f (Alarming: Carbon Monoxide) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 4, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x0f (Alarming: Carbon Monoxide) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 5, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x0f (Alarming: Carbon Monoxide) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 7, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x0f (Alarming: Carbon Monoxide) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 9, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x0f (Alarming: Carbon Monoxide) - Sound level", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 8, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x0f (Alarming: Carbon Monoxide) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 15, + "propertyKey": 6, + "propertyName": "Alarming: Carbon Monoxide", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x0f (Alarming: Carbon Monoxide) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 15, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 1, + "propertyName": "Bypass challenge", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x10 (Bypass challenge) - Multilevel", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 3, + "propertyName": "Bypass challenge", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x10 (Bypass challenge) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 4, + "propertyName": "Bypass challenge", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x10 (Bypass challenge) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 5, + "propertyName": "Bypass challenge", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x10 (Bypass challenge) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 7, + "propertyName": "Bypass challenge", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x10 (Bypass challenge) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 9, + "propertyName": "Bypass challenge", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x10 (Bypass challenge) - Sound level", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 8, + "propertyName": "Bypass challenge", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x10 (Bypass challenge) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 16, + "propertyKey": 6, + "propertyName": "Bypass challenge", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x10 (Bypass challenge) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 16, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 1, + "propertyName": "Entry Delay", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x11 (Entry Delay) - Multilevel", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 3, + "propertyName": "Entry Delay", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x11 (Entry Delay) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 4, + "propertyName": "Entry Delay", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x11 (Entry Delay) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 5, + "propertyName": "Entry Delay", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x11 (Entry Delay) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 7, + "propertyName": "Entry Delay", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x11 (Entry Delay) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 9, + "propertyName": "Entry Delay", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x11 (Entry Delay) - Sound level", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 8, + "propertyName": "Entry Delay", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x11 (Entry Delay) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 17, + "propertyKey": 6, + "propertyName": "Entry Delay", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x11 (Entry Delay) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 17, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 1, + "propertyName": "Exit Delay", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x12 (Exit Delay) - Multilevel", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 3, + "propertyName": "Exit Delay", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x12 (Exit Delay) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 4, + "propertyName": "Exit Delay", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x12 (Exit Delay) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 5, + "propertyName": "Exit Delay", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x12 (Exit Delay) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 7, + "propertyName": "Exit Delay", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x12 (Exit Delay) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 9, + "propertyName": "Exit Delay", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x12 (Exit Delay) - Sound level", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 8, + "propertyName": "Exit Delay", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x12 (Exit Delay) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 18, + "propertyKey": 6, + "propertyName": "Exit Delay", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x12 (Exit Delay) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 18, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 1, + "propertyName": "Alarming: Medical", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x13 (Alarming: Medical) - Multilevel", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 3, + "propertyName": "Alarming: Medical", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x13 (Alarming: Medical) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 4, + "propertyName": "Alarming: Medical", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x13 (Alarming: Medical) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 5, + "propertyName": "Alarming: Medical", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x13 (Alarming: Medical) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 7, + "propertyName": "Alarming: Medical", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x13 (Alarming: Medical) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 9, + "propertyName": "Alarming: Medical", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x13 (Alarming: Medical) - Sound level", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 8, + "propertyName": "Alarming: Medical", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x13 (Alarming: Medical) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 19, + "propertyKey": 6, + "propertyName": "Alarming: Medical", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x13 (Alarming: Medical) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 19, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 1, + "propertyName": "Node Identify", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x50 (Node Identify) - Multilevel", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 3, + "propertyName": "Node Identify", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x50 (Node Identify) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 4, + "propertyName": "Node Identify", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x50 (Node Identify) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 5, + "propertyName": "Node Identify", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x50 (Node Identify) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 7, + "propertyName": "Node Identify", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x50 (Node Identify) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 9, + "propertyName": "Node Identify", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x50 (Node Identify) - Sound level", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 8, + "propertyName": "Node Identify", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x50 (Node Identify) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 6, + "propertyName": "Node Identify", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x50 (Node Identify) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 1, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x60 (Generic event sound notification 1) - Multilevel", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 3, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x60 (Generic event sound notification 1) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 4, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x60 (Generic event sound notification 1) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 5, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x60 (Generic event sound notification 1) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 7, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x60 (Generic event sound notification 1) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 9, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x60 (Generic event sound notification 1) - Sound level", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 8, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x60 (Generic event sound notification 1) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 96, + "propertyKey": 6, + "propertyName": "Generic event sound notification 1", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x60 (Generic event sound notification 1) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 96, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 1, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x61 (Generic event sound notification 2) - Multilevel", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 3, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x61 (Generic event sound notification 2) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 4, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x61 (Generic event sound notification 2) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 5, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x61 (Generic event sound notification 2) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 7, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x61 (Generic event sound notification 2) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 9, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x61 (Generic event sound notification 2) - Sound level", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 8, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x61 (Generic event sound notification 2) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 97, + "propertyKey": 6, + "propertyName": "Generic event sound notification 2", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x61 (Generic event sound notification 2) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 97, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 1, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x62 (Generic event sound notification 3) - Multilevel", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 3, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x62 (Generic event sound notification 3) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 4, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x62 (Generic event sound notification 3) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 5, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x62 (Generic event sound notification 3) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 7, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x62 (Generic event sound notification 3) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 9, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x62 (Generic event sound notification 3) - Sound level", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 8, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x62 (Generic event sound notification 3) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 98, + "propertyKey": 6, + "propertyName": "Generic event sound notification 3", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x62 (Generic event sound notification 3) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 98, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 1, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x63 (Generic event sound notification 4) - Multilevel", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 3, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x63 (Generic event sound notification 4) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 4, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x63 (Generic event sound notification 4) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 5, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x63 (Generic event sound notification 4) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 7, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x63 (Generic event sound notification 4) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 9, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x63 (Generic event sound notification 4) - Sound level", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 8, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x63 (Generic event sound notification 4) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 99, + "propertyKey": 6, + "propertyName": "Generic event sound notification 4", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x63 (Generic event sound notification 4) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 99, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 1, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x64 (Generic event sound notification 5) - Multilevel", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 3, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x64 (Generic event sound notification 5) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 4, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x64 (Generic event sound notification 5) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 5, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x64 (Generic event sound notification 5) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 7, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x64 (Generic event sound notification 5) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 9, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x64 (Generic event sound notification 5) - Sound level", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 8, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x64 (Generic event sound notification 5) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 100, + "propertyKey": 6, + "propertyName": "Generic event sound notification 5", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x64 (Generic event sound notification 5) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 100, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 1, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x14 (Alarming: Freeze warning) - Multilevel", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 3, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x14 (Alarming: Freeze warning) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 4, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x14 (Alarming: Freeze warning) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 5, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x14 (Alarming: Freeze warning) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 7, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x14 (Alarming: Freeze warning) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 9, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x14 (Alarming: Freeze warning) - Sound level", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 8, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x14 (Alarming: Freeze warning) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 20, + "propertyKey": 6, + "propertyName": "Alarming: Freeze warning", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x14 (Alarming: Freeze warning) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 20, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 1, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x15 (Alarming: Water leak) - Multilevel", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 3, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x15 (Alarming: Water leak) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 4, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x15 (Alarming: Water leak) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 5, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x15 (Alarming: Water leak) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 7, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x15 (Alarming: Water leak) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 9, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x15 (Alarming: Water leak) - Sound level", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 8, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x15 (Alarming: Water leak) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 21, + "propertyKey": 6, + "propertyName": "Alarming: Water leak", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x15 (Alarming: Water leak) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 21, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 1, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x81 (Manufacturer defined 2) - Multilevel", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 3, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x81 (Manufacturer defined 2) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 4, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x81 (Manufacturer defined 2) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 5, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x81 (Manufacturer defined 2) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 7, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x81 (Manufacturer defined 2) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 9, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x81 (Manufacturer defined 2) - Sound level", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 8, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x81 (Manufacturer defined 2) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 129, + "propertyKey": 6, + "propertyName": "Manufacturer defined 2", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x81 (Manufacturer defined 2) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 129, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 1, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x82 (Manufacturer defined 3) - Multilevel", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 3, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x82 (Manufacturer defined 3) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 4, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x82 (Manufacturer defined 3) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 5, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x82 (Manufacturer defined 3) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 7, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x82 (Manufacturer defined 3) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 9, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x82 (Manufacturer defined 3) - Sound level", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 8, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x82 (Manufacturer defined 3) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 130, + "propertyKey": 6, + "propertyName": "Manufacturer defined 3", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x82 (Manufacturer defined 3) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 130, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 1, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x83 (Manufacturer defined 4) - Multilevel", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 3, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x83 (Manufacturer defined 4) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 4, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x83 (Manufacturer defined 4) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 5, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x83 (Manufacturer defined 4) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 7, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x83 (Manufacturer defined 4) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 9, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x83 (Manufacturer defined 4) - Sound level", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 8, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x83 (Manufacturer defined 4) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 131, + "propertyKey": 6, + "propertyName": "Manufacturer defined 4", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x83 (Manufacturer defined 4) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 131, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 1, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x84 (Manufacturer defined 5) - Multilevel", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 3, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x84 (Manufacturer defined 5) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 4, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x84 (Manufacturer defined 5) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 5, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x84 (Manufacturer defined 5) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 7, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x84 (Manufacturer defined 5) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 9, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x84 (Manufacturer defined 5) - Sound level", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 8, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x84 (Manufacturer defined 5) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 132, + "propertyKey": 6, + "propertyName": "Manufacturer defined 5", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x84 (Manufacturer defined 5) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 132, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 1, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "Multilevel", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "0x85 (Manufacturer defined 6) - Multilevel", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 1 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 3, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "0x85 (Manufacturer defined 6) - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 3 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 4, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "0x85 (Manufacturer defined 6) - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 4 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 5, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "0x85 (Manufacturer defined 6) - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 5 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 7, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "Timeout: Seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of seconds. Can be used together with other timeout properties", + "label": "0x85 (Manufacturer defined 6) - Timeout: Seconds", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 7 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 9, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "Sound level", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the volume of a indicator. 0 means off/mute.", + "label": "0x85 (Manufacturer defined 6) - Sound level", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 9 + }, + "max": 100, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 8, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "Timeout: 1/100th seconds", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of 1/100th seconds. Can be used together with other timeout properties", + "label": "0x85 (Manufacturer defined 6) - Timeout: 1/100th seconds", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 8 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 133, + "propertyKey": 6, + "propertyName": "Manufacturer defined 6", + "propertyKeyName": "Timeout: Minutes", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Turns the indicator of after this amount of minutes. Can be used together with other timeout properties", + "label": "0x85 (Manufacturer defined 6) - Timeout: Minutes", + "ccSpecific": { + "indicatorId": 133, + "propertyId": 6 + }, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "value", + "propertyName": "value", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Indicator value", + "ccSpecific": { + "indicatorId": 0 + }, + "min": 0, + "max": 255, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "identify", + "propertyName": "identify", + "ccVersion": 3, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Identify", + "states": { + "true": "Identify" + }, + "stateful": true, + "secret": false + } + } + ], + "endpoints": [ + { + "nodeId": 4, + "index": 0, + "installerIcon": 8193, + "userIcon": 8193, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing End Node" + }, + "generic": { + "key": 64, + "label": "Entry Control" + }, + "specific": { + "key": 11, + "label": "Secure Keypad" + } + }, + "commandClasses": [ + { + "id": 159, + "name": "Security 2", + "version": 1, + "isSecure": true + }, + { + "id": 89, + "name": "Association Group Information", + "version": 3, + "isSecure": true + }, + { + "id": 133, + "name": "Association", + "version": 2, + "isSecure": true + }, + { + "id": 128, + "name": "Battery", + "version": 2, + "isSecure": true + }, + { + "id": 112, + "name": "Configuration", + "version": 4, + "isSecure": true + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": true + }, + { + "id": 111, + "name": "Entry Control", + "version": 1, + "isSecure": true + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 5, + "isSecure": true + }, + { + "id": 135, + "name": "Indicator", + "version": 3, + "isSecure": true + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": true + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 3, + "isSecure": true + }, + { + "id": 113, + "name": "Notification", + "version": 8, + "isSecure": true + }, + { + "id": 115, + "name": "Powerlevel", + "version": 1, + "isSecure": true + }, + { + "id": 134, + "name": "Version", + "version": 3, + "isSecure": true + }, + { + "id": 152, + "name": "Security", + "version": 1, + "isSecure": true + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 108, + "name": "Supervision", + "version": 1, + "isSecure": false + }, + { + "id": 85, + "name": "Transport Service", + "version": 2, + "isSecure": false + } + ] + } + ] +} diff --git a/tests/components/zwave_js/snapshots/test_diagnostics.ambr b/tests/components/zwave_js/snapshots/test_diagnostics.ambr index dc0dbba59b5..40ed3bbf836 100644 --- a/tests/components/zwave_js/snapshots/test_diagnostics.ambr +++ b/tests/components/zwave_js/snapshots/test_diagnostics.ambr @@ -97,8 +97,8 @@ 'value_id': '52-113-0-Home Security-Cover status', }), dict({ - 'disabled': False, - 'disabled_by': None, + 'disabled': True, + 'disabled_by': 'integration', 'domain': 'button', 'entity_category': 'config', 'entity_id': 'button.multisensor_6_idle_home_security_cover_status', @@ -120,8 +120,8 @@ 'value_id': '52-113-0-Home Security-Cover status', }), dict({ - 'disabled': False, - 'disabled_by': None, + 'disabled': True, + 'disabled_by': 'integration', 'domain': 'button', 'entity_category': 'config', 'entity_id': 'button.multisensor_6_idle_home_security_motion_sensor_status', diff --git a/tests/components/zwave_js/test_binary_sensor.py b/tests/components/zwave_js/test_binary_sensor.py index 93ac52f9041..5dfbb0f5bd8 100644 --- a/tests/components/zwave_js/test_binary_sensor.py +++ b/tests/components/zwave_js/test_binary_sensor.py @@ -1,10 +1,13 @@ """Test the Z-Wave JS binary sensor platform.""" +from datetime import timedelta + import pytest from zwave_js_server.event import Event from zwave_js_server.model.node import Node from homeassistant.components.binary_sensor import BinarySensorDeviceClass +from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY from homeassistant.const import ( ATTR_DEVICE_CLASS, STATE_OFF, @@ -15,17 +18,17 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.util import dt as dt_util from .common import ( DISABLED_LEGACY_BINARY_SENSOR, ENABLED_LEGACY_BINARY_SENSOR, - LOW_BATTERY_BINARY_SENSOR, NOTIFICATION_MOTION_BINARY_SENSOR, PROPERTY_DOOR_STATUS_BINARY_SENSOR, TAMPER_SENSOR, ) -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed @pytest.fixture @@ -34,21 +37,56 @@ def platforms() -> list[str]: return [Platform.BINARY_SENSOR] -async def test_low_battery_sensor( - hass: HomeAssistant, entity_registry: er.EntityRegistry, multisensor_6, integration +async def test_battery_sensors( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + ring_keypad: Node, + integration: MockConfigEntry, ) -> None: - """Test boolean binary sensor of type low battery.""" - state = hass.states.get(LOW_BATTERY_BINARY_SENSOR) + """Test boolean battery binary sensors.""" + entity_id = "binary_sensor.keypad_v2_low_battery_level" + state = hass.states.get(entity_id) assert state assert state.state == STATE_OFF assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.BATTERY - entity_entry = entity_registry.async_get(LOW_BATTERY_BINARY_SENSOR) + entity_entry = entity_registry.async_get(entity_id) assert entity_entry assert entity_entry.entity_category is EntityCategory.DIAGNOSTIC + disabled_binary_sensor_battery_entities = ( + "binary_sensor.keypad_v2_battery_is_disconnected", + "binary_sensor.keypad_v2_fluid_is_low", + "binary_sensor.keypad_v2_overheating", + "binary_sensor.keypad_v2_rechargeable", + "binary_sensor.keypad_v2_used_as_backup", + ) + + for entity_id in disabled_binary_sensor_battery_entities: + state = hass.states.get(entity_id) + assert state is None # disabled by default + + entity_entry = entity_registry.async_get(entity_id) + + assert entity_entry + assert entity_entry.entity_category is EntityCategory.DIAGNOSTIC + assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + + entity_registry.async_update_entity(entity_id, disabled_by=None) + + async_fire_time_changed( + hass, + dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1), + ) + await hass.async_block_till_done() + + for entity_id in disabled_binary_sensor_battery_entities: + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_OFF + async def test_enabled_legacy_sensor( hass: HomeAssistant, ecolink_door_sensor, integration diff --git a/tests/components/zwave_js/test_button.py b/tests/components/zwave_js/test_button.py index 0282a268b54..422888cab23 100644 --- a/tests/components/zwave_js/test_button.py +++ b/tests/components/zwave_js/test_button.py @@ -1,13 +1,21 @@ """Test the Z-Wave JS button entities.""" +from datetime import timedelta +from unittest.mock import MagicMock + import pytest +from zwave_js_server.model.node import Node from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.components.zwave_js.const import DOMAIN, SERVICE_REFRESH_VALUE from homeassistant.components.zwave_js.helpers import get_valueless_base_unique_id -from homeassistant.const import ATTR_ENTITY_ID, Platform +from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY +from homeassistant.const import ATTR_ENTITY_ID, EntityCategory, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.util import dt as dt_util + +from tests.common import MockConfigEntry, async_fire_time_changed @pytest.fixture @@ -71,11 +79,32 @@ async def test_ping_entity( async def test_notification_idle_button( - hass: HomeAssistant, client, multisensor_6, integration + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + client: MagicMock, + multisensor_6: Node, + integration: MockConfigEntry, ) -> None: """Test Notification idle button.""" node = multisensor_6 - state = hass.states.get("button.multisensor_6_idle_home_security_cover_status") + entity_id = "button.multisensor_6_idle_home_security_cover_status" + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category is EntityCategory.CONFIG + assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + assert hass.states.get(entity_id) is None # disabled by default + + entity_registry.async_update_entity( + entity_id, + disabled_by=None, + ) + async_fire_time_changed( + hass, + dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1), + ) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) assert state assert state.state == "unknown" assert ( @@ -88,13 +117,13 @@ async def test_notification_idle_button( BUTTON_DOMAIN, SERVICE_PRESS, { - ATTR_ENTITY_ID: "button.multisensor_6_idle_home_security_cover_status", + ATTR_ENTITY_ID: entity_id, }, blocking=True, ) - assert len(client.async_send_command_no_wait.call_args_list) == 1 - args = client.async_send_command_no_wait.call_args_list[0][0][0] + assert client.async_send_command_no_wait.call_count == 1 + args = client.async_send_command_no_wait.call_args[0][0] assert args["command"] == "node.manually_idle_notification_value" assert args["nodeId"] == node.node_id assert args["valueId"] == { diff --git a/tests/components/zwave_js/test_discovery.py b/tests/components/zwave_js/test_discovery.py index 7ef5f0e480f..02296262d1f 100644 --- a/tests/components/zwave_js/test_discovery.py +++ b/tests/components/zwave_js/test_discovery.py @@ -1,10 +1,12 @@ """Test entity discovery for device-specific schemas for the Z-Wave JS integration.""" +from datetime import timedelta +from unittest.mock import MagicMock + import pytest from zwave_js_server.event import Event from zwave_js_server.model.node import Node -from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.components.light import ATTR_SUPPORTED_COLOR_MODES, ColorMode from homeassistant.components.number import ( @@ -12,7 +14,6 @@ from homeassistant.components.number import ( DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE, ) -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.switch import ( DOMAIN as SWITCH_DOMAIN, SERVICE_TURN_OFF, @@ -26,12 +27,13 @@ from homeassistant.components.zwave_js.discovery import ( from homeassistant.components.zwave_js.discovery_data_template import ( DynamicCurrentTempClimateDataTemplate, ) -from homeassistant.components.zwave_js.helpers import get_device_id +from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_UNKNOWN, EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.util import dt as dt_util -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed async def test_aeon_smart_switch_6_state( @@ -222,17 +224,24 @@ async def test_merten_507801_disabled_enitites( async def test_zooz_zen72( hass: HomeAssistant, entity_registry: er.EntityRegistry, - client, - switch_zooz_zen72, - integration, + client: MagicMock, + switch_zooz_zen72: Node, + integration: MockConfigEntry, ) -> None: """Test that Zooz ZEN72 Indicators are discovered as number entities.""" - assert len(hass.states.async_entity_ids(NUMBER_DOMAIN)) == 1 - assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 2 # includes ping entity_id = "number.z_wave_plus_700_series_dimmer_switch_indicator_value" - entry = entity_registry.async_get(entity_id) - assert entry - assert entry.entity_category == EntityCategory.CONFIG + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category == EntityCategory.CONFIG + assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + assert hass.states.get(entity_id) is None # disabled by default + entity_registry.async_update_entity(entity_id, disabled_by=None) + async_fire_time_changed( + hass, + dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1), + ) + await hass.async_block_till_done() + client.async_send_command.reset_mock() state = hass.states.get(entity_id) assert state assert state.state == STATE_UNKNOWN @@ -246,7 +255,7 @@ async def test_zooz_zen72( }, blocking=True, ) - assert len(client.async_send_command.call_args_list) == 1 + assert client.async_send_command.call_count == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "node.set_value" assert args["nodeId"] == switch_zooz_zen72.node_id @@ -260,16 +269,18 @@ async def test_zooz_zen72( client.async_send_command.reset_mock() entity_id = "button.z_wave_plus_700_series_dimmer_switch_identify" - entry = entity_registry.async_get(entity_id) - assert entry - assert entry.entity_category == EntityCategory.CONFIG + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category == EntityCategory.CONFIG + assert entity_entry.disabled_by is None + assert hass.states.get(entity_id) is not None await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, {ATTR_ENTITY_ID: entity_id}, blocking=True, ) - assert len(client.async_send_command.call_args_list) == 1 + assert client.async_send_command.call_count == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "node.set_value" assert args["nodeId"] == switch_zooz_zen72.node_id @@ -285,53 +296,55 @@ async def test_indicator_test( hass: HomeAssistant, device_registry: dr.DeviceRegistry, entity_registry: er.EntityRegistry, - client, - indicator_test, - integration, + client: MagicMock, + indicator_test: Node, + integration: MockConfigEntry, ) -> None: """Test that Indicators are discovered properly. This test covers indicators that we don't already have device fixtures for. """ - device = device_registry.async_get_device( - identifiers={get_device_id(client.driver, indicator_test)} + binary_sensor_entity_id = "binary_sensor.this_is_a_fake_device_binary_sensor" + sensor_entity_id = "sensor.this_is_a_fake_device_sensor" + switch_entity_id = "switch.this_is_a_fake_device_switch" + + for entity_id in ( + binary_sensor_entity_id, + sensor_entity_id, + ): + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category == EntityCategory.DIAGNOSTIC + assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + assert hass.states.get(entity_id) is None # disabled by default + entity_registry.async_update_entity(entity_id, disabled_by=None) + + entity_id = switch_entity_id + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category == EntityCategory.CONFIG + assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + assert hass.states.get(entity_id) is None # disabled by default + entity_registry.async_update_entity(entity_id, disabled_by=None) + + async_fire_time_changed( + hass, + dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1), ) - assert device - entities = er.async_entries_for_device(entity_registry, device.id) + await hass.async_block_till_done() + client.async_send_command.reset_mock() - def len_domain(domain): - return len([entity for entity in entities if entity.domain == domain]) - - assert len_domain(NUMBER_DOMAIN) == 0 - assert len_domain(BUTTON_DOMAIN) == 1 # only ping - assert len_domain(BINARY_SENSOR_DOMAIN) == 1 - assert len_domain(SENSOR_DOMAIN) == 3 # include node status + last seen - assert len_domain(SWITCH_DOMAIN) == 1 - - entity_id = "binary_sensor.this_is_a_fake_device_binary_sensor" - entry = entity_registry.async_get(entity_id) - assert entry - assert entry.entity_category == EntityCategory.DIAGNOSTIC + entity_id = binary_sensor_entity_id state = hass.states.get(entity_id) assert state assert state.state == STATE_OFF - client.async_send_command.reset_mock() - - entity_id = "sensor.this_is_a_fake_device_sensor" - entry = entity_registry.async_get(entity_id) - assert entry - assert entry.entity_category == EntityCategory.DIAGNOSTIC + entity_id = sensor_entity_id state = hass.states.get(entity_id) assert state assert state.state == "0.0" - client.async_send_command.reset_mock() - - entity_id = "switch.this_is_a_fake_device_switch" - entry = entity_registry.async_get(entity_id) - assert entry - assert entry.entity_category == EntityCategory.CONFIG + entity_id = switch_entity_id state = hass.states.get(entity_id) assert state assert state.state == STATE_OFF @@ -342,7 +355,7 @@ async def test_indicator_test( {ATTR_ENTITY_ID: entity_id}, blocking=True, ) - assert len(client.async_send_command.call_args_list) == 1 + assert client.async_send_command.call_count == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "node.set_value" assert args["nodeId"] == indicator_test.node_id @@ -362,7 +375,7 @@ async def test_indicator_test( {ATTR_ENTITY_ID: entity_id}, blocking=True, ) - assert len(client.async_send_command.call_args_list) == 1 + assert client.async_send_command.call_count == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "node.set_value" assert args["nodeId"] == indicator_test.node_id diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index ef74373ad9e..fa82b051e59 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -1812,7 +1812,8 @@ async def test_disabled_node_status_entity_on_node_replaced( assert state.state == STATE_UNAVAILABLE -async def test_disabled_entity_on_value_removed( +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_remove_entity_on_value_removed( hass: HomeAssistant, zp3111: Node, client: MagicMock, @@ -1823,15 +1824,6 @@ async def test_disabled_entity_on_value_removed( "button.4_in_1_sensor_idle_home_security_cover_status" ) - # must reload the integration when enabling an entity - await hass.config_entries.async_unload(integration.entry_id) - await hass.async_block_till_done() - assert integration.state is ConfigEntryState.NOT_LOADED - integration.add_to_hass(hass) - await hass.config_entries.async_setup(integration.entry_id) - await hass.async_block_till_done() - assert integration.state is ConfigEntryState.LOADED - state = hass.states.get(idle_cover_status_button_entity) assert state assert state.state != STATE_UNAVAILABLE diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py index c93b722334b..c3580df1f27 100644 --- a/tests/components/zwave_js/test_sensor.py +++ b/tests/components/zwave_js/test_sensor.py @@ -1,6 +1,7 @@ """Test the Z-Wave JS sensor platform.""" import copy +from datetime import timedelta import pytest from zwave_js_server.const.command_class.meter import MeterType @@ -26,6 +27,7 @@ from homeassistant.components.zwave_js.sensor import ( CONTROLLER_STATISTICS_KEY_MAP, NODE_STATISTICS_KEY_MAP, ) +from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, @@ -35,6 +37,7 @@ from homeassistant.const import ( STATE_UNKNOWN, UV_INDEX, EntityCategory, + Platform, UnitOfElectricCurrent, UnitOfElectricPotential, UnitOfEnergy, @@ -45,6 +48,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er +from homeassistant.util import dt as dt_util from .common import ( AIR_TEMPERATURE_SENSOR, @@ -57,7 +61,94 @@ from .common import ( VOLTAGE_SENSOR, ) -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed + + +@pytest.fixture +def platforms() -> list[str]: + """Fixture to specify platforms to test.""" + return [Platform.SENSOR] + + +async def test_battery_sensors( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + ring_keypad: Node, + integration: MockConfigEntry, +) -> None: + """Test numeric battery sensors.""" + entity_id = "sensor.keypad_v2_battery_level" + state = hass.states.get(entity_id) + assert state + assert state.state == "100.0" + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.BATTERY + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT + + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category is EntityCategory.DIAGNOSTIC + + disabled_sensor_battery_entities = ( + "sensor.keypad_v2_chargingstatus", + "sensor.keypad_v2_maximum_capacity", + "sensor.keypad_v2_rechargeorreplace", + "sensor.keypad_v2_temperature", + ) + + for entity_id in disabled_sensor_battery_entities: + state = hass.states.get(entity_id) + assert state is None # disabled by default + + entity_entry = entity_registry.async_get(entity_id) + + assert entity_entry + assert entity_entry.entity_category is EntityCategory.DIAGNOSTIC + assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + + entity_registry.async_update_entity(entity_id, disabled_by=None) + + async_fire_time_changed( + hass, + dt_util.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1), + ) + await hass.async_block_till_done() + + entity_id = "sensor.keypad_v2_chargingstatus" + state = hass.states.get(entity_id) + assert state + assert state.state == "Maintaining" + assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert ATTR_STATE_CLASS not in state.attributes + + entity_id = "sensor.keypad_v2_maximum_capacity" + state = hass.states.get(entity_id) + assert state + assert ( + state.state == "0" + ) # This should be None/unknown but will be fixed in a future PR. + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == PERCENTAGE + assert ATTR_DEVICE_CLASS not in state.attributes + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT + + entity_id = "sensor.keypad_v2_rechargeorreplace" + state = hass.states.get(entity_id) + assert state + assert state.state == "No" + assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM + assert ATTR_STATE_CLASS not in state.attributes + + entity_id = "sensor.keypad_v2_temperature" + state = hass.states.get(entity_id) + assert state + assert ( + state.state == "0" + ) # This should be None/unknown but will be fixed in a future PR. + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.CELSIUS + assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE + assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT async def test_numeric_sensor( diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 38e7e1ae452..6b464faa110 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -16,6 +16,7 @@ from homeassistant import exceptions from homeassistant.auth.permissions import PolicyPermissions import homeassistant.components # noqa: F401 from homeassistant.components.group import DOMAIN as DOMAIN_GROUP, Group +from homeassistant.components.input_button import DOMAIN as DOMAIN_INPUT_BUTTON from homeassistant.components.logger import DOMAIN as DOMAIN_LOGGER from homeassistant.components.shell_command import DOMAIN as DOMAIN_SHELL_COMMAND from homeassistant.components.system_health import DOMAIN as DOMAIN_SYSTEM_HEALTH @@ -42,7 +43,12 @@ from homeassistant.helpers import ( entity_registry as er, service, ) -from homeassistant.loader import async_get_integration +from homeassistant.helpers.translation import async_get_translations +from homeassistant.loader import ( + Integration, + async_get_integration, + async_get_integrations, +) from homeassistant.setup import async_setup_component from homeassistant.util.yaml.loader import parse_yaml @@ -1092,38 +1098,66 @@ async def test_async_get_all_descriptions_failing_integration( """Test async_get_all_descriptions when async_get_integrations returns an exception.""" group_config = {DOMAIN_GROUP: {}} await async_setup_component(hass, DOMAIN_GROUP, group_config) - descriptions = await service.async_get_all_descriptions(hass) - - assert len(descriptions) == 1 - - assert "description" in descriptions["group"]["reload"] - assert "fields" in descriptions["group"]["reload"] logger_config = {DOMAIN_LOGGER: {}} await async_setup_component(hass, DOMAIN_LOGGER, logger_config) + + input_button_config = {DOMAIN_INPUT_BUTTON: {}} + await async_setup_component(hass, DOMAIN_INPUT_BUTTON, input_button_config) + + async def wrap_get_integrations( + hass: HomeAssistant, domains: Iterable[str] + ) -> dict[str, Integration | Exception]: + integrations = await async_get_integrations(hass, domains) + integrations[DOMAIN_LOGGER] = ImportError("Failed to load services.yaml") + return integrations + + async def wrap_get_translations( + hass: HomeAssistant, + language: str, + category: str, + integrations: Iterable[str] | None = None, + config_flow: bool | None = None, + ) -> dict[str, str]: + translations = await async_get_translations( + hass, language, category, integrations, config_flow + ) + return { + key: value + for key, value in translations.items() + if not key.startswith("component.logger.services.") + } + with ( patch( "homeassistant.helpers.service.async_get_integrations", - return_value={"logger": ImportError}, + wraps=wrap_get_integrations, ), patch( "homeassistant.helpers.service.translation.async_get_translations", - return_value={}, + wrap_get_translations, ), ): descriptions = await service.async_get_all_descriptions(hass) - assert len(descriptions) == 2 + assert len(descriptions) == 3 assert "Failed to load integration: logger" in caplog.text # Services are empty defaults if the load fails but should # not raise + assert descriptions[DOMAIN_GROUP]["remove"]["description"] + assert descriptions[DOMAIN_GROUP]["remove"]["fields"] + assert descriptions[DOMAIN_LOGGER]["set_level"] == { "description": "", "fields": {}, "name": "", } + assert descriptions[DOMAIN_INPUT_BUTTON]["press"]["description"] + assert descriptions[DOMAIN_INPUT_BUTTON]["press"]["fields"] == {} + assert "target" in descriptions[DOMAIN_INPUT_BUTTON]["press"] + hass.services.async_register(DOMAIN_LOGGER, "new_service", lambda x: None, None) service.async_set_service_schema( hass, DOMAIN_LOGGER, "new_service", {"description": "new service"}