Home Assistant Unofficial Reference 2024.12.1
network_settings_inconsistent.py
Go to the documentation of this file.
1 """ZHA repair for inconsistent network settings."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from zigpy.backups import NetworkBackup
9 
10 from homeassistant.components.repairs import RepairsFlow
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.core import HomeAssistant
13 from homeassistant.data_entry_flow import FlowResult
14 from homeassistant.helpers import issue_registry as ir
15 
16 from ..const import DOMAIN
17 from ..radio_manager import ZhaRadioManager
18 
19 _LOGGER = logging.getLogger(__name__)
20 
21 ISSUE_INCONSISTENT_NETWORK_SETTINGS = "inconsistent_network_settings"
22 
23 
24 def _format_settings_diff(old_state: NetworkBackup, new_state: NetworkBackup) -> str:
25  """Format the difference between two network backups."""
26  lines: list[str] = []
27 
28  def _add_difference(
29  lines: list[str], text: str, old: Any, new: Any, pre: bool = True
30  ) -> None:
31  """Add a line to the list if the values are different."""
32  wrap = "`" if pre else ""
33 
34  if old != new:
35  lines.append(f"{text}: {wrap}{old}{wrap} \u2192 {wrap}{new}{wrap}")
36 
37  _add_difference(
38  lines,
39  "Channel",
40  old=old_state.network_info.channel,
41  new=new_state.network_info.channel,
42  pre=False,
43  )
44  _add_difference(
45  lines,
46  "Node IEEE",
47  old=old_state.node_info.ieee,
48  new=new_state.node_info.ieee,
49  )
50  _add_difference(
51  lines,
52  "PAN ID",
53  old=old_state.network_info.pan_id,
54  new=new_state.network_info.pan_id,
55  )
56  _add_difference(
57  lines,
58  "Extended PAN ID",
59  old=old_state.network_info.extended_pan_id,
60  new=new_state.network_info.extended_pan_id,
61  )
62  _add_difference(
63  lines,
64  "NWK update ID",
65  old=old_state.network_info.nwk_update_id,
66  new=new_state.network_info.nwk_update_id,
67  pre=False,
68  )
69  _add_difference(
70  lines,
71  "TC Link Key",
72  old=old_state.network_info.tc_link_key.key,
73  new=new_state.network_info.tc_link_key.key,
74  )
75  _add_difference(
76  lines,
77  "Network Key",
78  old=old_state.network_info.network_key.key,
79  new=new_state.network_info.network_key.key,
80  )
81 
82  return "\n".join([f"- {line}" for line in lines])
83 
84 
86  hass: HomeAssistant,
87  config_entry: ConfigEntry,
88  old_state: NetworkBackup,
89  new_state: NetworkBackup,
90 ) -> None:
91  """Create a repair if the network settings are inconsistent with the last backup."""
92 
93  ir.async_create_issue(
94  hass,
95  domain=DOMAIN,
96  issue_id=ISSUE_INCONSISTENT_NETWORK_SETTINGS,
97  is_fixable=True,
98  severity=ir.IssueSeverity.ERROR,
99  translation_key=ISSUE_INCONSISTENT_NETWORK_SETTINGS,
100  data={
101  "config_entry_id": config_entry.entry_id,
102  "old_state": old_state.as_dict(),
103  "new_state": new_state.as_dict(),
104  },
105  )
106 
107 
109  """Handler for an issue fixing flow."""
110 
111  def __init__(self, hass: HomeAssistant, data: dict[str, Any]) -> None:
112  """Initialize the flow."""
113  self.hasshass = hass
114  self._old_state_old_state = NetworkBackup.from_dict(data["old_state"])
115  self._new_state_new_state = NetworkBackup.from_dict(data["new_state"])
116 
117  self._entry_id: str = data["config_entry_id"]
118 
119  config_entry = self.hasshass.config_entries.async_get_entry(self._entry_id)
120  assert config_entry is not None
121  self._radio_mgr_radio_mgr = ZhaRadioManager.from_config_entry(self.hasshass, config_entry)
122 
123  async def async_step_init(
124  self, user_input: dict[str, str] | None = None
125  ) -> FlowResult:
126  """Handle the first step of a fix flow."""
127  return self.async_show_menuasync_show_menu(
128  step_id="init",
129  menu_options=["restore_old_settings", "use_new_settings"],
130  description_placeholders={
131  "diff": _format_settings_diff(self._old_state_old_state, self._new_state_new_state)
132  },
133  )
134 
136  self, user_input: dict[str, str] | None = None
137  ) -> FlowResult:
138  """Step to use the new settings found on the radio."""
139  async with self._radio_mgr_radio_mgr.connect_zigpy_app() as app:
140  app.backups.add_backup(self._new_state_new_state)
141 
142  await self.hasshass.config_entries.async_reload(self._entry_id)
143  return self.async_create_entryasync_create_entry(title="", data={})
144 
146  self, user_input: dict[str, str] | None = None
147  ) -> FlowResult:
148  """Step to restore the most recent backup."""
149  await self._radio_mgr_radio_mgr.restore_backup(self._old_state_old_state)
150 
151  await self.hasshass.config_entries.async_reload(self._entry_id)
152  return self.async_create_entryasync_create_entry(title="", data={})
_FlowResultT async_show_menu(self, *str|None step_id=None, Container[str] menu_options, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
bool restore_backup(str config_dir_path)
None warn_on_inconsistent_network_settings(HomeAssistant hass, ConfigEntry config_entry, NetworkBackup old_state, NetworkBackup new_state)
str _format_settings_diff(NetworkBackup old_state, NetworkBackup new_state)