Fix optimistic mode and add tests (#22899)
This commit is contained in:
parent
6463b8165f
commit
38d92b2abf
3 changed files with 127 additions and 2 deletions
|
@ -79,12 +79,14 @@ class MqttLock(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate,
|
||||||
|
|
||||||
def __init__(self, config, config_entry, discovery_hash):
|
def __init__(self, config, config_entry, discovery_hash):
|
||||||
"""Initialize the lock."""
|
"""Initialize the lock."""
|
||||||
self._config = config
|
|
||||||
self._unique_id = config.get(CONF_UNIQUE_ID)
|
self._unique_id = config.get(CONF_UNIQUE_ID)
|
||||||
self._state = False
|
self._state = False
|
||||||
self._sub_state = None
|
self._sub_state = None
|
||||||
self._optimistic = False
|
self._optimistic = False
|
||||||
|
|
||||||
|
# Load config
|
||||||
|
self._setup_from_config(config)
|
||||||
|
|
||||||
device_config = config.get(CONF_DEVICE)
|
device_config = config.get(CONF_DEVICE)
|
||||||
|
|
||||||
MqttAttributes.__init__(self, config)
|
MqttAttributes.__init__(self, config)
|
||||||
|
@ -101,13 +103,19 @@ class MqttLock(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate,
|
||||||
async def discovery_update(self, discovery_payload):
|
async def discovery_update(self, discovery_payload):
|
||||||
"""Handle updated discovery message."""
|
"""Handle updated discovery message."""
|
||||||
config = PLATFORM_SCHEMA(discovery_payload)
|
config = PLATFORM_SCHEMA(discovery_payload)
|
||||||
self._config = config
|
self._setup_from_config(config)
|
||||||
await self.attributes_discovery_update(config)
|
await self.attributes_discovery_update(config)
|
||||||
await self.availability_discovery_update(config)
|
await self.availability_discovery_update(config)
|
||||||
await self.device_info_discovery_update(config)
|
await self.device_info_discovery_update(config)
|
||||||
await self._subscribe_topics()
|
await self._subscribe_topics()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
def _setup_from_config(self, config):
|
||||||
|
"""(Re)Setup the entity."""
|
||||||
|
self._config = config
|
||||||
|
|
||||||
|
self._optimistic = config[CONF_OPTIMISTIC]
|
||||||
|
|
||||||
async def _subscribe_topics(self):
|
async def _subscribe_topics(self):
|
||||||
"""(Re)Subscribe to topics."""
|
"""(Re)Subscribe to topics."""
|
||||||
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
value_template = self._config.get(CONF_VALUE_TEMPLATE)
|
||||||
|
|
|
@ -6,6 +6,7 @@ components. Instead call the service directly.
|
||||||
from homeassistant.components.lock import DOMAIN
|
from homeassistant.components.lock import DOMAIN
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CODE, ATTR_ENTITY_ID, SERVICE_LOCK, SERVICE_UNLOCK, SERVICE_OPEN)
|
ATTR_CODE, ATTR_ENTITY_ID, SERVICE_LOCK, SERVICE_UNLOCK, SERVICE_OPEN)
|
||||||
|
from homeassistant.core import callback
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +22,19 @@ def lock(hass, entity_id=None, code=None):
|
||||||
hass.services.call(DOMAIN, SERVICE_LOCK, data)
|
hass.services.call(DOMAIN, SERVICE_LOCK, data)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@bind_hass
|
||||||
|
def async_lock(hass, entity_id=None, code=None):
|
||||||
|
"""Lock all or specified locks."""
|
||||||
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
|
if entity_id:
|
||||||
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
|
hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_LOCK, data))
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def unlock(hass, entity_id=None, code=None):
|
def unlock(hass, entity_id=None, code=None):
|
||||||
"""Unlock all or specified locks."""
|
"""Unlock all or specified locks."""
|
||||||
|
@ -33,6 +47,19 @@ def unlock(hass, entity_id=None, code=None):
|
||||||
hass.services.call(DOMAIN, SERVICE_UNLOCK, data)
|
hass.services.call(DOMAIN, SERVICE_UNLOCK, data)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@bind_hass
|
||||||
|
def async_unlock(hass, entity_id=None, code=None):
|
||||||
|
"""Lock all or specified locks."""
|
||||||
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
|
if entity_id:
|
||||||
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
|
hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_UNLOCK, data))
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def open_lock(hass, entity_id=None, code=None):
|
def open_lock(hass, entity_id=None, code=None):
|
||||||
"""Open all or specified locks."""
|
"""Open all or specified locks."""
|
||||||
|
@ -43,3 +70,16 @@ def open_lock(hass, entity_id=None, code=None):
|
||||||
data[ATTR_ENTITY_ID] = entity_id
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
hass.services.call(DOMAIN, SERVICE_OPEN, data)
|
hass.services.call(DOMAIN, SERVICE_OPEN, data)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@bind_hass
|
||||||
|
def async_open_lock(hass, entity_id=None, code=None):
|
||||||
|
"""Lock all or specified locks."""
|
||||||
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
|
if entity_id:
|
||||||
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
|
hass.async_add_job(hass.services.async_call(DOMAIN, SERVICE_OPEN, data))
|
||||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.setup import async_setup_component
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component,
|
MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component,
|
||||||
mock_registry)
|
mock_registry)
|
||||||
|
from tests.components.lock import common
|
||||||
|
|
||||||
|
|
||||||
async def test_controlling_state_via_topic(hass, mqtt_mock):
|
async def test_controlling_state_via_topic(hass, mqtt_mock):
|
||||||
|
@ -75,6 +76,82 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock):
|
||||||
assert state.state is STATE_UNLOCKED
|
assert state.state is STATE_UNLOCKED
|
||||||
|
|
||||||
|
|
||||||
|
async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock):
|
||||||
|
"""Test optimistic mode without state topic."""
|
||||||
|
assert await async_setup_component(hass, lock.DOMAIN, {
|
||||||
|
lock.DOMAIN: {
|
||||||
|
'platform': 'mqtt',
|
||||||
|
'name': 'test',
|
||||||
|
'command_topic': 'command-topic',
|
||||||
|
'payload_lock': 'LOCK',
|
||||||
|
'payload_unlock': 'UNLOCK'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
state = hass.states.get('lock.test')
|
||||||
|
assert state.state is STATE_UNLOCKED
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
common.async_lock(hass, 'lock.test')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
'command-topic', 'LOCK', 0, False)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get('lock.test')
|
||||||
|
assert state.state is STATE_LOCKED
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
common.async_unlock(hass, 'lock.test')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
'command-topic', 'UNLOCK', 0, False)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get('lock.test')
|
||||||
|
assert state.state is STATE_UNLOCKED
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock):
|
||||||
|
"""Test optimistic mode without state topic."""
|
||||||
|
assert await async_setup_component(hass, lock.DOMAIN, {
|
||||||
|
lock.DOMAIN: {
|
||||||
|
'platform': 'mqtt',
|
||||||
|
'name': 'test',
|
||||||
|
'state_topic': 'state-topic',
|
||||||
|
'command_topic': 'command-topic',
|
||||||
|
'payload_lock': 'LOCK',
|
||||||
|
'payload_unlock': 'UNLOCK',
|
||||||
|
'optimistic': True
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
state = hass.states.get('lock.test')
|
||||||
|
assert state.state is STATE_UNLOCKED
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
common.async_lock(hass, 'lock.test')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
'command-topic', 'LOCK', 0, False)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get('lock.test')
|
||||||
|
assert state.state is STATE_LOCKED
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
common.async_unlock(hass, 'lock.test')
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
'command-topic', 'UNLOCK', 0, False)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get('lock.test')
|
||||||
|
assert state.state is STATE_UNLOCKED
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
|
||||||
async def test_default_availability_payload(hass, mqtt_mock):
|
async def test_default_availability_payload(hass, mqtt_mock):
|
||||||
"""Test availability by default payload with defined topic."""
|
"""Test availability by default payload with defined topic."""
|
||||||
assert await async_setup_component(hass, lock.DOMAIN, {
|
assert await async_setup_component(hass, lock.DOMAIN, {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue