Notify users when zwave device gets reset (#101362)

* Notify users when zwave device gets reset

* review comments
This commit is contained in:
Raman Gupta 2023-10-04 04:18:48 -04:00 committed by GitHub
parent cd175f679f
commit d3c5b9777b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 14 deletions

View file

@ -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,
( (

View file

@ -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 ""

View file

@ -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"]