1 """Switches for AVM Fritz!Box functions."""
3 from __future__
import annotations
23 SWITCH_TYPE_DEFLECTION,
24 SWITCH_TYPE_PORTFORWARD,
26 SWITCH_TYPE_WIFINETWORK,
30 from .coordinator
import (
35 device_filter_out_from_trackers,
37 from .entity
import FritzBoxBaseEntity, FritzDeviceBase
39 _LOGGER = logging.getLogger(__name__)
43 avm_wrapper: AvmWrapper, device_friendly_name: str
44 ) -> list[FritzBoxDeflectionSwitch]:
45 """Get list of deflection entities."""
47 _LOGGER.debug(
"Setting up %s switches", SWITCH_TYPE_DEFLECTION)
49 if not (call_deflections := avm_wrapper.data[
"call_deflections"]):
50 _LOGGER.debug(
"The FRITZ!Box has no %s options", SWITCH_TYPE_DEFLECTION)
55 for cd_id
in call_deflections
60 avm_wrapper: AvmWrapper, device_friendly_name: str, local_ip: str
61 ) -> list[FritzBoxPortSwitch]:
62 """Get list of port forwarding entities."""
64 _LOGGER.debug(
"Setting up %s switches", SWITCH_TYPE_PORTFORWARD)
65 entities_list: list[FritzBoxPortSwitch] = []
66 if not avm_wrapper.device_conn_type:
67 _LOGGER.debug(
"The FRITZ!Box has no %s options", SWITCH_TYPE_PORTFORWARD)
71 resp = await avm_wrapper.async_get_num_port_mapping(avm_wrapper.device_conn_type)
73 _LOGGER.debug(
"The FRITZ!Box has no %s options", SWITCH_TYPE_PORTFORWARD)
76 port_forwards_count: int = resp[
"NewPortMappingNumberOfEntries"]
79 "Specific %s response: GetPortMappingNumberOfEntries=%s",
80 SWITCH_TYPE_PORTFORWARD,
84 _LOGGER.debug(
"IP source for %s is %s", avm_wrapper.host, local_ip)
86 for i
in range(port_forwards_count):
87 portmap = await avm_wrapper.async_get_port_mapping(
88 avm_wrapper.device_conn_type, i
91 _LOGGER.debug(
"The FRITZ!Box has no %s options", SWITCH_TYPE_DEFLECTION)
95 "Specific %s response: GetGenericPortMappingEntry=%s",
96 SWITCH_TYPE_PORTFORWARD,
101 if portmap[
"NewInternalClient"] == local_ip:
102 port_name = portmap[
"NewPortMappingDescription"]
103 for entity
in entities_list:
104 if entity.port_mapping
and (
105 port_name
in entity.port_mapping[
"NewPortMappingDescription"]
107 port_name = f
"{port_name} {portmap['NewExternalPort']}"
108 entities_list.append(
111 device_friendly_name,
115 avm_wrapper.device_conn_type,
123 avm_wrapper: AvmWrapper, device_friendly_name: str
124 ) -> list[FritzBoxWifiSwitch]:
125 """Get list of wifi entities."""
126 _LOGGER.debug(
"Setting up %s switches", SWITCH_TYPE_WIFINETWORK)
134 for s
in avm_wrapper.connection.services
135 if s.startswith(
"WLANConfiguration")
138 _LOGGER.debug(
"WiFi networks count: %s", wifi_count)
140 for i
in range(1, wifi_count + 1):
141 network_info = await avm_wrapper.async_get_wlan_configuration(i)
143 if not (wifi_count == 4
and i == 2):
145 "ssid": network_info[
"NewSSID"],
146 "bssid": network_info[
"NewBSSID"],
147 "standard": network_info[
"NewStandard"],
148 "enabled": network_info[
"NewEnable"],
149 "status": network_info[
"NewStatus"],
151 for i, network
in networks.copy().items():
152 networks[i][
"switch_name"] = network[
"ssid"]
157 for j, n
in networks.items()
163 networks[i][
"switch_name"] += f
" ({WIFI_STANDARD[i]})"
165 _LOGGER.debug(
"WiFi networks list: %s", networks)
168 for index, data
in networks.items()
173 avm_wrapper: AvmWrapper,
174 data_fritz: FritzData,
175 ) -> list[FritzBoxProfileSwitch]:
176 """Add new tracker entities from the AVM device."""
177 _LOGGER.debug(
"Setting up %s switches", SWITCH_TYPE_PROFILE)
179 new_profiles: list[FritzBoxProfileSwitch] = []
181 if "X_AVM-DE_HostFilter1" not in avm_wrapper.connection.services:
184 if avm_wrapper.unique_id
not in data_fritz.profile_switches:
185 data_fritz.profile_switches[avm_wrapper.unique_id] = set()
187 for mac, device
in avm_wrapper.devices.items():
189 mac, device, data_fritz.profile_switches.values()
192 "Skipping profile switch creation for device %s", device.hostname
197 data_fritz.profile_switches[avm_wrapper.unique_id].
add(mac)
199 _LOGGER.debug(
"Creating %s profile switches", len(new_profiles))
204 avm_wrapper: AvmWrapper,
205 device_friendly_name: str,
206 data_fritz: FritzData,
209 """Get a list of all entities."""
211 if avm_wrapper.mesh_role == MeshRoles.SLAVE:
223 hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
226 _LOGGER.debug(
"Setting up switches")
227 avm_wrapper: AvmWrapper = hass.data[DOMAIN][entry.entry_id]
228 data_fritz: FritzData = hass.data[DATA_FRITZ]
230 _LOGGER.debug(
"Fritzbox services: %s", avm_wrapper.connection.services)
243 async
def async_update_avm_device() -> None:
244 """Update the values of the AVM device."""
247 entry.async_on_unload(
249 hass, avm_wrapper.signal_device_new, async_update_avm_device
255 """Fritz switch coordinator base class."""
257 entity_description: SwitchEntityDescription
258 _attr_has_entity_name =
True
262 avm_wrapper: AvmWrapper,
264 description: SwitchEntityDescription,
266 """Init device info class."""
274 """Return the device information."""
276 configuration_url=f
"http://{self.coordinator.host}",
277 connections={(CONNECTION_NETWORK_MAC, self.coordinator.mac)},
278 identifiers={(DOMAIN, self.coordinator.unique_id)},
280 model=self.coordinator.model,
282 sw_version=self.coordinator.current_firmware,
286 def data(self) -> dict[str, Any]:
287 """Return entity data from coordinator data."""
288 raise NotImplementedError
292 """Return availability based on data availability."""
296 """Handle switch state change request."""
297 raise NotImplementedError
300 """Turn on switch."""
304 """Turn off switch."""
309 """Fritz switch base class."""
313 avm_wrapper: AvmWrapper,
314 device_friendly_name: str,
315 switch_info: SwitchInfo,
317 """Init Fritzbox base switch."""
318 super().
__init__(avm_wrapper, device_friendly_name)
322 self.
_icon_icon = switch_info[
"icon"]
323 self.
_type_type = switch_info[
"type"]
324 self.
_update_update = switch_info[
"callback_update"]
325 self.
_switch_switch = switch_info[
"callback_switch"]
328 self.
_name_name = f
"{self._friendly_name} {self._description}"
329 self.
_unique_id_unique_id = f
"{self._avm_wrapper.unique_id}-{slugify(self._description)}"
331 self._attributes: dict[str, str |
None] = {}
337 return self.
_name_name
342 return self.
_icon_icon
346 """Return unique id."""
351 """Return availability."""
356 """Return device attributes."""
357 return self._attributes
361 _LOGGER.debug(
"Updating '%s' (%s) switch state", self.
namenamename, self.
_type_type)
365 """Turn on switch."""
369 """Turn off switch."""
373 """Handle switch state change request."""
374 await self.
_switch_switch(turn_on)
379 """Defines a FRITZ!Box Tools PortForward switch."""
383 avm_wrapper: AvmWrapper,
384 device_friendly_name: str,
385 port_mapping: dict[str, Any] |
None,
388 connection_type: str,
390 """Init Fritzbox port switch."""
399 if port_mapping
is None:
403 description=f
"Port forward {port_name}",
404 friendly_name=device_friendly_name,
405 icon=
"mdi:check-network",
406 type=SWITCH_TYPE_PORTFORWARD,
409 init_state=port_mapping[
"NewEnabled"],
411 super().
__init__(avm_wrapper, device_friendly_name, switch_info)
420 "Specific %s response: %s", SWITCH_TYPE_PORTFORWARD, self.
port_mappingport_mapping
430 "NewInternalClient":
"internal_ip",
431 "NewInternalPort":
"internal_port",
432 "NewExternalPort":
"external_port",
433 "NewProtocol":
"protocol",
434 "NewPortMappingDescription":
"description",
437 for key, attr
in attributes_dict.items():
444 self.
port_mappingport_mapping[
"NewEnabled"] =
"1" if turn_on
else "0"
449 return bool(resp
is not None)
453 """Defines a FRITZ!Box Tools PortForward switch."""
455 _attr_entity_category = EntityCategory.CONFIG
459 avm_wrapper: AvmWrapper,
460 device_friendly_name: str,
463 """Init Fritxbox Deflection class."""
466 key=f
"call_deflection_{self.deflection_id}",
467 name=f
"Call deflection {self.deflection_id}",
468 icon=
"mdi:phone-forward",
470 super().
__init__(avm_wrapper, device_friendly_name, description)
473 def data(self) -> dict[str, Any]:
474 """Return call deflection data."""
475 return self.coordinator.data[
"call_deflections"].
get(self.
deflection_iddeflection_id, {})
479 """Return device attributes."""
483 "deflection_to_number": self.
datadatadatadata[
"DeflectionToNumber"],
495 """Handle deflection switch."""
500 """Defines a FRITZ!Box Tools DeviceProfile switch."""
502 _attr_icon =
"mdi:router-wireless-settings"
504 def __init__(self, avm_wrapper: AvmWrapper, device: FritzDevice) ->
None:
505 """Init Fritz profile."""
506 super().
__init__(avm_wrapper, device)
507 self._attr_is_on: bool =
False
508 self.
_name_name = f
"{device.hostname} Internet Access"
512 connections={(CONNECTION_NETWORK_MAC, self._mac)},
513 default_manufacturer=
"AVM",
514 default_model=
"FRITZ!Box Tracked device",
515 default_name=device.hostname,
518 avm_wrapper.unique_id,
525 return self.
_avm_wrapper_avm_wrapper.devices[self._mac].wan_access
529 """Return availability of the switch."""
530 if self.
_avm_wrapper_avm_wrapper.devices[self._mac].wan_access
is None:
532 return super().available
535 """Turn on switch."""
539 """Turn off switch."""
543 """Handle switch state change request."""
552 """Defines a FRITZ!Box Tools Wifi switch."""
556 avm_wrapper: AvmWrapper,
557 device_friendly_name: str,
561 """Init Fritz Wifi switch."""
569 description=f
"Wi-Fi {network_data['switch_name']}",
570 friendly_name=device_friendly_name,
572 type=SWITCH_TYPE_WIFINETWORK,
575 init_state=network_data[
"enabled"],
586 "Specific %s response: GetInfo=%s", SWITCH_TYPE_WIFINETWORK, wifi_info
596 std = wifi_info[
"NewStandard"]
597 self.
_attributes_attributes[
"standard"] = std
if std
else None
598 self.
_attributes_attributes[
"bssid"] = wifi_info[
"NewBSSID"]
599 self.
_attributes_attributes[
"mac_address_control"] = wifi_info[
600 "NewMACAddressControlEnabled"
604 """Handle wifi switch."""
dict[str, Any] async_set_allow_wan_access(self, str ip_address, bool turn_on)
dict[str, Any] async_set_deflection_enable(self, int index, bool turn_on)
str|None ip_address(self)
None _async_handle_turn_on_off(self, bool turn_on)
DeviceInfo device_info(self)
None async_turn_on(self, **Any kwargs)
None __init__(self, AvmWrapper avm_wrapper, str device_name, SwitchEntityDescription description)
dict[str, Any] data(self)
None async_turn_off(self, **Any kwargs)
None _async_handle_turn_on_off(self, bool turn_on)
None __init__(self, AvmWrapper avm_wrapper, str device_friendly_name, SwitchInfo switch_info)
None async_turn_off(self, **Any kwargs)
dict[str, str|None] extra_state_attributes(self)
None async_turn_on(self, **Any kwargs)
None __init__(self, AvmWrapper avm_wrapper, str device_friendly_name, int deflection_id)
None _async_handle_turn_on_off(self, bool turn_on)
dict[str, str] extra_state_attributes(self)
dict[str, Any] data(self)
None __init__(self, AvmWrapper avm_wrapper, str device_friendly_name, dict[str, Any]|None port_mapping, str port_name, int idx, str connection_type)
bool _async_switch_on_off_executor(self, bool turn_on)
None _async_fetch_update(self)
None async_turn_off(self, **Any kwargs)
None async_turn_on(self, **Any kwargs)
None __init__(self, AvmWrapper avm_wrapper, FritzDevice device)
bool _async_handle_turn_on_off(self, bool turn_on)
None _async_fetch_update(self)
None _async_switch_on_off_executor(self, bool turn_on)
None __init__(self, AvmWrapper avm_wrapper, str device_friendly_name, int network_num, dict network_data)
None async_write_ha_state(self)
str|UndefinedType|None name(self)
bool add(self, _T matcher)
web.Response get(self, web.Request request, str config_key)
bool device_filter_out_from_trackers(str mac, FritzDevice device, ValuesView current_devices)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
list[FritzBoxPortSwitch] _async_port_entities_list(AvmWrapper avm_wrapper, str device_friendly_name, str local_ip)
list[FritzBoxWifiSwitch] _async_wifi_entities_list(AvmWrapper avm_wrapper, str device_friendly_name)
list[Entity] async_all_entities_list(AvmWrapper avm_wrapper, str device_friendly_name, FritzData data_fritz, str local_ip)
list[FritzBoxDeflectionSwitch] _async_deflection_entities_list(AvmWrapper avm_wrapper, str device_friendly_name)
list[FritzBoxProfileSwitch] _async_profile_entities_list(AvmWrapper avm_wrapper, FritzData data_fritz)
str async_get_source_ip(HomeAssistant hass, str|UndefinedType target_ip=UNDEFINED)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)