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
Add MQTT WS command to remove device #31989
Conversation
Hey there @home-assistant/core, mind taking a look at this pull request as its been labeled with a integration ( |
@@ -1247,6 +1265,18 @@ def device_info(self): | |||
return device_info_from_config(self._device_config) | |||
|
|||
|
|||
@websocket_api.websocket_command( | |||
{vol.Required("type"): "mqtt/device/remove", vol.Required("device_id"): str} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's nothing specific to MQTT in the implementation, should this maybe be moved elsewhere?
async def async_removed_from_registry(self) -> None: | ||
"""Clear retained discovery topic in broker.""" | ||
async_publish( | ||
self.hass, self._discovery_topic, "", retain=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will also cause the state to be removed from the state machine.
@@ -534,6 +540,9 @@ def async_on_remove(self, func: CALLBACK_TYPE) -> None: | |||
async def _async_registry_updated(self, event): | |||
"""Handle entity registry update.""" | |||
data = event.data | |||
if data["action"] == "remove" and data["entity_id"] == self.entity_id: | |||
await self.async_removed_from_registry() | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could call self.async_remove()
here as well to remove the state from the state machine, but that should probably be done in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So can you document/explain to me how delete changes cascade throughout the MQTT system?
These two things can happen:
- A user indicates that an MQTT device needs to be removed
- An empty discovery configuration is pushed to a topic that we previously got a configuration from.
These two cases need to be handled and we should document this in the MQTT integration somewhere how this works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creation / Discovery of MQTT entities / devices by MQTT:
Handled by https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/mqtt/discovery.py
When receiving a discovery message, a state is created in the Hass state machine, or updated if it already exists.
(In case of an MQTT device trigger, no state is created).
If unique_id
is set in the config data, an entity registry entry will be created, or updated if it already exists.
If device
dict is set in the config data, a device registry will be created, or updated if it already exists.
Discovered MQTT enitities will listen for discovery updates - which must be sent on the same MQTT topic - with help of the MqttDiscoveryUpdated mixin:
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/__init__.py#L1159-L1160
Update of MQTT entities / devices by MQTT:
Handled by the MqttDiscoveryUpdated mixin:
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/__init__.py#L1187-L1190
Removal of MQTT entities / devices by MQTT:
If an empty discovery message is received, this will result in:
- Removal of the state from the state machine
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/__init__.py#L1182-L1184 - Entity registry entry will NOT be removed
This should maybe be changed such that entity registry entries are removed? - Device registry entry will NOT be removed
This should maybe be changed such that device registry entries are removed?
Removal of MQTT entities / devices by WS:
A WS helper exists to fully remove an MQTT device, which will result in:
- Removal of the states from the state machine (as a result of clearing the discovery topics)
- Entity registry entries related to the device will be removed
- Device registry entry will be removed, including MQTT device triggers
- Any retained MQTT discovery topics for entities are cleared from the broker
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/__init__.py#L1199-L1204 - Any retained MQTT discovery topics for triggers are cleared from the broker
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/device_trigger.py#L233-L241
self._discovery_hash = ( | ||
discovery_data[ATTR_DISCOVERY_HASH] if discovery_data else None | ||
) | ||
self._discovery_topic = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why keep two variables around if you can just store discovery_data
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, cleaned up.
{vol.Required("type"): "mqtt/device/remove", vol.Required("device_id"): str} | ||
) | ||
@websocket_api.async_response | ||
async def websocket_remove_device(hass, connection, msg): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also remove the retained discovery config from MQTT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done:
- Any retained MQTT discovery topics for entities are cleared from the broker
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/__init__.py#L1199-L1204 - Any retained MQTT discovery topics for triggers are cleared from the broker
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/device_trigger.py#L233-L241
"""Delete device.""" | ||
device_id = msg["device_id"] | ||
dev_registry = await get_dev_reg(hass) | ||
dev_registry.async_remove_device(device_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can raise a KeyError
. We should also only delete devices that are part of MQTT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine if it throws, this will result in an error response to the WS call?
Added check to only delete devices that are part of MQTT:
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/homeassistant/components/mqtt/__init__.py#L1277-L1279
@@ -98,15 +98,14 @@ | |||
|
|||
async def async_discover(discovery_payload): | |||
"""Discover and add an MQTT alarm control panel.""" | |||
discovery_data = discovery_payload.__discovery_data__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there double underscores before/after the name ? Those are reserved for system values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add _discovery_data_
. But why not make it _discovery_data_
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, OK, changed to discovery_data
.
if config_entry.domain == DOMAIN: | ||
dev_registry.async_remove_device(device_id) | ||
connection.send_message(websocket_api.result_message(msg["id"])) | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not sending an error if you can't find the device .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but isn't it enough to not send success?
Trying to remove a non existing device is tested in this test case:
https://github.com/home-assistant/home-assistant/blob/e170a11547b716de8cfe46a99473f4dbfcb7f62b/tests/components/mqtt/test_init.py#L881
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. This will cause the test to never finish if you're awaiting a response.
Codecov Report
@@ Coverage Diff @@
## dev #31989 +/- ##
=======================================
Coverage 94.69% 94.70%
=======================================
Files 766 766
Lines 55567 55613 +46
=======================================
+ Hits 52619 52667 +48
+ Misses 2948 2946 -2
Continue to review full report at Codecov.
|
Proposed change
Add MQTT WS command to remove a device from device registry.
When removing the device:
Possible extensions (in this or separate follow-up PRs):
Type of change
Checklist
black --fast homeassistant tests
)The integration reached or maintains the following Integration Quality Scale: