ZHA siren and warning device support (#26046)
* add ias warning device support * use channel only clusters for warning devices * squawk service * add warning device warning service * update services.yaml * remove debugging statement * update required attr access * fix constant * add error logging to IASWD servicespull/26775/head
parent
aaf0f9890d
commit
62adff23f9
|
@ -19,9 +19,16 @@ from .core.const import (
|
|||
ATTR_COMMAND,
|
||||
ATTR_COMMAND_TYPE,
|
||||
ATTR_ENDPOINT_ID,
|
||||
ATTR_LEVEL,
|
||||
ATTR_MANUFACTURER,
|
||||
ATTR_NAME,
|
||||
ATTR_VALUE,
|
||||
ATTR_WARNING_DEVICE_DURATION,
|
||||
ATTR_WARNING_DEVICE_MODE,
|
||||
ATTR_WARNING_DEVICE_STROBE,
|
||||
ATTR_WARNING_DEVICE_STROBE_DUTY_CYCLE,
|
||||
ATTR_WARNING_DEVICE_STROBE_INTENSITY,
|
||||
CHANNEL_IAS_WD,
|
||||
CLUSTER_COMMAND_SERVER,
|
||||
CLUSTER_COMMANDS_CLIENT,
|
||||
CLUSTER_COMMANDS_SERVER,
|
||||
|
@ -31,6 +38,11 @@ from .core.const import (
|
|||
DATA_ZHA_GATEWAY,
|
||||
DOMAIN,
|
||||
MFG_CLUSTER_ID_START,
|
||||
WARNING_DEVICE_MODE_EMERGENCY,
|
||||
WARNING_DEVICE_SOUND_HIGH,
|
||||
WARNING_DEVICE_SQUAWK_MODE_ARMED,
|
||||
WARNING_DEVICE_STROBE_HIGH,
|
||||
WARNING_DEVICE_STROBE_YES,
|
||||
)
|
||||
from .core.helpers import async_is_bindable_target, convert_ieee, get_matched_clusters
|
||||
|
||||
|
@ -56,6 +68,8 @@ SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE = "set_zigbee_cluster_attribute"
|
|||
SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND = "issue_zigbee_cluster_command"
|
||||
SERVICE_DIRECT_ZIGBEE_BIND = "issue_direct_zigbee_bind"
|
||||
SERVICE_DIRECT_ZIGBEE_UNBIND = "issue_direct_zigbee_unbind"
|
||||
SERVICE_WARNING_DEVICE_SQUAWK = "warning_device_squawk"
|
||||
SERVICE_WARNING_DEVICE_WARN = "warning_device_warn"
|
||||
SERVICE_ZIGBEE_BIND = "service_zigbee_bind"
|
||||
IEEE_SERVICE = "ieee_based_service"
|
||||
|
||||
|
@ -80,6 +94,41 @@ SERVICE_SCHEMAS = {
|
|||
vol.Optional(ATTR_MANUFACTURER): cv.positive_int,
|
||||
}
|
||||
),
|
||||
SERVICE_WARNING_DEVICE_SQUAWK: vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_IEEE): convert_ieee,
|
||||
vol.Optional(
|
||||
ATTR_WARNING_DEVICE_MODE, default=WARNING_DEVICE_SQUAWK_MODE_ARMED
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
ATTR_WARNING_DEVICE_STROBE, default=WARNING_DEVICE_STROBE_YES
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
ATTR_LEVEL, default=WARNING_DEVICE_SOUND_HIGH
|
||||
): cv.positive_int,
|
||||
}
|
||||
),
|
||||
SERVICE_WARNING_DEVICE_WARN: vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_IEEE): convert_ieee,
|
||||
vol.Optional(
|
||||
ATTR_WARNING_DEVICE_MODE, default=WARNING_DEVICE_MODE_EMERGENCY
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
ATTR_WARNING_DEVICE_STROBE, default=WARNING_DEVICE_STROBE_YES
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
ATTR_LEVEL, default=WARNING_DEVICE_SOUND_HIGH
|
||||
): cv.positive_int,
|
||||
vol.Optional(ATTR_WARNING_DEVICE_DURATION, default=5): cv.positive_int,
|
||||
vol.Optional(
|
||||
ATTR_WARNING_DEVICE_STROBE_DUTY_CYCLE, default=0x00
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
ATTR_WARNING_DEVICE_STROBE_INTENSITY, default=WARNING_DEVICE_STROBE_HIGH
|
||||
): cv.positive_int,
|
||||
}
|
||||
),
|
||||
SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND: vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_IEEE): convert_ieee,
|
||||
|
@ -610,6 +659,85 @@ def async_load_api(hass):
|
|||
schema=SERVICE_SCHEMAS[SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND],
|
||||
)
|
||||
|
||||
async def warning_device_squawk(service):
|
||||
"""Issue the squawk command for an IAS warning device."""
|
||||
ieee = service.data[ATTR_IEEE]
|
||||
mode = service.data.get(ATTR_WARNING_DEVICE_MODE)
|
||||
strobe = service.data.get(ATTR_WARNING_DEVICE_STROBE)
|
||||
level = service.data.get(ATTR_LEVEL)
|
||||
|
||||
zha_device = zha_gateway.get_device(ieee)
|
||||
if zha_device is not None:
|
||||
channel = zha_device.cluster_channels.get(CHANNEL_IAS_WD)
|
||||
if channel:
|
||||
await channel.squawk(mode, strobe, level)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Squawking IASWD: %s is missing the required IASWD channel!",
|
||||
"{}: [{}]".format(ATTR_IEEE, str(ieee)),
|
||||
)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Squawking IASWD: %s could not be found!",
|
||||
"{}: [{}]".format(ATTR_IEEE, str(ieee)),
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Squawking IASWD: %s %s %s %s",
|
||||
"{}: [{}]".format(ATTR_IEEE, str(ieee)),
|
||||
"{}: [{}]".format(ATTR_WARNING_DEVICE_MODE, mode),
|
||||
"{}: [{}]".format(ATTR_WARNING_DEVICE_STROBE, strobe),
|
||||
"{}: [{}]".format(ATTR_LEVEL, level),
|
||||
)
|
||||
|
||||
hass.helpers.service.async_register_admin_service(
|
||||
DOMAIN,
|
||||
SERVICE_WARNING_DEVICE_SQUAWK,
|
||||
warning_device_squawk,
|
||||
schema=SERVICE_SCHEMAS[SERVICE_WARNING_DEVICE_SQUAWK],
|
||||
)
|
||||
|
||||
async def warning_device_warn(service):
|
||||
"""Issue the warning command for an IAS warning device."""
|
||||
ieee = service.data[ATTR_IEEE]
|
||||
mode = service.data.get(ATTR_WARNING_DEVICE_MODE)
|
||||
strobe = service.data.get(ATTR_WARNING_DEVICE_STROBE)
|
||||
level = service.data.get(ATTR_LEVEL)
|
||||
duration = service.data.get(ATTR_WARNING_DEVICE_DURATION)
|
||||
duty_mode = service.data.get(ATTR_WARNING_DEVICE_STROBE_DUTY_CYCLE)
|
||||
intensity = service.data.get(ATTR_WARNING_DEVICE_STROBE_INTENSITY)
|
||||
|
||||
zha_device = zha_gateway.get_device(ieee)
|
||||
if zha_device is not None:
|
||||
channel = zha_device.cluster_channels.get(CHANNEL_IAS_WD)
|
||||
if channel:
|
||||
await channel.start_warning(
|
||||
mode, strobe, level, duration, duty_mode, intensity
|
||||
)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Warning IASWD: %s is missing the required IASWD channel!",
|
||||
"{}: [{}]".format(ATTR_IEEE, str(ieee)),
|
||||
)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Warning IASWD: %s could not be found!",
|
||||
"{}: [{}]".format(ATTR_IEEE, str(ieee)),
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Warning IASWD: %s %s %s %s",
|
||||
"{}: [{}]".format(ATTR_IEEE, str(ieee)),
|
||||
"{}: [{}]".format(ATTR_WARNING_DEVICE_MODE, mode),
|
||||
"{}: [{}]".format(ATTR_WARNING_DEVICE_STROBE, strobe),
|
||||
"{}: [{}]".format(ATTR_LEVEL, level),
|
||||
)
|
||||
|
||||
hass.helpers.service.async_register_admin_service(
|
||||
DOMAIN,
|
||||
SERVICE_WARNING_DEVICE_WARN,
|
||||
warning_device_warn,
|
||||
schema=SERVICE_SCHEMAS[SERVICE_WARNING_DEVICE_WARN],
|
||||
)
|
||||
|
||||
websocket_api.async_register_command(hass, websocket_permit_devices)
|
||||
websocket_api.async_register_command(hass, websocket_get_devices)
|
||||
websocket_api.async_register_command(hass, websocket_get_device)
|
||||
|
@ -629,3 +757,5 @@ def async_unload_api(hass):
|
|||
hass.services.async_remove(DOMAIN, SERVICE_REMOVE)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_SET_ZIGBEE_CLUSTER_ATTRIBUTE)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_ISSUE_ZIGBEE_CLUSTER_COMMAND)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_WARNING_DEVICE_SQUAWK)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_WARNING_DEVICE_WARN)
|
||||
|
|
|
@ -13,7 +13,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|||
|
||||
from . import ZigbeeChannel
|
||||
from .. import registries
|
||||
from ..const import SIGNAL_ATTR_UPDATED
|
||||
from ..const import (
|
||||
CLUSTER_COMMAND_SERVER,
|
||||
SIGNAL_ATTR_UPDATED,
|
||||
WARNING_DEVICE_MODE_EMERGENCY,
|
||||
WARNING_DEVICE_SOUND_HIGH,
|
||||
WARNING_DEVICE_SQUAWK_MODE_ARMED,
|
||||
WARNING_DEVICE_STROBE_HIGH,
|
||||
WARNING_DEVICE_STROBE_YES,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -25,11 +33,95 @@ class IasAce(ZigbeeChannel):
|
|||
pass
|
||||
|
||||
|
||||
@registries.CHANNEL_ONLY_CLUSTERS.register(security.IasWd.cluster_id)
|
||||
@registries.ZIGBEE_CHANNEL_REGISTRY.register(security.IasWd.cluster_id)
|
||||
class IasWd(ZigbeeChannel):
|
||||
"""IAS Warning Device channel."""
|
||||
|
||||
pass
|
||||
@staticmethod
|
||||
def set_bit(destination_value, destination_bit, source_value, source_bit):
|
||||
"""Set the specified bit in the value."""
|
||||
|
||||
if IasWd.get_bit(source_value, source_bit):
|
||||
return destination_value | (1 << destination_bit)
|
||||
return destination_value
|
||||
|
||||
@staticmethod
|
||||
def get_bit(value, bit):
|
||||
"""Get the specified bit from the value."""
|
||||
return (value & (1 << bit)) != 0
|
||||
|
||||
async def squawk(
|
||||
self,
|
||||
mode=WARNING_DEVICE_SQUAWK_MODE_ARMED,
|
||||
strobe=WARNING_DEVICE_STROBE_YES,
|
||||
squawk_level=WARNING_DEVICE_SOUND_HIGH,
|
||||
):
|
||||
"""Issue a squawk command.
|
||||
|
||||
This command uses the WD capabilities to emit a quick audible/visible pulse called a
|
||||
"squawk". The squawk command has no effect if the WD is currently active
|
||||
(warning in progress).
|
||||
"""
|
||||
value = 0
|
||||
value = IasWd.set_bit(value, 0, squawk_level, 0)
|
||||
value = IasWd.set_bit(value, 1, squawk_level, 1)
|
||||
|
||||
value = IasWd.set_bit(value, 3, strobe, 0)
|
||||
|
||||
value = IasWd.set_bit(value, 4, mode, 0)
|
||||
value = IasWd.set_bit(value, 5, mode, 1)
|
||||
value = IasWd.set_bit(value, 6, mode, 2)
|
||||
value = IasWd.set_bit(value, 7, mode, 3)
|
||||
|
||||
await self.device.issue_cluster_command(
|
||||
self.cluster.endpoint.endpoint_id,
|
||||
self.cluster.cluster_id,
|
||||
0x0001,
|
||||
CLUSTER_COMMAND_SERVER,
|
||||
[value],
|
||||
)
|
||||
|
||||
async def start_warning(
|
||||
self,
|
||||
mode=WARNING_DEVICE_MODE_EMERGENCY,
|
||||
strobe=WARNING_DEVICE_STROBE_YES,
|
||||
siren_level=WARNING_DEVICE_SOUND_HIGH,
|
||||
warning_duration=5, # seconds
|
||||
strobe_duty_cycle=0x00,
|
||||
strobe_intensity=WARNING_DEVICE_STROBE_HIGH,
|
||||
):
|
||||
"""Issue a start warning command.
|
||||
|
||||
This command starts the WD operation. The WD alerts the surrounding area by audible
|
||||
(siren) and visual (strobe) signals.
|
||||
|
||||
strobe_duty_cycle indicates the length of the flash cycle. This provides a means
|
||||
of varying the flash duration for different alarm types (e.g., fire, police, burglar).
|
||||
Valid range is 0-100 in increments of 10. All other values SHALL be rounded to the
|
||||
nearest valid value. Strobe SHALL calculate duty cycle over a duration of one second.
|
||||
The ON state SHALL precede the OFF state. For example, if Strobe Duty Cycle Field specifies
|
||||
“40,” then the strobe SHALL flash ON for 4/10ths of a second and then turn OFF for
|
||||
6/10ths of a second.
|
||||
"""
|
||||
value = 0
|
||||
value = IasWd.set_bit(value, 0, siren_level, 0)
|
||||
value = IasWd.set_bit(value, 1, siren_level, 1)
|
||||
|
||||
value = IasWd.set_bit(value, 2, strobe, 0)
|
||||
|
||||
value = IasWd.set_bit(value, 4, mode, 0)
|
||||
value = IasWd.set_bit(value, 5, mode, 1)
|
||||
value = IasWd.set_bit(value, 6, mode, 2)
|
||||
value = IasWd.set_bit(value, 7, mode, 3)
|
||||
|
||||
await self.device.issue_cluster_command(
|
||||
self.cluster.endpoint.endpoint_id,
|
||||
self.cluster.cluster_id,
|
||||
0x0000,
|
||||
CLUSTER_COMMAND_SERVER,
|
||||
[value, warning_duration, strobe_duty_cycle, strobe_intensity],
|
||||
)
|
||||
|
||||
|
||||
@registries.BINARY_SENSOR_CLUSTERS.register(security.IasZone.cluster_id)
|
||||
|
|
|
@ -34,6 +34,11 @@ ATTR_RSSI = "rssi"
|
|||
ATTR_SIGNATURE = "signature"
|
||||
ATTR_TYPE = "type"
|
||||
ATTR_VALUE = "value"
|
||||
ATTR_WARNING_DEVICE_DURATION = "duration"
|
||||
ATTR_WARNING_DEVICE_MODE = "mode"
|
||||
ATTR_WARNING_DEVICE_STROBE = "strobe"
|
||||
ATTR_WARNING_DEVICE_STROBE_DUTY_CYCLE = "duty_cycle"
|
||||
ATTR_WARNING_DEVICE_STROBE_INTENSITY = "intensity"
|
||||
|
||||
BAUD_RATES = [2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000]
|
||||
|
||||
|
@ -44,6 +49,7 @@ CHANNEL_DOORLOCK = "door_lock"
|
|||
CHANNEL_ELECTRICAL_MEASUREMENT = "electrical_measurement"
|
||||
CHANNEL_EVENT_RELAY = "event_relay"
|
||||
CHANNEL_FAN = "fan"
|
||||
CHANNEL_IAS_WD = "ias_wd"
|
||||
CHANNEL_LEVEL = ATTR_LEVEL
|
||||
CHANNEL_ON_OFF = "on_off"
|
||||
CHANNEL_POWER_CONFIGURATION = "power"
|
||||
|
@ -177,6 +183,30 @@ UNKNOWN = "unknown"
|
|||
UNKNOWN_MANUFACTURER = "unk_manufacturer"
|
||||
UNKNOWN_MODEL = "unk_model"
|
||||
|
||||
WARNING_DEVICE_MODE_STOP = 0
|
||||
WARNING_DEVICE_MODE_BURGLAR = 1
|
||||
WARNING_DEVICE_MODE_FIRE = 2
|
||||
WARNING_DEVICE_MODE_EMERGENCY = 3
|
||||
WARNING_DEVICE_MODE_POLICE_PANIC = 4
|
||||
WARNING_DEVICE_MODE_FIRE_PANIC = 5
|
||||
WARNING_DEVICE_MODE_EMERGENCY_PANIC = 6
|
||||
|
||||
WARNING_DEVICE_STROBE_NO = 0
|
||||
WARNING_DEVICE_STROBE_YES = 1
|
||||
|
||||
WARNING_DEVICE_SOUND_LOW = 0
|
||||
WARNING_DEVICE_SOUND_MEDIUM = 1
|
||||
WARNING_DEVICE_SOUND_HIGH = 2
|
||||
WARNING_DEVICE_SOUND_VERY_HIGH = 3
|
||||
|
||||
WARNING_DEVICE_STROBE_LOW = 0x00
|
||||
WARNING_DEVICE_STROBE_MEDIUM = 0x01
|
||||
WARNING_DEVICE_STROBE_HIGH = 0x02
|
||||
WARNING_DEVICE_STROBE_VERY_HIGH = 0x03
|
||||
|
||||
WARNING_DEVICE_SQUAWK_MODE_ARMED = 0
|
||||
WARNING_DEVICE_SQUAWK_MODE_DISARMED = 1
|
||||
|
||||
ZHA_DISCOVERY_NEW = "zha_discovery_new_{}"
|
||||
ZHA_GW_MSG_RAW_INIT = "raw_device_initialized"
|
||||
ZHA_GW_MSG = "zha_gateway_message"
|
||||
|
|
|
@ -82,3 +82,55 @@ issue_zigbee_cluster_command:
|
|||
manufacturer:
|
||||
description: manufacturer code
|
||||
example: 0x00FC
|
||||
|
||||
warning_device_squawk:
|
||||
description: >-
|
||||
This service uses the WD capabilities to emit a quick audible/visible pulse called a "squawk". The squawk command has no effect if the WD is currently active (warning in progress).
|
||||
fields:
|
||||
ieee:
|
||||
description: IEEE address for the device
|
||||
example: "00:0d:6f:00:05:7d:2d:34"
|
||||
mode:
|
||||
description: >-
|
||||
The Squawk Mode field is used as a 4-bit enumeration, and can have one of the values shown in Table 8-24 of the ZCL spec - Squawk Mode Field. The exact operation of each mode (how the WD “squawks”) is implementation specific.
|
||||
example: 1
|
||||
strobe:
|
||||
description: >-
|
||||
The strobe field is used as a Boolean, and determines if the visual indication is also required in addition to the audible squawk, as shown in Table 8-25 of the ZCL spec - Strobe Bit.
|
||||
example: 1
|
||||
level:
|
||||
description: >-
|
||||
The squawk level field is used as a 2-bit enumeration, and determines the intensity of audible squawk sound as shown in Table 8-26 of the ZCL spec - Squawk Level Field Values.
|
||||
example: 2
|
||||
|
||||
warning_device_warn:
|
||||
description: >-
|
||||
This service starts the WD operation. The WD alerts the surrounding area by audible (siren) and visual (strobe) signals.
|
||||
fields:
|
||||
ieee:
|
||||
description: IEEE address for the device
|
||||
example: "00:0d:6f:00:05:7d:2d:34"
|
||||
mode:
|
||||
description: >-
|
||||
The Warning Mode field is used as an 4-bit enumeration, can have one of the values defined below in table 8-20 of the ZCL spec. The exact behavior of the WD device in each mode is according to the relevant security standards.
|
||||
example: 1
|
||||
strobe:
|
||||
description: >-
|
||||
The Strobe field is used as a 2-bit enumeration, and determines if the visual indication is required in addition to the audible siren, as indicated in Table 8-21 of the ZCL spec. If the strobe field is “1” and the Warning Mode is “0” (“Stop”) then only the strobe is activated.
|
||||
example: 1
|
||||
level:
|
||||
description: >-
|
||||
The Siren Level field is used as a 2-bit enumeration, and indicates the intensity of audible squawk sound as shown in Table 8-22 of the ZCL spec.
|
||||
example: 2
|
||||
duration:
|
||||
description: >-
|
||||
Requested duration of warning, in seconds. If both Strobe and Warning Mode are "0" this field SHALL be ignored.
|
||||
example: 2
|
||||
duty_cycle:
|
||||
description: >-
|
||||
Indicates the length of the flash cycle. This provides a means of varying the flash duration for different alarm types (e.g., fire, police, burglar). Valid range is 0-100 in increments of 10. All other values SHALL be rounded to the nearest valid value. Strobe SHALL calculate duty cycle over a duration of one second. The ON state SHALL precede the OFF state. For example, if Strobe Duty Cycle Field specifies “40,” then the strobe SHALL flash ON for 4/10ths of a second and then turn OFF for 6/10ths of a second.
|
||||
example: 2
|
||||
intensity:
|
||||
description: >-
|
||||
Indicates the intensity of the strobe as shown in Table 8-23 of the ZCL spec. This attribute is designed to vary the output of the strobe (i.e., brightness) and not its frequency, which is detailed in section 8.4.2.3.1.6 of the ZCL spec.
|
||||
example: 2
|
||||
|
|
Loading…
Reference in New Issue