mirror of
https://github.com/Electric-Special/ha-core.git
synced 2026-03-21 05:06:13 +01:00
Display Z-Wave home IDs as hexadecimal (#161624)
This commit is contained in:
@@ -71,6 +71,7 @@ from .const import (
|
||||
ATTR_EVENT_TYPE,
|
||||
ATTR_EVENT_TYPE_LABEL,
|
||||
ATTR_HOME_ID,
|
||||
ATTR_HOME_ID_HEX,
|
||||
ATTR_LABEL,
|
||||
ATTR_NODE_ID,
|
||||
ATTR_PARAMETERS,
|
||||
@@ -123,6 +124,7 @@ from .helpers import (
|
||||
async_disable_server_logging_if_needed,
|
||||
async_enable_server_logging_if_needed,
|
||||
async_enable_statistics,
|
||||
format_home_id_for_display,
|
||||
get_device_id,
|
||||
get_device_id_ext,
|
||||
get_network_identifier_for_notification,
|
||||
@@ -955,6 +957,7 @@ class NodeEvents:
|
||||
ATTR_DOMAIN: DOMAIN,
|
||||
ATTR_NODE_ID: notification.node.node_id,
|
||||
ATTR_HOME_ID: driver.controller.home_id,
|
||||
ATTR_HOME_ID_HEX: format_home_id_for_display(driver.controller.home_id),
|
||||
ATTR_ENDPOINT: notification.endpoint,
|
||||
ATTR_DEVICE_ID: device.id,
|
||||
ATTR_COMMAND_CLASS: notification.command_class,
|
||||
@@ -992,6 +995,7 @@ class NodeEvents:
|
||||
ATTR_DOMAIN: DOMAIN,
|
||||
ATTR_NODE_ID: notification.node.node_id,
|
||||
ATTR_HOME_ID: driver.controller.home_id,
|
||||
ATTR_HOME_ID_HEX: format_home_id_for_display(driver.controller.home_id),
|
||||
ATTR_ENDPOINT: notification.endpoint_idx,
|
||||
ATTR_DEVICE_ID: device.id,
|
||||
ATTR_COMMAND_CLASS: notification.command_class,
|
||||
@@ -1077,6 +1081,7 @@ class NodeEvents:
|
||||
{
|
||||
ATTR_NODE_ID: value.node.node_id,
|
||||
ATTR_HOME_ID: driver.controller.home_id,
|
||||
ATTR_HOME_ID_HEX: format_home_id_for_display(driver.controller.home_id),
|
||||
ATTR_DEVICE_ID: device.id,
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_COMMAND_CLASS: value.command_class,
|
||||
|
||||
@@ -73,6 +73,7 @@ from .helpers import (
|
||||
CannotConnect,
|
||||
async_get_version_info,
|
||||
async_wait_for_driver_ready_event,
|
||||
format_home_id_for_display,
|
||||
)
|
||||
from .models import ZwaveJSConfigEntry
|
||||
|
||||
@@ -467,7 +468,8 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
await self.async_set_unique_id(home_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
self.ws_address = f"ws://{discovery_info.host}:{discovery_info.port}"
|
||||
self.context.update({"title_placeholders": {CONF_NAME: home_id}})
|
||||
home_id_display = format_home_id_for_display(int(home_id))
|
||||
self.context.update({"title_placeholders": {CONF_NAME: home_id_display}})
|
||||
return await self.async_step_zeroconf_confirm()
|
||||
|
||||
async def async_step_zeroconf_confirm(
|
||||
@@ -479,10 +481,11 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
assert self.ws_address
|
||||
assert self.unique_id
|
||||
home_id_display = format_home_id_for_display(int(self.unique_id))
|
||||
return self.async_show_form(
|
||||
step_id="zeroconf_confirm",
|
||||
description_placeholders={
|
||||
"home_id": self.unique_id,
|
||||
"home_id": home_id_display,
|
||||
CONF_URL: self.ws_address[5:],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -57,6 +57,7 @@ ZWAVE_JS_NOTIFICATION_EVENT = f"{DOMAIN}_notification"
|
||||
ZWAVE_JS_VALUE_UPDATED_EVENT = f"{DOMAIN}_value_updated"
|
||||
ATTR_NODE_ID = "node_id"
|
||||
ATTR_HOME_ID = "home_id"
|
||||
ATTR_HOME_ID_HEX = "home_id_hex"
|
||||
ATTR_ENDPOINT = "endpoint"
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_VALUE = "value"
|
||||
|
||||
@@ -184,6 +184,13 @@ async def async_disable_server_logging_if_needed(
|
||||
LOGGER.info("Zwave-js-server logging is enabled")
|
||||
|
||||
|
||||
def format_home_id_for_display(home_id: int | None) -> str:
|
||||
"""Format home ID as hexadecimal string for display."""
|
||||
if home_id is None:
|
||||
return "Unknown"
|
||||
return f"0x{home_id:08x}"
|
||||
|
||||
|
||||
def get_valueless_base_unique_id(driver: Driver, node: ZwaveNode) -> str:
|
||||
"""Return the base unique ID for an entity that is not based on a value."""
|
||||
return f"{driver.controller.home_id}.{node.node_id}"
|
||||
@@ -555,9 +562,9 @@ def get_network_identifier_for_notification(
|
||||
hass: HomeAssistant, config_entry: ZwaveJSConfigEntry, controller: Controller
|
||||
) -> str:
|
||||
"""Return the network identifier string for persistent notifications."""
|
||||
home_id = str(controller.home_id)
|
||||
home_id = format_home_id_for_display(controller.home_id)
|
||||
if len(hass.config_entries.async_entries(DOMAIN)) > 1:
|
||||
if str(home_id) != config_entry.title:
|
||||
if 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 ""
|
||||
|
||||
@@ -8,7 +8,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from .const import DOMAIN
|
||||
from .helpers import async_get_node_from_device_id
|
||||
from .helpers import async_get_node_from_device_id, format_home_id_for_display
|
||||
|
||||
|
||||
class DeviceConfigFileChangedFlow(RepairsFlow):
|
||||
@@ -62,13 +62,26 @@ class MigrateUniqueIDFlow(RepairsFlow):
|
||||
|
||||
def __init__(self, data: dict[str, str]) -> None:
|
||||
"""Initialize."""
|
||||
# Format IDs for display
|
||||
|
||||
try:
|
||||
new_unique_id_hex = format_home_id_for_display(int(data["new_unique_id"]))
|
||||
except (ValueError, TypeError):
|
||||
new_unique_id_hex = data["new_unique_id"]
|
||||
|
||||
try:
|
||||
old_unique_id_hex = format_home_id_for_display(int(data["old_unique_id"]))
|
||||
except (ValueError, TypeError):
|
||||
old_unique_id_hex = data["old_unique_id"]
|
||||
|
||||
self.description_placeholders: dict[str, str] = {
|
||||
"config_entry_title": data["config_entry_title"],
|
||||
"controller_model": data["controller_model"],
|
||||
"new_unique_id": data["new_unique_id"],
|
||||
"old_unique_id": data["old_unique_id"],
|
||||
"new_unique_id": new_unique_id_hex,
|
||||
"old_unique_id": old_unique_id_hex,
|
||||
}
|
||||
self._config_entry_id: str = data["config_entry_id"]
|
||||
self._new_unique_id: str = data["new_unique_id"]
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
@@ -88,7 +101,7 @@ class MigrateUniqueIDFlow(RepairsFlow):
|
||||
if config_entry is not None:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
unique_id=self.description_placeholders["new_unique_id"],
|
||||
unique_id=self._new_unique_id,
|
||||
)
|
||||
self.hass.config_entries.async_schedule_reload(config_entry.entry_id)
|
||||
return self.async_create_entry(data={})
|
||||
|
||||
@@ -12,6 +12,7 @@ from homeassistant.components.zwave_js.helpers import (
|
||||
async_get_node_status_sensor_entity_id,
|
||||
async_get_nodes_from_area_id,
|
||||
async_get_provisioning_entry_from_device_id,
|
||||
format_home_id_for_display,
|
||||
get_value_state_schema,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
@@ -138,3 +139,18 @@ async def test_async_get_provisioning_entry_from_device_id(
|
||||
):
|
||||
result = await async_get_provisioning_entry_from_device_id(hass, device.id)
|
||||
assert result == provisioning_entry
|
||||
|
||||
|
||||
def test_format_home_id_for_display() -> None:
|
||||
"""Test format_home_id_for_display."""
|
||||
# Test with standard home ID
|
||||
assert format_home_id_for_display(3245146787) == "0xc16d02a3"
|
||||
|
||||
# Test with zero
|
||||
assert format_home_id_for_display(0) == "0x00000000"
|
||||
|
||||
# Test with max 32-bit value
|
||||
assert format_home_id_for_display(4294967295) == "0xffffffff"
|
||||
|
||||
# Test with None
|
||||
assert format_home_id_for_display(None) == "Unknown"
|
||||
|
||||
@@ -2077,18 +2077,18 @@ async def test_identify_event(
|
||||
assert len(notifications) == 1
|
||||
assert list(notifications)[0] == msg_id
|
||||
assert (
|
||||
"network `Mock Title`, with the home ID `3245146787`"
|
||||
"network `Mock Title`, with the home ID `0xc16d02a3`"
|
||||
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")
|
||||
hass.config_entries.async_update_entry(integration, title="0xc16d02a3")
|
||||
client.driver.controller.receive_event(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"]
|
||||
assert "network with the home ID `0xc16d02a3`" in notifications[msg_id]["message"]
|
||||
|
||||
|
||||
async def test_server_logging(
|
||||
@@ -2241,13 +2241,13 @@ async def test_factory_reset_node(
|
||||
assert len(notifications) == 1
|
||||
assert list(notifications)[0] == msg_id
|
||||
assert (
|
||||
"network `Mock Title`, with the home ID `3245146787`"
|
||||
"network `Mock Title`, with the home ID `0xc16d02a3`"
|
||||
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")
|
||||
hass.config_entries.async_update_entry(integration, title="0xc16d02a3")
|
||||
add_event = Event(
|
||||
type="node added",
|
||||
data={
|
||||
@@ -2264,7 +2264,7 @@ async def test_factory_reset_node(
|
||||
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"]
|
||||
assert "network with the home ID `0xc16d02a3`" in notifications[msg_id]["message"]
|
||||
|
||||
|
||||
async def test_entity_available_when_node_dead(
|
||||
|
||||
@@ -379,8 +379,8 @@ async def test_migrate_unique_id(
|
||||
assert data["description_placeholders"] == {
|
||||
"config_entry_title": "Z-Wave JS",
|
||||
"controller_model": "ZW090",
|
||||
"new_unique_id": "3245146787",
|
||||
"old_unique_id": old_unique_id,
|
||||
"new_unique_id": "0xc16d02a3",
|
||||
"old_unique_id": "0x075bcd15",
|
||||
}
|
||||
|
||||
# Apply fix
|
||||
@@ -446,8 +446,8 @@ async def test_migrate_unique_id_missing_config_entry(
|
||||
assert data["description_placeholders"] == {
|
||||
"config_entry_title": "Z-Wave JS",
|
||||
"controller_model": "ZW090",
|
||||
"new_unique_id": "3245146787",
|
||||
"old_unique_id": old_unique_id,
|
||||
"new_unique_id": "0xc16d02a3",
|
||||
"old_unique_id": "0x075bcd15",
|
||||
}
|
||||
|
||||
# Apply fix
|
||||
@@ -459,3 +459,78 @@ async def test_migrate_unique_id_missing_config_entry(
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("client")
|
||||
async def test_migrate_unique_id_non_integer_ids(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test the migrate unique id flow with non-integer unique IDs."""
|
||||
old_unique_id = "non_numeric_id"
|
||||
new_unique_id = "also_invalid"
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Z-Wave JS",
|
||||
data={
|
||||
"url": "ws://test.org",
|
||||
},
|
||||
unique_id=old_unique_id,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
# Manually create the repair issue with non-integer unique IDs
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"migrate_unique_id.{config_entry.entry_id}",
|
||||
data={
|
||||
"config_entry_id": config_entry.entry_id,
|
||||
"config_entry_title": "Z-Wave JS",
|
||||
"controller_model": "ZW090",
|
||||
"new_unique_id": new_unique_id,
|
||||
"old_unique_id": old_unique_id,
|
||||
},
|
||||
is_fixable=True,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="migrate_unique_id",
|
||||
)
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
http_client = await hass_client()
|
||||
|
||||
# Assert the issue is present
|
||||
await ws_client.send_json_auto_id({"type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 1
|
||||
issue = msg["result"]["issues"][0]
|
||||
issue_id = issue["issue_id"]
|
||||
assert issue_id == f"migrate_unique_id.{config_entry.entry_id}"
|
||||
|
||||
data = await start_repair_fix_flow(http_client, DOMAIN, issue_id)
|
||||
|
||||
flow_id = data["flow_id"]
|
||||
assert data["step_id"] == "confirm"
|
||||
# The non-integer IDs should be displayed as-is
|
||||
assert data["description_placeholders"] == {
|
||||
"config_entry_title": "Z-Wave JS",
|
||||
"controller_model": "ZW090",
|
||||
"new_unique_id": new_unique_id,
|
||||
"old_unique_id": old_unique_id,
|
||||
}
|
||||
|
||||
# Apply fix
|
||||
data = await process_repair_fix_flow(http_client, flow_id)
|
||||
|
||||
assert data["type"] == "create_entry"
|
||||
assert config_entry.unique_id == new_unique_id
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 0
|
||||
|
||||
Reference in New Issue
Block a user