2021-06-29 15:57:34 +00:00
""" Switches for AVM Fritz!Box functions. """
from __future__ import annotations
import logging
from typing import Any
import xmltodict
2021-07-22 18:12:33 +00:00
from homeassistant . components . network import async_get_source_ip
2021-06-29 15:57:34 +00:00
from homeassistant . components . switch import SwitchEntity
from homeassistant . config_entries import ConfigEntry
2021-07-16 11:38:37 +00:00
from homeassistant . core import HomeAssistant , callback
2022-01-05 07:16:43 +00:00
from homeassistant . helpers . device_registry import CONNECTION_NETWORK_MAC
2021-07-16 11:38:37 +00:00
from homeassistant . helpers . dispatcher import async_dispatcher_connect
2022-01-05 07:16:43 +00:00
from homeassistant . helpers . entity import DeviceInfo , Entity , EntityCategory
2021-06-29 15:57:34 +00:00
from homeassistant . helpers . entity_platform import AddEntitiesCallback
2021-07-22 18:12:33 +00:00
from homeassistant . util import slugify
2021-06-29 15:57:34 +00:00
2021-07-16 11:38:37 +00:00
from . common import (
2022-01-20 11:43:32 +00:00
AvmWrapper ,
2021-07-16 11:38:37 +00:00
FritzBoxBaseEntity ,
FritzData ,
FritzDevice ,
FritzDeviceBase ,
SwitchInfo ,
2021-09-29 03:59:03 +00:00
device_filter_out_from_trackers ,
2021-07-16 11:38:37 +00:00
)
2021-06-29 15:57:34 +00:00
from . const import (
2021-07-16 11:38:37 +00:00
DATA_FRITZ ,
2021-06-29 15:57:34 +00:00
DOMAIN ,
SWITCH_TYPE_DEFLECTION ,
SWITCH_TYPE_PORTFORWARD ,
2022-03-04 14:49:22 +00:00
SWITCH_TYPE_PROFILE ,
2021-06-29 15:57:34 +00:00
SWITCH_TYPE_WIFINETWORK ,
2022-02-18 08:13:36 +00:00
WIFI_STANDARD ,
2022-01-02 13:59:41 +00:00
MeshRoles ,
2021-06-29 15:57:34 +00:00
)
_LOGGER = logging . getLogger ( __name__ )
def deflection_entities_list (
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper , device_friendly_name : str
2021-06-29 15:57:34 +00:00
) - > list [ FritzBoxDeflectionSwitch ] :
""" Get list of deflection entities. """
_LOGGER . debug ( " Setting up %s switches " , SWITCH_TYPE_DEFLECTION )
2022-01-23 12:04:19 +00:00
deflections_response = avm_wrapper . get_ontel_num_deflections ( )
2021-06-29 15:57:34 +00:00
if not deflections_response :
_LOGGER . debug ( " The FRITZ!Box has no %s options " , SWITCH_TYPE_DEFLECTION )
return [ ]
_LOGGER . debug (
" Specific %s response: GetNumberOfDeflections= %s " ,
SWITCH_TYPE_DEFLECTION ,
deflections_response ,
)
if deflections_response [ " NewNumberOfDeflections " ] == 0 :
_LOGGER . debug ( " The FRITZ!Box has no %s options " , SWITCH_TYPE_DEFLECTION )
return [ ]
2022-02-19 16:21:26 +00:00
if not ( deflection_list := avm_wrapper . get_ontel_deflections ( ) ) :
2021-06-29 15:57:34 +00:00
return [ ]
2022-01-23 12:04:19 +00:00
items = xmltodict . parse ( deflection_list [ " NewDeflectionList " ] ) [ " List " ] [ " Item " ]
if not isinstance ( items , list ) :
items = [ items ]
2021-06-29 15:57:34 +00:00
return [
2022-01-20 11:43:32 +00:00
FritzBoxDeflectionSwitch ( avm_wrapper , device_friendly_name , dict_of_deflection )
2022-01-23 12:04:19 +00:00
for dict_of_deflection in items
2021-06-29 15:57:34 +00:00
]
def port_entities_list (
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper , device_friendly_name : str , local_ip : str
2021-06-29 15:57:34 +00:00
) - > list [ FritzBoxPortSwitch ] :
""" Get list of port forwarding entities. """
_LOGGER . debug ( " Setting up %s switches " , SWITCH_TYPE_PORTFORWARD )
2021-09-29 22:11:22 +00:00
entities_list : list [ FritzBoxPortSwitch ] = [ ]
2022-02-09 08:44:04 +00:00
if not avm_wrapper . device_conn_type :
2021-06-29 15:57:34 +00:00
_LOGGER . debug ( " The FRITZ!Box has no %s options " , SWITCH_TYPE_PORTFORWARD )
return [ ]
# Query port forwardings and setup a switch for each forward for the current device
2022-02-09 08:44:04 +00:00
resp = avm_wrapper . get_num_port_mapping ( avm_wrapper . device_conn_type )
2021-06-29 15:57:34 +00:00
if not resp :
_LOGGER . debug ( " The FRITZ!Box has no %s options " , SWITCH_TYPE_DEFLECTION )
return [ ]
port_forwards_count : int = resp [ " NewPortMappingNumberOfEntries " ]
_LOGGER . debug (
" Specific %s response: GetPortMappingNumberOfEntries= %s " ,
SWITCH_TYPE_PORTFORWARD ,
port_forwards_count ,
)
2022-01-20 11:43:32 +00:00
_LOGGER . debug ( " IP source for %s is %s " , avm_wrapper . host , local_ip )
2021-06-29 15:57:34 +00:00
for i in range ( port_forwards_count ) :
2022-02-09 08:44:04 +00:00
portmap = avm_wrapper . get_port_mapping ( avm_wrapper . device_conn_type , i )
2021-06-29 15:57:34 +00:00
if not portmap :
_LOGGER . debug ( " The FRITZ!Box has no %s options " , SWITCH_TYPE_DEFLECTION )
continue
_LOGGER . debug (
" Specific %s response: GetGenericPortMappingEntry= %s " ,
SWITCH_TYPE_PORTFORWARD ,
portmap ,
)
# We can only handle port forwards of the given device
if portmap [ " NewInternalClient " ] == local_ip :
2021-09-29 22:11:22 +00:00
port_name = portmap [ " NewPortMappingDescription " ]
for entity in entities_list :
if entity . port_mapping and (
port_name in entity . port_mapping [ " NewPortMappingDescription " ]
) :
port_name = f " { port_name } { portmap [ ' NewExternalPort ' ] } "
2021-06-29 15:57:34 +00:00
entities_list . append (
FritzBoxPortSwitch (
2022-01-20 11:43:32 +00:00
avm_wrapper ,
2021-06-29 15:57:34 +00:00
device_friendly_name ,
portmap ,
2021-09-29 22:11:22 +00:00
port_name ,
2021-06-29 15:57:34 +00:00
i ,
2022-02-09 08:44:04 +00:00
avm_wrapper . device_conn_type ,
2021-06-29 15:57:34 +00:00
)
)
return entities_list
def wifi_entities_list (
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper , device_friendly_name : str
2021-06-29 15:57:34 +00:00
) - > list [ FritzBoxWifiSwitch ] :
""" Get list of wifi entities. """
_LOGGER . debug ( " Setting up %s switches " , SWITCH_TYPE_WIFINETWORK )
2021-12-02 08:59:07 +00:00
2022-02-18 08:13:36 +00:00
#
# https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/wlanconfigSCPD.pdf
#
wifi_count = len (
[
s
for s in avm_wrapper . connection . services
if s . startswith ( " WLANConfiguration " )
]
)
_LOGGER . debug ( " WiFi networks count: %s " , wifi_count )
2021-06-29 15:57:34 +00:00
networks : dict = { }
2022-02-18 08:13:36 +00:00
for i in range ( 1 , wifi_count + 1 ) :
network_info = avm_wrapper . connection . call_action (
f " WLANConfiguration { i } " , " GetInfo "
)
# Devices with 4 WLAN services, use the 2nd for internal communications
if not ( wifi_count == 4 and i == 2 ) :
networks [ i ] = {
" ssid " : network_info [ " NewSSID " ] ,
" bssid " : network_info [ " NewBSSID " ] ,
" standard " : network_info [ " NewStandard " ] ,
" enabled " : network_info [ " NewEnable " ] ,
" status " : network_info [ " NewStatus " ] ,
}
for i , network in networks . copy ( ) . items ( ) :
networks [ i ] [ " switch_name " ] = network [ " ssid " ]
if len ( [ j for j , n in networks . items ( ) if n [ " ssid " ] == network [ " ssid " ] ] ) > 1 :
networks [ i ] [ " switch_name " ] + = f " ( { WIFI_STANDARD [ i ] } ) "
_LOGGER . debug ( " WiFi networks list: %s " , networks )
2021-06-29 15:57:34 +00:00
return [
2022-02-18 08:13:36 +00:00
FritzBoxWifiSwitch (
avm_wrapper , device_friendly_name , index , data [ " switch_name " ]
)
for index , data in networks . items ( )
2021-06-29 15:57:34 +00:00
]
2021-07-16 11:38:37 +00:00
def profile_entities_list (
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper ,
2021-09-29 03:59:03 +00:00
data_fritz : FritzData ,
2021-07-16 11:38:37 +00:00
) - > list [ FritzBoxProfileSwitch ] :
2022-01-06 11:15:40 +00:00
""" Add new tracker entities from the AVM device. """
2022-03-04 14:49:22 +00:00
_LOGGER . debug ( " Setting up %s switches " , SWITCH_TYPE_PROFILE )
2021-07-16 11:38:37 +00:00
new_profiles : list [ FritzBoxProfileSwitch ] = [ ]
2022-01-20 11:43:32 +00:00
if " X_AVM-DE_HostFilter1 " not in avm_wrapper . connection . services :
2021-07-16 11:38:37 +00:00
return new_profiles
2022-01-20 11:43:32 +00:00
if avm_wrapper . unique_id not in data_fritz . profile_switches :
data_fritz . profile_switches [ avm_wrapper . unique_id ] = set ( )
2021-07-16 11:38:37 +00:00
2022-01-20 11:43:32 +00:00
for mac , device in avm_wrapper . devices . items ( ) :
2021-09-29 03:59:03 +00:00
if device_filter_out_from_trackers (
2021-10-01 14:18:49 +00:00
mac , device , data_fritz . profile_switches . values ( )
2021-09-29 03:59:03 +00:00
) :
2022-03-04 14:49:22 +00:00
_LOGGER . debug (
" Skipping profile switch creation for device %s " , device . hostname
)
2021-07-16 11:38:37 +00:00
continue
2022-01-20 11:43:32 +00:00
new_profiles . append ( FritzBoxProfileSwitch ( avm_wrapper , device ) )
data_fritz . profile_switches [ avm_wrapper . unique_id ] . add ( mac )
2021-07-16 11:38:37 +00:00
2022-03-04 14:49:22 +00:00
_LOGGER . debug ( " Creating %s profile switches " , len ( new_profiles ) )
2021-07-16 11:38:37 +00:00
return new_profiles
2021-06-29 15:57:34 +00:00
def all_entities_list (
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper ,
2021-07-22 18:12:33 +00:00
device_friendly_name : str ,
data_fritz : FritzData ,
local_ip : str ,
2021-06-29 15:57:34 +00:00
) - > list [ Entity ] :
""" Get a list of all entities. """
2022-01-02 13:59:41 +00:00
2022-01-20 11:43:32 +00:00
if avm_wrapper . mesh_role == MeshRoles . SLAVE :
2022-01-02 13:59:41 +00:00
return [ ]
2021-06-29 15:57:34 +00:00
return [
2022-01-20 11:43:32 +00:00
* deflection_entities_list ( avm_wrapper , device_friendly_name ) ,
* port_entities_list ( avm_wrapper , device_friendly_name , local_ip ) ,
* wifi_entities_list ( avm_wrapper , device_friendly_name ) ,
* profile_entities_list ( avm_wrapper , data_fritz ) ,
2021-06-29 15:57:34 +00:00
]
async def async_setup_entry (
hass : HomeAssistant , entry : ConfigEntry , async_add_entities : AddEntitiesCallback
) - > None :
""" Set up entry. """
_LOGGER . debug ( " Setting up switches " )
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper = hass . data [ DOMAIN ] [ entry . entry_id ]
2021-07-16 11:38:37 +00:00
data_fritz : FritzData = hass . data [ DATA_FRITZ ]
2021-06-29 15:57:34 +00:00
2022-01-20 11:43:32 +00:00
_LOGGER . debug ( " Fritzbox services: %s " , avm_wrapper . connection . services )
2021-06-29 15:57:34 +00:00
2022-01-20 11:43:32 +00:00
local_ip = await async_get_source_ip ( avm_wrapper . hass , target_ip = avm_wrapper . host )
2021-07-22 18:12:33 +00:00
2021-06-29 15:57:34 +00:00
entities_list = await hass . async_add_executor_job (
2021-09-29 03:59:03 +00:00
all_entities_list ,
2022-01-20 11:43:32 +00:00
avm_wrapper ,
2021-09-29 03:59:03 +00:00
entry . title ,
data_fritz ,
local_ip ,
2021-06-29 15:57:34 +00:00
)
2021-07-16 11:38:37 +00:00
2021-06-29 15:57:34 +00:00
async_add_entities ( entities_list )
2021-07-16 11:38:37 +00:00
@callback
2022-01-06 11:15:40 +00:00
def update_avm_device ( ) - > None :
""" Update the values of the AVM device. """
2022-01-20 11:43:32 +00:00
async_add_entities ( profile_entities_list ( avm_wrapper , data_fritz ) )
2021-07-16 11:38:37 +00:00
entry . async_on_unload (
2022-01-20 11:43:32 +00:00
async_dispatcher_connect ( hass , avm_wrapper . signal_device_new , update_avm_device )
2021-07-16 11:38:37 +00:00
)
2021-06-29 15:57:34 +00:00
class FritzBoxBaseSwitch ( FritzBoxBaseEntity ) :
""" Fritz switch base class. """
2022-01-23 10:31:01 +00:00
_attr_is_on : bool | None = False
2021-06-29 15:57:34 +00:00
def __init__ (
self ,
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper ,
2021-06-29 15:57:34 +00:00
device_friendly_name : str ,
switch_info : SwitchInfo ,
) - > None :
""" Init Fritzbox port switch. """
2022-01-20 11:43:32 +00:00
super ( ) . __init__ ( avm_wrapper , device_friendly_name )
2021-06-29 15:57:34 +00:00
self . _description = switch_info [ " description " ]
self . _friendly_name = switch_info [ " friendly_name " ]
self . _icon = switch_info [ " icon " ]
self . _type = switch_info [ " type " ]
self . _update = switch_info [ " callback_update " ]
self . _switch = switch_info [ " callback_switch " ]
self . _name = f " { self . _friendly_name } { self . _description } "
2022-01-20 11:43:32 +00:00
self . _unique_id = f " { self . _avm_wrapper . unique_id } - { slugify ( self . _description ) } "
2021-06-29 15:57:34 +00:00
self . _attributes : dict [ str , str ] = { }
self . _is_available = True
@property
def name ( self ) - > str :
""" Return name. """
return self . _name
@property
def icon ( self ) - > str :
""" Return name. """
return self . _icon
@property
def unique_id ( self ) - > str :
""" Return unique id. """
return self . _unique_id
@property
def available ( self ) - > bool :
""" Return availability. """
return self . _is_available
@property
def extra_state_attributes ( self ) - > dict [ str , str ] :
""" Return device attributes. """
return self . _attributes
async def async_update ( self ) - > None :
""" Update data. """
_LOGGER . debug ( " Updating ' %s ' ( %s ) switch state " , self . name , self . _type )
await self . _update ( )
async def async_turn_on ( self , * * kwargs : Any ) - > None :
""" Turn on switch. """
await self . _async_handle_turn_on_off ( turn_on = True )
async def async_turn_off ( self , * * kwargs : Any ) - > None :
""" Turn off switch. """
await self . _async_handle_turn_on_off ( turn_on = False )
2021-08-24 16:10:32 +00:00
async def _async_handle_turn_on_off ( self , turn_on : bool ) - > None :
2021-06-29 15:57:34 +00:00
""" Handle switch state change request. """
await self . _switch ( turn_on )
self . _attr_is_on = turn_on
class FritzBoxPortSwitch ( FritzBoxBaseSwitch , SwitchEntity ) :
""" Defines a FRITZ!Box Tools PortForward switch. """
def __init__ (
self ,
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper ,
2021-06-29 15:57:34 +00:00
device_friendly_name : str ,
port_mapping : dict [ str , Any ] | None ,
2021-09-29 22:11:22 +00:00
port_name : str ,
2021-06-29 15:57:34 +00:00
idx : int ,
connection_type : str ,
) - > None :
""" Init Fritzbox port switch. """
2022-01-20 11:43:32 +00:00
self . _avm_wrapper = avm_wrapper
2021-06-29 15:57:34 +00:00
self . _attributes = { }
self . connection_type = connection_type
self . port_mapping = port_mapping # dict in the format as it comes from fritzconnection. eg: {'NewRemoteHost': '0.0.0.0', 'NewExternalPort': 22, 'NewProtocol': 'TCP', 'NewInternalPort': 22, 'NewInternalClient': '192.168.178.31', 'NewEnabled': True, 'NewPortMappingDescription': 'Beast SSH ', 'NewLeaseDuration': 0}
self . _idx = idx # needed for update routine
2021-12-10 13:58:34 +00:00
self . _attr_entity_category = EntityCategory . CONFIG
2021-06-29 15:57:34 +00:00
if port_mapping is None :
return
switch_info = SwitchInfo (
2021-09-29 22:11:22 +00:00
description = f " Port forward { port_name } " ,
2021-06-29 15:57:34 +00:00
friendly_name = device_friendly_name ,
icon = " mdi:check-network " ,
type = SWITCH_TYPE_PORTFORWARD ,
callback_update = self . _async_fetch_update ,
2022-01-23 12:04:19 +00:00
callback_switch = self . _async_switch_on_off_executor ,
2021-06-29 15:57:34 +00:00
)
2022-01-20 11:43:32 +00:00
super ( ) . __init__ ( avm_wrapper , device_friendly_name , switch_info )
2021-06-29 15:57:34 +00:00
async def _async_fetch_update ( self ) - > None :
""" Fetch updates. """
2022-01-23 12:04:19 +00:00
self . port_mapping = await self . _avm_wrapper . async_get_port_mapping (
self . connection_type , self . _idx
2021-06-29 15:57:34 +00:00
)
_LOGGER . debug (
" Specific %s response: %s " , SWITCH_TYPE_PORTFORWARD , self . port_mapping
)
2022-01-23 12:04:19 +00:00
if not self . port_mapping :
2021-06-29 15:57:34 +00:00
self . _is_available = False
return
self . _attr_is_on = self . port_mapping [ " NewEnabled " ] is True
self . _is_available = True
attributes_dict = {
2021-08-24 16:10:32 +00:00
" NewInternalClient " : " internal_ip " ,
" NewInternalPort " : " internal_port " ,
" NewExternalPort " : " external_port " ,
2021-06-29 15:57:34 +00:00
" NewProtocol " : " protocol " ,
" NewPortMappingDescription " : " description " ,
}
2021-07-15 04:44:57 +00:00
for key , attr in attributes_dict . items ( ) :
self . _attributes [ attr ] = self . port_mapping [ key ]
2021-06-29 15:57:34 +00:00
2022-01-23 12:04:19 +00:00
async def _async_switch_on_off_executor ( self , turn_on : bool ) - > bool :
2021-06-29 15:57:34 +00:00
if self . port_mapping is None :
return False
self . port_mapping [ " NewEnabled " ] = " 1 " if turn_on else " 0 "
2022-01-23 12:04:19 +00:00
resp = await self . _avm_wrapper . async_add_port_mapping (
self . connection_type , self . port_mapping
2021-06-29 15:57:34 +00:00
)
return bool ( resp is not None )
class FritzBoxDeflectionSwitch ( FritzBoxBaseSwitch , SwitchEntity ) :
""" Defines a FRITZ!Box Tools PortForward switch. """
def __init__ (
self ,
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper ,
2021-06-29 15:57:34 +00:00
device_friendly_name : str ,
dict_of_deflection : Any ,
) - > None :
""" Init Fritxbox Deflection class. """
2022-01-20 11:43:32 +00:00
self . _avm_wrapper = avm_wrapper
2021-06-29 15:57:34 +00:00
self . dict_of_deflection = dict_of_deflection
self . _attributes = { }
self . id = int ( self . dict_of_deflection [ " DeflectionId " ] )
2021-12-10 13:58:34 +00:00
self . _attr_entity_category = EntityCategory . CONFIG
2021-06-29 15:57:34 +00:00
switch_info = SwitchInfo (
description = f " Call deflection { self . id } " ,
friendly_name = device_friendly_name ,
icon = " mdi:phone-forward " ,
type = SWITCH_TYPE_DEFLECTION ,
callback_update = self . _async_fetch_update ,
callback_switch = self . _async_switch_on_off_executor ,
)
2022-01-20 11:43:32 +00:00
super ( ) . __init__ ( self . _avm_wrapper , device_friendly_name , switch_info )
2021-06-29 15:57:34 +00:00
async def _async_fetch_update ( self ) - > None :
""" Fetch updates. """
2022-01-23 12:04:19 +00:00
resp = await self . _avm_wrapper . async_get_ontel_deflections ( )
2021-06-29 15:57:34 +00:00
if not resp :
self . _is_available = False
return
self . dict_of_deflection = xmltodict . parse ( resp [ " NewDeflectionList " ] ) [ " List " ] [
" Item "
]
if isinstance ( self . dict_of_deflection , list ) :
self . dict_of_deflection = self . dict_of_deflection [ self . id ]
_LOGGER . debug (
" Specific %s response: NewDeflectionList= %s " ,
SWITCH_TYPE_DEFLECTION ,
self . dict_of_deflection ,
)
self . _attr_is_on = self . dict_of_deflection [ " Enable " ] == " 1 "
self . _is_available = True
2021-08-24 16:10:32 +00:00
self . _attributes [ " type " ] = self . dict_of_deflection [ " Type " ]
self . _attributes [ " number " ] = self . dict_of_deflection [ " Number " ]
self . _attributes [ " deflection_to_number " ] = self . dict_of_deflection [
2021-06-29 15:57:34 +00:00
" DeflectionToNumber "
]
# Return mode sample: "eImmediately"
2021-08-24 16:10:32 +00:00
self . _attributes [ " mode " ] = self . dict_of_deflection [ " Mode " ] [ 1 : ]
self . _attributes [ " outgoing " ] = self . dict_of_deflection [ " Outgoing " ]
self . _attributes [ " phonebook_id " ] = self . dict_of_deflection [ " PhonebookID " ]
2021-06-29 15:57:34 +00:00
async def _async_switch_on_off_executor ( self , turn_on : bool ) - > None :
""" Handle deflection switch. """
2022-01-23 12:04:19 +00:00
await self . _avm_wrapper . async_set_deflection_enable ( self . id , turn_on )
2021-06-29 15:57:34 +00:00
2021-07-16 11:38:37 +00:00
class FritzBoxProfileSwitch ( FritzDeviceBase , SwitchEntity ) :
2021-06-29 15:57:34 +00:00
""" Defines a FRITZ!Box Tools DeviceProfile switch. """
2021-07-20 16:38:16 +00:00
_attr_icon = " mdi:router-wireless-settings "
2022-01-20 11:43:32 +00:00
def __init__ ( self , avm_wrapper : AvmWrapper , device : FritzDevice ) - > None :
2021-06-29 15:57:34 +00:00
""" Init Fritz profile. """
2022-01-20 11:43:32 +00:00
super ( ) . __init__ ( avm_wrapper , device )
2021-07-16 11:38:37 +00:00
self . _attr_is_on : bool = False
2021-07-20 16:38:16 +00:00
self . _name = f " { device . hostname } Internet Access "
self . _attr_unique_id = f " { self . _mac } _internet_access "
2021-12-10 13:58:34 +00:00
self . _attr_entity_category = EntityCategory . CONFIG
2022-02-22 23:39:19 +00:00
self . _attr_device_info = DeviceInfo (
connections = { ( CONNECTION_NETWORK_MAC , self . _mac ) } ,
default_manufacturer = " AVM " ,
default_model = " FRITZ!Box Tracked device " ,
default_name = device . hostname ,
identifiers = { ( DOMAIN , self . _mac ) } ,
via_device = (
DOMAIN ,
avm_wrapper . unique_id ,
) ,
)
2021-06-29 15:57:34 +00:00
2021-12-16 12:24:32 +00:00
@property
2022-01-30 21:22:32 +00:00
def is_on ( self ) - > bool | None :
2021-12-16 12:24:32 +00:00
""" Switch status. """
2022-01-20 11:43:32 +00:00
return self . _avm_wrapper . devices [ self . _mac ] . wan_access
2021-07-16 11:38:37 +00:00
2022-01-30 21:22:32 +00:00
@property
def available ( self ) - > bool :
""" Return availability of the switch. """
if self . _avm_wrapper . devices [ self . _mac ] . wan_access is None :
return False
return super ( ) . available
2021-07-16 11:38:37 +00:00
async def async_turn_on ( self , * * kwargs : Any ) - > None :
""" Turn on switch. """
await self . _async_handle_turn_on_off ( turn_on = True )
async def async_turn_off ( self , * * kwargs : Any ) - > None :
""" Turn off switch. """
await self . _async_handle_turn_on_off ( turn_on = False )
async def _async_handle_turn_on_off ( self , turn_on : bool ) - > bool :
""" Handle switch state change request. """
2022-01-23 12:04:19 +00:00
if not self . ip_address :
return False
await self . _avm_wrapper . async_set_allow_wan_access ( self . ip_address , turn_on )
2021-07-16 11:38:37 +00:00
self . async_write_ha_state ( )
return True
2021-06-29 15:57:34 +00:00
class FritzBoxWifiSwitch ( FritzBoxBaseSwitch , SwitchEntity ) :
""" Defines a FRITZ!Box Tools Wifi switch. """
def __init__ (
self ,
2022-01-20 11:43:32 +00:00
avm_wrapper : AvmWrapper ,
2021-06-29 15:57:34 +00:00
device_friendly_name : str ,
network_num : int ,
network_name : str ,
) - > None :
""" Init Fritz Wifi switch. """
2022-01-20 11:43:32 +00:00
self . _avm_wrapper = avm_wrapper
2021-06-29 15:57:34 +00:00
self . _attributes = { }
2021-12-10 13:58:34 +00:00
self . _attr_entity_category = EntityCategory . CONFIG
2021-06-29 15:57:34 +00:00
self . _network_num = network_num
switch_info = SwitchInfo (
description = f " Wi-Fi { network_name } " ,
friendly_name = device_friendly_name ,
icon = " mdi:wifi " ,
type = SWITCH_TYPE_WIFINETWORK ,
callback_update = self . _async_fetch_update ,
callback_switch = self . _async_switch_on_off_executor ,
)
2022-01-20 11:43:32 +00:00
super ( ) . __init__ ( self . _avm_wrapper , device_friendly_name , switch_info )
2021-06-29 15:57:34 +00:00
async def _async_fetch_update ( self ) - > None :
""" Fetch updates. """
2022-01-23 12:04:19 +00:00
wifi_info = await self . _avm_wrapper . async_get_wlan_configuration (
self . _network_num
2021-06-29 15:57:34 +00:00
)
_LOGGER . debug (
" Specific %s response: GetInfo= %s " , SWITCH_TYPE_WIFINETWORK , wifi_info
)
2022-01-23 12:04:19 +00:00
if not wifi_info :
2021-06-29 15:57:34 +00:00
self . _is_available = False
return
self . _attr_is_on = wifi_info [ " NewEnable " ] is True
self . _is_available = True
std = wifi_info [ " NewStandard " ]
self . _attributes [ " standard " ] = std if std else None
2021-08-24 16:10:32 +00:00
self . _attributes [ " bssid " ] = wifi_info [ " NewBSSID " ]
2021-06-29 15:57:34 +00:00
self . _attributes [ " mac_address_control " ] = wifi_info [
" NewMACAddressControlEnabled "
]
async def _async_switch_on_off_executor ( self , turn_on : bool ) - > None :
""" Handle wifi switch. """
2022-01-23 12:04:19 +00:00
await self . _avm_wrapper . async_set_wlan_configuration ( self . _network_num , turn_on )