mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add attribute to show who last un/set alarm (SPC) (#9906)
* Add attribute to show who last un/set alarm. This allows showing the name of the SPC user who last issued an arm/disarm command and also allows for automations to depend on this value. * Optimize * Update spc.py * Update spc.py * fix * Fix test. * Fix for removed is_state_attr.
This commit is contained in:
parent
68fb995c63
commit
db56748d88
@ -34,10 +34,8 @@ def async_setup_platform(hass, config, async_add_devices,
|
|||||||
discovery_info[ATTR_DISCOVER_AREAS] is None):
|
discovery_info[ATTR_DISCOVER_AREAS] is None):
|
||||||
return
|
return
|
||||||
|
|
||||||
devices = [SpcAlarm(hass=hass,
|
api = hass.data[DATA_API]
|
||||||
area_id=area['id'],
|
devices = [SpcAlarm(api, area)
|
||||||
name=area['name'],
|
|
||||||
state=_get_alarm_state(area['mode']))
|
|
||||||
for area in discovery_info[ATTR_DISCOVER_AREAS]]
|
for area in discovery_info[ATTR_DISCOVER_AREAS]]
|
||||||
|
|
||||||
async_add_devices(devices)
|
async_add_devices(devices)
|
||||||
@ -46,21 +44,29 @@ def async_setup_platform(hass, config, async_add_devices,
|
|||||||
class SpcAlarm(alarm.AlarmControlPanel):
|
class SpcAlarm(alarm.AlarmControlPanel):
|
||||||
"""Represents the SPC alarm panel."""
|
"""Represents the SPC alarm panel."""
|
||||||
|
|
||||||
def __init__(self, hass, area_id, name, state):
|
def __init__(self, api, area):
|
||||||
"""Initialize the SPC alarm panel."""
|
"""Initialize the SPC alarm panel."""
|
||||||
self._hass = hass
|
self._area_id = area['id']
|
||||||
self._area_id = area_id
|
self._name = area['name']
|
||||||
self._name = name
|
self._state = _get_alarm_state(area['mode'])
|
||||||
self._state = state
|
if self._state == STATE_ALARM_DISARMED:
|
||||||
self._api = hass.data[DATA_API]
|
self._changed_by = area.get('last_unset_user_name', 'unknown')
|
||||||
|
else:
|
||||||
hass.data[DATA_REGISTRY].register_alarm_device(area_id, self)
|
self._changed_by = area.get('last_set_user_name', 'unknown')
|
||||||
|
self._api = api
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_update_from_spc(self, state):
|
def async_added_to_hass(self):
|
||||||
|
"""Calbback for init handlers."""
|
||||||
|
self.hass.data[DATA_REGISTRY].register_alarm_device(
|
||||||
|
self._area_id, self)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_update_from_spc(self, state, extra):
|
||||||
"""Update the alarm panel with a new state."""
|
"""Update the alarm panel with a new state."""
|
||||||
self._state = state
|
self._state = state
|
||||||
yield from self.async_update_ha_state()
|
self._changed_by = extra.get('changed_by', 'unknown')
|
||||||
|
self.async_schedule_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
@ -72,6 +78,11 @@ class SpcAlarm(alarm.AlarmControlPanel):
|
|||||||
"""Return the name of the device."""
|
"""Return the name of the device."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def changed_by(self):
|
||||||
|
"""Return the user the last change was triggered by."""
|
||||||
|
return self._changed_by
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the device."""
|
"""Return the state of the device."""
|
||||||
|
@ -67,7 +67,7 @@ class SpcBinarySensor(BinarySensorDevice):
|
|||||||
spc_registry.register_sensor_device(zone_id, self)
|
spc_registry.register_sensor_device(zone_id, self)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_update_from_spc(self, state):
|
def async_update_from_spc(self, state, extra):
|
||||||
"""Update the state of the device."""
|
"""Update the state of the device."""
|
||||||
self._state = state
|
self._state = state
|
||||||
yield from self.async_update_ha_state()
|
yield from self.async_update_ha_state()
|
||||||
|
@ -87,9 +87,14 @@ def _async_process_message(sia_message, spc_registry):
|
|||||||
# ZX - Zone Short
|
# ZX - Zone Short
|
||||||
# ZD - Zone Disconnected
|
# ZD - Zone Disconnected
|
||||||
|
|
||||||
if sia_code in ('BA', 'CG', 'NL', 'OG', 'OQ'):
|
extra = {}
|
||||||
|
|
||||||
|
if sia_code in ('BA', 'CG', 'NL', 'OG'):
|
||||||
# change in area status, notify alarm panel device
|
# change in area status, notify alarm panel device
|
||||||
device = spc_registry.get_alarm_device(spc_id)
|
device = spc_registry.get_alarm_device(spc_id)
|
||||||
|
data = sia_message['description'].split('¦')
|
||||||
|
if len(data) == 3:
|
||||||
|
extra['changed_by'] = data[1]
|
||||||
else:
|
else:
|
||||||
# change in zone status, notify sensor device
|
# change in zone status, notify sensor device
|
||||||
device = spc_registry.get_sensor_device(spc_id)
|
device = spc_registry.get_sensor_device(spc_id)
|
||||||
@ -98,7 +103,6 @@ def _async_process_message(sia_message, spc_registry):
|
|||||||
'CG': STATE_ALARM_ARMED_AWAY,
|
'CG': STATE_ALARM_ARMED_AWAY,
|
||||||
'NL': STATE_ALARM_ARMED_HOME,
|
'NL': STATE_ALARM_ARMED_HOME,
|
||||||
'OG': STATE_ALARM_DISARMED,
|
'OG': STATE_ALARM_DISARMED,
|
||||||
'OQ': STATE_ALARM_DISARMED,
|
|
||||||
'ZO': STATE_ON,
|
'ZO': STATE_ON,
|
||||||
'ZC': STATE_OFF,
|
'ZC': STATE_OFF,
|
||||||
'ZX': STATE_UNKNOWN,
|
'ZX': STATE_UNKNOWN,
|
||||||
@ -110,7 +114,7 @@ def _async_process_message(sia_message, spc_registry):
|
|||||||
_LOGGER.warning("No device mapping found for SPC area/zone id %s.",
|
_LOGGER.warning("No device mapping found for SPC area/zone id %s.",
|
||||||
spc_id)
|
spc_id)
|
||||||
elif new_state:
|
elif new_state:
|
||||||
yield from device.async_update_from_spc(new_state)
|
yield from device.async_update_from_spc(new_state, extra)
|
||||||
|
|
||||||
|
|
||||||
class SpcRegistry:
|
class SpcRegistry:
|
||||||
|
@ -38,7 +38,7 @@ def test_setup_platform(hass):
|
|||||||
'last_set_user_name': 'Pelle',
|
'last_set_user_name': 'Pelle',
|
||||||
'last_unset_time': '1485800564',
|
'last_unset_time': '1485800564',
|
||||||
'last_unset_user_id': '1',
|
'last_unset_user_id': '1',
|
||||||
'last_unset_user_name': 'Pelle',
|
'last_unset_user_name': 'Lisa',
|
||||||
'last_alarm': '1478174896'
|
'last_alarm': '1478174896'
|
||||||
}, {
|
}, {
|
||||||
'id': '3',
|
'id': '3',
|
||||||
@ -46,7 +46,7 @@ def test_setup_platform(hass):
|
|||||||
'mode': '0',
|
'mode': '0',
|
||||||
'last_set_time': '1483705803',
|
'last_set_time': '1483705803',
|
||||||
'last_set_user_id': '9998',
|
'last_set_user_id': '9998',
|
||||||
'last_set_user_name': 'Lisa',
|
'last_set_user_name': 'Pelle',
|
||||||
'last_unset_time': '1483705808',
|
'last_unset_time': '1483705808',
|
||||||
'last_unset_user_id': '9998',
|
'last_unset_user_id': '9998',
|
||||||
'last_unset_user_name': 'Lisa'
|
'last_unset_user_name': 'Lisa'
|
||||||
@ -58,7 +58,11 @@ def test_setup_platform(hass):
|
|||||||
discovery_info=areas)
|
discovery_info=areas)
|
||||||
|
|
||||||
assert len(added_entities) == 2
|
assert len(added_entities) == 2
|
||||||
|
|
||||||
assert added_entities[0].name == 'House'
|
assert added_entities[0].name == 'House'
|
||||||
assert added_entities[0].state == STATE_ALARM_ARMED_AWAY
|
assert added_entities[0].state == STATE_ALARM_ARMED_AWAY
|
||||||
|
assert added_entities[0].changed_by == 'Pelle'
|
||||||
|
|
||||||
assert added_entities[1].name == 'Garage'
|
assert added_entities[1].name == 'Garage'
|
||||||
assert added_entities[1].state == STATE_ALARM_DISARMED
|
assert added_entities[1].state == STATE_ALARM_DISARMED
|
||||||
|
assert added_entities[1].changed_by == 'Lisa'
|
||||||
|
@ -7,7 +7,9 @@ from homeassistant.components import spc
|
|||||||
from homeassistant.bootstrap import async_setup_component
|
from homeassistant.bootstrap import async_setup_component
|
||||||
from tests.common import async_test_home_assistant
|
from tests.common import async_test_home_assistant
|
||||||
from tests.test_util.aiohttp import mock_aiohttp_client
|
from tests.test_util.aiohttp import mock_aiohttp_client
|
||||||
from homeassistant.const import (STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
|
from homeassistant.const import (
|
||||||
|
STATE_ON, STATE_OFF, STATE_ALARM_ARMED_AWAY,
|
||||||
|
STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -57,7 +59,13 @@ def aioclient_mock():
|
|||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_alarm_device(hass, aioclient_mock, monkeypatch):
|
@pytest.mark.parametrize("sia_code,state", [
|
||||||
|
('NL', STATE_ALARM_ARMED_HOME),
|
||||||
|
('CG', STATE_ALARM_ARMED_AWAY),
|
||||||
|
('OG', STATE_ALARM_DISARMED)
|
||||||
|
])
|
||||||
|
def test_update_alarm_device(hass, aioclient_mock, monkeypatch,
|
||||||
|
sia_code, state):
|
||||||
"""Test that alarm panel state changes on incoming websocket data."""
|
"""Test that alarm panel state changes on incoming websocket data."""
|
||||||
monkeypatch.setattr("homeassistant.components.spc.SpcWebGateway."
|
monkeypatch.setattr("homeassistant.components.spc.SpcWebGateway."
|
||||||
"start_listener", lambda x, *args: None)
|
"start_listener", lambda x, *args: None)
|
||||||
@ -74,18 +82,30 @@ def test_update_alarm_device(hass, aioclient_mock, monkeypatch):
|
|||||||
|
|
||||||
assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED
|
assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED
|
||||||
|
|
||||||
msg = {"sia_code": "NL", "sia_address": "1", "description": "House|Sam|1"}
|
msg = {"sia_code": sia_code, "sia_address": "1",
|
||||||
|
"description": "House¦Sam¦1"}
|
||||||
yield from spc._async_process_message(msg, hass.data[spc.DATA_REGISTRY])
|
yield from spc._async_process_message(msg, hass.data[spc.DATA_REGISTRY])
|
||||||
assert hass.states.get(entity_id).state == STATE_ALARM_ARMED_HOME
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
msg = {"sia_code": "OQ", "sia_address": "1", "description": "Sam"}
|
state_obj = hass.states.get(entity_id)
|
||||||
yield from spc._async_process_message(msg, hass.data[spc.DATA_REGISTRY])
|
assert state_obj.state == state
|
||||||
assert hass.states.get(entity_id).state == STATE_ALARM_DISARMED
|
assert state_obj.attributes['changed_by'] == 'Sam'
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_update_sensor_device(hass, aioclient_mock, monkeypatch):
|
@pytest.mark.parametrize("sia_code,state", [
|
||||||
"""Test that sensors change state on incoming websocket data."""
|
('ZO', STATE_ON),
|
||||||
|
('ZC', STATE_OFF)
|
||||||
|
])
|
||||||
|
def test_update_sensor_device(hass, aioclient_mock, monkeypatch,
|
||||||
|
sia_code, state):
|
||||||
|
"""
|
||||||
|
Test that sensors change state on incoming websocket data.
|
||||||
|
|
||||||
|
Note that we don't test for the ZD (disconnected) and ZX (problem/short)
|
||||||
|
codes since the binary sensor component is hardcoded to only
|
||||||
|
let on/off states through.
|
||||||
|
"""
|
||||||
monkeypatch.setattr("homeassistant.components.spc.SpcWebGateway."
|
monkeypatch.setattr("homeassistant.components.spc.SpcWebGateway."
|
||||||
"start_listener", lambda x, *args: None)
|
"start_listener", lambda x, *args: None)
|
||||||
config = {
|
config = {
|
||||||
@ -97,15 +117,13 @@ def test_update_sensor_device(hass, aioclient_mock, monkeypatch):
|
|||||||
yield from async_setup_component(hass, 'spc', config)
|
yield from async_setup_component(hass, 'spc', config)
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get('binary_sensor.hallway_pir').state == 'off'
|
assert hass.states.get('binary_sensor.hallway_pir').state == STATE_OFF
|
||||||
|
|
||||||
msg = {"sia_code": "ZO", "sia_address": "3", "description": "Hallway PIR"}
|
msg = {"sia_code": sia_code, "sia_address": "3",
|
||||||
|
"description": "Hallway PIR"}
|
||||||
yield from spc._async_process_message(msg, hass.data[spc.DATA_REGISTRY])
|
yield from spc._async_process_message(msg, hass.data[spc.DATA_REGISTRY])
|
||||||
assert hass.states.get('binary_sensor.hallway_pir').state == 'on'
|
yield from hass.async_block_till_done()
|
||||||
|
assert hass.states.get('binary_sensor.hallway_pir').state == state
|
||||||
msg = {"sia_code": "ZC", "sia_address": "3", "description": "Hallway PIR"}
|
|
||||||
yield from spc._async_process_message(msg, hass.data[spc.DATA_REGISTRY])
|
|
||||||
assert hass.states.get('binary_sensor.hallway_pir').state == 'off'
|
|
||||||
|
|
||||||
|
|
||||||
class TestSpcRegistry:
|
class TestSpcRegistry:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user