diff --git a/homeassistant/components/unifi/__init__.py b/homeassistant/components/unifi/__init__.py index e435b68fc39..afe73babfe7 100644 --- a/homeassistant/components/unifi/__init__.py +++ b/homeassistant/components/unifi/__init__.py @@ -11,8 +11,8 @@ from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from .const import DOMAIN as UNIFI_DOMAIN, PLATFORMS, UNIFI_WIRELESS_CLIENTS -from .controller import UniFiController, get_unifi_controller from .errors import AuthenticationRequired, CannotConnect +from .hub import UnifiHub, get_unifi_api from .services import async_setup_services, async_unload_services SAVE_DELAY = 10 @@ -35,7 +35,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b hass.data.setdefault(UNIFI_DOMAIN, {}) try: - api = await get_unifi_controller(hass, config_entry.data) + api = await get_unifi_api(hass, config_entry.data) except CannotConnect as err: raise ConfigEntryNotReady from err @@ -43,20 +43,20 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b except AuthenticationRequired as err: raise ConfigEntryAuthFailed from err - controller = UniFiController(hass, config_entry, api) - await controller.initialize() - hass.data[UNIFI_DOMAIN][config_entry.entry_id] = controller + hub = UnifiHub(hass, config_entry, api) + await hub.initialize() + hass.data[UNIFI_DOMAIN][config_entry.entry_id] = hub await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) - controller.async_update_device_registry() + hub.async_update_device_registry() if len(hass.data[UNIFI_DOMAIN]) == 1: async_setup_services(hass) - controller.start_websocket() + hub.start_websocket() config_entry.async_on_unload( - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, controller.shutdown) + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, hub.shutdown) ) return True @@ -64,12 +64,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Unload a config entry.""" - controller: UniFiController = hass.data[UNIFI_DOMAIN].pop(config_entry.entry_id) + hub: UnifiHub = hass.data[UNIFI_DOMAIN].pop(config_entry.entry_id) if not hass.data[UNIFI_DOMAIN]: async_unload_services(hass) - return await controller.async_reset() + return await hub.async_reset() class UnifiWirelessClients: diff --git a/homeassistant/components/unifi/button.py b/homeassistant/components/unifi/button.py index c77a1f01447..f03971267bb 100644 --- a/homeassistant/components/unifi/button.py +++ b/homeassistant/components/unifi/button.py @@ -30,7 +30,6 @@ from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .controller import UniFiController from .entity import ( HandlerT, UnifiEntity, @@ -38,6 +37,7 @@ from .entity import ( async_device_available_fn, async_device_device_info_fn, ) +from .hub import UnifiHub @callback @@ -79,7 +79,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiButtonEntityDescription, ...] = ( entity_category=EntityCategory.CONFIG, has_entity_name=True, device_class=ButtonDeviceClass.RESTART, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, control_fn=async_restart_device_control_fn, @@ -89,15 +89,15 @@ ENTITY_DESCRIPTIONS: tuple[UnifiButtonEntityDescription, ...] = ( name_fn=lambda _: "Restart", object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"device_restart-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"device_restart-{obj_id}", ), UnifiButtonEntityDescription[Ports, Port]( key="PoE power cycle", entity_category=EntityCategory.CONFIG, has_entity_name=True, device_class=ButtonDeviceClass.RESTART, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.ports, available_fn=async_device_available_fn, control_fn=async_power_cycle_port_control_fn, @@ -107,8 +107,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiButtonEntityDescription, ...] = ( name_fn=lambda port: f"{port.name} Power Cycle", object_fn=lambda api, obj_id: api.ports[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: controller.api.ports[obj_id].port_poe, - unique_id_fn=lambda controller, obj_id: f"power_cycle-{obj_id}", + supported_fn=lambda hub, obj_id: hub.api.ports[obj_id].port_poe, + unique_id_fn=lambda hub, obj_id: f"power_cycle-{obj_id}", ), ) @@ -119,7 +119,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up button platform for UniFi Network integration.""" - UniFiController.register_platform( + UnifiHub.register_platform( hass, config_entry, async_add_entities, @@ -136,7 +136,7 @@ class UnifiButtonEntity(UnifiEntity[HandlerT, ApiItemT], ButtonEntity): async def async_press(self) -> None: """Press the button.""" - await self.entity_description.control_fn(self.controller.api, self._obj_id) + await self.entity_description.control_fn(self.hub.api, self._obj_id) @callback def async_update_state(self, event: ItemEvent, obj_id: str) -> None: diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index 85aceb68a4f..fabdc9849fa 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -47,8 +47,8 @@ from .const import ( DEFAULT_DPI_RESTRICTIONS, DOMAIN as UNIFI_DOMAIN, ) -from .controller import UniFiController, get_unifi_controller from .errors import AuthenticationRequired, CannotConnect +from .hub import UnifiHub, get_unifi_api DEFAULT_PORT = 443 DEFAULT_SITE_ID = "default" @@ -99,11 +99,9 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): } try: - controller = await get_unifi_controller( - self.hass, MappingProxyType(self.config) - ) - await controller.sites.update() - self.sites = controller.sites + hub = await get_unifi_api(self.hass, MappingProxyType(self.config)) + await hub.sites.update() + self.sites = hub.sites except AuthenticationRequired: errors["base"] = "faulty_credentials" @@ -160,11 +158,11 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): abort_reason = "reauth_successful" if config_entry: - controller: UniFiController | None = self.hass.data.get( - UNIFI_DOMAIN, {} - ).get(config_entry.entry_id) + hub: UnifiHub | None = self.hass.data.get(UNIFI_DOMAIN, {}).get( + config_entry.entry_id + ) - if controller and controller.available: + if hub and hub.available: return self.async_abort(reason="already_configured") return self.async_update_reload_and_abort( @@ -240,7 +238,7 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): class UnifiOptionsFlowHandler(config_entries.OptionsFlow): """Handle Unifi Network options.""" - controller: UniFiController + hub: UnifiHub def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize UniFi Network options flow.""" @@ -253,8 +251,8 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): """Manage the UniFi Network options.""" if self.config_entry.entry_id not in self.hass.data[UNIFI_DOMAIN]: return self.async_abort(reason="integration_not_setup") - self.controller = self.hass.data[UNIFI_DOMAIN][self.config_entry.entry_id] - self.options[CONF_BLOCK_CLIENT] = self.controller.option_block_clients + self.hub = self.hass.data[UNIFI_DOMAIN][self.config_entry.entry_id] + self.options[CONF_BLOCK_CLIENT] = self.hub.option_block_clients if self.show_advanced_options: return await self.async_step_configure_entity_sources() @@ -271,7 +269,7 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): clients_to_block = {} - for client in self.controller.api.clients.values(): + for client in self.hub.api.clients.values(): clients_to_block[ client.mac ] = f"{client.name or client.hostname} ({client.mac})" @@ -282,11 +280,11 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): { vol.Optional( CONF_TRACK_CLIENTS, - default=self.controller.option_track_clients, + default=self.hub.option_track_clients, ): bool, vol.Optional( CONF_TRACK_DEVICES, - default=self.controller.option_track_devices, + default=self.hub.option_track_devices, ): bool, vol.Optional( CONF_BLOCK_CLIENT, default=self.options[CONF_BLOCK_CLIENT] @@ -306,7 +304,7 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): clients = { client.mac: f"{client.name or client.hostname} ({client.mac})" - for client in self.controller.api.clients.values() + for client in self.hub.api.clients.values() } clients |= { mac: f"Unknown ({mac})" @@ -338,16 +336,16 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_client_control() ssids = ( - {wlan.name for wlan in self.controller.api.wlans.values()} + {wlan.name for wlan in self.hub.api.wlans.values()} | { f"{wlan.name}{wlan.name_combine_suffix}" - for wlan in self.controller.api.wlans.values() + for wlan in self.hub.api.wlans.values() if not wlan.name_combine_enabled and wlan.name_combine_suffix is not None } | { wlan["name"] - for ap in self.controller.api.devices.values() + for ap in self.hub.api.devices.values() for wlan in ap.wlan_overrides if "name" in wlan } @@ -355,7 +353,7 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): ssid_filter = {ssid: ssid for ssid in sorted(ssids)} selected_ssids_to_filter = [ - ssid for ssid in self.controller.option_ssid_filter if ssid in ssid_filter + ssid for ssid in self.hub.option_ssid_filter if ssid in ssid_filter ] return self.async_show_form( @@ -364,28 +362,26 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): { vol.Optional( CONF_TRACK_CLIENTS, - default=self.controller.option_track_clients, + default=self.hub.option_track_clients, ): bool, vol.Optional( CONF_TRACK_WIRED_CLIENTS, - default=self.controller.option_track_wired_clients, + default=self.hub.option_track_wired_clients, ): bool, vol.Optional( CONF_TRACK_DEVICES, - default=self.controller.option_track_devices, + default=self.hub.option_track_devices, ): bool, vol.Optional( CONF_SSID_FILTER, default=selected_ssids_to_filter ): cv.multi_select(ssid_filter), vol.Optional( CONF_DETECTION_TIME, - default=int( - self.controller.option_detection_time.total_seconds() - ), + default=int(self.hub.option_detection_time.total_seconds()), ): int, vol.Optional( CONF_IGNORE_WIRED_BUG, - default=self.controller.option_ignore_wired_bug, + default=self.hub.option_ignore_wired_bug, ): bool, } ), @@ -402,7 +398,7 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): clients_to_block = {} - for client in self.controller.api.clients.values(): + for client in self.hub.api.clients.values(): clients_to_block[ client.mac ] = f"{client.name or client.hostname} ({client.mac})" @@ -445,11 +441,11 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): { vol.Optional( CONF_ALLOW_BANDWIDTH_SENSORS, - default=self.controller.option_allow_bandwidth_sensors, + default=self.hub.option_allow_bandwidth_sensors, ): bool, vol.Optional( CONF_ALLOW_UPTIME_SENSORS, - default=self.controller.option_allow_uptime_sensors, + default=self.hub.option_allow_uptime_sensors, ): bool, } ), diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 88667d8e811..fe36125a6c4 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -25,13 +25,13 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.helpers.entity_registry as er import homeassistant.util.dt as dt_util -from .controller import UNIFI_DOMAIN, UniFiController from .entity import ( HandlerT, UnifiEntity, UnifiEntityDescription, async_device_available_fn, ) +from .hub import UNIFI_DOMAIN, UnifiHub LOGGER = logging.getLogger(__name__) @@ -79,23 +79,23 @@ WIRELESS_DISCONNECTION = ( @callback -def async_client_allowed_fn(controller: UniFiController, obj_id: str) -> bool: +def async_client_allowed_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if client is allowed.""" - if obj_id in controller.option_supported_clients: + if obj_id in hub.option_supported_clients: return True - if not controller.option_track_clients: + if not hub.option_track_clients: return False - client = controller.api.clients[obj_id] - if client.mac not in controller.wireless_clients: - if not controller.option_track_wired_clients: + client = hub.api.clients[obj_id] + if client.mac not in hub.wireless_clients: + if not hub.option_track_wired_clients: return False elif ( client.essid - and controller.option_ssid_filter - and client.essid not in controller.option_ssid_filter + and hub.option_ssid_filter + and client.essid not in hub.option_ssid_filter ): return False @@ -103,25 +103,25 @@ def async_client_allowed_fn(controller: UniFiController, obj_id: str) -> bool: @callback -def async_client_is_connected_fn(controller: UniFiController, obj_id: str) -> bool: +def async_client_is_connected_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if device object is disabled.""" - client = controller.api.clients[obj_id] + client = hub.api.clients[obj_id] - if controller.wireless_clients.is_wireless(client) and client.is_wired: - if not controller.option_ignore_wired_bug: + if hub.wireless_clients.is_wireless(client) and client.is_wired: + if not hub.option_ignore_wired_bug: return False # Wired bug in action if ( not client.is_wired and client.essid - and controller.option_ssid_filter - and client.essid not in controller.option_ssid_filter + and hub.option_ssid_filter + and client.essid not in hub.option_ssid_filter ): return False if ( dt_util.utcnow() - dt_util.utc_from_timestamp(client.last_seen or 0) - > controller.option_detection_time + > hub.option_detection_time ): return False @@ -129,11 +129,9 @@ def async_client_is_connected_fn(controller: UniFiController, obj_id: str) -> bo @callback -def async_device_heartbeat_timedelta_fn( - controller: UniFiController, obj_id: str -) -> timedelta: +def async_device_heartbeat_timedelta_fn(hub: UnifiHub, obj_id: str) -> timedelta: """Check if device object is disabled.""" - device = controller.api.devices[obj_id] + device = hub.api.devices[obj_id] return timedelta(seconds=device.next_interval + 60) @@ -141,9 +139,9 @@ def async_device_heartbeat_timedelta_fn( class UnifiEntityTrackerDescriptionMixin(Generic[HandlerT, ApiItemT]): """Device tracker local functions.""" - heartbeat_timedelta_fn: Callable[[UniFiController, str], timedelta] + heartbeat_timedelta_fn: Callable[[UnifiHub, str], timedelta] ip_address_fn: Callable[[aiounifi.Controller, str], str | None] - is_connected_fn: Callable[[UniFiController, str], bool] + is_connected_fn: Callable[[UnifiHub, str], bool] hostname_fn: Callable[[aiounifi.Controller, str], str | None] @@ -161,7 +159,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = ( has_entity_name=True, allowed_fn=async_client_allowed_fn, api_handler_fn=lambda api: api.clients, - available_fn=lambda controller, obj_id: controller.available, + available_fn=lambda hub, obj_id: hub.available, device_info_fn=lambda api, obj_id: None, event_is_on=(WIRED_CONNECTION + WIRELESS_CONNECTION), event_to_subscribe=( @@ -170,20 +168,20 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = ( + WIRELESS_CONNECTION + WIRELESS_DISCONNECTION ), - heartbeat_timedelta_fn=lambda controller, _: controller.option_detection_time, + heartbeat_timedelta_fn=lambda hub, _: hub.option_detection_time, is_connected_fn=async_client_is_connected_fn, name_fn=lambda client: client.name or client.hostname, object_fn=lambda api, obj_id: api.clients[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"{controller.site}-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"{hub.site}-{obj_id}", ip_address_fn=lambda api, obj_id: api.clients[obj_id].ip, hostname_fn=lambda api, obj_id: api.clients[obj_id].hostname, ), UnifiTrackerEntityDescription[Devices, Device]( key="Device scanner", has_entity_name=True, - allowed_fn=lambda controller, obj_id: controller.option_track_devices, + allowed_fn=lambda hub, obj_id: hub.option_track_devices, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=lambda api, obj_id: None, @@ -194,8 +192,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = ( name_fn=lambda device: device.name or device.model, object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: obj_id, + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: obj_id, ip_address_fn=lambda api, obj_id: api.devices[obj_id].ip, hostname_fn=lambda api, obj_id: None, ), @@ -208,21 +206,21 @@ def async_update_unique_id(hass: HomeAssistant, config_entry: ConfigEntry) -> No Introduced with release 2023.12. """ - controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub: UnifiHub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] ent_reg = er.async_get(hass) @callback def update_unique_id(obj_id: str) -> None: """Rework unique ID.""" - new_unique_id = f"{controller.site}-{obj_id}" + new_unique_id = f"{hub.site}-{obj_id}" if ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, new_unique_id): return - unique_id = f"{obj_id}-{controller.site}" + unique_id = f"{obj_id}-{hub.site}" if entity_id := ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, unique_id): ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) - for obj_id in list(controller.api.clients) + list(controller.api.clients_all): + for obj_id in list(hub.api.clients) + list(hub.api.clients_all): update_unique_id(obj_id) @@ -233,7 +231,7 @@ async def async_setup_entry( ) -> None: """Set up device tracker for UniFi Network integration.""" async_update_unique_id(hass, config_entry) - UniFiController.register_platform( + UnifiHub.register_platform( hass, config_entry, async_add_entities, UnifiScannerEntity, ENTITY_DESCRIPTIONS ) @@ -256,12 +254,12 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): description = self.entity_description self._event_is_on = description.event_is_on or () self._ignore_events = False - self._is_connected = description.is_connected_fn(self.controller, self._obj_id) + self._is_connected = description.is_connected_fn(self.hub, self._obj_id) if self.is_connected: - self.controller.async_heartbeat( + self.hub.async_heartbeat( self.unique_id, dt_util.utcnow() - + description.heartbeat_timedelta_fn(self.controller, self._obj_id), + + description.heartbeat_timedelta_fn(self.hub, self._obj_id), ) @property @@ -272,12 +270,12 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): @property def hostname(self) -> str | None: """Return hostname of the device.""" - return self.entity_description.hostname_fn(self.controller.api, self._obj_id) + return self.entity_description.hostname_fn(self.hub.api, self._obj_id) @property def ip_address(self) -> str | None: """Return the primary ip address of the device.""" - return self.entity_description.ip_address_fn(self.controller.api, self._obj_id) + return self.entity_description.ip_address_fn(self.hub.api, self._obj_id) @property def mac_address(self) -> str: @@ -304,7 +302,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): def async_update_state(self, event: ItemEvent, obj_id: str) -> None: """Update entity state. - Remove heartbeat check if controller state has changed + Remove heartbeat check if hub connection state has changed and entity is unavailable. Update is_connected. Schedule new heartbeat check if connected. @@ -319,15 +317,15 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): # From unifi.entity.async_signal_reachable_callback # Controller connection state has changed and entity is unavailable # Cancel heartbeat - self.controller.async_heartbeat(self.unique_id) + self.hub.async_heartbeat(self.unique_id) return - if is_connected := description.is_connected_fn(self.controller, self._obj_id): + if is_connected := description.is_connected_fn(self.hub, self._obj_id): self._is_connected = is_connected - self.controller.async_heartbeat( + self.hub.async_heartbeat( self.unique_id, dt_util.utcnow() - + description.heartbeat_timedelta_fn(self.controller, self._obj_id), + + description.heartbeat_timedelta_fn(self.hub, self._obj_id), ) @callback @@ -337,17 +335,15 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): return if event.key in self._event_is_on: - self.controller.async_heartbeat(self.unique_id) + self.hub.async_heartbeat(self.unique_id) self._is_connected = True self.async_write_ha_state() return - self.controller.async_heartbeat( + self.hub.async_heartbeat( self.unique_id, dt_util.utcnow() - + self.entity_description.heartbeat_timedelta_fn( - self.controller, self._obj_id - ), + + self.entity_description.heartbeat_timedelta_fn(self.hub, self._obj_id), ) async def async_added_to_hass(self) -> None: @@ -356,7 +352,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): self.async_on_remove( async_dispatcher_connect( self.hass, - f"{self.controller.signal_heartbeat_missed}_{self.unique_id}", + f"{self.hub.signal_heartbeat_missed}_{self.unique_id}", self._make_disconnected, ) ) @@ -364,7 +360,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): async def async_will_remove_from_hass(self) -> None: """Disconnect object when removed.""" await super().async_will_remove_from_hass() - self.controller.async_heartbeat(self.unique_id) + self.hub.async_heartbeat(self.unique_id) @property def extra_state_attributes(self) -> Mapping[str, Any] | None: @@ -372,7 +368,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity): if self.entity_description.key != "Client device scanner": return None - client = self.entity_description.object_fn(self.controller.api, self._obj_id) + client = self.entity_description.object_fn(self.hub.api, self._obj_id) raw = client.raw attributes_to_check = CLIENT_STATIC_ATTRIBUTES diff --git a/homeassistant/components/unifi/diagnostics.py b/homeassistant/components/unifi/diagnostics.py index c01dc193078..2482f5ca314 100644 --- a/homeassistant/components/unifi/diagnostics.py +++ b/homeassistant/components/unifi/diagnostics.py @@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import format_mac from .const import DOMAIN as UNIFI_DOMAIN -from .controller import UniFiController +from .hub import UnifiHub TO_REDACT = {CONF_PASSWORD} REDACT_CONFIG = {CONF_HOST, CONF_PASSWORD, CONF_USERNAME} @@ -75,16 +75,16 @@ async def async_get_config_entry_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub: UnifiHub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] diag: dict[str, Any] = {} macs_to_redact: dict[str, str] = {} counter = 0 - for mac in chain(controller.api.clients, controller.api.devices): + for mac in chain(hub.api.clients, hub.api.devices): macs_to_redact[mac] = format_mac(str(counter).zfill(12)) counter += 1 - for device in controller.api.devices.values(): + for device in hub.api.devices.values(): for entry in device.raw.get("ethernet_table", []): mac = entry.get("mac", "") if mac not in macs_to_redact: @@ -94,26 +94,26 @@ async def async_get_config_entry_diagnostics( diag["config"] = async_redact_data( async_replace_dict_data(config_entry.as_dict(), macs_to_redact), REDACT_CONFIG ) - diag["role_is_admin"] = controller.is_admin + diag["role_is_admin"] = hub.is_admin diag["clients"] = { macs_to_redact[k]: async_redact_data( async_replace_dict_data(v.raw, macs_to_redact), REDACT_CLIENTS ) - for k, v in controller.api.clients.items() + for k, v in hub.api.clients.items() } diag["devices"] = { macs_to_redact[k]: async_redact_data( async_replace_dict_data(v.raw, macs_to_redact), REDACT_DEVICES ) - for k, v in controller.api.devices.items() + for k, v in hub.api.devices.items() } - diag["dpi_apps"] = {k: v.raw for k, v in controller.api.dpi_apps.items()} - diag["dpi_groups"] = {k: v.raw for k, v in controller.api.dpi_groups.items()} + diag["dpi_apps"] = {k: v.raw for k, v in hub.api.dpi_apps.items()} + diag["dpi_groups"] = {k: v.raw for k, v in hub.api.dpi_groups.items()} diag["wlans"] = { k: async_redact_data( async_replace_dict_data(v.raw, macs_to_redact), REDACT_WLANS ) - for k, v in controller.api.wlans.items() + for k, v in hub.api.wlans.items() } return diag diff --git a/homeassistant/components/unifi/entity.py b/homeassistant/components/unifi/entity.py index 08dda12c11d..a88f4c9b657 100644 --- a/homeassistant/components/unifi/entity.py +++ b/homeassistant/components/unifi/entity.py @@ -29,36 +29,36 @@ from homeassistant.helpers.entity import Entity, EntityDescription from .const import ATTR_MANUFACTURER, DOMAIN if TYPE_CHECKING: - from .controller import UniFiController + from .hub import UnifiHub HandlerT = TypeVar("HandlerT", bound=APIHandler) SubscriptionT = Callable[[CallbackType, ItemEvent], UnsubscribeType] @callback -def async_device_available_fn(controller: UniFiController, obj_id: str) -> bool: +def async_device_available_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if device is available.""" if "_" in obj_id: # Sub device (outlet or port) obj_id = obj_id.partition("_")[0] - device = controller.api.devices[obj_id] - return controller.available and not device.disabled + device = hub.api.devices[obj_id] + return hub.available and not device.disabled @callback -def async_wlan_available_fn(controller: UniFiController, obj_id: str) -> bool: +def async_wlan_available_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if WLAN is available.""" - wlan = controller.api.wlans[obj_id] - return controller.available and wlan.enabled + wlan = hub.api.wlans[obj_id] + return hub.available and wlan.enabled @callback -def async_device_device_info_fn(controller: UniFiController, obj_id: str) -> DeviceInfo: +def async_device_device_info_fn(hub: UnifiHub, obj_id: str) -> DeviceInfo: """Create device registry entry for device.""" if "_" in obj_id: # Sub device (outlet or port) obj_id = obj_id.partition("_")[0] - device = controller.api.devices[obj_id] + device = hub.api.devices[obj_id] return DeviceInfo( connections={(CONNECTION_NETWORK_MAC, device.mac)}, manufacturer=ATTR_MANUFACTURER, @@ -70,9 +70,9 @@ def async_device_device_info_fn(controller: UniFiController, obj_id: str) -> Dev @callback -def async_wlan_device_info_fn(controller: UniFiController, obj_id: str) -> DeviceInfo: +def async_wlan_device_info_fn(hub: UnifiHub, obj_id: str) -> DeviceInfo: """Create device registry entry for WLAN.""" - wlan = controller.api.wlans[obj_id] + wlan = hub.api.wlans[obj_id] return DeviceInfo( entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, wlan.id)}, @@ -83,9 +83,9 @@ def async_wlan_device_info_fn(controller: UniFiController, obj_id: str) -> Devic @callback -def async_client_device_info_fn(controller: UniFiController, obj_id: str) -> DeviceInfo: +def async_client_device_info_fn(hub: UnifiHub, obj_id: str) -> DeviceInfo: """Create device registry entry for client.""" - client = controller.api.clients[obj_id] + client = hub.api.clients[obj_id] return DeviceInfo( connections={(CONNECTION_NETWORK_MAC, obj_id)}, default_manufacturer=client.oui, @@ -97,17 +97,17 @@ def async_client_device_info_fn(controller: UniFiController, obj_id: str) -> Dev class UnifiDescription(Generic[HandlerT, ApiItemT]): """Validate and load entities from different UniFi handlers.""" - allowed_fn: Callable[[UniFiController, str], bool] + allowed_fn: Callable[[UnifiHub, str], bool] api_handler_fn: Callable[[aiounifi.Controller], HandlerT] - available_fn: Callable[[UniFiController, str], bool] - device_info_fn: Callable[[UniFiController, str], DeviceInfo | None] + available_fn: Callable[[UnifiHub, str], bool] + device_info_fn: Callable[[UnifiHub, str], DeviceInfo | None] event_is_on: tuple[EventKey, ...] | None event_to_subscribe: tuple[EventKey, ...] | None name_fn: Callable[[ApiItemT], str | None] object_fn: Callable[[aiounifi.Controller, str], ApiItemT] should_poll: bool - supported_fn: Callable[[UniFiController, str], bool | None] - unique_id_fn: Callable[[UniFiController, str], str] + supported_fn: Callable[[UnifiHub, str], bool | None] + unique_id_fn: Callable[[UnifiHub, str], str] @dataclass(frozen=True) @@ -124,36 +124,36 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]): def __init__( self, obj_id: str, - controller: UniFiController, + hub: UnifiHub, description: UnifiEntityDescription[HandlerT, ApiItemT], ) -> None: """Set up UniFi switch entity.""" self._obj_id = obj_id - self.controller = controller + self.hub = hub self.entity_description = description - controller.known_objects.add((description.key, obj_id)) + hub.known_objects.add((description.key, obj_id)) self._removed = False - self._attr_available = description.available_fn(controller, obj_id) - self._attr_device_info = description.device_info_fn(controller, obj_id) + self._attr_available = description.available_fn(hub, obj_id) + self._attr_device_info = description.device_info_fn(hub, obj_id) self._attr_should_poll = description.should_poll - self._attr_unique_id = description.unique_id_fn(controller, obj_id) + self._attr_unique_id = description.unique_id_fn(hub, obj_id) - obj = description.object_fn(self.controller.api, obj_id) + obj = description.object_fn(self.hub.api, obj_id) self._attr_name = description.name_fn(obj) self.async_initiate_state() async def async_added_to_hass(self) -> None: """Register callbacks.""" description = self.entity_description - handler = description.api_handler_fn(self.controller.api) + handler = description.api_handler_fn(self.hub.api) @callback def unregister_object() -> None: """Remove object ID from known_objects when unloaded.""" - self.controller.known_objects.discard((description.key, self._obj_id)) + self.hub.known_objects.discard((description.key, self._obj_id)) self.async_on_remove(unregister_object) @@ -165,11 +165,11 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]): ) ) - # State change from controller or websocket + # State change from hub or websocket self.async_on_remove( async_dispatcher_connect( self.hass, - self.controller.signal_reachable, + self.hub.signal_reachable, self.async_signal_reachable_callback, ) ) @@ -178,7 +178,7 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]): self.async_on_remove( async_dispatcher_connect( self.hass, - self.controller.signal_options_update, + self.hub.signal_options_update, self.async_signal_options_updated, ) ) @@ -186,7 +186,7 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]): # Subscribe to events if defined if description.event_to_subscribe is not None: self.async_on_remove( - self.controller.api.events.subscribe( + self.hub.api.events.subscribe( self.async_event_callback, description.event_to_subscribe, ) @@ -200,22 +200,22 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]): return description = self.entity_description - if not description.supported_fn(self.controller, self._obj_id): + if not description.supported_fn(self.hub, self._obj_id): self.hass.async_create_task(self.remove_item({self._obj_id})) return - self._attr_available = description.available_fn(self.controller, self._obj_id) + self._attr_available = description.available_fn(self.hub, self._obj_id) self.async_update_state(event, obj_id) self.async_write_ha_state() @callback def async_signal_reachable_callback(self) -> None: - """Call when controller connection state change.""" + """Call when hub connection state change.""" self.async_signalling_callback(ItemEvent.ADDED, self._obj_id) async def async_signal_options_updated(self) -> None: """Config entry options are updated, remove entity if option is disabled.""" - if not self.entity_description.allowed_fn(self.controller, self._obj_id): + if not self.entity_description.allowed_fn(self.hub, self._obj_id): await self.remove_item({self._obj_id}) async def remove_item(self, keys: set) -> None: diff --git a/homeassistant/components/unifi/errors.py b/homeassistant/components/unifi/errors.py index c3b2bb23d8e..568bd5fb842 100644 --- a/homeassistant/components/unifi/errors.py +++ b/homeassistant/components/unifi/errors.py @@ -15,7 +15,7 @@ class AuthenticationRequired(UnifiException): class CannotConnect(UnifiException): - """Unable to connect to the controller.""" + """Unable to connect to UniFi Network.""" class LoginRequired(UnifiException): diff --git a/homeassistant/components/unifi/controller.py b/homeassistant/components/unifi/hub.py similarity index 96% rename from homeassistant/components/unifi/controller.py rename to homeassistant/components/unifi/hub.py index 5ef8963e2ac..5604ecbe400 100644 --- a/homeassistant/components/unifi/controller.py +++ b/homeassistant/components/unifi/hub.py @@ -80,7 +80,7 @@ CHECK_HEARTBEAT_INTERVAL = timedelta(seconds=1) CHECK_WEBSOCKET_INTERVAL = timedelta(minutes=1) -class UniFiController: +class UnifiHub: """Manages a single UniFi Network instance.""" def __init__( @@ -165,7 +165,7 @@ class UniFiController: @property def host(self) -> str: - """Return the host of this controller.""" + """Return the host of this hub.""" host: str = self.config_entry.data[CONF_HOST] return host @@ -180,10 +180,10 @@ class UniFiController: requires_admin: bool = False, ) -> None: """Register platform for UniFi entity management.""" - controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - if requires_admin and not controller.is_admin: + hub: UnifiHub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + if requires_admin and not hub.is_admin: return - controller.register_platform_add_entities( + hub.register_platform_add_entities( entity_class, descriptions, async_add_entities ) @@ -351,7 +351,7 @@ class UniFiController: @property def device_info(self) -> DeviceInfo: - """UniFi controller device info.""" + """UniFi Network device info.""" assert self.config_entry.unique_id is not None version: str | None = None @@ -384,10 +384,10 @@ class UniFiController: If config entry is updated due to reauth flow the entry might already have been reset and thus is not available. """ - if not (controller := hass.data[UNIFI_DOMAIN].get(config_entry.entry_id)): + if not (hub := hass.data[UNIFI_DOMAIN].get(config_entry.entry_id)): return - controller.load_config_entry_options() - async_dispatcher_send(hass, controller.signal_options_update) + hub.load_config_entry_options() + async_dispatcher_send(hass, hub.signal_options_update) @callback def start_websocket(self) -> None: @@ -449,7 +449,7 @@ class UniFiController: self.ws_task.cancel() async def async_reset(self) -> bool: - """Reset this controller to default state. + """Reset this hub to default state. Will cancel any scheduled setup retry and will unload the config entry. @@ -489,11 +489,11 @@ class UniFiController: return True -async def get_unifi_controller( +async def get_unifi_api( hass: HomeAssistant, config: MappingProxyType[str, Any], ) -> aiounifi.Controller: - """Create a controller object and verify authentication.""" + """Create a aiounifi object and verify authentication.""" ssl_context: ssl.SSLContext | Literal[False] = False if verify_ssl := config.get(CONF_VERIFY_SSL): @@ -505,7 +505,7 @@ async def get_unifi_controller( hass, verify_ssl=False, cookie_jar=CookieJar(unsafe=True) ) - controller = aiounifi.Controller( + api = aiounifi.Controller( Configuration( session, host=config[CONF_HOST], @@ -519,8 +519,8 @@ async def get_unifi_controller( try: async with asyncio.timeout(10): - await controller.login() - return controller + await api.login() + return api except aiounifi.Unauthorized as err: LOGGER.warning( diff --git a/homeassistant/components/unifi/image.py b/homeassistant/components/unifi/image.py index a4fb8d5eb33..a070c158772 100644 --- a/homeassistant/components/unifi/image.py +++ b/homeassistant/components/unifi/image.py @@ -20,7 +20,6 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.util.dt as dt_util -from .controller import UniFiController from .entity import ( HandlerT, UnifiEntity, @@ -28,19 +27,20 @@ from .entity import ( async_wlan_available_fn, async_wlan_device_info_fn, ) +from .hub import UnifiHub @callback -def async_wlan_qr_code_image_fn(controller: UniFiController, wlan: Wlan) -> bytes: +def async_wlan_qr_code_image_fn(hub: UnifiHub, wlan: Wlan) -> bytes: """Calculate receiving data transfer value.""" - return controller.api.wlans.generate_wlan_qr_code(wlan) + return hub.api.wlans.generate_wlan_qr_code(wlan) @dataclass(frozen=True) class UnifiImageEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): """Validate and load entities from different UniFi handlers.""" - image_fn: Callable[[UniFiController, ApiItemT], bytes] + image_fn: Callable[[UnifiHub, ApiItemT], bytes] value_fn: Callable[[ApiItemT], str | None] @@ -59,7 +59,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiImageEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, has_entity_name=True, entity_registry_enabled_default=False, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.wlans, available_fn=async_wlan_available_fn, device_info_fn=async_wlan_device_info_fn, @@ -68,8 +68,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiImageEntityDescription, ...] = ( name_fn=lambda wlan: "QR Code", object_fn=lambda api, obj_id: api.wlans[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"qr_code-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"qr_code-{obj_id}", image_fn=async_wlan_qr_code_image_fn, value_fn=lambda obj: obj.x_passphrase, ), @@ -82,7 +82,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up image platform for UniFi Network integration.""" - UniFiController.register_platform( + UnifiHub.register_platform( hass, config_entry, async_add_entities, @@ -104,26 +104,26 @@ class UnifiImageEntity(UnifiEntity[HandlerT, ApiItemT], ImageEntity): def __init__( self, obj_id: str, - controller: UniFiController, + hub: UnifiHub, description: UnifiEntityDescription[HandlerT, ApiItemT], ) -> None: """Initiatlize UniFi Image entity.""" - super().__init__(obj_id, controller, description) - ImageEntity.__init__(self, controller.hass) + super().__init__(obj_id, hub, description) + ImageEntity.__init__(self, hub.hass) def image(self) -> bytes | None: """Return bytes of image.""" if self.current_image is None: description = self.entity_description - obj = description.object_fn(self.controller.api, self._obj_id) - self.current_image = description.image_fn(self.controller, obj) + obj = description.object_fn(self.hub.api, self._obj_id) + self.current_image = description.image_fn(self.hub, obj) return self.current_image @callback def async_update_state(self, event: ItemEvent, obj_id: str) -> None: """Update entity state.""" description = self.entity_description - obj = description.object_fn(self.controller.api, self._obj_id) + obj = description.object_fn(self.hub.api, self._obj_id) if (value := description.value_fn(obj)) != self.previous_value: self.previous_value = value self.current_image = None diff --git a/homeassistant/components/unifi/sensor.py b/homeassistant/components/unifi/sensor.py index a0cd3a7f1e7..ab76e662859 100644 --- a/homeassistant/components/unifi/sensor.py +++ b/homeassistant/components/unifi/sensor.py @@ -40,7 +40,6 @@ from homeassistant.helpers.typing import StateType import homeassistant.util.dt as dt_util from .const import DEVICE_STATES -from .controller import UniFiController from .entity import ( HandlerT, UnifiEntity, @@ -51,44 +50,43 @@ from .entity import ( async_wlan_available_fn, async_wlan_device_info_fn, ) +from .hub import UnifiHub @callback -def async_bandwidth_sensor_allowed_fn(controller: UniFiController, obj_id: str) -> bool: +def async_bandwidth_sensor_allowed_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if client is allowed.""" - if obj_id in controller.option_supported_clients: + if obj_id in hub.option_supported_clients: return True - return controller.option_allow_bandwidth_sensors + return hub.option_allow_bandwidth_sensors @callback -def async_uptime_sensor_allowed_fn(controller: UniFiController, obj_id: str) -> bool: +def async_uptime_sensor_allowed_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if client is allowed.""" - if obj_id in controller.option_supported_clients: + if obj_id in hub.option_supported_clients: return True - return controller.option_allow_uptime_sensors + return hub.option_allow_uptime_sensors @callback -def async_client_rx_value_fn(controller: UniFiController, client: Client) -> float: +def async_client_rx_value_fn(hub: UnifiHub, client: Client) -> float: """Calculate receiving data transfer value.""" - if controller.wireless_clients.is_wireless(client): + if hub.wireless_clients.is_wireless(client): return client.rx_bytes_r / 1000000 return client.wired_rx_bytes_r / 1000000 @callback -def async_client_tx_value_fn(controller: UniFiController, client: Client) -> float: +def async_client_tx_value_fn(hub: UnifiHub, client: Client) -> float: """Calculate transmission data transfer value.""" - if controller.wireless_clients.is_wireless(client): + if hub.wireless_clients.is_wireless(client): return client.tx_bytes_r / 1000000 return client.wired_tx_bytes_r / 1000000 @callback -def async_client_uptime_value_fn( - controller: UniFiController, client: Client -) -> datetime: +def async_client_uptime_value_fn(hub: UnifiHub, client: Client) -> datetime: """Calculate the uptime of the client.""" if client.uptime < 1000000000: return dt_util.now() - timedelta(seconds=client.uptime) @@ -96,23 +94,21 @@ def async_client_uptime_value_fn( @callback -def async_wlan_client_value_fn(controller: UniFiController, wlan: Wlan) -> int: +def async_wlan_client_value_fn(hub: UnifiHub, wlan: Wlan) -> int: """Calculate the amount of clients connected to a wlan.""" return len( [ client.mac - for client in controller.api.clients.values() + for client in hub.api.clients.values() if client.essid == wlan.name and dt_util.utcnow() - dt_util.utc_from_timestamp(client.last_seen or 0) - < controller.option_detection_time + < hub.option_detection_time ] ) @callback -def async_device_uptime_value_fn( - controller: UniFiController, device: Device -) -> datetime | None: +def async_device_uptime_value_fn(hub: UnifiHub, device: Device) -> datetime | None: """Calculate the approximate time the device started (based on uptime returned from API, in seconds).""" if device.uptime <= 0: # Library defaults to 0 if uptime is not provided, e.g. when offline @@ -131,29 +127,27 @@ def async_device_uptime_value_changed_fn( @callback -def async_device_outlet_power_supported_fn( - controller: UniFiController, obj_id: str -) -> bool: +def async_device_outlet_power_supported_fn(hub: UnifiHub, obj_id: str) -> bool: """Determine if an outlet has the power property.""" # At this time, an outlet_caps value of 3 is expected to indicate that the outlet # supports metering - return controller.api.outlets[obj_id].caps == 3 + return hub.api.outlets[obj_id].caps == 3 @callback -def async_device_outlet_supported_fn(controller: UniFiController, obj_id: str) -> bool: +def async_device_outlet_supported_fn(hub: UnifiHub, obj_id: str) -> bool: """Determine if a device supports reading overall power metrics.""" - return controller.api.devices[obj_id].outlet_ac_power_budget is not None + return hub.api.devices[obj_id].outlet_ac_power_budget is not None @callback -def async_client_is_connected_fn(controller: UniFiController, obj_id: str) -> bool: +def async_client_is_connected_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if client was last seen recently.""" - client = controller.api.clients[obj_id] + client = hub.api.clients[obj_id] if ( dt_util.utcnow() - dt_util.utc_from_timestamp(client.last_seen or 0) - > controller.option_detection_time + > hub.option_detection_time ): return False @@ -164,11 +158,11 @@ def async_client_is_connected_fn(controller: UniFiController, obj_id: str) -> bo class UnifiSensorEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): """Validate and load entities from different UniFi handlers.""" - value_fn: Callable[[UniFiController, ApiItemT], datetime | float | str | None] + value_fn: Callable[[UnifiHub, ApiItemT], datetime | float | str | None] @callback -def async_device_state_value_fn(controller: UniFiController, device: Device) -> str: +def async_device_state_value_fn(hub: UnifiHub, device: Device) -> str: """Retrieve the state of the device.""" return DEVICE_STATES[device.state] @@ -181,7 +175,7 @@ class UnifiSensorEntityDescription( ): """Class describing UniFi sensor entity.""" - is_connected_fn: Callable[[UniFiController, str], bool] | None = None + is_connected_fn: Callable[[UnifiHub, str], bool] | None = None # Custom function to determine whether a state change should be recorded value_changed_fn: Callable[ [StateType | date | datetime | Decimal, datetime | float | str | None], @@ -200,7 +194,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( has_entity_name=True, allowed_fn=async_bandwidth_sensor_allowed_fn, api_handler_fn=lambda api: api.clients, - available_fn=lambda controller, _: controller.available, + available_fn=lambda hub, _: hub.available, device_info_fn=async_client_device_info_fn, event_is_on=None, event_to_subscribe=None, @@ -208,8 +202,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( name_fn=lambda _: "RX", object_fn=lambda api, obj_id: api.clients[obj_id], should_poll=False, - supported_fn=lambda controller, _: controller.option_allow_bandwidth_sensors, - unique_id_fn=lambda controller, obj_id: f"rx-{obj_id}", + supported_fn=lambda hub, _: hub.option_allow_bandwidth_sensors, + unique_id_fn=lambda hub, obj_id: f"rx-{obj_id}", value_fn=async_client_rx_value_fn, ), UnifiSensorEntityDescription[Clients, Client]( @@ -222,7 +216,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( has_entity_name=True, allowed_fn=async_bandwidth_sensor_allowed_fn, api_handler_fn=lambda api: api.clients, - available_fn=lambda controller, _: controller.available, + available_fn=lambda hub, _: hub.available, device_info_fn=async_client_device_info_fn, event_is_on=None, event_to_subscribe=None, @@ -230,8 +224,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( name_fn=lambda _: "TX", object_fn=lambda api, obj_id: api.clients[obj_id], should_poll=False, - supported_fn=lambda controller, _: controller.option_allow_bandwidth_sensors, - unique_id_fn=lambda controller, obj_id: f"tx-{obj_id}", + supported_fn=lambda hub, _: hub.option_allow_bandwidth_sensors, + unique_id_fn=lambda hub, obj_id: f"tx-{obj_id}", value_fn=async_client_tx_value_fn, ), UnifiSensorEntityDescription[Ports, Port]( @@ -241,7 +235,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( native_unit_of_measurement=UnitOfPower.WATT, has_entity_name=True, entity_registry_enabled_default=False, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.ports, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -250,8 +244,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( name_fn=lambda port: f"{port.name} PoE Power", object_fn=lambda api, obj_id: api.ports[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: controller.api.ports[obj_id].port_poe, - unique_id_fn=lambda controller, obj_id: f"poe_power-{obj_id}", + supported_fn=lambda hub, obj_id: hub.api.ports[obj_id].port_poe, + unique_id_fn=lambda hub, obj_id: f"poe_power-{obj_id}", value_fn=lambda _, obj: obj.poe_power if obj.poe_mode != "off" else "0", ), UnifiSensorEntityDescription[Clients, Client]( @@ -262,15 +256,15 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( entity_registry_enabled_default=False, allowed_fn=async_uptime_sensor_allowed_fn, api_handler_fn=lambda api: api.clients, - available_fn=lambda controller, obj_id: controller.available, + available_fn=lambda hub, obj_id: hub.available, device_info_fn=async_client_device_info_fn, event_is_on=None, event_to_subscribe=None, name_fn=lambda client: "Uptime", object_fn=lambda api, obj_id: api.clients[obj_id], should_poll=False, - supported_fn=lambda controller, _: controller.option_allow_uptime_sensors, - unique_id_fn=lambda controller, obj_id: f"uptime-{obj_id}", + supported_fn=lambda hub, _: hub.option_allow_uptime_sensors, + unique_id_fn=lambda hub, obj_id: f"uptime-{obj_id}", value_fn=async_client_uptime_value_fn, ), UnifiSensorEntityDescription[Wlans, Wlan]( @@ -278,7 +272,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, has_entity_name=True, state_class=SensorStateClass.MEASUREMENT, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.wlans, available_fn=async_wlan_available_fn, device_info_fn=async_wlan_device_info_fn, @@ -287,8 +281,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( name_fn=lambda wlan: None, object_fn=lambda api, obj_id: api.wlans[obj_id], should_poll=True, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"wlan_clients-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"wlan_clients-{obj_id}", value_fn=async_wlan_client_value_fn, ), UnifiSensorEntityDescription[Outlets, Outlet]( @@ -297,7 +291,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfPower.WATT, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.outlets, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -307,7 +301,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( object_fn=lambda api, obj_id: api.outlets[obj_id], should_poll=True, supported_fn=async_device_outlet_power_supported_fn, - unique_id_fn=lambda controller, obj_id: f"outlet_power-{obj_id}", + unique_id_fn=lambda hub, obj_id: f"outlet_power-{obj_id}", value_fn=lambda _, obj: obj.power if obj.relay_state else "0", ), UnifiSensorEntityDescription[Devices, Device]( @@ -317,7 +311,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( native_unit_of_measurement=UnitOfPower.WATT, suggested_display_precision=1, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -327,8 +321,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, supported_fn=async_device_outlet_supported_fn, - unique_id_fn=lambda controller, obj_id: f"ac_power_budget-{obj_id}", - value_fn=lambda controller, device: device.outlet_ac_power_budget, + unique_id_fn=lambda hub, obj_id: f"ac_power_budget-{obj_id}", + value_fn=lambda hub, device: device.outlet_ac_power_budget, ), UnifiSensorEntityDescription[Devices, Device]( key="SmartPower AC power consumption", @@ -337,7 +331,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( native_unit_of_measurement=UnitOfPower.WATT, suggested_display_precision=1, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -347,15 +341,15 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, supported_fn=async_device_outlet_supported_fn, - unique_id_fn=lambda controller, obj_id: f"ac_power_conumption-{obj_id}", - value_fn=lambda controller, device: device.outlet_ac_power_consumption, + unique_id_fn=lambda hub, obj_id: f"ac_power_conumption-{obj_id}", + value_fn=lambda hub, device: device.outlet_ac_power_consumption, ), UnifiSensorEntityDescription[Devices, Device]( key="Device uptime", device_class=SensorDeviceClass.TIMESTAMP, entity_category=EntityCategory.DIAGNOSTIC, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -364,8 +358,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( name_fn=lambda device: "Uptime", object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"device_uptime-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"device_uptime-{obj_id}", value_fn=async_device_uptime_value_fn, value_changed_fn=async_device_uptime_value_changed_fn, ), @@ -375,7 +369,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement=UnitOfTemperature.CELSIUS, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -385,7 +379,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, supported_fn=lambda ctrlr, obj_id: ctrlr.api.devices[obj_id].has_temperature, - unique_id_fn=lambda controller, obj_id: f"device_temperature-{obj_id}", + unique_id_fn=lambda hub, obj_id: f"device_temperature-{obj_id}", value_fn=lambda ctrlr, device: device.general_temperature, ), UnifiSensorEntityDescription[Devices, Device]( @@ -393,7 +387,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( device_class=SensorDeviceClass.ENUM, entity_category=EntityCategory.DIAGNOSTIC, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, @@ -402,8 +396,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( name_fn=lambda device: "State", object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"device_state-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"device_state-{obj_id}", value_fn=async_device_state_value_fn, options=list(DEVICE_STATES.values()), ), @@ -416,7 +410,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up sensors for UniFi Network integration.""" - UniFiController.register_platform( + UnifiHub.register_platform( hass, config_entry, async_add_entities, UnifiSensorEntity, ENTITY_DESCRIPTIONS ) @@ -443,19 +437,19 @@ class UnifiSensorEntity(UnifiEntity[HandlerT, ApiItemT], SensorEntity): Update native_value. """ description = self.entity_description - obj = description.object_fn(self.controller.api, self._obj_id) + obj = description.object_fn(self.hub.api, self._obj_id) # Update the value only if value is considered to have changed relative to its previous state if description.value_changed_fn( - self.native_value, (value := description.value_fn(self.controller, obj)) + self.native_value, (value := description.value_fn(self.hub, obj)) ): self._attr_native_value = value if description.is_connected_fn is not None: # Send heartbeat if client is connected - if description.is_connected_fn(self.controller, self._obj_id): - self.controller.async_heartbeat( + if description.is_connected_fn(self.hub, self._obj_id): + self.hub.async_heartbeat( self._attr_unique_id, - dt_util.utcnow() + self.controller.option_detection_time, + dt_util.utcnow() + self.hub.option_detection_time, ) async def async_added_to_hass(self) -> None: @@ -467,7 +461,7 @@ class UnifiSensorEntity(UnifiEntity[HandlerT, ApiItemT], SensorEntity): self.async_on_remove( async_dispatcher_connect( self.hass, - f"{self.controller.signal_heartbeat_missed}_{self.unique_id}", + f"{self.hub.signal_heartbeat_missed}_{self.unique_id}", self._make_disconnected, ) ) @@ -478,4 +472,4 @@ class UnifiSensorEntity(UnifiEntity[HandlerT, ApiItemT], SensorEntity): if self.entity_description.is_connected_fn is not None: # Remove heartbeat registration - self.controller.async_heartbeat(self._attr_unique_id) + self.hub.async_heartbeat(self._attr_unique_id) diff --git a/homeassistant/components/unifi/services.py b/homeassistant/components/unifi/services.py index 06de01d822a..2017db4a0a8 100644 --- a/homeassistant/components/unifi/services.py +++ b/homeassistant/components/unifi/services.py @@ -72,31 +72,31 @@ async def async_reconnect_client(hass: HomeAssistant, data: Mapping[str, Any]) - if mac == "": return - for controller in hass.data[UNIFI_DOMAIN].values(): + for hub in hass.data[UNIFI_DOMAIN].values(): if ( - not controller.available - or (client := controller.api.clients.get(mac)) is None + not hub.available + or (client := hub.api.clients.get(mac)) is None or client.is_wired ): continue - await controller.api.request(ClientReconnectRequest.create(mac)) + await hub.api.request(ClientReconnectRequest.create(mac)) async def async_remove_clients(hass: HomeAssistant, data: Mapping[str, Any]) -> None: - """Remove select clients from controller. + """Remove select clients from UniFi Network. Validates based on: - Total time between first seen and last seen is less than 15 minutes. - Neither IP, hostname nor name is configured. """ - for controller in hass.data[UNIFI_DOMAIN].values(): - if not controller.available: + for hub in hass.data[UNIFI_DOMAIN].values(): + if not hub.available: continue clients_to_remove = [] - for client in controller.api.clients_all.values(): + for client in hub.api.clients_all.values(): if ( client.last_seen and client.first_seen @@ -110,4 +110,4 @@ async def async_remove_clients(hass: HomeAssistant, data: Mapping[str, Any]) -> clients_to_remove.append(client.mac) if clients_to_remove: - await controller.api.request(ClientRemoveRequest.create(clients_to_remove)) + await hub.api.request(ClientRemoveRequest.create(clients_to_remove)) diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 371676f4786..74d371eba09 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -45,7 +45,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback import homeassistant.helpers.entity_registry as er from .const import ATTR_MANUFACTURER -from .controller import UNIFI_DOMAIN, UniFiController from .entity import ( HandlerT, SubscriptionT, @@ -56,25 +55,24 @@ from .entity import ( async_device_device_info_fn, async_wlan_device_info_fn, ) +from .hub import UNIFI_DOMAIN, UnifiHub CLIENT_BLOCKED = (EventKey.WIRED_CLIENT_BLOCKED, EventKey.WIRELESS_CLIENT_BLOCKED) CLIENT_UNBLOCKED = (EventKey.WIRED_CLIENT_UNBLOCKED, EventKey.WIRELESS_CLIENT_UNBLOCKED) @callback -def async_block_client_allowed_fn(controller: UniFiController, obj_id: str) -> bool: +def async_block_client_allowed_fn(hub: UnifiHub, obj_id: str) -> bool: """Check if client is allowed.""" - if obj_id in controller.option_supported_clients: + if obj_id in hub.option_supported_clients: return True - return obj_id in controller.option_block_clients + return obj_id in hub.option_block_clients @callback -def async_dpi_group_is_on_fn( - controller: UniFiController, dpi_group: DPIRestrictionGroup -) -> bool: +def async_dpi_group_is_on_fn(hub: UnifiHub, dpi_group: DPIRestrictionGroup) -> bool: """Calculate if all apps are enabled.""" - api = controller.api + api = hub.api return all( api.dpi_apps[app_id].enabled for app_id in dpi_group.dpiapp_ids or [] @@ -83,9 +81,7 @@ def async_dpi_group_is_on_fn( @callback -def async_dpi_group_device_info_fn( - controller: UniFiController, obj_id: str -) -> DeviceInfo: +def async_dpi_group_device_info_fn(hub: UnifiHub, obj_id: str) -> DeviceInfo: """Create device registry entry for DPI group.""" return DeviceInfo( entry_type=DeviceEntryType.SERVICE, @@ -97,11 +93,9 @@ def async_dpi_group_device_info_fn( @callback -def async_port_forward_device_info_fn( - controller: UniFiController, obj_id: str -) -> DeviceInfo: +def async_port_forward_device_info_fn(hub: UnifiHub, obj_id: str) -> DeviceInfo: """Create device registry entry for port forward.""" - unique_id = controller.config_entry.unique_id + unique_id = hub.config_entry.unique_id assert unique_id is not None return DeviceInfo( entry_type=DeviceEntryType.SERVICE, @@ -113,79 +107,67 @@ def async_port_forward_device_info_fn( async def async_block_client_control_fn( - controller: UniFiController, obj_id: str, target: bool + hub: UnifiHub, obj_id: str, target: bool ) -> None: """Control network access of client.""" - await controller.api.request(ClientBlockRequest.create(obj_id, not target)) + await hub.api.request(ClientBlockRequest.create(obj_id, not target)) -async def async_dpi_group_control_fn( - controller: UniFiController, obj_id: str, target: bool -) -> None: +async def async_dpi_group_control_fn(hub: UnifiHub, obj_id: str, target: bool) -> None: """Enable or disable DPI group.""" - dpi_group = controller.api.dpi_groups[obj_id] + dpi_group = hub.api.dpi_groups[obj_id] await asyncio.gather( *[ - controller.api.request( - DPIRestrictionAppEnableRequest.create(app_id, target) - ) + hub.api.request(DPIRestrictionAppEnableRequest.create(app_id, target)) for app_id in dpi_group.dpiapp_ids or [] ] ) @callback -def async_outlet_supports_switching_fn( - controller: UniFiController, obj_id: str -) -> bool: +def async_outlet_supports_switching_fn(hub: UnifiHub, obj_id: str) -> bool: """Determine if an outlet supports switching.""" - outlet = controller.api.outlets[obj_id] + outlet = hub.api.outlets[obj_id] return outlet.has_relay or outlet.caps in (1, 3) -async def async_outlet_control_fn( - controller: UniFiController, obj_id: str, target: bool -) -> None: +async def async_outlet_control_fn(hub: UnifiHub, obj_id: str, target: bool) -> None: """Control outlet relay.""" mac, _, index = obj_id.partition("_") - device = controller.api.devices[mac] - await controller.api.request( + device = hub.api.devices[mac] + await hub.api.request( DeviceSetOutletRelayRequest.create(device, int(index), target) ) -async def async_poe_port_control_fn( - controller: UniFiController, obj_id: str, target: bool -) -> None: +async def async_poe_port_control_fn(hub: UnifiHub, obj_id: str, target: bool) -> None: """Control poe state.""" mac, _, index = obj_id.partition("_") - port = controller.api.ports[obj_id] + port = hub.api.ports[obj_id] on_state = "auto" if port.raw["poe_caps"] != 8 else "passthrough" state = on_state if target else "off" - controller.async_queue_poe_port_command(mac, int(index), state) + hub.async_queue_poe_port_command(mac, int(index), state) async def async_port_forward_control_fn( - controller: UniFiController, obj_id: str, target: bool + hub: UnifiHub, obj_id: str, target: bool ) -> None: """Control port forward state.""" - port_forward = controller.api.port_forwarding[obj_id] - await controller.api.request(PortForwardEnableRequest.create(port_forward, target)) + port_forward = hub.api.port_forwarding[obj_id] + await hub.api.request(PortForwardEnableRequest.create(port_forward, target)) -async def async_wlan_control_fn( - controller: UniFiController, obj_id: str, target: bool -) -> None: +async def async_wlan_control_fn(hub: UnifiHub, obj_id: str, target: bool) -> None: """Control outlet relay.""" - await controller.api.request(WlanEnableRequest.create(obj_id, target)) + await hub.api.request(WlanEnableRequest.create(obj_id, target)) @dataclass(frozen=True) class UnifiSwitchEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): """Validate and load entities from different UniFi handlers.""" - control_fn: Callable[[UniFiController, str, bool], Coroutine[Any, Any, None]] - is_on_fn: Callable[[UniFiController, ApiItemT], bool] + control_fn: Callable[[UnifiHub, str, bool], Coroutine[Any, Any, None]] + is_on_fn: Callable[[UnifiHub, ApiItemT], bool] @dataclass(frozen=True) @@ -209,26 +191,26 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( icon="mdi:ethernet", allowed_fn=async_block_client_allowed_fn, api_handler_fn=lambda api: api.clients, - available_fn=lambda controller, obj_id: controller.available, + available_fn=lambda hub, obj_id: hub.available, control_fn=async_block_client_control_fn, device_info_fn=async_client_device_info_fn, event_is_on=CLIENT_UNBLOCKED, event_to_subscribe=CLIENT_BLOCKED + CLIENT_UNBLOCKED, - is_on_fn=lambda controller, client: not client.blocked, + is_on_fn=lambda hub, client: not client.blocked, name_fn=lambda client: None, object_fn=lambda api, obj_id: api.clients[obj_id], only_event_for_state_change=True, should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"block-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"block-{obj_id}", ), UnifiSwitchEntityDescription[DPIRestrictionGroups, DPIRestrictionGroup]( key="DPI restriction", entity_category=EntityCategory.CONFIG, icon="mdi:network", - allowed_fn=lambda controller, obj_id: controller.option_dpi_restrictions, + allowed_fn=lambda hub, obj_id: hub.option_dpi_restrictions, api_handler_fn=lambda api: api.dpi_groups, - available_fn=lambda controller, obj_id: controller.available, + available_fn=lambda hub, obj_id: hub.available, control_fn=async_dpi_group_control_fn, custom_subscribe=lambda api: api.dpi_apps.subscribe, device_info_fn=async_dpi_group_device_info_fn, @@ -239,25 +221,25 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( object_fn=lambda api, obj_id: api.dpi_groups[obj_id], should_poll=False, supported_fn=lambda c, obj_id: bool(c.api.dpi_groups[obj_id].dpiapp_ids), - unique_id_fn=lambda controller, obj_id: obj_id, + unique_id_fn=lambda hub, obj_id: obj_id, ), UnifiSwitchEntityDescription[Outlets, Outlet]( key="Outlet control", device_class=SwitchDeviceClass.OUTLET, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.outlets, available_fn=async_device_available_fn, control_fn=async_outlet_control_fn, device_info_fn=async_device_device_info_fn, event_is_on=None, event_to_subscribe=None, - is_on_fn=lambda controller, outlet: outlet.relay_state, + is_on_fn=lambda hub, outlet: outlet.relay_state, name_fn=lambda outlet: outlet.name, object_fn=lambda api, obj_id: api.outlets[obj_id], should_poll=False, supported_fn=async_outlet_supports_switching_fn, - unique_id_fn=lambda controller, obj_id: f"outlet-{obj_id}", + unique_id_fn=lambda hub, obj_id: f"outlet-{obj_id}", ), UnifiSwitchEntityDescription[PortForwarding, PortForward]( key="Port forward control", @@ -265,19 +247,19 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( entity_category=EntityCategory.CONFIG, has_entity_name=True, icon="mdi:upload-network", - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.port_forwarding, - available_fn=lambda controller, obj_id: controller.available, + available_fn=lambda hub, obj_id: hub.available, control_fn=async_port_forward_control_fn, device_info_fn=async_port_forward_device_info_fn, event_is_on=None, event_to_subscribe=None, - is_on_fn=lambda controller, port_forward: port_forward.enabled, + is_on_fn=lambda hub, port_forward: port_forward.enabled, name_fn=lambda port_forward: f"{port_forward.name}", object_fn=lambda api, obj_id: api.port_forwarding[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"port_forward-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"port_forward-{obj_id}", ), UnifiSwitchEntityDescription[Ports, Port]( key="PoE port control", @@ -286,19 +268,19 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( has_entity_name=True, entity_registry_enabled_default=False, icon="mdi:ethernet", - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.ports, available_fn=async_device_available_fn, control_fn=async_poe_port_control_fn, device_info_fn=async_device_device_info_fn, event_is_on=None, event_to_subscribe=None, - is_on_fn=lambda controller, port: port.poe_mode != "off", + is_on_fn=lambda hub, port: port.poe_mode != "off", name_fn=lambda port: f"{port.name} PoE", object_fn=lambda api, obj_id: api.ports[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: controller.api.ports[obj_id].port_poe, - unique_id_fn=lambda controller, obj_id: f"poe-{obj_id}", + supported_fn=lambda hub, obj_id: hub.api.ports[obj_id].port_poe, + unique_id_fn=lambda hub, obj_id: f"poe-{obj_id}", ), UnifiSwitchEntityDescription[Wlans, Wlan]( key="WLAN control", @@ -306,19 +288,19 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( entity_category=EntityCategory.CONFIG, has_entity_name=True, icon="mdi:wifi-check", - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.wlans, - available_fn=lambda controller, _: controller.available, + available_fn=lambda hub, _: hub.available, control_fn=async_wlan_control_fn, device_info_fn=async_wlan_device_info_fn, event_is_on=None, event_to_subscribe=None, - is_on_fn=lambda controller, wlan: wlan.enabled, + is_on_fn=lambda hub, wlan: wlan.enabled, name_fn=lambda wlan: None, object_fn=lambda api, obj_id: api.wlans[obj_id], should_poll=False, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"wlan-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"wlan-{obj_id}", ), ) @@ -329,7 +311,7 @@ def async_update_unique_id(hass: HomeAssistant, config_entry: ConfigEntry) -> No Introduced with release 2023.12. """ - controller: UniFiController = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub: UnifiHub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] ent_reg = er.async_get(hass) @callback @@ -344,10 +326,10 @@ def async_update_unique_id(hass: HomeAssistant, config_entry: ConfigEntry) -> No if entity_id := ent_reg.async_get_entity_id(DOMAIN, UNIFI_DOMAIN, unique_id): ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) - for obj_id in controller.api.outlets: + for obj_id in hub.api.outlets: update_unique_id(obj_id, "outlet") - for obj_id in controller.api.ports: + for obj_id in hub.api.ports: update_unique_id(obj_id, "poe") @@ -358,7 +340,7 @@ async def async_setup_entry( ) -> None: """Set up switches for UniFi Network integration.""" async_update_unique_id(hass, config_entry) - UniFiController.register_platform( + UnifiHub.register_platform( hass, config_entry, async_add_entities, @@ -384,11 +366,11 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn on switch.""" - await self.entity_description.control_fn(self.controller, self._obj_id, True) + await self.entity_description.control_fn(self.hub, self._obj_id, True) async def async_turn_off(self, **kwargs: Any) -> None: """Turn off switch.""" - await self.entity_description.control_fn(self.controller, self._obj_id, False) + await self.entity_description.control_fn(self.hub, self._obj_id, False) @callback def async_update_state(self, event: ItemEvent, obj_id: str) -> None: @@ -400,8 +382,8 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity): return description = self.entity_description - obj = description.object_fn(self.controller.api, self._obj_id) - if (is_on := description.is_on_fn(self.controller, obj)) != self.is_on: + obj = description.object_fn(self.hub.api, self._obj_id) + if (is_on := description.is_on_fn(self.hub, obj)) != self.is_on: self._attr_is_on = is_on @callback @@ -416,7 +398,7 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity): if event.key in description.event_to_subscribe: self._attr_is_on = event.key in description.event_is_on - self._attr_available = description.available_fn(self.controller, self._obj_id) + self._attr_available = description.available_fn(self.hub, self._obj_id) self.async_write_ha_state() async def async_added_to_hass(self) -> None: @@ -425,7 +407,7 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity): if self.entity_description.custom_subscribe is not None: self.async_on_remove( - self.entity_description.custom_subscribe(self.controller.api)( + self.entity_description.custom_subscribe(self.hub.api)( self.async_signalling_callback, ItemEvent.CHANGED ), ) diff --git a/homeassistant/components/unifi/update.py b/homeassistant/components/unifi/update.py index a0d2da328a2..b7f33b632b3 100644 --- a/homeassistant/components/unifi/update.py +++ b/homeassistant/components/unifi/update.py @@ -21,13 +21,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .controller import UniFiController from .entity import ( UnifiEntity, UnifiEntityDescription, async_device_available_fn, async_device_device_info_fn, ) +from .hub import UnifiHub LOGGER = logging.getLogger(__name__) @@ -62,7 +62,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiUpdateEntityDescription, ...] = ( key="Upgrade device", device_class=UpdateDeviceClass.FIRMWARE, has_entity_name=True, - allowed_fn=lambda controller, obj_id: True, + allowed_fn=lambda hub, obj_id: True, api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, control_fn=async_device_control_fn, @@ -73,8 +73,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiUpdateEntityDescription, ...] = ( object_fn=lambda api, obj_id: api.devices[obj_id], should_poll=False, state_fn=lambda api, device: device.state == 4, - supported_fn=lambda controller, obj_id: True, - unique_id_fn=lambda controller, obj_id: f"device_update-{obj_id}", + supported_fn=lambda hub, obj_id: True, + unique_id_fn=lambda hub, obj_id: f"device_update-{obj_id}", ), ) @@ -85,7 +85,7 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up update entities for UniFi Network integration.""" - UniFiController.register_platform( + UnifiHub.register_platform( hass, config_entry, async_add_entities, @@ -103,7 +103,7 @@ class UnifiDeviceUpdateEntity(UnifiEntity[_HandlerT, _DataT], UpdateEntity): def async_initiate_state(self) -> None: """Initiate entity state.""" self._attr_supported_features = UpdateEntityFeature.PROGRESS - if self.controller.is_admin: + if self.hub.is_admin: self._attr_supported_features |= UpdateEntityFeature.INSTALL self.async_update_state(ItemEvent.ADDED, self._obj_id) @@ -112,7 +112,7 @@ class UnifiDeviceUpdateEntity(UnifiEntity[_HandlerT, _DataT], UpdateEntity): self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install an update.""" - await self.entity_description.control_fn(self.controller.api, self._obj_id) + await self.entity_description.control_fn(self.hub.api, self._obj_id) @callback def async_update_state(self, event: ItemEvent, obj_id: str) -> None: @@ -122,7 +122,7 @@ class UnifiDeviceUpdateEntity(UnifiEntity[_HandlerT, _DataT], UpdateEntity): """ description = self.entity_description - obj = description.object_fn(self.controller.api, self._obj_id) - self._attr_in_progress = description.state_fn(self.controller.api, obj) + obj = description.object_fn(self.hub.api, self._obj_id) + self._attr_in_progress = description.state_fn(self.hub.api, obj) self._attr_installed_version = obj.version self._attr_latest_version = obj.upgrade_to_firmware or obj.version diff --git a/tests/components/unifi/conftest.py b/tests/components/unifi/conftest.py index 7337ddd99cb..474210e95f8 100644 --- a/tests/components/unifi/conftest.py +++ b/tests/components/unifi/conftest.py @@ -9,14 +9,14 @@ from aiounifi.models.message import MessageKey import pytest from homeassistant.components.unifi.const import DOMAIN as UNIFI_DOMAIN -from homeassistant.components.unifi.controller import RETRY_TIMER +from homeassistant.components.unifi.hub import RETRY_TIMER from homeassistant.const import CONTENT_TYPE_JSON from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr import homeassistant.util.dt as dt_util from tests.common import MockConfigEntry, async_fire_time_changed -from tests.components.unifi.test_controller import DEFAULT_CONFIG_ENTRY_ID +from tests.components.unifi.test_hub import DEFAULT_CONFIG_ENTRY_ID from tests.test_util.aiohttp import AiohttpClientMocker @@ -43,12 +43,12 @@ class WebsocketStateManager(asyncio.Event): Mock api calls done by 'await self.api.login'. Fail will make 'await self.api.start_websocket' return immediately. """ - controller = self.hass.data[UNIFI_DOMAIN][DEFAULT_CONFIG_ENTRY_ID] + hub = self.hass.data[UNIFI_DOMAIN][DEFAULT_CONFIG_ENTRY_ID] self.aioclient_mock.get( - f"https://{controller.host}:1234", status=302 + f"https://{hub.host}:1234", status=302 ) # Check UniFi OS self.aioclient_mock.post( - f"https://{controller.host}:1234/api/login", + f"https://{hub.host}:1234/api/login", json={"data": "login successful", "meta": {"rc": "ok"}}, headers={"content-type": CONTENT_TYPE_JSON}, ) @@ -79,13 +79,13 @@ def mock_unifi_websocket(hass): data: list[dict] | dict | None = None, ): """Generate a websocket call.""" - controller = hass.data[UNIFI_DOMAIN][DEFAULT_CONFIG_ENTRY_ID] + hub = hass.data[UNIFI_DOMAIN][DEFAULT_CONFIG_ENTRY_ID] if data and not message: - controller.api.messages.handler(data) + hub.api.messages.handler(data) elif data and message: if not isinstance(data, list): data = [data] - controller.api.messages.handler( + hub.api.messages.handler( { "meta": {"message": message.value}, "data": data, diff --git a/tests/components/unifi/test_button.py b/tests/components/unifi/test_button.py index 11d0e2c3fae..4518393226c 100644 --- a/tests/components/unifi/test_button.py +++ b/tests/components/unifi/test_button.py @@ -6,7 +6,7 @@ from homeassistant.const import ATTR_DEVICE_CLASS, STATE_UNAVAILABLE, EntityCate from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from .test_controller import setup_unifi_integration +from .test_hub import setup_unifi_integration from tests.test_util.aiohttp import AiohttpClientMocker @@ -36,7 +36,7 @@ async def test_restart_device_button( } ], ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 1 @@ -52,7 +52,7 @@ async def test_restart_device_button( # Send restart device command aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/devmgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/devmgr", ) await hass.services.async_call( @@ -120,7 +120,7 @@ async def test_power_cycle_poe( } ], ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 2 @@ -136,7 +136,7 @@ async def test_power_cycle_poe( # Send restart device command aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/devmgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/devmgr", ) await hass.services.async_call( diff --git a/tests/components/unifi/test_config_flow.py b/tests/components/unifi/test_config_flow.py index 71d2dc038c1..4596d36c2eb 100644 --- a/tests/components/unifi/test_config_flow.py +++ b/tests/components/unifi/test_config_flow.py @@ -33,7 +33,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant -from .test_controller import setup_unifi_integration +from .test_hub import setup_unifi_integration from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMocker @@ -356,7 +356,7 @@ async def test_flow_fails_user_credentials_faulty( assert result["errors"] == {"base": "faulty_credentials"} -async def test_flow_fails_controller_unavailable( +async def test_flow_fails_hub_unavailable( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test config flow.""" @@ -388,10 +388,10 @@ async def test_flow_fails_controller_unavailable( async def test_reauth_flow_update_configuration( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: - """Verify reauth flow can update controller configuration.""" + """Verify reauth flow can update hub configuration.""" config_entry = await setup_unifi_integration(hass, aioclient_mock) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.available = False + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub.available = False result = await hass.config_entries.flow.async_init( UNIFI_DOMAIN, diff --git a/tests/components/unifi/test_device_tracker.py b/tests/components/unifi/test_device_tracker.py index 16bd7adf144..9a4b406c364 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -21,7 +21,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er import homeassistant.util.dt as dt_util -from .test_controller import ENTRY_CONFIG, setup_unifi_integration +from .test_hub import ENTRY_CONFIG, setup_unifi_integration from tests.common import async_fire_time_changed from tests.test_util.aiohttp import AiohttpClientMocker @@ -55,7 +55,7 @@ async def test_tracked_wireless_clients( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_response=[client] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1 assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME @@ -70,7 +70,7 @@ async def test_tracked_wireless_clients( # Change time to mark client as away - new_time = dt_util.utcnow() + controller.option_detection_time + new_time = dt_util.utcnow() + hub.option_detection_time with freeze_time(new_time): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() @@ -194,7 +194,7 @@ async def test_tracked_wireless_clients_event_source( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_response=[client] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1 assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME @@ -243,7 +243,7 @@ async def test_tracked_wireless_clients_event_source( assert hass.states.get("device_tracker.client").state == STATE_HOME # Change time to mark client as away - freezer.tick(controller.option_detection_time + timedelta(seconds=1)) + freezer.tick(hub.option_detection_time + timedelta(seconds=1)) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -282,7 +282,7 @@ async def test_tracked_wireless_clients_event_source( assert hass.states.get("device_tracker.client").state == STATE_HOME # Change time to mark client as away - freezer.tick(controller.option_detection_time + timedelta(seconds=1)) + freezer.tick(hub.option_detection_time + timedelta(seconds=1)) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -407,13 +407,13 @@ async def test_remove_clients( assert hass.states.get("device_tracker.client_2") -async def test_controller_state_change( +async def test_hub_state_change( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, websocket_mock, mock_device_registry, ) -> None: - """Verify entities state reflect on controller becoming unavailable.""" + """Verify entities state reflect on hub connection becoming unavailable.""" client = { "essid": "ssid", "hostname": "client", @@ -682,7 +682,7 @@ async def test_option_ssid_filter( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_response=[client, client_on_ssid2] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 2 @@ -711,7 +711,7 @@ async def test_option_ssid_filter( mock_unifi_websocket(message=MessageKey.CLIENT, data=client_on_ssid2) await hass.async_block_till_done() - new_time = dt_util.utcnow() + controller.option_detection_time + new_time = dt_util.utcnow() + hub.option_detection_time with freeze_time(new_time): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() @@ -739,7 +739,7 @@ async def test_option_ssid_filter( # Time pass to mark client as away - new_time += controller.option_detection_time + new_time += hub.option_detection_time with freeze_time(new_time): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() @@ -758,7 +758,7 @@ async def test_option_ssid_filter( mock_unifi_websocket(message=MessageKey.CLIENT, data=client_on_ssid2) await hass.async_block_till_done() - new_time += controller.option_detection_time + new_time += hub.option_detection_time with freeze_time(new_time): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() @@ -788,7 +788,7 @@ async def test_wireless_client_go_wired_issue( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_response=[client] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1 @@ -807,7 +807,7 @@ async def test_wireless_client_go_wired_issue( assert client_state.state == STATE_HOME # Pass time - new_time = dt_util.utcnow() + controller.option_detection_time + new_time = dt_util.utcnow() + hub.option_detection_time with freeze_time(new_time): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() @@ -859,7 +859,7 @@ async def test_option_ignore_wired_bug( options={CONF_IGNORE_WIRED_BUG: True}, clients_response=[client], ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1 # Client is wireless @@ -876,7 +876,7 @@ async def test_option_ignore_wired_bug( assert client_state.state == STATE_HOME # pass time - new_time = dt_util.utcnow() + controller.option_detection_time + new_time = dt_util.utcnow() + hub.option_detection_time with freeze_time(new_time): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() diff --git a/tests/components/unifi/test_diagnostics.py b/tests/components/unifi/test_diagnostics.py index 127b9b79c2b..ce5290bccef 100644 --- a/tests/components/unifi/test_diagnostics.py +++ b/tests/components/unifi/test_diagnostics.py @@ -7,7 +7,7 @@ from homeassistant.components.unifi.const import ( ) from homeassistant.core import HomeAssistant -from .test_controller import setup_unifi_integration +from .test_hub import setup_unifi_integration from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.test_util.aiohttp import AiohttpClientMocker diff --git a/tests/components/unifi/test_controller.py b/tests/components/unifi/test_hub.py similarity index 84% rename from tests/components/unifi/test_controller.py rename to tests/components/unifi/test_hub.py index 17ae018d67f..f7dc8b70812 100644 --- a/tests/components/unifi/test_controller.py +++ b/tests/components/unifi/test_hub.py @@ -26,8 +26,8 @@ from homeassistant.components.unifi.const import ( PLATFORMS, UNIFI_WIRELESS_CLIENTS, ) -from homeassistant.components.unifi.controller import get_unifi_controller from homeassistant.components.unifi.errors import AuthenticationRequired, CannotConnect +from homeassistant.components.unifi.hub import get_unifi_api from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, @@ -194,7 +194,6 @@ async def setup_unifi_integration( system_information_response=None, wlans_response=None, known_wireless_clients=None, - controllers=None, unique_id="1", config_entry_id=DEFAULT_CONFIG_ENTRY_ID, ): @@ -241,7 +240,7 @@ async def setup_unifi_integration( return config_entry -async def test_controller_setup( +async def test_hub_setup( hass: HomeAssistant, device_registry: dr.DeviceRegistry, aioclient_mock: AiohttpClientMocker, @@ -254,9 +253,9 @@ async def test_controller_setup( config_entry = await setup_unifi_integration( hass, aioclient_mock, system_information_response=SYSTEM_INFORMATION ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - entry = controller.config_entry + entry = hub.config_entry assert len(forward_entry_setup.mock_calls) == len(PLATFORMS) assert forward_entry_setup.mock_calls[0][1] == (entry, BUTTON_DOMAIN) assert forward_entry_setup.mock_calls[1][1] == (entry, TRACKER_DOMAIN) @@ -264,21 +263,21 @@ async def test_controller_setup( assert forward_entry_setup.mock_calls[3][1] == (entry, SENSOR_DOMAIN) assert forward_entry_setup.mock_calls[4][1] == (entry, SWITCH_DOMAIN) - assert controller.host == ENTRY_CONFIG[CONF_HOST] - assert controller.is_admin == (SITE[0]["role"] == "admin") + assert hub.host == ENTRY_CONFIG[CONF_HOST] + assert hub.is_admin == (SITE[0]["role"] == "admin") - assert controller.option_allow_bandwidth_sensors == DEFAULT_ALLOW_BANDWIDTH_SENSORS - assert controller.option_allow_uptime_sensors == DEFAULT_ALLOW_UPTIME_SENSORS - assert isinstance(controller.option_block_clients, list) - assert controller.option_track_clients == DEFAULT_TRACK_CLIENTS - assert controller.option_track_devices == DEFAULT_TRACK_DEVICES - assert controller.option_track_wired_clients == DEFAULT_TRACK_WIRED_CLIENTS - assert controller.option_detection_time == timedelta(seconds=DEFAULT_DETECTION_TIME) - assert isinstance(controller.option_ssid_filter, set) + assert hub.option_allow_bandwidth_sensors == DEFAULT_ALLOW_BANDWIDTH_SENSORS + assert hub.option_allow_uptime_sensors == DEFAULT_ALLOW_UPTIME_SENSORS + assert isinstance(hub.option_block_clients, list) + assert hub.option_track_clients == DEFAULT_TRACK_CLIENTS + assert hub.option_track_devices == DEFAULT_TRACK_DEVICES + assert hub.option_track_wired_clients == DEFAULT_TRACK_WIRED_CLIENTS + assert hub.option_detection_time == timedelta(seconds=DEFAULT_DETECTION_TIME) + assert isinstance(hub.option_ssid_filter, set) - assert controller.signal_reachable == "unifi-reachable-1" - assert controller.signal_options_update == "unifi-options-1" - assert controller.signal_heartbeat_missed == "unifi-heartbeat-missed" + assert hub.signal_reachable == "unifi-reachable-1" + assert hub.signal_options_update == "unifi-options-1" + assert hub.signal_heartbeat_missed == "unifi-heartbeat-missed" device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, @@ -288,20 +287,20 @@ async def test_controller_setup( assert device_entry.sw_version == "7.4.162" -async def test_controller_not_accessible(hass: HomeAssistant) -> None: +async def test_hub_not_accessible(hass: HomeAssistant) -> None: """Retry to login gets scheduled when connection fails.""" with patch( - "homeassistant.components.unifi.controller.get_unifi_controller", + "homeassistant.components.unifi.hub.get_unifi_api", side_effect=CannotConnect, ): await setup_unifi_integration(hass) assert hass.data[UNIFI_DOMAIN] == {} -async def test_controller_trigger_reauth_flow(hass: HomeAssistant) -> None: +async def test_hub_trigger_reauth_flow(hass: HomeAssistant) -> None: """Failed authentication trigger a reauthentication flow.""" with patch( - "homeassistant.components.unifi.get_unifi_controller", + "homeassistant.components.unifi.get_unifi_api", side_effect=AuthenticationRequired, ), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init: await setup_unifi_integration(hass) @@ -309,10 +308,10 @@ async def test_controller_trigger_reauth_flow(hass: HomeAssistant) -> None: assert hass.data[UNIFI_DOMAIN] == {} -async def test_controller_unknown_error(hass: HomeAssistant) -> None: +async def test_hub_unknown_error(hass: HomeAssistant) -> None: """Unknown errors are handled.""" with patch( - "homeassistant.components.unifi.controller.get_unifi_controller", + "homeassistant.components.unifi.hub.get_unifi_api", side_effect=Exception, ): await setup_unifi_integration(hass) @@ -324,10 +323,10 @@ async def test_config_entry_updated( ) -> None: """Calling reset when the entry has been setup.""" config_entry = await setup_unifi_integration(hass, aioclient_mock) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] event_call = Mock() - unsub = async_dispatcher_connect(hass, controller.signal_options_update, event_call) + unsub = async_dispatcher_connect(hass, hub.signal_options_update, event_call) hass.config_entries.async_update_entry( config_entry, options={CONF_TRACK_CLIENTS: False, CONF_TRACK_DEVICES: False} @@ -347,9 +346,9 @@ async def test_reset_after_successful_setup( ) -> None: """Calling reset when the entry has been setup.""" config_entry = await setup_unifi_integration(hass, aioclient_mock) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - result = await controller.async_reset() + result = await hub.async_reset() await hass.async_block_till_done() assert result is True @@ -360,13 +359,13 @@ async def test_reset_fails( ) -> None: """Calling reset when the entry has been setup can return false.""" config_entry = await setup_unifi_integration(hass, aioclient_mock) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] with patch( "homeassistant.config_entries.ConfigEntries.async_forward_entry_unload", return_value=False, ): - result = await controller.async_reset() + result = await hub.async_reset() await hass.async_block_till_done() assert result is False @@ -435,7 +434,7 @@ async def test_reconnect_mechanism_exceptions( await setup_unifi_integration(hass, aioclient_mock) with patch("aiounifi.Controller.login", side_effect=exception), patch( - "homeassistant.components.unifi.controller.UniFiController.reconnect" + "homeassistant.components.unifi.hub.UnifiHub.reconnect" ) as mock_reconnect: await websocket_mock.disconnect() @@ -443,18 +442,18 @@ async def test_reconnect_mechanism_exceptions( mock_reconnect.assert_called_once() -async def test_get_unifi_controller(hass: HomeAssistant) -> None: +async def test_get_unifi_api(hass: HomeAssistant) -> None: """Successful call.""" with patch("aiounifi.Controller.login", return_value=True): - assert await get_unifi_controller(hass, ENTRY_CONFIG) + assert await get_unifi_api(hass, ENTRY_CONFIG) -async def test_get_unifi_controller_verify_ssl_false(hass: HomeAssistant) -> None: +async def test_get_unifi_api_verify_ssl_false(hass: HomeAssistant) -> None: """Successful call with verify ssl set to false.""" - controller_data = dict(ENTRY_CONFIG) - controller_data[CONF_VERIFY_SSL] = False + hub_data = dict(ENTRY_CONFIG) + hub_data[CONF_VERIFY_SSL] = False with patch("aiounifi.Controller.login", return_value=True): - assert await get_unifi_controller(hass, controller_data) + assert await get_unifi_api(hass, hub_data) @pytest.mark.parametrize( @@ -471,11 +470,11 @@ async def test_get_unifi_controller_verify_ssl_false(hass: HomeAssistant) -> Non (aiounifi.AiounifiException, AuthenticationRequired), ], ) -async def test_get_unifi_controller_fails_to_connect( +async def test_get_unifi_api_fails_to_connect( hass: HomeAssistant, side_effect, raised_exception ) -> None: - """Check that get_unifi_controller can handle controller being unavailable.""" + """Check that get_unifi_api can handle UniFi Network being unavailable.""" with patch("aiounifi.Controller.login", side_effect=side_effect), pytest.raises( raised_exception ): - await get_unifi_controller(hass, ENTRY_CONFIG) + await get_unifi_api(hass, ENTRY_CONFIG) diff --git a/tests/components/unifi/test_image.py b/tests/components/unifi/test_image.py index 74e1e9b97b3..8efef579e9c 100644 --- a/tests/components/unifi/test_image.py +++ b/tests/components/unifi/test_image.py @@ -15,7 +15,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_registry import RegistryEntryDisabler from homeassistant.util import dt as dt_util -from .test_controller import setup_unifi_integration +from .test_hub import setup_unifi_integration from tests.common import async_fire_time_changed from tests.test_util.aiohttp import AiohttpClientMocker diff --git a/tests/components/unifi/test_init.py b/tests/components/unifi/test_init.py index a1b817d67e2..f4d735c1d52 100644 --- a/tests/components/unifi/test_init.py +++ b/tests/components/unifi/test_init.py @@ -8,14 +8,14 @@ from homeassistant.components.unifi.errors import AuthenticationRequired, Cannot from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from .test_controller import DEFAULT_CONFIG_ENTRY_ID, setup_unifi_integration +from .test_hub import DEFAULT_CONFIG_ENTRY_ID, setup_unifi_integration from tests.common import flush_store from tests.test_util.aiohttp import AiohttpClientMocker async def test_setup_with_no_config(hass: HomeAssistant) -> None: - """Test that we do not discover anything or try to set up a controller.""" + """Test that we do not discover anything or try to set up a hub.""" assert await async_setup_component(hass, UNIFI_DOMAIN, {}) is True assert UNIFI_DOMAIN not in hass.data @@ -31,7 +31,7 @@ async def test_successful_config_entry( async def test_setup_entry_fails_config_entry_not_ready(hass: HomeAssistant) -> None: """Failed authentication trigger a reauthentication flow.""" with patch( - "homeassistant.components.unifi.get_unifi_controller", + "homeassistant.components.unifi.get_unifi_api", side_effect=CannotConnect, ): await setup_unifi_integration(hass) @@ -42,7 +42,7 @@ async def test_setup_entry_fails_config_entry_not_ready(hass: HomeAssistant) -> async def test_setup_entry_fails_trigger_reauth_flow(hass: HomeAssistant) -> None: """Failed authentication trigger a reauthentication flow.""" with patch( - "homeassistant.components.unifi.get_unifi_controller", + "homeassistant.components.unifi.get_unifi_api", side_effect=AuthenticationRequired, ), patch.object(hass.config_entries.flow, "async_init") as mock_flow_init: await setup_unifi_integration(hass) diff --git a/tests/components/unifi/test_sensor.py b/tests/components/unifi/test_sensor.py index c89e193d5b7..8adc379dde8 100644 --- a/tests/components/unifi/test_sensor.py +++ b/tests/components/unifi/test_sensor.py @@ -8,7 +8,6 @@ from aiounifi.models.message import MessageKey from freezegun.api import FrozenDateTimeFactory, freeze_time import pytest -from homeassistant.components.device_tracker import DOMAIN as TRACKER_DOMAIN from homeassistant.components.sensor import ( ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN, @@ -31,7 +30,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_registry import RegistryEntryDisabler import homeassistant.util.dt as dt_util -from .test_controller import setup_unifi_integration +from .test_hub import setup_unifi_integration from tests.common import async_fire_time_changed from tests.test_util.aiohttp import AiohttpClientMocker @@ -396,7 +395,7 @@ async def test_bandwidth_sensors( # Verify reset sensor after heartbeat expires - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] new_time = dt_util.utcnow() wireless_client["last_seen"] = dt_util.as_timestamp(new_time) @@ -410,7 +409,7 @@ async def test_bandwidth_sensors( assert hass.states.get("sensor.wireless_client_rx").state == "3456.0" assert hass.states.get("sensor.wireless_client_tx").state == "7891.0" - new_time = new_time + controller.option_detection_time + timedelta(seconds=1) + new_time = new_time + hub.option_detection_time + timedelta(seconds=1) with freeze_time(new_time): async_fire_time_changed(hass, new_time) @@ -578,9 +577,7 @@ async def test_remove_sensors( clients_response=[wired_client, wireless_client], ) - assert len(hass.states.async_all()) == 9 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 6 - assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 2 assert hass.states.get("sensor.wired_client_rx") assert hass.states.get("sensor.wired_client_tx") assert hass.states.get("sensor.wired_client_uptime") @@ -593,9 +590,7 @@ async def test_remove_sensors( mock_unifi_websocket(message=MessageKey.CLIENT_REMOVED, data=wired_client) await hass.async_block_till_done() - assert len(hass.states.async_all()) == 5 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 3 - assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1 assert hass.states.get("sensor.wired_client_rx") is None assert hass.states.get("sensor.wired_client_tx") is None assert hass.states.get("sensor.wired_client_uptime") is None diff --git a/tests/components/unifi/test_services.py b/tests/components/unifi/test_services.py index 4013da11a5a..117a64a2c8b 100644 --- a/tests/components/unifi/test_services.py +++ b/tests/components/unifi/test_services.py @@ -11,7 +11,7 @@ from homeassistant.const import ATTR_DEVICE_ID from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr -from .test_controller import setup_unifi_integration +from .test_hub import setup_unifi_integration from tests.test_util.aiohttp import AiohttpClientMocker @@ -66,11 +66,11 @@ async def test_reconnect_client( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_response=clients ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/stamgr", ) device_entry = device_registry.async_get_or_create( @@ -128,12 +128,12 @@ async def test_reconnect_device_without_mac( assert aioclient_mock.call_count == 0 -async def test_reconnect_client_controller_unavailable( +async def test_reconnect_client_hub_unavailable( hass: HomeAssistant, device_registry: dr.DeviceRegistry, aioclient_mock: AiohttpClientMocker, ) -> None: - """Verify no call is made if controller is unavailable.""" + """Verify no call is made if hub is unavailable.""" clients = [ { "is_wired": False, @@ -143,12 +143,12 @@ async def test_reconnect_client_controller_unavailable( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_response=clients ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.available = False + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub.available = False aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/stamgr", ) device_entry = device_registry.async_get_or_create( @@ -170,14 +170,14 @@ async def test_reconnect_client_unknown_mac( device_registry: dr.DeviceRegistry, aioclient_mock: AiohttpClientMocker, ) -> None: - """Verify no call is made if trying to reconnect a mac unknown to controller.""" + """Verify no call is made if trying to reconnect a mac unknown to hub.""" config_entry = await setup_unifi_integration(hass, aioclient_mock) aioclient_mock.clear_requests() device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, - connections={(dr.CONNECTION_NETWORK_MAC, "mac unknown to controller")}, + connections={(dr.CONNECTION_NETWORK_MAC, "mac unknown to hub")}, ) await hass.services.async_call( @@ -261,11 +261,11 @@ async def test_remove_clients( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_all_response=clients ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/stamgr", ) await hass.services.async_call(UNIFI_DOMAIN, SERVICE_REMOVE_CLIENTS, blocking=True) @@ -277,10 +277,10 @@ async def test_remove_clients( assert await hass.config_entries.async_unload(config_entry.entry_id) -async def test_remove_clients_controller_unavailable( +async def test_remove_clients_hub_unavailable( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: - """Verify no call is made if controller is unavailable.""" + """Verify no call is made if UniFi Network is unavailable.""" clients = [ { "first_seen": 100, @@ -291,8 +291,8 @@ async def test_remove_clients_controller_unavailable( config_entry = await setup_unifi_integration( hass, aioclient_mock, clients_all_response=clients ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] - controller.available = False + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub.available = False aioclient_mock.clear_requests() diff --git a/tests/components/unifi/test_switch.py b/tests/components/unifi/test_switch.py index bdeab83f6aa..35b2327bcdc 100644 --- a/tests/components/unifi/test_switch.py +++ b/tests/components/unifi/test_switch.py @@ -33,12 +33,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_registry import RegistryEntryDisabler from homeassistant.util import dt as dt_util -from .test_controller import ( - CONTROLLER_HOST, - ENTRY_CONFIG, - SITE, - setup_unifi_integration, -) +from .test_hub import CONTROLLER_HOST, ENTRY_CONFIG, SITE, setup_unifi_integration from tests.common import async_fire_time_changed from tests.test_util.aiohttp import AiohttpClientMocker @@ -780,10 +775,10 @@ async def test_no_clients( assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 0 -async def test_controller_not_client( +async def test_hub_not_client( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: - """Test that the controller doesn't become a switch.""" + """Test that the cloud key doesn't become a switch.""" await setup_unifi_integration( hass, aioclient_mock, @@ -834,7 +829,7 @@ async def test_switches( dpigroup_response=DPI_GROUPS, dpiapp_response=DPI_APPS, ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 3 @@ -862,7 +857,7 @@ async def test_switches( # Block and unblock client aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/stamgr", ) await hass.services.async_call( @@ -886,7 +881,7 @@ async def test_switches( # Enable and disable DPI aioclient_mock.clear_requests() aioclient_mock.put( - f"https://{controller.host}:1234/api/s/{controller.site}/rest/dpiapp/5f976f62e3c58f018ec7e17d", + f"https://{hub.host}:1234/api/s/{hub.site}/rest/dpiapp/5f976f62e3c58f018ec7e17d", ) await hass.services.async_call( @@ -956,7 +951,7 @@ async def test_block_switches( clients_response=[UNBLOCKED], clients_all_response=[BLOCKED], ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 2 @@ -986,7 +981,7 @@ async def test_block_switches( aioclient_mock.clear_requests() aioclient_mock.post( - f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", + f"https://{hub.host}:1234/api/s/{hub.site}/cmd/stamgr", ) await hass.services.async_call( @@ -1147,7 +1142,7 @@ async def test_outlet_switches( config_entry = await setup_unifi_integration( hass, aioclient_mock, devices_response=[test_data] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == expected_switches # Validate state object switch_1 = hass.states.get(f"switch.{entity_id}") @@ -1166,7 +1161,7 @@ async def test_outlet_switches( device_id = test_data["device_id"] aioclient_mock.clear_requests() aioclient_mock.put( - f"https://{controller.host}:1234/api/s/{controller.site}/rest/device/{device_id}", + f"https://{hub.host}:1234/api/s/{hub.site}/rest/device/{device_id}", ) await hass.services.async_call( @@ -1338,7 +1333,7 @@ async def test_poe_port_switches( config_entry = await setup_unifi_integration( hass, aioclient_mock, devices_response=[DEVICE_1] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 0 @@ -1377,7 +1372,7 @@ async def test_poe_port_switches( # Turn off PoE aioclient_mock.clear_requests() aioclient_mock.put( - f"https://{controller.host}:1234/api/s/{controller.site}/rest/device/mock-id", + f"https://{hub.host}:1234/api/s/{hub.site}/rest/device/mock-id", ) await hass.services.async_call( @@ -1450,7 +1445,7 @@ async def test_wlan_switches( config_entry = await setup_unifi_integration( hass, aioclient_mock, wlans_response=[WLAN] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 @@ -1474,8 +1469,7 @@ async def test_wlan_switches( # Disable WLAN aioclient_mock.clear_requests() aioclient_mock.put( - f"https://{controller.host}:1234/api/s/{controller.site}" - + f"/rest/wlanconf/{WLAN['_id']}", + f"https://{hub.host}:1234/api/s/{hub.site}" + f"/rest/wlanconf/{WLAN['_id']}", ) await hass.services.async_call( @@ -1531,7 +1525,7 @@ async def test_port_forwarding_switches( config_entry = await setup_unifi_integration( hass, aioclient_mock, port_forward_response=[_data.copy()] ) - controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + hub = hass.data[UNIFI_DOMAIN][config_entry.entry_id] assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 @@ -1555,7 +1549,7 @@ async def test_port_forwarding_switches( # Disable port forward aioclient_mock.clear_requests() aioclient_mock.put( - f"https://{controller.host}:1234/api/s/{controller.site}" + f"https://{hub.host}:1234/api/s/{hub.site}" + f"/rest/portforward/{data['_id']}", ) diff --git a/tests/components/unifi/test_update.py b/tests/components/unifi/test_update.py index a9fe3fdae7c..a9440e20339 100644 --- a/tests/components/unifi/test_update.py +++ b/tests/components/unifi/test_update.py @@ -25,7 +25,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant -from .test_controller import SITE, setup_unifi_integration +from .test_hub import SITE, setup_unifi_integration from tests.test_util.aiohttp import AiohttpClientMocker @@ -183,10 +183,10 @@ async def test_install( ) -async def test_controller_state_change( +async def test_hub_state_change( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, websocket_mock ) -> None: - """Verify entities state reflect on controller becoming unavailable.""" + """Verify entities state reflect on hub becoming unavailable.""" await setup_unifi_integration(hass, aioclient_mock, devices_response=[DEVICE_1]) assert len(hass.states.async_entity_ids(UPDATE_DOMAIN)) == 1