Notify users when zwave device gets reset (#101362)
* Notify users when zwave device gets reset * review comments
This commit is contained in:
parent
cd175f679f
commit
d3c5b9777b
3 changed files with 99 additions and 14 deletions
|
@ -113,6 +113,7 @@ from .helpers import (
|
||||||
async_enable_statistics,
|
async_enable_statistics,
|
||||||
get_device_id,
|
get_device_id,
|
||||||
get_device_id_ext,
|
get_device_id_ext,
|
||||||
|
get_network_identifier_for_notification,
|
||||||
get_unique_id,
|
get_unique_id,
|
||||||
get_valueless_base_unique_id,
|
get_valueless_base_unique_id,
|
||||||
)
|
)
|
||||||
|
@ -448,6 +449,28 @@ class ControllerEvents:
|
||||||
"remove_entity"
|
"remove_entity"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
elif reason == RemoveNodeReason.RESET:
|
||||||
|
device_name = device.name_by_user or device.name or f"Node {node.node_id}"
|
||||||
|
identifier = get_network_identifier_for_notification(
|
||||||
|
self.hass, self.config_entry, self.driver_events.driver.controller
|
||||||
|
)
|
||||||
|
notification_msg = (
|
||||||
|
f"`{device_name}` has been factory reset "
|
||||||
|
"and removed from the Z-Wave network"
|
||||||
|
)
|
||||||
|
if identifier:
|
||||||
|
# Remove trailing comma if it's there
|
||||||
|
if identifier[-1] == ",":
|
||||||
|
identifier = identifier[:-1]
|
||||||
|
notification_msg = f"{notification_msg} {identifier}."
|
||||||
|
else:
|
||||||
|
notification_msg = f"{notification_msg}."
|
||||||
|
async_create(
|
||||||
|
self.hass,
|
||||||
|
notification_msg,
|
||||||
|
"Device Was Factory Reset!",
|
||||||
|
f"{DOMAIN}.node_reset_and_removed.{dev_id[1]}",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.remove_device(device)
|
self.remove_device(device)
|
||||||
|
|
||||||
|
@ -459,21 +482,12 @@ class ControllerEvents:
|
||||||
dev_id = get_device_id(self.driver_events.driver, node)
|
dev_id = get_device_id(self.driver_events.driver, node)
|
||||||
device = self.dev_reg.async_get_device(identifiers={dev_id})
|
device = self.dev_reg.async_get_device(identifiers={dev_id})
|
||||||
assert device
|
assert device
|
||||||
device_name = device.name_by_user or device.name
|
device_name = device.name_by_user or device.name or f"Node {node.node_id}"
|
||||||
home_id = self.driver_events.driver.controller.home_id
|
|
||||||
# We do this because we know at this point the controller has its home ID as
|
|
||||||
# as it is part of the device ID
|
|
||||||
assert home_id
|
|
||||||
# In case the user has multiple networks, we should give them more information
|
# In case the user has multiple networks, we should give them more information
|
||||||
# about the network for the controller being identified.
|
# about the network for the controller being identified.
|
||||||
identifier = ""
|
identifier = get_network_identifier_for_notification(
|
||||||
if len(self.hass.config_entries.async_entries(DOMAIN)) > 1:
|
self.hass, self.config_entry, self.driver_events.driver.controller
|
||||||
if str(home_id) != self.config_entry.title:
|
|
||||||
identifier = (
|
|
||||||
f"`{self.config_entry.title}`, with the home ID `{home_id}`, "
|
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
identifier = f"with the home ID `{home_id}` "
|
|
||||||
async_create(
|
async_create(
|
||||||
self.hass,
|
self.hass,
|
||||||
(
|
(
|
||||||
|
|
|
@ -14,6 +14,7 @@ from zwave_js_server.const import (
|
||||||
ConfigurationValueType,
|
ConfigurationValueType,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
)
|
)
|
||||||
|
from zwave_js_server.model.controller import Controller
|
||||||
from zwave_js_server.model.driver import Driver
|
from zwave_js_server.model.driver import Driver
|
||||||
from zwave_js_server.model.log_config import LogConfig
|
from zwave_js_server.model.log_config import LogConfig
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
|
@ -512,3 +513,15 @@ def get_device_info(driver: Driver, node: ZwaveNode) -> DeviceInfo:
|
||||||
manufacturer=node.device_config.manufacturer,
|
manufacturer=node.device_config.manufacturer,
|
||||||
suggested_area=node.location if node.location else None,
|
suggested_area=node.location if node.location else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_network_identifier_for_notification(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry, controller: Controller
|
||||||
|
) -> str:
|
||||||
|
"""Return the network identifier string for persistent notifications."""
|
||||||
|
home_id = str(controller.home_id)
|
||||||
|
if len(hass.config_entries.async_entries(DOMAIN)) > 1:
|
||||||
|
if str(home_id) != config_entry.title:
|
||||||
|
return f"`{config_entry.title}`, with the home ID `{home_id}`,"
|
||||||
|
return f"with the home ID `{home_id}`"
|
||||||
|
return ""
|
||||||
|
|
|
@ -1644,3 +1644,61 @@ async def test_server_logging(hass: HomeAssistant, client) -> None:
|
||||||
assert len(client.async_send_command.call_args_list) == 0
|
assert len(client.async_send_command.call_args_list) == 0
|
||||||
assert not client.enable_server_logging.called
|
assert not client.enable_server_logging.called
|
||||||
assert not client.disable_server_logging.called
|
assert not client.disable_server_logging.called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_factory_reset_node(
|
||||||
|
hass: HomeAssistant, client, multisensor_6, multisensor_6_state, integration
|
||||||
|
) -> None:
|
||||||
|
"""Test when a node is removed because it was reset."""
|
||||||
|
# One config entry scenario
|
||||||
|
remove_event = Event(
|
||||||
|
type="node removed",
|
||||||
|
data={
|
||||||
|
"source": "controller",
|
||||||
|
"event": "node removed",
|
||||||
|
"reason": 5,
|
||||||
|
"node": deepcopy(multisensor_6_state),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
dev_id = get_device_id(client.driver, multisensor_6)
|
||||||
|
msg_id = f"{DOMAIN}.node_reset_and_removed.{dev_id[1]}"
|
||||||
|
|
||||||
|
client.driver.controller.receive_event(remove_event)
|
||||||
|
notifications = async_get_persistent_notifications(hass)
|
||||||
|
assert len(notifications) == 1
|
||||||
|
assert list(notifications)[0] == msg_id
|
||||||
|
assert notifications[msg_id]["message"].startswith("`Multisensor 6`")
|
||||||
|
assert "with the home ID" not in notifications[msg_id]["message"]
|
||||||
|
async_dismiss(hass, msg_id)
|
||||||
|
|
||||||
|
# Add mock config entry to simulate having multiple entries
|
||||||
|
new_entry = MockConfigEntry(domain=DOMAIN)
|
||||||
|
new_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
# Re-add the node then remove it again
|
||||||
|
client.driver.controller.nodes[multisensor_6_state["nodeId"]] = Node(
|
||||||
|
client, deepcopy(multisensor_6_state)
|
||||||
|
)
|
||||||
|
remove_event.data["node"] = deepcopy(multisensor_6_state)
|
||||||
|
client.driver.controller.receive_event(remove_event)
|
||||||
|
# Test case where config entry title and home ID don't match
|
||||||
|
notifications = async_get_persistent_notifications(hass)
|
||||||
|
assert len(notifications) == 1
|
||||||
|
assert list(notifications)[0] == msg_id
|
||||||
|
assert (
|
||||||
|
"network `Mock Title`, with the home ID `3245146787`."
|
||||||
|
in notifications[msg_id]["message"]
|
||||||
|
)
|
||||||
|
async_dismiss(hass, msg_id)
|
||||||
|
|
||||||
|
# Test case where config entry title and home ID do match
|
||||||
|
hass.config_entries.async_update_entry(integration, title="3245146787")
|
||||||
|
client.driver.controller.nodes[multisensor_6_state["nodeId"]] = Node(
|
||||||
|
client, deepcopy(multisensor_6_state)
|
||||||
|
)
|
||||||
|
remove_event.data["node"] = deepcopy(multisensor_6_state)
|
||||||
|
client.driver.controller.receive_event(remove_event)
|
||||||
|
notifications = async_get_persistent_notifications(hass)
|
||||||
|
assert len(notifications) == 1
|
||||||
|
assert list(notifications)[0] == msg_id
|
||||||
|
assert "network with the home ID `3245146787`" in notifications[msg_id]["message"]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue