mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Add jammed state support for MQTT lock (#86010)
* Add jammed state support for MQTT lock * Correct payload jammed key * Add tests - rename solved to ok * Rename jammed state and template topics to motor * Use state topic for handling motor state * Follow up comments * Change default behaviour `state_unjammed` * Skip `state_unjammed`
This commit is contained in:
parent
c8b9260f92
commit
92742ae423
@ -198,6 +198,7 @@ ABBREVIATIONS = {
|
||||
"stat_cla": "state_class",
|
||||
"stat_clsd": "state_closed",
|
||||
"stat_closing": "state_closing",
|
||||
"stat_jam": "state_jammed",
|
||||
"stat_off": "state_off",
|
||||
"stat_on": "state_on",
|
||||
"stat_open": "state_open",
|
||||
|
@ -43,6 +43,7 @@ CONF_STATE_LOCKED = "state_locked"
|
||||
CONF_STATE_LOCKING = "state_locking"
|
||||
CONF_STATE_UNLOCKED = "state_unlocked"
|
||||
CONF_STATE_UNLOCKING = "state_unlocking"
|
||||
CONF_STATE_JAMMED = "state_jammed"
|
||||
|
||||
DEFAULT_NAME = "MQTT Lock"
|
||||
DEFAULT_PAYLOAD_LOCK = "LOCK"
|
||||
@ -52,6 +53,7 @@ DEFAULT_STATE_LOCKED = "LOCKED"
|
||||
DEFAULT_STATE_LOCKING = "LOCKING"
|
||||
DEFAULT_STATE_UNLOCKED = "UNLOCKED"
|
||||
DEFAULT_STATE_UNLOCKING = "UNLOCKING"
|
||||
DEFAULT_STATE_JAMMED = "JAMMED"
|
||||
|
||||
MQTT_LOCK_ATTRIBUTES_BLOCKED = frozenset(
|
||||
{
|
||||
@ -66,6 +68,7 @@ PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend(
|
||||
vol.Optional(CONF_PAYLOAD_LOCK, default=DEFAULT_PAYLOAD_LOCK): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_UNLOCK, default=DEFAULT_PAYLOAD_UNLOCK): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_OPEN): 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_LOCKING, default=DEFAULT_STATE_LOCKING): cv.string,
|
||||
vol.Optional(CONF_STATE_UNLOCKED, default=DEFAULT_STATE_UNLOCKED): cv.string,
|
||||
@ -83,6 +86,7 @@ PLATFORM_SCHEMA = vol.All(
|
||||
DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA)
|
||||
|
||||
STATE_CONFIG_KEYS = [
|
||||
CONF_STATE_JAMMED,
|
||||
CONF_STATE_LOCKED,
|
||||
CONF_STATE_LOCKING,
|
||||
CONF_STATE_UNLOCKED,
|
||||
@ -157,15 +161,20 @@ class MqttLock(MqttEntity, LockEntity):
|
||||
def _prepare_subscribe_topics(self) -> None:
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
||||
topics: dict[str, dict[str, Any]] = {}
|
||||
qos: int = self._config[CONF_QOS]
|
||||
encoding: str | None = self._config[CONF_ENCODING] or None
|
||||
|
||||
@callback
|
||||
@log_messages(self.hass, self.entity_id)
|
||||
def message_received(msg: ReceiveMessage) -> None:
|
||||
"""Handle new MQTT messages."""
|
||||
"""Handle new lock state messages."""
|
||||
payload = self._value_template(msg.payload)
|
||||
if payload in self._valid_states:
|
||||
self._attr_is_locked = payload == self._config[CONF_STATE_LOCKED]
|
||||
self._attr_is_locking = payload == self._config[CONF_STATE_LOCKING]
|
||||
self._attr_is_unlocking = payload == self._config[CONF_STATE_UNLOCKING]
|
||||
self._attr_is_jammed = payload == self._config[CONF_STATE_JAMMED]
|
||||
|
||||
get_mqtt_data(self.hass).state_write_requests.write_state_request(self)
|
||||
|
||||
@ -173,18 +182,18 @@ class MqttLock(MqttEntity, LockEntity):
|
||||
# Force into optimistic mode.
|
||||
self._optimistic = True
|
||||
else:
|
||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||
self.hass,
|
||||
self._sub_state,
|
||||
{
|
||||
"state_topic": {
|
||||
"topic": self._config.get(CONF_STATE_TOPIC),
|
||||
"msg_callback": message_received,
|
||||
"qos": self._config[CONF_QOS],
|
||||
"encoding": self._config[CONF_ENCODING] or None,
|
||||
}
|
||||
},
|
||||
)
|
||||
topics[CONF_STATE_TOPIC] = {
|
||||
"topic": self._config.get(CONF_STATE_TOPIC),
|
||||
"msg_callback": message_received,
|
||||
CONF_QOS: qos,
|
||||
CONF_ENCODING: encoding,
|
||||
}
|
||||
|
||||
self._sub_state = subscription.async_prepare_subscribe_topics(
|
||||
self.hass,
|
||||
self._sub_state,
|
||||
topics,
|
||||
)
|
||||
|
||||
async def _subscribe_topics(self) -> None:
|
||||
"""(Re)Subscribe to topics."""
|
||||
|
@ -8,6 +8,7 @@ from homeassistant.components.lock import (
|
||||
SERVICE_LOCK,
|
||||
SERVICE_OPEN,
|
||||
SERVICE_UNLOCK,
|
||||
STATE_JAMMED,
|
||||
STATE_LOCKED,
|
||||
STATE_LOCKING,
|
||||
STATE_UNLOCKED,
|
||||
@ -21,6 +22,7 @@ from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .test_common import (
|
||||
@ -468,6 +470,112 @@ async def test_sending_mqtt_commands_support_open_and_explicit_optimistic(
|
||||
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||
|
||||
|
||||
async def test_sending_mqtt_commands_pessimistic(
|
||||
hass: HomeAssistant, mqtt_mock_entry_with_yaml_config
|
||||
) -> None:
|
||||
"""Test function of the lock with state topics."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
mqtt.DOMAIN,
|
||||
{
|
||||
mqtt.DOMAIN: {
|
||||
lock.DOMAIN: {
|
||||
"name": "test",
|
||||
"command_topic": "command-topic",
|
||||
"state_topic": "state-topic",
|
||||
"payload_lock": "LOCK",
|
||||
"payload_unlock": "UNLOCK",
|
||||
"payload_open": "OPEN",
|
||||
"state_locked": "LOCKED",
|
||||
"state_locking": "LOCKING",
|
||||
"state_unlocked": "UNLOCKED",
|
||||
"state_unlocking": "UNLOCKING",
|
||||
"state_jammed": "JAMMED",
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock = await mqtt_mock_entry_with_yaml_config()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_UNLOCKED
|
||||
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == LockEntityFeature.OPEN
|
||||
|
||||
# send lock command to lock
|
||||
await hass.services.async_call(
|
||||
lock.DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
|
||||
)
|
||||
|
||||
mqtt_mock.async_publish.assert_called_once_with("command-topic", "LOCK", 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# receive state from lock
|
||||
async_fire_mqtt_message(hass, "state-topic", "LOCKED")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_LOCKED
|
||||
|
||||
await hass.services.async_call(
|
||||
lock.DOMAIN, SERVICE_UNLOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
|
||||
)
|
||||
|
||||
mqtt_mock.async_publish.assert_called_once_with("command-topic", "UNLOCK", 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# receive state from lock
|
||||
async_fire_mqtt_message(hass, "state-topic", "UNLOCKED")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_UNLOCKED
|
||||
|
||||
await hass.services.async_call(
|
||||
lock.DOMAIN, SERVICE_OPEN, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
|
||||
)
|
||||
|
||||
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# receive state from lock
|
||||
async_fire_mqtt_message(hass, "state-topic", "UNLOCKED")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_UNLOCKED
|
||||
|
||||
# send lock command to lock
|
||||
await hass.services.async_call(
|
||||
lock.DOMAIN, SERVICE_LOCK, {ATTR_ENTITY_ID: "lock.test"}, blocking=True
|
||||
)
|
||||
|
||||
# Go to locking state
|
||||
mqtt_mock.async_publish.assert_called_once_with("command-topic", "LOCK", 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# receive locking state from lock
|
||||
async_fire_mqtt_message(hass, "state-topic", "LOCKING")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_LOCKING
|
||||
|
||||
# receive jammed state from lock
|
||||
async_fire_mqtt_message(hass, "state-topic", "JAMMED")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_JAMMED
|
||||
|
||||
# receive solved state from lock
|
||||
async_fire_mqtt_message(hass, "state-topic", "LOCKED")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test")
|
||||
assert state.state is STATE_LOCKED
|
||||
|
||||
|
||||
async def test_availability_when_connection_lost(
|
||||
hass, mqtt_mock_entry_with_yaml_config
|
||||
):
|
||||
|
Loading…
x
Reference in New Issue
Block a user