From d81139377c51190c5872c5d9429fd567e533ce88 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 7 Feb 2022 12:00:02 +0100 Subject: [PATCH] Add Netgear allow/block switch (#65705) * add allow/block switch * keep api private * typing * change default to None * retain None state * change default to None --- .coveragerc | 1 + homeassistant/components/netgear/const.py | 4 +- homeassistant/components/netgear/router.py | 8 ++ homeassistant/components/netgear/switch.py | 106 +++++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/netgear/switch.py diff --git a/.coveragerc b/.coveragerc index be94c4a9a7f..8bc20400dba 100644 --- a/.coveragerc +++ b/.coveragerc @@ -733,6 +733,7 @@ omit = homeassistant/components/netgear/device_tracker.py homeassistant/components/netgear/router.py homeassistant/components/netgear/sensor.py + homeassistant/components/netgear/switch.py homeassistant/components/netgear_lte/* homeassistant/components/netio/switch.py homeassistant/components/neurio_energy/sensor.py diff --git a/homeassistant/components/netgear/const.py b/homeassistant/components/netgear/const.py index 02b78e164ac..d366ae39961 100644 --- a/homeassistant/components/netgear/const.py +++ b/homeassistant/components/netgear/const.py @@ -5,14 +5,14 @@ from homeassistant.const import Platform DOMAIN = "netgear" +PLATFORMS = [Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH] + CONF_CONSIDER_HOME = "consider_home" KEY_ROUTER = "router" KEY_COORDINATOR = "coordinator" KEY_COORDINATOR_TRAFFIC = "coordinator_traffic" -PLATFORMS = [Platform.DEVICE_TRACKER, Platform.SENSOR] - DEFAULT_CONSIDER_HOME = timedelta(seconds=180) DEFAULT_NAME = "Netgear router" diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index b358939d122..722bcb27ae0 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -147,6 +147,7 @@ class NetgearRouter: "ip": None, "ssid": None, "conn_ap_mac": None, + "allow_or_block": None, } return True @@ -200,6 +201,13 @@ class NetgearRouter: async with self._api_lock: return await self.hass.async_add_executor_job(self._api.get_traffic_meter) + async def async_allow_block_device(self, mac: str, allow_block: str) -> None: + """Allow or block a device connected to the router.""" + async with self._api_lock: + await self.hass.async_add_executor_job( + self._api.allow_block_device, mac, allow_block + ) + @property def port(self) -> int: """Port used by the API.""" diff --git a/homeassistant/components/netgear/switch.py b/homeassistant/components/netgear/switch.py new file mode 100644 index 00000000000..cf8cd835f89 --- /dev/null +++ b/homeassistant/components/netgear/switch.py @@ -0,0 +1,106 @@ +"""Support for Netgear switches.""" +import logging + +from pynetgear import ALLOW, BLOCK + +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import DOMAIN, KEY_COORDINATOR, KEY_ROUTER +from .router import NetgearDeviceEntity, NetgearRouter + +_LOGGER = logging.getLogger(__name__) + + +SWITCH_TYPES = [ + SwitchEntityDescription( + key="allow_or_block", + name="Allowed on network", + icon="mdi:block-helper", + entity_category=EntityCategory.CONFIG, + ) +] + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up switches for Netgear component.""" + router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER] + coordinator = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR] + tracked = set() + + @callback + def new_device_callback() -> None: + """Add new devices if needed.""" + new_entities = [] + if not coordinator.data: + return + + for mac, device in router.devices.items(): + if mac in tracked: + continue + + new_entities.extend( + [ + NetgearAllowBlock(coordinator, router, device, entity_description) + for entity_description in SWITCH_TYPES + ] + ) + tracked.add(mac) + + if new_entities: + async_add_entities(new_entities) + + entry.async_on_unload(coordinator.async_add_listener(new_device_callback)) + + coordinator.data = True + new_device_callback() + + +class NetgearAllowBlock(NetgearDeviceEntity, SwitchEntity): + """Allow or Block a device from the network.""" + + _attr_entity_registry_enabled_default = False + + def __init__( + self, + coordinator: DataUpdateCoordinator, + router: NetgearRouter, + device: dict, + entity_description: SwitchEntityDescription, + ) -> None: + """Initialize a Netgear device.""" + super().__init__(coordinator, router, device) + self.entity_description = entity_description + self._name = f"{self.get_device_name()} {self.entity_description.name}" + self._unique_id = f"{self._mac}-{self.entity_description.key}" + self._state = None + self.async_update_device() + + @property + def is_on(self): + """Return true if switch is on.""" + return self._state + + async def async_turn_on(self, **kwargs): + """Turn the switch on.""" + await self._router.async_allow_block_device(self._mac, ALLOW) + + async def async_turn_off(self, **kwargs): + """Turn the switch off.""" + await self._router.async_allow_block_device(self._mac, BLOCK) + + @callback + def async_update_device(self) -> None: + """Update the Netgear device.""" + self._device = self._router.devices[self._mac] + self._active = self._device["active"] + if self._device[self.entity_description.key] is None: + self._state = None + else: + self._state = self._device[self.entity_description.key] == "Allow"