Add open and opening state support to MQTT lock (#117110)
This commit is contained in:
parent
159f0fcce7
commit
840d8cb39f
2 changed files with 68 additions and 11 deletions
|
@ -31,6 +31,8 @@ from .const import (
|
||||||
CONF_PAYLOAD_RESET,
|
CONF_PAYLOAD_RESET,
|
||||||
CONF_QOS,
|
CONF_QOS,
|
||||||
CONF_RETAIN,
|
CONF_RETAIN,
|
||||||
|
CONF_STATE_OPEN,
|
||||||
|
CONF_STATE_OPENING,
|
||||||
CONF_STATE_TOPIC,
|
CONF_STATE_TOPIC,
|
||||||
)
|
)
|
||||||
from .debug_info import log_messages
|
from .debug_info import log_messages
|
||||||
|
@ -56,6 +58,7 @@ CONF_PAYLOAD_OPEN = "payload_open"
|
||||||
|
|
||||||
CONF_STATE_LOCKED = "state_locked"
|
CONF_STATE_LOCKED = "state_locked"
|
||||||
CONF_STATE_LOCKING = "state_locking"
|
CONF_STATE_LOCKING = "state_locking"
|
||||||
|
|
||||||
CONF_STATE_UNLOCKED = "state_unlocked"
|
CONF_STATE_UNLOCKED = "state_unlocked"
|
||||||
CONF_STATE_UNLOCKING = "state_unlocking"
|
CONF_STATE_UNLOCKING = "state_unlocking"
|
||||||
CONF_STATE_JAMMED = "state_jammed"
|
CONF_STATE_JAMMED = "state_jammed"
|
||||||
|
@ -67,6 +70,8 @@ DEFAULT_PAYLOAD_OPEN = "OPEN"
|
||||||
DEFAULT_PAYLOAD_RESET = "None"
|
DEFAULT_PAYLOAD_RESET = "None"
|
||||||
DEFAULT_STATE_LOCKED = "LOCKED"
|
DEFAULT_STATE_LOCKED = "LOCKED"
|
||||||
DEFAULT_STATE_LOCKING = "LOCKING"
|
DEFAULT_STATE_LOCKING = "LOCKING"
|
||||||
|
DEFAULT_STATE_OPEN = "OPEN"
|
||||||
|
DEFAULT_STATE_OPENING = "OPENING"
|
||||||
DEFAULT_STATE_UNLOCKED = "UNLOCKED"
|
DEFAULT_STATE_UNLOCKED = "UNLOCKED"
|
||||||
DEFAULT_STATE_UNLOCKING = "UNLOCKING"
|
DEFAULT_STATE_UNLOCKING = "UNLOCKING"
|
||||||
DEFAULT_STATE_JAMMED = "JAMMED"
|
DEFAULT_STATE_JAMMED = "JAMMED"
|
||||||
|
@ -90,6 +95,8 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend(
|
||||||
vol.Optional(CONF_STATE_JAMMED, default=DEFAULT_STATE_JAMMED): cv.string,
|
vol.Optional(CONF_STATE_JAMMED, default=DEFAULT_STATE_JAMMED): cv.string,
|
||||||
vol.Optional(CONF_STATE_LOCKED, default=DEFAULT_STATE_LOCKED): cv.string,
|
vol.Optional(CONF_STATE_LOCKED, default=DEFAULT_STATE_LOCKED): cv.string,
|
||||||
vol.Optional(CONF_STATE_LOCKING, default=DEFAULT_STATE_LOCKING): cv.string,
|
vol.Optional(CONF_STATE_LOCKING, default=DEFAULT_STATE_LOCKING): cv.string,
|
||||||
|
vol.Optional(CONF_STATE_OPEN, default=DEFAULT_STATE_OPEN): cv.string,
|
||||||
|
vol.Optional(CONF_STATE_OPENING, default=DEFAULT_STATE_OPENING): cv.string,
|
||||||
vol.Optional(CONF_STATE_UNLOCKED, default=DEFAULT_STATE_UNLOCKED): cv.string,
|
vol.Optional(CONF_STATE_UNLOCKED, default=DEFAULT_STATE_UNLOCKED): cv.string,
|
||||||
vol.Optional(CONF_STATE_UNLOCKING, default=DEFAULT_STATE_UNLOCKING): cv.string,
|
vol.Optional(CONF_STATE_UNLOCKING, default=DEFAULT_STATE_UNLOCKING): cv.string,
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
|
@ -102,6 +109,8 @@ STATE_CONFIG_KEYS = [
|
||||||
CONF_STATE_JAMMED,
|
CONF_STATE_JAMMED,
|
||||||
CONF_STATE_LOCKED,
|
CONF_STATE_LOCKED,
|
||||||
CONF_STATE_LOCKING,
|
CONF_STATE_LOCKING,
|
||||||
|
CONF_STATE_OPEN,
|
||||||
|
CONF_STATE_OPENING,
|
||||||
CONF_STATE_UNLOCKED,
|
CONF_STATE_UNLOCKED,
|
||||||
CONF_STATE_UNLOCKING,
|
CONF_STATE_UNLOCKING,
|
||||||
]
|
]
|
||||||
|
@ -189,6 +198,8 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
"_attr_is_jammed",
|
"_attr_is_jammed",
|
||||||
"_attr_is_locked",
|
"_attr_is_locked",
|
||||||
"_attr_is_locking",
|
"_attr_is_locking",
|
||||||
|
"_attr_is_open",
|
||||||
|
"_attr_is_opening",
|
||||||
"_attr_is_unlocking",
|
"_attr_is_unlocking",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -202,6 +213,8 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
elif payload in self._valid_states:
|
elif payload in self._valid_states:
|
||||||
self._attr_is_locked = payload == self._config[CONF_STATE_LOCKED]
|
self._attr_is_locked = payload == self._config[CONF_STATE_LOCKED]
|
||||||
self._attr_is_locking = payload == self._config[CONF_STATE_LOCKING]
|
self._attr_is_locking = payload == self._config[CONF_STATE_LOCKING]
|
||||||
|
self._attr_is_open = payload == self._config[CONF_STATE_OPEN]
|
||||||
|
self._attr_is_opening = payload == self._config[CONF_STATE_OPENING]
|
||||||
self._attr_is_unlocking = payload == self._config[CONF_STATE_UNLOCKING]
|
self._attr_is_unlocking = payload == self._config[CONF_STATE_UNLOCKING]
|
||||||
self._attr_is_jammed = payload == self._config[CONF_STATE_JAMMED]
|
self._attr_is_jammed = payload == self._config[CONF_STATE_JAMMED]
|
||||||
|
|
||||||
|
@ -286,5 +299,5 @@ class MqttLock(MqttEntity, LockEntity):
|
||||||
)
|
)
|
||||||
if self._optimistic:
|
if self._optimistic:
|
||||||
# Optimistically assume that the lock unlocks when opened.
|
# Optimistically assume that the lock unlocks when opened.
|
||||||
self._attr_is_locked = False
|
self._attr_is_open = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -13,6 +13,8 @@ from homeassistant.components.lock import (
|
||||||
STATE_JAMMED,
|
STATE_JAMMED,
|
||||||
STATE_LOCKED,
|
STATE_LOCKED,
|
||||||
STATE_LOCKING,
|
STATE_LOCKING,
|
||||||
|
STATE_OPEN,
|
||||||
|
STATE_OPENING,
|
||||||
STATE_UNLOCKED,
|
STATE_UNLOCKED,
|
||||||
STATE_UNLOCKING,
|
STATE_UNLOCKING,
|
||||||
LockEntityFeature,
|
LockEntityFeature,
|
||||||
|
@ -75,8 +77,10 @@ CONFIG_WITH_STATES = {
|
||||||
"payload_unlock": "UNLOCK",
|
"payload_unlock": "UNLOCK",
|
||||||
"state_locked": "closed",
|
"state_locked": "closed",
|
||||||
"state_locking": "closing",
|
"state_locking": "closing",
|
||||||
"state_unlocked": "open",
|
"state_open": "open",
|
||||||
"state_unlocking": "opening",
|
"state_opening": "opening",
|
||||||
|
"state_unlocked": "unlocked",
|
||||||
|
"state_unlocking": "unlocking",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,8 +91,10 @@ CONFIG_WITH_STATES = {
|
||||||
[
|
[
|
||||||
(CONFIG_WITH_STATES, "closed", STATE_LOCKED),
|
(CONFIG_WITH_STATES, "closed", STATE_LOCKED),
|
||||||
(CONFIG_WITH_STATES, "closing", STATE_LOCKING),
|
(CONFIG_WITH_STATES, "closing", STATE_LOCKING),
|
||||||
(CONFIG_WITH_STATES, "open", STATE_UNLOCKED),
|
(CONFIG_WITH_STATES, "open", STATE_OPEN),
|
||||||
(CONFIG_WITH_STATES, "opening", STATE_UNLOCKING),
|
(CONFIG_WITH_STATES, "opening", STATE_OPENING),
|
||||||
|
(CONFIG_WITH_STATES, "unlocked", STATE_UNLOCKED),
|
||||||
|
(CONFIG_WITH_STATES, "unlocking", STATE_UNLOCKING),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_controlling_state_via_topic(
|
async def test_controlling_state_via_topic(
|
||||||
|
@ -117,8 +123,10 @@ async def test_controlling_state_via_topic(
|
||||||
[
|
[
|
||||||
(CONFIG_WITH_STATES, "closed", STATE_LOCKED),
|
(CONFIG_WITH_STATES, "closed", STATE_LOCKED),
|
||||||
(CONFIG_WITH_STATES, "closing", STATE_LOCKING),
|
(CONFIG_WITH_STATES, "closing", STATE_LOCKING),
|
||||||
(CONFIG_WITH_STATES, "open", STATE_UNLOCKED),
|
(CONFIG_WITH_STATES, "open", STATE_OPEN),
|
||||||
(CONFIG_WITH_STATES, "opening", STATE_UNLOCKING),
|
(CONFIG_WITH_STATES, "opening", STATE_OPENING),
|
||||||
|
(CONFIG_WITH_STATES, "unlocked", STATE_UNLOCKED),
|
||||||
|
(CONFIG_WITH_STATES, "unlocking", STATE_UNLOCKING),
|
||||||
(CONFIG_WITH_STATES, "None", STATE_UNKNOWN),
|
(CONFIG_WITH_STATES, "None", STATE_UNKNOWN),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -168,7 +176,7 @@ async def test_controlling_non_default_state_via_topic(
|
||||||
CONFIG_WITH_STATES,
|
CONFIG_WITH_STATES,
|
||||||
({"value_template": "{{ value_json.val }}"},),
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
),
|
),
|
||||||
'{"val":"opening"}',
|
'{"val":"unlocking"}',
|
||||||
STATE_UNLOCKING,
|
STATE_UNLOCKING,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -178,6 +186,24 @@ async def test_controlling_non_default_state_via_topic(
|
||||||
({"value_template": "{{ value_json.val }}"},),
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
),
|
),
|
||||||
'{"val":"open"}',
|
'{"val":"open"}',
|
||||||
|
STATE_OPEN,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
help_custom_config(
|
||||||
|
lock.DOMAIN,
|
||||||
|
CONFIG_WITH_STATES,
|
||||||
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
|
),
|
||||||
|
'{"val":"opening"}',
|
||||||
|
STATE_OPENING,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
help_custom_config(
|
||||||
|
lock.DOMAIN,
|
||||||
|
CONFIG_WITH_STATES,
|
||||||
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
|
),
|
||||||
|
'{"val":"unlocked"}',
|
||||||
STATE_UNLOCKED,
|
STATE_UNLOCKED,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -237,7 +263,7 @@ async def test_controlling_state_via_topic_and_json_message(
|
||||||
({"value_template": "{{ value_json.val }}"},),
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
),
|
),
|
||||||
'{"val":"open"}',
|
'{"val":"open"}',
|
||||||
STATE_UNLOCKED,
|
STATE_OPEN,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
help_custom_config(
|
help_custom_config(
|
||||||
|
@ -246,6 +272,24 @@ async def test_controlling_state_via_topic_and_json_message(
|
||||||
({"value_template": "{{ value_json.val }}"},),
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
),
|
),
|
||||||
'{"val":"opening"}',
|
'{"val":"opening"}',
|
||||||
|
STATE_OPENING,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
help_custom_config(
|
||||||
|
lock.DOMAIN,
|
||||||
|
CONFIG_WITH_STATES,
|
||||||
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
|
),
|
||||||
|
'{"val":"unlocked"}',
|
||||||
|
STATE_UNLOCKED,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
help_custom_config(
|
||||||
|
lock.DOMAIN,
|
||||||
|
CONFIG_WITH_STATES,
|
||||||
|
({"value_template": "{{ value_json.val }}"},),
|
||||||
|
),
|
||||||
|
'{"val":"unlocking"}',
|
||||||
STATE_UNLOCKING,
|
STATE_UNLOCKING,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -483,7 +527,7 @@ async def test_sending_mqtt_commands_support_open_and_optimistic(
|
||||||
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
|
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
|
||||||
mqtt_mock.async_publish.reset_mock()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
state = hass.states.get("lock.test")
|
state = hass.states.get("lock.test")
|
||||||
assert state.state is STATE_UNLOCKED
|
assert state.state is STATE_OPEN
|
||||||
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
|
||||||
|
@ -545,7 +589,7 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
|
||||||
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
|
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
|
||||||
mqtt_mock.async_publish.reset_mock()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
state = hass.states.get("lock.test")
|
state = hass.states.get("lock.test")
|
||||||
assert state.state is STATE_UNLOCKED
|
assert state.state is STATE_OPEN
|
||||||
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue