Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove binary sensors for ZHA remotes and controllers #24370

Merged
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
66 changes: 17 additions & 49 deletions homeassistant/components/zha/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
"""Binary sensors on Zigbee Home Automation networks."""
import logging

from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDevice
from homeassistant.components.binary_sensor import (
DOMAIN, BinarySensorDevice, DEVICE_CLASS_MOVING, DEVICE_CLASS_MOTION,
DEVICE_CLASS_OPENING, DEVICE_CLASS_MOISTURE, DEVICE_CLASS_SMOKE,
DEVICE_CLASS_GAS, DEVICE_CLASS_VIBRATION, DEVICE_CLASS_OCCUPANCY
)
from homeassistant.const import STATE_ON
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .core.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW, ON_OFF_CHANNEL,
LEVEL_CHANNEL, ZONE_CHANNEL, SIGNAL_ATTR_UPDATED, SIGNAL_MOVE_LEVEL,
SIGNAL_SET_LEVEL, ATTRIBUTE_CHANNEL, UNKNOWN, OPENING, ZONE, OCCUPANCY,
ATTR_LEVEL, SENSOR_TYPE, ACCELERATION)
ZONE_CHANNEL, SIGNAL_ATTR_UPDATED, ATTRIBUTE_CHANNEL, UNKNOWN, OPENING,
ZONE, OCCUPANCY, SENSOR_TYPE, ACCELERATION
)
from .entity import ZhaEntity

_LOGGER = logging.getLogger(__name__)

# Zigbee Cluster Library Zone Type to Home Assistant device class
CLASS_MAPPING = {
0x000d: 'motion',
0x0015: 'opening',
0x0028: 'smoke',
0x002a: 'moisture',
0x002b: 'gas',
0x002d: 'vibration',
0x000d: DEVICE_CLASS_MOTION,
0x0015: DEVICE_CLASS_OPENING,
0x0028: DEVICE_CLASS_SMOKE,
0x002a: DEVICE_CLASS_MOISTURE,
0x002b: DEVICE_CLASS_GAS,
0x002d: DEVICE_CLASS_VIBRATION,
}


Expand All @@ -33,10 +37,10 @@ async def get_ias_device_class(channel):

DEVICE_CLASS_REGISTRY = {
UNKNOWN: None,
OPENING: OPENING,
OPENING: DEVICE_CLASS_OPENING,
ZONE: get_ias_device_class,
OCCUPANCY: OCCUPANCY,
ACCELERATION: 'moving',
OCCUPANCY: DEVICE_CLASS_OCCUPANCY,
ACCELERATION: DEVICE_CLASS_MOVING,
}


Expand Down Expand Up @@ -85,10 +89,8 @@ def __init__(self, **kwargs):
self._device_state_attributes = {}
self._zone_channel = self.cluster_channels.get(ZONE_CHANNEL)
self._on_off_channel = self.cluster_channels.get(ON_OFF_CHANNEL)
self._level_channel = self.cluster_channels.get(LEVEL_CHANNEL)
self._attr_channel = self.cluster_channels.get(ATTRIBUTE_CHANNEL)
self._zha_sensor_type = kwargs[SENSOR_TYPE]
self._level = None

async def _determine_device_class(self):
"""Determine the device class for this binary sensor."""
Expand All @@ -105,11 +107,6 @@ async def async_added_to_hass(self):
"""Run when about to be added to hass."""
self._device_class = await self._determine_device_class()
await super().async_added_to_hass()
if self._level_channel:
await self.async_accept_signal(
self._level_channel, SIGNAL_SET_LEVEL, self.set_level)
await self.async_accept_signal(
self._level_channel, SIGNAL_MOVE_LEVEL, self.move_level)
if self._on_off_channel:
await self.async_accept_signal(
self._on_off_channel, SIGNAL_ATTR_UPDATED,
Expand All @@ -126,8 +123,6 @@ def async_restore_last_state(self, last_state):
"""Restore previous state."""
super().async_restore_last_state(last_state)
self._state = last_state.state == STATE_ON
if 'level' in last_state.attributes:
self._level = last_state.attributes['level']

@property
def is_on(self) -> bool:
Expand All @@ -146,36 +141,9 @@ def async_set_state(self, state):
self._state = bool(state)
self.async_schedule_update_ha_state()

def move_level(self, change):
"""Increment the level, setting state if appropriate."""
level = self._level or 0
if not self._state and change > 0:
level = 0
self._level = min(254, max(0, level + change))
self._state = bool(self._level)
self.async_schedule_update_ha_state()

def set_level(self, level):
"""Set the level, setting state if appropriate."""
self._level = level
self._state = bool(level)
self.async_schedule_update_ha_state()

@property
def device_state_attributes(self):
"""Return the device state attributes."""
if self._level_channel is not None:
self._device_state_attributes.update({
ATTR_LEVEL: self._state and self._level or 0
})
return self._device_state_attributes

async def async_update(self):
"""Attempt to retrieve on off state from the binary sensor."""
await super().async_update()
if self._level_channel:
self._level = await self._level_channel.get_attribute_value(
'current_level')
if self._on_off_channel:
self._state = await self._on_off_channel.get_attribute_value(
'on_off')
Expand Down
16 changes: 2 additions & 14 deletions homeassistant/components/zha/core/registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,11 @@ def get_deconz_radio():
BINDABLE_CLUSTERS.append(zcl.clusters.lighting.Color.cluster_id)

DEVICE_CLASS[zha.PROFILE_ID].update({
zha.DeviceType.ON_OFF_SWITCH: BINARY_SENSOR,
zha.DeviceType.LEVEL_CONTROL_SWITCH: BINARY_SENSOR,
zha.DeviceType.REMOTE_CONTROL: BINARY_SENSOR,
zha.DeviceType.SMART_PLUG: SWITCH,
zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT: LIGHT,
zha.DeviceType.ON_OFF_LIGHT: LIGHT,
zha.DeviceType.DIMMABLE_LIGHT: LIGHT,
zha.DeviceType.COLOR_DIMMABLE_LIGHT: LIGHT,
zha.DeviceType.ON_OFF_LIGHT_SWITCH: BINARY_SENSOR,
zha.DeviceType.DIMMER_SWITCH: BINARY_SENSOR,
zha.DeviceType.COLOR_DIMMER_SWITCH: BINARY_SENSOR,
zha.DeviceType.COLOR_DIMMABLE_LIGHT: LIGHT
})

DEVICE_CLASS[zll.PROFILE_ID].update({
Expand All @@ -130,12 +124,7 @@ def get_deconz_radio():
zll.DeviceType.DIMMABLE_PLUGIN_UNIT: LIGHT,
zll.DeviceType.COLOR_LIGHT: LIGHT,
zll.DeviceType.EXTENDED_COLOR_LIGHT: LIGHT,
zll.DeviceType.COLOR_TEMPERATURE_LIGHT: LIGHT,
zll.DeviceType.COLOR_CONTROLLER: BINARY_SENSOR,
zll.DeviceType.COLOR_SCENE_CONTROLLER: BINARY_SENSOR,
zll.DeviceType.CONTROLLER: BINARY_SENSOR,
zll.DeviceType.SCENE_CONTROLLER: BINARY_SENSOR,
zll.DeviceType.ON_OFF_SENSOR: BINARY_SENSOR,
zll.DeviceType.COLOR_TEMPERATURE_LIGHT: LIGHT
})

SINGLE_INPUT_CLUSTER_DEVICE_CLASS.update({
Expand Down Expand Up @@ -285,7 +274,6 @@ def get_deconz_radio():
})

BINARY_SENSOR_CLUSTERS.add(zcl.clusters.general.OnOff.cluster_id)
BINARY_SENSOR_CLUSTERS.add(zcl.clusters.general.LevelControl.cluster_id)
BINARY_SENSOR_CLUSTERS.add(zcl.clusters.security.IasZone.cluster_id)
BINARY_SENSOR_CLUSTERS.add(
zcl.clusters.measurement.OccupancySensing.cluster_id)
Expand Down
62 changes: 2 additions & 60 deletions tests/components/zha/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ async def test_binary_sensor(hass, config_entry, zha_gateway):
"""Test zha binary_sensor platform."""
from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.clusters.general import OnOff, LevelControl, Basic
from zigpy.profiles.zha import DeviceType
from zigpy.zcl.clusters.general import Basic

# create zigpy devices
zigpy_device_zone = await async_init_zigpy_device(
Expand All @@ -23,17 +22,6 @@ async def test_binary_sensor(hass, config_entry, zha_gateway):
zha_gateway
)

zigpy_device_remote = await async_init_zigpy_device(
hass,
[Basic.cluster_id],
[OnOff.cluster_id, LevelControl.cluster_id],
DeviceType.LEVEL_CONTROL_SWITCH,
zha_gateway,
ieee="00:0d:6f:11:0a:90:69:e7",
manufacturer="FakeManufacturer",
model="FakeRemoteModel"
)

zigpy_device_occupancy = await async_init_zigpy_device(
hass,
[OccupancySensing.cluster_id, Basic.cluster_id],
Expand Down Expand Up @@ -63,46 +51,20 @@ async def test_binary_sensor(hass, config_entry, zha_gateway):
DOMAIN, zigpy_device_occupancy, occupancy_cluster)
occupancy_zha_device = zha_gateway.get_device(zigpy_device_occupancy.ieee)

# dimmable binary_sensor
remote_on_off_cluster = zigpy_device_remote.endpoints.get(
1).out_clusters[OnOff.cluster_id]
remote_level_cluster = zigpy_device_remote.endpoints.get(
1).out_clusters[LevelControl.cluster_id]
remote_entity_id = make_entity_id(DOMAIN, zigpy_device_remote,
remote_on_off_cluster,
use_suffix=False)
remote_zha_device = zha_gateway.get_device(zigpy_device_remote.ieee)

# test that the sensors exist and are in the unavailable state
assert hass.states.get(zone_entity_id).state == STATE_UNAVAILABLE
assert hass.states.get(remote_entity_id).state == STATE_UNAVAILABLE
assert hass.states.get(occupancy_entity_id).state == STATE_UNAVAILABLE

await async_enable_traffic(hass, zha_gateway,
[zone_zha_device, remote_zha_device,
occupancy_zha_device])
[zone_zha_device, occupancy_zha_device])

# test that the sensors exist and are in the off state
assert hass.states.get(zone_entity_id).state == STATE_OFF
assert hass.states.get(remote_entity_id).state == STATE_OFF
assert hass.states.get(occupancy_entity_id).state == STATE_OFF

# test getting messages that trigger and reset the sensors
await async_test_binary_sensor_on_off(hass, occupancy_cluster,
occupancy_entity_id)
await async_test_binary_sensor_on_off(hass, remote_on_off_cluster,
remote_entity_id)

# test changing the level attribute for dimming remotes
await async_test_remote_level(
hass, remote_level_cluster, remote_entity_id, 150, STATE_ON)
await async_test_remote_level(
hass, remote_level_cluster, remote_entity_id, 0, STATE_OFF)
await async_test_remote_level(
hass, remote_level_cluster, remote_entity_id, 255, STATE_ON)

await async_test_remote_move_level(
hass, remote_level_cluster, remote_entity_id, 20, STATE_ON)

# test IASZone binary sensors
await async_test_iaszone_on_off(hass, zone_cluster, zone_entity_id)
Expand All @@ -127,26 +89,6 @@ async def async_test_binary_sensor_on_off(hass, cluster, entity_id):
assert hass.states.get(entity_id).state == STATE_OFF


async def async_test_remote_level(hass, cluster, entity_id, level,
expected_state):
"""Test dimmer functionality from the remote."""
attr = make_attribute(0, level)
cluster.handle_message(False, 1, 0x0a, [[attr]])
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == expected_state
assert hass.states.get(entity_id).attributes.get('level') == level


async def async_test_remote_move_level(hass, cluster, entity_id, change,
expected_state):
"""Test move to level command."""
level = hass.states.get(entity_id).attributes.get('level')
cluster.listener_event('cluster_command', 1, 1, [1, change])
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == expected_state
assert hass.states.get(entity_id).attributes.get('level') == level - change


async def async_test_iaszone_on_off(hass, cluster, entity_id):
"""Test getting on and off messages for iaszone binary sensors."""
# binary sensor on
Expand Down