Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions homeassistant/components/zha/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@
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,
Expand All @@ -31,6 +38,11 @@
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

Expand All @@ -56,6 +68,8 @@
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"

Expand All @@ -80,6 +94,41 @@
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,
Expand Down Expand Up @@ -610,6 +659,85 @@ async def issue_zigbee_cluster_command(service):
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(
Comment thread
Adminiuga marked this conversation as resolved.
Outdated
"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)
Expand All @@ -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)
96 changes: 94 additions & 2 deletions homeassistant/components/zha/core/channels/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@

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__)

Expand All @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions homeassistant/components/zha/core/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
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]

Expand All @@ -44,6 +49,7 @@
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"
Expand Down Expand Up @@ -177,6 +183,30 @@ def list(cls):
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"
Expand Down
52 changes: 52 additions & 0 deletions homeassistant/components/zha/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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