Add zwave config parameter entities (#92223)
* Add zwave config parameter entities * Remove unused entity const * remove unusued imports * review comments * switch to reserved values * fix test
This commit is contained in:
parent
55c2bb59c8
commit
65187c6f11
15 changed files with 431 additions and 106 deletions
|
@ -315,6 +315,10 @@ async def async_setup_entry(
|
|||
config_entry, driver, info, property_description
|
||||
)
|
||||
)
|
||||
elif info.platform_hint == "config_parameter":
|
||||
entities.append(
|
||||
ZWaveConfigParameterBinarySensor(config_entry, driver, info)
|
||||
)
|
||||
else:
|
||||
# boolean sensor
|
||||
entities.append(ZWaveBooleanBinarySensor(config_entry, driver, info))
|
||||
|
@ -411,3 +415,22 @@ class ZWavePropertyBinarySensor(ZWaveBaseEntity, BinarySensorEntity):
|
|||
if self.info.primary_value.value is None:
|
||||
return None
|
||||
return self.info.primary_value.value in self.entity_description.on_states
|
||||
|
||||
|
||||
class ZWaveConfigParameterBinarySensor(ZWaveBooleanBinarySensor):
|
||||
"""Representation of a Z-Wave config parameter binary sensor."""
|
||||
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
||||
) -> None:
|
||||
"""Initialize a ZWaveConfigParameterBinarySensor entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
|
||||
property_key_name = self.info.primary_value.property_key_name
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
)
|
||||
|
|
|
@ -36,6 +36,9 @@ EVENT_DEVICE_ADDED_TO_REGISTRY = f"{DOMAIN}_device_added_to_registry"
|
|||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
# constants extra state attributes
|
||||
ATTR_RESERVED_VALUES = "reserved_values" # ConfigurationValue number entities
|
||||
|
||||
# constants for events
|
||||
ZWAVE_JS_VALUE_NOTIFICATION_EVENT = f"{DOMAIN}_value_notification"
|
||||
ZWAVE_JS_NOTIFICATION_EVENT = f"{DOMAIN}_notification"
|
||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
|
||||
from collections.abc import Generator
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from awesomeversion import AwesomeVersion
|
||||
from zwave_js_server.const import (
|
||||
|
@ -41,7 +41,11 @@ from zwave_js_server.const.command_class.thermostat import (
|
|||
from zwave_js_server.exceptions import UnknownValueData
|
||||
from zwave_js_server.model.device_class import DeviceClassItem
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.model.value import Value as ZwaveValue
|
||||
from zwave_js_server.model.value import (
|
||||
ConfigurationValue,
|
||||
ConfigurationValueType,
|
||||
Value as ZwaveValue,
|
||||
)
|
||||
|
||||
from homeassistant.backports.enum import StrEnum
|
||||
from homeassistant.const import EntityCategory, Platform
|
||||
|
@ -205,32 +209,6 @@ class ZWaveDiscoverySchema:
|
|||
entity_category: EntityCategory | None = None
|
||||
|
||||
|
||||
def get_config_parameter_discovery_schema(
|
||||
property_: set[str | int] | None = None,
|
||||
property_name: set[str] | None = None,
|
||||
property_key: set[str | int | None] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> ZWaveDiscoverySchema:
|
||||
"""Return a discovery schema for a config parameter.
|
||||
|
||||
Supports all keyword arguments to ZWaveValueDiscoverySchema except platform, hint,
|
||||
and primary_value.
|
||||
"""
|
||||
return ZWaveDiscoverySchema(
|
||||
platform=Platform.SENSOR,
|
||||
hint="config_parameter",
|
||||
primary_value=ZWaveValueDiscoverySchema(
|
||||
command_class={CommandClass.CONFIGURATION},
|
||||
property=property_,
|
||||
property_name=property_name,
|
||||
property_key=property_key,
|
||||
type={ValueType.NUMBER},
|
||||
),
|
||||
entity_registry_enabled_default=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
DOOR_LOCK_CURRENT_MODE_SCHEMA = ZWaveValueDiscoverySchema(
|
||||
command_class={CommandClass.DOOR_LOCK},
|
||||
property={CURRENT_MODE_PROPERTY},
|
||||
|
@ -596,13 +574,6 @@ DISCOVERY_SCHEMAS = [
|
|||
),
|
||||
absent_values=[SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA],
|
||||
),
|
||||
# ====== START OF CONFIG PARAMETER SPECIFIC MAPPING SCHEMAS =======
|
||||
# Door lock mode config parameter. Functionality equivalent to Notification CC
|
||||
# list sensors.
|
||||
get_config_parameter_discovery_schema(
|
||||
property_name={"Door lock mode"},
|
||||
device_class_generic={"Entry Control"},
|
||||
),
|
||||
# ====== START OF GENERIC MAPPING SCHEMAS =======
|
||||
# locks
|
||||
# Door Lock CC
|
||||
|
@ -1112,6 +1083,85 @@ def async_discover_single_value(
|
|||
# by other schemas/platforms
|
||||
return
|
||||
|
||||
if value.command_class == CommandClass.CONFIGURATION:
|
||||
yield from async_discover_single_configuration_value(
|
||||
cast(ConfigurationValue, value)
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_discover_single_configuration_value(
|
||||
value: ConfigurationValue,
|
||||
) -> Generator[ZwaveDiscoveryInfo, None, None]:
|
||||
"""Run discovery on a single ZWave configuration value and return matching schema info."""
|
||||
if value.metadata.writeable and value.metadata.readable:
|
||||
if value.configuration_value_type == ConfigurationValueType.ENUMERATED:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.SELECT,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
elif value.configuration_value_type in (
|
||||
ConfigurationValueType.RANGE,
|
||||
ConfigurationValueType.MANUAL_ENTRY,
|
||||
):
|
||||
if value.metadata.type == ValueType.BOOLEAN or (
|
||||
value.metadata.min == 0 and value.metadata.max == 1
|
||||
):
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.SWITCH,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
else:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.NUMBER,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
elif not value.metadata.writeable and value.metadata.readable:
|
||||
if value.metadata.type == ValueType.BOOLEAN or (
|
||||
value.metadata.min == 0
|
||||
and value.metadata.max == 1
|
||||
and not value.metadata.states
|
||||
):
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.BINARY_SENSOR,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
else:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.SENSOR,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def check_value(value: ZwaveValue, schema: ZWaveValueDiscoverySchema) -> bool:
|
||||
|
|
|
@ -2,13 +2,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from zwave_js_server.const import NodeStatus
|
||||
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import Value as ZwaveValue, get_value_id_str
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
|
@ -292,3 +295,19 @@ class ZWaveBaseEntity(Entity):
|
|||
):
|
||||
self.watched_value_ids.add(return_value.value_id)
|
||||
return return_value
|
||||
|
||||
async def _async_set_value(
|
||||
self,
|
||||
value: ZwaveValue,
|
||||
new_value: Any,
|
||||
options: dict | None = None,
|
||||
wait_for_result: bool | None = None,
|
||||
) -> bool | None:
|
||||
"""Set value on node."""
|
||||
try:
|
||||
return await self.info.node.async_set_value(
|
||||
value, new_value, options=options, wait_for_result=wait_for_result
|
||||
)
|
||||
except BaseZwaveJSServerError as err:
|
||||
LOGGER.error("Unable to set value %s: %s", value.value_id, err)
|
||||
raise HomeAssistantError from err
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
"""Support for Z-Wave controls using the number platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, cast
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import TARGET_VALUE_PROPERTY
|
||||
|
@ -10,12 +11,13 @@ from zwave_js_server.model.value import Value
|
|||
|
||||
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, NumberEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DATA_CLIENT, DOMAIN
|
||||
from .const import ATTR_RESERVED_VALUES, DATA_CLIENT, DOMAIN
|
||||
from .discovery import ZwaveDiscoveryInfo
|
||||
from .entity import ZWaveBaseEntity
|
||||
|
||||
|
@ -38,6 +40,10 @@ async def async_setup_entry(
|
|||
entities: list[ZWaveBaseEntity] = []
|
||||
if info.platform_hint == "volume":
|
||||
entities.append(ZwaveVolumeNumberEntity(config_entry, driver, info))
|
||||
elif info.platform_hint == "config_parameter":
|
||||
entities.append(
|
||||
ZWaveConfigParameterNumberEntity(config_entry, driver, info)
|
||||
)
|
||||
else:
|
||||
entities.append(ZwaveNumberEntity(config_entry, driver, info))
|
||||
async_add_entities(entities)
|
||||
|
@ -98,7 +104,37 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity):
|
|||
"""Set new value."""
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(target_value, value)
|
||||
await self._async_set_value(target_value, value)
|
||||
|
||||
|
||||
class ZWaveConfigParameterNumberEntity(ZwaveNumberEntity):
|
||||
"""Representation of a Z-Wave config parameter number."""
|
||||
|
||||
_attr_entity_category = EntityCategory.CONFIG
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
||||
) -> None:
|
||||
"""Initialize a ZWaveConfigParameterNumber entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
|
||||
property_key_name = self.info.primary_value.property_key_name
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return extra state attributes for entity."""
|
||||
if not self.info.primary_value.metadata.states:
|
||||
return None
|
||||
return {
|
||||
ATTR_RESERVED_VALUES: {
|
||||
int(k): v for k, v in self.info.primary_value.metadata.states.items()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity):
|
||||
|
|
|
@ -42,6 +42,10 @@ async def async_setup_entry(
|
|||
entities.append(
|
||||
ZwaveMultilevelSwitchSelectEntity(config_entry, driver, info)
|
||||
)
|
||||
elif info.platform_hint == "config_parameter":
|
||||
entities.append(
|
||||
ZWaveConfigParameterSelectEntity(config_entry, driver, info)
|
||||
)
|
||||
else:
|
||||
entities.append(ZwaveSelectEntity(config_entry, driver, info))
|
||||
async_add_entities(entities)
|
||||
|
@ -91,6 +95,25 @@ class ZwaveSelectEntity(ZWaveBaseEntity, SelectEntity):
|
|||
await self.info.node.async_set_value(self.info.primary_value, int(key))
|
||||
|
||||
|
||||
class ZWaveConfigParameterSelectEntity(ZwaveSelectEntity):
|
||||
"""Representation of a Z-Wave config parameter select."""
|
||||
|
||||
_attr_entity_category = EntityCategory.CONFIG
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
||||
) -> None:
|
||||
"""Initialize a ZWaveConfigParameterSelect entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
|
||||
property_key_name = self.info.primary_value.property_key_name
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
)
|
||||
|
||||
|
||||
class ZwaveDefaultToneSelectEntity(ZWaveBaseEntity, SelectEntity):
|
||||
"""Representation of a Z-Wave default tone select entity."""
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ from typing import cast
|
|||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import CommandClass, ConfigurationValueType, NodeStatus
|
||||
from zwave_js_server.const import CommandClass, NodeStatus
|
||||
from zwave_js_server.const.command_class.meter import (
|
||||
RESET_METER_OPTION_TARGET_VALUE,
|
||||
RESET_METER_OPTION_TYPE,
|
||||
)
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.model.value import ConfigurationValue
|
||||
from zwave_js_server.model.value import ConfigurationValue, ConfigurationValueType
|
||||
from zwave_js_server.util.command_class.meter import get_meter_type
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
|
@ -490,6 +490,13 @@ class ZWaveListSensor(ZwaveSensor):
|
|||
additional_info=[self.info.primary_value.property_key_name],
|
||||
)
|
||||
|
||||
@property
|
||||
def options(self) -> list[str] | None:
|
||||
"""Return options for enum sensor."""
|
||||
if self.device_class == SensorDeviceClass.ENUM:
|
||||
return list(self.info.primary_value.metadata.states.values())
|
||||
return None
|
||||
|
||||
@property
|
||||
def device_class(self) -> SensorDeviceClass | None:
|
||||
"""Return sensor device class."""
|
||||
|
@ -499,13 +506,6 @@ class ZWaveListSensor(ZwaveSensor):
|
|||
return SensorDeviceClass.ENUM
|
||||
return None
|
||||
|
||||
@property
|
||||
def options(self) -> list[str] | None:
|
||||
"""Return options for enum sensor."""
|
||||
if self.device_class == SensorDeviceClass.ENUM:
|
||||
return list(self.info.primary_value.metadata.states.values())
|
||||
return None
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""Return the device specific state attributes."""
|
||||
|
@ -518,6 +518,8 @@ class ZWaveListSensor(ZwaveSensor):
|
|||
class ZWaveConfigParameterSensor(ZWaveListSensor):
|
||||
"""Representation of a Z-Wave config parameter sensor."""
|
||||
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config_entry: ConfigEntry,
|
||||
|
@ -537,7 +539,6 @@ class ZWaveConfigParameterSensor(ZWaveListSensor):
|
|||
self._attr_name = self.generate_name(
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
name_prefix="Config parameter",
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -555,10 +556,7 @@ class ZWaveConfigParameterSensor(ZWaveListSensor):
|
|||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""Return the device specific state attributes."""
|
||||
if (
|
||||
self._primary_value.configuration_value_type == ConfigurationValueType.RANGE
|
||||
or (value := self.info.primary_value.value) is None
|
||||
):
|
||||
if (value := self.info.primary_value.value) is None:
|
||||
return None
|
||||
# add the value's int value as property for multi-value (list) items
|
||||
return {ATTR_VALUE: value}
|
||||
|
|
|
@ -12,6 +12,7 @@ from zwave_js_server.model.driver import Driver
|
|||
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
@ -41,6 +42,8 @@ async def async_setup_entry(
|
|||
entities.append(
|
||||
ZWaveBarrierEventSignalingSwitch(config_entry, driver, info)
|
||||
)
|
||||
elif info.platform_hint == "config_parameter":
|
||||
entities.append(ZWaveConfigParameterSwitch(config_entry, driver, info))
|
||||
elif info.platform_hint == "indicator":
|
||||
entities.append(ZWaveIndicatorSwitch(config_entry, driver, info))
|
||||
else:
|
||||
|
@ -79,12 +82,12 @@ class ZWaveSwitch(ZWaveBaseEntity, SwitchEntity):
|
|||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
if self._target_value is not None:
|
||||
await self.info.node.async_set_value(self._target_value, True)
|
||||
await self._async_set_value(self._target_value, True)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch off."""
|
||||
if self._target_value is not None:
|
||||
await self.info.node.async_set_value(self._target_value, False)
|
||||
await self._async_set_value(self._target_value, False)
|
||||
|
||||
|
||||
class ZWaveIndicatorSwitch(ZWaveSwitch):
|
||||
|
@ -129,7 +132,7 @@ class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity):
|
|||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
await self.info.node.async_set_value(
|
||||
await self._async_set_value(
|
||||
self.info.primary_value, BarrierEventSignalingSubsystemState.ON
|
||||
)
|
||||
# this value is not refreshed, so assume success
|
||||
|
@ -138,7 +141,7 @@ class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity):
|
|||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch off."""
|
||||
await self.info.node.async_set_value(
|
||||
await self._async_set_value(
|
||||
self.info.primary_value, BarrierEventSignalingSubsystemState.OFF
|
||||
)
|
||||
# this value is not refreshed, so assume success
|
||||
|
@ -152,3 +155,30 @@ class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity):
|
|||
self._state = (
|
||||
self.info.primary_value.value == BarrierEventSignalingSubsystemState.ON
|
||||
)
|
||||
|
||||
|
||||
class ZWaveConfigParameterSwitch(ZWaveSwitch):
|
||||
"""Representation of a Z-Wave config parameter switch."""
|
||||
|
||||
_attr_entity_category = EntityCategory.CONFIG
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
||||
) -> None:
|
||||
"""Initialize a ZWaveConfigParameterSwitch entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
|
||||
property_key_name = self.info.primary_value.property_key_name
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch on."""
|
||||
await self._async_set_value(self.info.primary_value, 1)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the switch off."""
|
||||
await self._async_set_value(self.info.primary_value, 0)
|
||||
|
|
|
@ -39,9 +39,6 @@ BULB_6_MULTI_COLOR_LIGHT_ENTITY = "light.bulb_6_multi_color"
|
|||
EATON_RF9640_ENTITY = "light.allloaddimmer"
|
||||
AEON_SMART_SWITCH_LIGHT_ENTITY = "light.smart_switch_6"
|
||||
SCHLAGE_BE469_LOCK_ENTITY = "lock.touchscreen_deadbolt"
|
||||
ID_LOCK_CONFIG_PARAMETER_SENSOR = (
|
||||
"sensor.z_wave_module_for_id_lock_150_and_101_config_parameter_door_lock_mode"
|
||||
)
|
||||
ZEN_31_ENTITY = "light.kitchen_under_cabinet_lights"
|
||||
METER_ENERGY_SENSOR = "sensor.smart_switch_6_electric_consumed_kwh"
|
||||
METER_VOLTAGE_SENSOR = "sensor.smart_switch_6_electric_consumed_v"
|
||||
|
|
|
@ -259,3 +259,29 @@ async def test_property_sensor_door_status(
|
|||
state = hass.states.get(PROPERTY_DOOR_STATUS_BINARY_SENSOR)
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_config_parameter_binary_sensor(
|
||||
hass: HomeAssistant, climate_adc_t3000, integration
|
||||
) -> None:
|
||||
"""Test config parameter binary sensor is created."""
|
||||
binary_sensor_entity_id = "binary_sensor.adc_t3000_system_configuration_override"
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_entry = ent_reg.async_get(binary_sensor_entity_id)
|
||||
assert entity_entry
|
||||
assert entity_entry.disabled
|
||||
assert entity_entry.entity_category == EntityCategory.DIAGNOSTIC
|
||||
|
||||
updated_entry = ent_reg.async_update_entity(
|
||||
binary_sensor_entity_id, **{"disabled_by": None}
|
||||
)
|
||||
assert updated_entry != entity_entry
|
||||
assert updated_entry.disabled is False
|
||||
|
||||
# reload integration and check if entity is correctly there
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(binary_sensor_entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
|
|
|
@ -963,7 +963,7 @@ async def test_removed_device(
|
|||
# Check how many entities there are
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id)
|
||||
assert len(entity_entries) == 36
|
||||
assert len(entity_entries) == 62
|
||||
|
||||
# Remove a node and reload the entry
|
||||
old_node = driver.controller.nodes.pop(13)
|
||||
|
@ -975,7 +975,7 @@ async def test_removed_device(
|
|||
device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id)
|
||||
assert len(device_entries) == 2
|
||||
entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id)
|
||||
assert len(entity_entries) == 23
|
||||
assert len(entity_entries) == 38
|
||||
assert dev_reg.async_get_device({get_device_id(driver, old_node)}) is None
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
from zwave_js_server.event import Event
|
||||
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.const import STATE_UNKNOWN, EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
@ -229,3 +229,37 @@ async def test_disabled_basic_number(
|
|||
assert entity_entry
|
||||
assert entity_entry.disabled
|
||||
assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
|
||||
|
||||
|
||||
async def test_config_parameter_number(
|
||||
hass: HomeAssistant, climate_adc_t3000, integration
|
||||
) -> None:
|
||||
"""Test config parameter number is created."""
|
||||
number_entity_id = "number.adc_t3000_heat_staging_delay"
|
||||
number_with_states_entity_id = "number.adc_t3000_calibration_temperature"
|
||||
ent_reg = er.async_get(hass)
|
||||
for entity_id in (number_entity_id, number_with_states_entity_id):
|
||||
entity_entry = ent_reg.async_get(entity_id)
|
||||
assert entity_entry
|
||||
assert entity_entry.disabled
|
||||
assert entity_entry.entity_category == EntityCategory.CONFIG
|
||||
|
||||
for entity_id in (number_entity_id, number_with_states_entity_id):
|
||||
updated_entry = ent_reg.async_update_entity(entity_id, **{"disabled_by": None})
|
||||
assert updated_entry != entity_entry
|
||||
assert updated_entry.disabled is False
|
||||
|
||||
# reload integration and check if entity is correctly there
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(number_entity_id)
|
||||
assert state
|
||||
assert state.state == "30.0"
|
||||
assert "reserved_values" not in state.attributes
|
||||
|
||||
state = hass.states.get(number_with_states_entity_id)
|
||||
assert state
|
||||
assert state.state == "0.0"
|
||||
assert "reserved_values" in state.attributes
|
||||
assert state.attributes["reserved_values"] == {-1: "Disabled"}
|
||||
|
|
|
@ -294,3 +294,29 @@ async def test_multilevel_switch_select_no_value(
|
|||
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_config_parameter_select(
|
||||
hass: HomeAssistant, climate_adc_t3000, integration
|
||||
) -> None:
|
||||
"""Test config parameter select is created."""
|
||||
select_entity_id = "select.adc_t3000_hvac_system_type"
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_entry = ent_reg.async_get(select_entity_id)
|
||||
assert entity_entry
|
||||
assert entity_entry.disabled
|
||||
assert entity_entry.entity_category == EntityCategory.CONFIG
|
||||
|
||||
updated_entry = ent_reg.async_update_entity(
|
||||
select_entity_id, **{"disabled_by": None}
|
||||
)
|
||||
assert updated_entry != entity_entry
|
||||
assert updated_entry.disabled is False
|
||||
|
||||
# reload integration and check if entity is correctly there
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(select_entity_id)
|
||||
assert state
|
||||
assert state.state == "Normal"
|
||||
|
|
|
@ -45,7 +45,6 @@ from .common import (
|
|||
CURRENT_SENSOR,
|
||||
ENERGY_SENSOR,
|
||||
HUMIDITY_SENSOR,
|
||||
ID_LOCK_CONFIG_PARAMETER_SENSOR,
|
||||
METER_ENERGY_SENSOR,
|
||||
NOTIFICATION_MOTION_SENSOR,
|
||||
POWER_SENSOR,
|
||||
|
@ -188,7 +187,9 @@ async def test_disabled_notification_sensor(
|
|||
|
||||
state = hass.states.get(NOTIFICATION_MOTION_SENSOR)
|
||||
assert state.state == "Motion detection"
|
||||
assert state.attributes["value"] == 8
|
||||
assert state.attributes[ATTR_VALUE] == 8
|
||||
assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM
|
||||
assert state.attributes[ATTR_OPTIONS] == ["idle", "Motion detection"]
|
||||
|
||||
event = Event(
|
||||
"value updated",
|
||||
|
@ -218,13 +219,34 @@ async def test_disabled_notification_sensor(
|
|||
|
||||
|
||||
async def test_config_parameter_sensor(
|
||||
hass: HomeAssistant, lock_id_lock_as_id150, integration
|
||||
hass: HomeAssistant, climate_adc_t3000, lock_id_lock_as_id150, integration
|
||||
) -> None:
|
||||
"""Test config parameter sensor is created."""
|
||||
sensor_entity_id = "sensor.adc_t3000_system_configuration_cool_stages"
|
||||
sensor_with_states_entity_id = "sensor.adc_t3000_power_source"
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_entry = ent_reg.async_get(ID_LOCK_CONFIG_PARAMETER_SENSOR)
|
||||
for entity_id in (sensor_entity_id, sensor_with_states_entity_id):
|
||||
entity_entry = ent_reg.async_get(entity_id)
|
||||
assert entity_entry
|
||||
assert entity_entry.disabled
|
||||
assert entity_entry.entity_category == EntityCategory.DIAGNOSTIC
|
||||
|
||||
for entity_id in (sensor_entity_id, sensor_with_states_entity_id):
|
||||
updated_entry = ent_reg.async_update_entity(entity_id, **{"disabled_by": None})
|
||||
assert updated_entry != entity_entry
|
||||
assert updated_entry.disabled is False
|
||||
|
||||
# reload integration and check if entity is correctly there
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(sensor_entity_id)
|
||||
assert state
|
||||
assert state.state == "1"
|
||||
|
||||
state = hass.states.get(sensor_with_states_entity_id)
|
||||
assert state
|
||||
assert state.state == "C-Wire"
|
||||
|
||||
updated_entry = ent_reg.async_update_entity(
|
||||
entity_entry.entity_id, **{"disabled_by": None}
|
||||
|
@ -236,43 +258,6 @@ async def test_config_parameter_sensor(
|
|||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ID_LOCK_CONFIG_PARAMETER_SENSOR)
|
||||
assert state
|
||||
assert state.state == "Disable Away Manual Lock"
|
||||
assert state.attributes[ATTR_VALUE] == 0
|
||||
assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.ENUM
|
||||
assert state.attributes[ATTR_OPTIONS] == [
|
||||
"Disable Away Manual Lock",
|
||||
"Disable Away Auto Lock",
|
||||
"Enable Away Manual Lock",
|
||||
"Enable Away Auto Lock",
|
||||
]
|
||||
|
||||
event = Event(
|
||||
"value updated",
|
||||
{
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": lock_id_lock_as_id150.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 1,
|
||||
"newValue": None,
|
||||
"prevValue": 0,
|
||||
"propertyName": "Door lock mode",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
lock_id_lock_as_id150.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(ID_LOCK_CONFIG_PARAMETER_SENSOR)
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert ATTR_VALUE not in state.attributes
|
||||
|
||||
|
||||
async def test_node_status_sensor(
|
||||
hass: HomeAssistant, client, lock_id_lock_as_id150, integration
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
"""Test the Z-Wave JS switch platform."""
|
||||
import pytest
|
||||
from zwave_js_server.const import CURRENT_VALUE_PROPERTY, CommandClass
|
||||
from zwave_js_server.event import Event
|
||||
from zwave_js_server.exceptions import FailedZWaveCommand
|
||||
from zwave_js_server.model.node import Node
|
||||
|
||||
from homeassistant.components.switch import DOMAIN, SERVICE_TURN_OFF, SERVICE_TURN_ON
|
||||
from homeassistant.components.zwave_js.helpers import ZwaveValueMatcher
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN, EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .common import SWITCH_ENTITY, replace_value_of_zwave_value
|
||||
|
||||
|
@ -209,3 +213,74 @@ async def test_switch_no_value(
|
|||
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_config_parameter_switch(
|
||||
hass: HomeAssistant, hank_binary_switch, integration, client
|
||||
) -> None:
|
||||
"""Test config parameter switch is created."""
|
||||
switch_entity_id = "switch.smart_plug_with_two_usb_ports_overload_protection"
|
||||
ent_reg = er.async_get(hass)
|
||||
entity_entry = ent_reg.async_get(switch_entity_id)
|
||||
assert entity_entry
|
||||
assert entity_entry.disabled
|
||||
|
||||
updated_entry = ent_reg.async_update_entity(
|
||||
switch_entity_id, **{"disabled_by": None}
|
||||
)
|
||||
assert updated_entry != entity_entry
|
||||
assert updated_entry.disabled is False
|
||||
assert entity_entry.entity_category == EntityCategory.CONFIG
|
||||
|
||||
# reload integration and check if entity is correctly there
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(switch_entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_ON
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test turning on
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_TURN_ON, {"entity_id": switch_entity_id}, blocking=True
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
args = client.async_send_command.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == hank_binary_switch.node_id
|
||||
assert args["value"] == 1
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 20,
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test turning off
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_TURN_OFF, {"entity_id": switch_entity_id}, blocking=True
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
args = client.async_send_command.call_args[0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == hank_binary_switch.node_id
|
||||
assert args["value"] == 0
|
||||
assert args["valueId"] == {
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 20,
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.side_effect = FailedZWaveCommand("test", 1, "test")
|
||||
|
||||
# Test turning off error raises proper exception
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN, SERVICE_TURN_OFF, {"entity_id": switch_entity_id}, blocking=True
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue