1 """Support for TPLink Omada device toggle options."""
3 from __future__
import annotations
5 from collections.abc
import Awaitable, Callable
6 from dataclasses
import dataclass
7 from functools
import partial
8 from typing
import Any, Generic, TypeVar
10 from tplink_omada_client
import OmadaSiteClient, SwitchPortOverrides
11 from tplink_omada_client.definitions
import GatewayPortMode, PoEMode, PortType
12 from tplink_omada_client.devices
import (
15 OmadaGatewayPortConfig,
16 OmadaGatewayPortStatus,
18 OmadaSwitchPortDetails,
20 from tplink_omada_client.omadasiteclient
import GatewayPortSettings
27 from .
import OmadaConfigEntry
28 from .controller
import OmadaGatewayCoordinator, OmadaSwitchPortCoordinator
29 from .coordinator
import OmadaCoordinator
30 from .entity
import OmadaDeviceEntity
32 TPort = TypeVar(
"TPort")
33 TDevice = TypeVar(
"TDevice", bound=
"OmadaDevice")
34 TCoordinator = TypeVar(
"TCoordinator", bound=
"OmadaCoordinator[Any]")
39 config_entry: OmadaConfigEntry,
40 async_add_entities: AddEntitiesCallback,
42 """Set up switches."""
43 controller = config_entry.runtime_data
44 omada_client = controller.omada_client
47 network_switches = await omada_client.get_switches()
51 ns
for ns
in network_switches
if ns.device_capabilities.supports_poe
53 coordinator = controller.get_switch_port_coordinator(switch)
54 await coordinator.async_request_refresh()
57 OmadaDevicePortSwitchEntity[
58 OmadaSwitchPortCoordinator, OmadaSwitch, OmadaSwitchPortDetails
67 for port
in coordinator.data.values()
68 for desc
in SWITCH_PORT_DETAILS_SWITCHES
69 if desc.exists_func(switch, port)
72 gateway_coordinator = controller.gateway_coordinator
73 if gateway_coordinator:
74 for gateway
in gateway_coordinator.data.values():
76 OmadaDevicePortSwitchEntity[
77 OmadaGatewayCoordinator, OmadaGateway, OmadaGatewayPortStatus
78 ](gateway_coordinator, gateway, p,
str(p.port_number), desc)
79 for p
in gateway.port_status
80 for desc
in GATEWAY_PORT_STATUS_SWITCHES
81 if desc.exists_func(gateway, p)
84 OmadaDevicePortSwitchEntity[
85 OmadaGatewayCoordinator, OmadaGateway, OmadaGatewayPortConfig
86 ](gateway_coordinator, gateway, p,
str(p.port_number), desc)
87 for p
in gateway.port_configs
88 for desc
in GATEWAY_PORT_CONFIG_SWITCHES
89 if desc.exists_func(gateway, p)
96 """Get display name for a switch port."""
98 if port.name == f
"Port{port.port}":
100 return f
"{port.port} ({port.name})"
103 @dataclass(frozen=True, kw_only=True)
105 SwitchEntityDescription, Generic[TCoordinator, TDevice, TPort]
107 """Entity description for a toggle switch derived from a network port on an Omada device."""
109 exists_func: Callable[[TDevice, TPort], bool] =
lambda _, p:
True
110 coordinator_update_func: Callable[[TCoordinator, TDevice, TPort], TPort |
None]
111 set_func: Callable[[OmadaSiteClient, TDevice, TPort, bool], Awaitable[TPort |
None]]
112 update_func: Callable[[TPort], bool]
115 @dataclass(frozen=True, kw_only=True)
117 OmadaDevicePortSwitchEntityDescription[
118 OmadaSwitchPortCoordinator, OmadaSwitch, OmadaSwitchPortDetails
121 """Entity description for a toggle switch for a feature of a Port on an Omada Switch."""
123 coordinator_update_func: Callable[
124 [OmadaSwitchPortCoordinator, OmadaSwitch, OmadaSwitchPortDetails],
125 OmadaSwitchPortDetails |
None,
126 ] =
lambda coord, _, port: coord.data.get(port.port_id)
129 @dataclass(frozen=True, kw_only=True)
131 OmadaDevicePortSwitchEntityDescription[
132 OmadaGatewayCoordinator, OmadaGateway, OmadaGatewayPortConfig
135 """Entity description for a toggle switch for a configuration of a Port on an Omada Gateway."""
137 coordinator_update_func: Callable[
138 [OmadaGatewayCoordinator, OmadaGateway, OmadaGatewayPortConfig],
139 OmadaGatewayPortConfig |
None,
140 ] =
lambda coord, device, port: next(
142 for p
in coord.data[device.mac].port_configs
143 if p.port_number == port.port_number
147 @dataclass(frozen=True, kw_only=True)
149 OmadaDevicePortSwitchEntityDescription[
150 OmadaGatewayCoordinator, OmadaGateway, OmadaGatewayPortStatus
153 """Entity description for a toggle switch for a status of a Port on an Omada Gateway."""
155 coordinator_update_func: Callable[
156 [OmadaGatewayCoordinator, OmadaGateway, OmadaGatewayPortStatus],
157 OmadaGatewayPortStatus,
158 ] =
lambda coord, device, port: next(
160 for p
in coord.data[device.mac].port_status
161 if p.port_number == port.port_number
166 client: OmadaSiteClient,
168 port: OmadaGatewayPortStatus,
173 await client.set_gateway_wan_port_connect_state(
174 port.port_number, enable, device, ipv6=ipv6
178 SWITCH_PORT_DETAILS_SWITCHES: list[OmadaSwitchPortSwitchEntityDescription] = [
181 translation_key=
"poe_control",
183 lambda d, p: d.device_capabilities.supports_poe
and p.type != PortType.SFP
186 lambda client, device, port, enable: client.update_switch_port(
187 device, port, overrides=SwitchPortOverrides(enable_poe=enable)
190 update_func=
lambda p: p.poe_mode != PoEMode.DISABLED,
191 entity_category=EntityCategory.CONFIG,
195 GATEWAY_PORT_STATUS_SWITCHES: list[OmadaGatewayPortStatusSwitchEntityDescription] = [
197 key=
"wan_connect_ipv4",
198 translation_key=
"wan_connect_ipv4",
199 exists_func=
lambda _, p: p.mode == GatewayPortMode.WAN,
200 set_func=partial(_wan_connect_disconnect, ipv6=
False),
201 update_func=
lambda p: p.wan_connected,
204 key=
"wan_connect_ipv6",
205 translation_key=
"wan_connect_ipv6",
206 exists_func=
lambda _, p: p.mode == GatewayPortMode.WAN
and p.wan_ipv6_enabled,
207 set_func=partial(_wan_connect_disconnect, ipv6=
True),
208 update_func=
lambda p: p.ipv6_wan_connected,
212 GATEWAY_PORT_CONFIG_SWITCHES: list[OmadaGatewayPortConfigSwitchEntityDescription] = [
215 translation_key=
"poe_control",
216 exists_func=
lambda _, port: port.poe_mode != PoEMode.NONE,
217 set_func=
lambda client, device, port, enable: client.set_gateway_port_settings(
218 port.port_number, GatewayPortSettings(enable_poe=enable), device
220 update_func=
lambda p: p.poe_mode != PoEMode.DISABLED,
226 OmadaDeviceEntity[TCoordinator],
228 Generic[TCoordinator, TDevice, TPort],
230 """Generic toggle switch entity for a Netork Port of an Omada Device."""
232 entity_description: OmadaDevicePortSwitchEntityDescription[
233 TCoordinator, TDevice, TPort
238 coordinator: TCoordinator,
242 entity_description: OmadaDevicePortSwitchEntityDescription[
243 TCoordinator, TDevice, TPort
245 port_name: str |
None =
None,
247 """Initialize the toggle switch."""
248 super().
__init__(coordinator, device)
252 self.
_attr_unique_id_attr_unique_id = f
"{device.mac}_{port_id}_{entity_description.key}"
256 """When entity is added to hass."""
270 await self.coordinator.async_request_refresh()
274 """Turn the entity on."""
278 """Turn the entity off."""
283 """Return true if entity is available."""
291 latest_port_details = self.
entity_descriptionentity_description.coordinator_update_func(
294 if latest_port_details:
300 """Handle updated data from the coordinator."""
None _handle_coordinator_update(self)
None async_turn_off(self, **Any kwargs)
None async_added_to_hass(self)
None __init__(self, TCoordinator coordinator, TDevice device, TPort port_details, str port_id, OmadaDevicePortSwitchEntityDescription[TCoordinator, TDevice, TPort] entity_description, str|None port_name=None)
None async_turn_on(self, **Any kwargs)
_attr_translation_placeholders
None _async_turn_on_off(self, bool enable)
None async_write_ha_state(self)
None _wan_connect_disconnect(OmadaSiteClient client, OmadaDevice device, OmadaGatewayPortStatus port, bool enable, bool ipv6)
str _get_switch_port_base_name(OmadaSwitchPortDetails port)
None async_setup_entry(HomeAssistant hass, OmadaConfigEntry config_entry, AddEntitiesCallback async_add_entities)