"""Clients are devices on a UniFi network.""" from dataclasses import dataclass from typing import Self, TypedDict, cast from .api import ApiItem, ApiRequest class TypedClient(TypedDict): """Client type definition.""" _id: str _is_guest_by_uap: bool _is_guest_by_ugw: bool _is_guest_by_usw: bool _last_seen_by_uap: int _last_seen_by_ugw: int _last_seen_by_usw: int _uptime_by_uap: int _uptime_by_ugw: int _uptime_by_usw: int anomalies: int ap_mac: str assoc_time: int authorized: bool blocked: bool bssid: str bytes_r: int ccq: int channel: int dev_cat: int dev_family: int dev_id: int dev_id_override: int dev_vendor: int device_name: str dhcpend_time: int disconnect_timestamp: int eagerly_discovered: bool essid: str fingerprint_engine_version: str fingerprint_override: bool fingerprint_source: int first_seen: int fixed_ip: str fw_version: str gw_mac: str hostname: str hostname_source: str idletime: int ip: str is_11r: bool is_guest: bool is_wired: bool last_seen: bool latest_assoc_time: bool mac: str name: str network: str network_id: str noise: int noted: bool os_class: int os_name: int oui: str powersave_enabled: bool qos_policy_applied: bool radio: str radio_name: str radio_proto: str rssi: int rx_bytes: int rx_packets: int rx_rate: int satisfaction: int score: int signal: int site_id: str sw_depth: int sw_mac: str sw_port: int tx_bytes: int tx_packets: int tx_power: int tx_rate: int tx_retries: int uptime: int use_fixedip: bool user_id: str usergroup_id: str vlan: int wifi_tx_attempts: int wired_rate_mbps: int wired_tx_bytes: int wired_rx_bytes: int wired_tx_packets: int wired_rx_packets: int wired_tx_bytes_r: int wired_rx_bytes_r: int @dataclass class AllClientListRequest(ApiRequest): """Request object for all clients list.""" @classmethod def create(cls) -> Self: """Create all clients list request.""" return cls(method="get", path="/rest/user") @dataclass class ClientListRequest(ApiRequest): """Request object for active client list.""" @classmethod def create(cls) -> Self: """Create active client list request.""" return cls(method="get", path="/stat/sta") @dataclass class ClientBlockRequest(ApiRequest): """Request object for client block.""" @classmethod def create(cls, mac: str, block: bool) -> Self: """Create client block request.""" return cls( method="post", path="/cmd/stamgr", data={ "cmd": "block-sta" if block else "unblock-sta", "mac": mac, }, ) @dataclass class ClientReconnectRequest(ApiRequest): """Request object for client reconnect.""" @classmethod def create(cls, mac: str) -> Self: """Create client reconnect request.""" return cls( method="post", path="/cmd/stamgr", data={ "cmd": "kick-sta", "mac": mac, }, ) @dataclass class ClientRemoveRequest(ApiRequest): """Request object for client removal.""" @classmethod def create(cls, macs: list[str]) -> Self: """Create client removal request.""" return cls( method="post", path="/cmd/stamgr", data={ "cmd": "forget-sta", "macs": macs, }, ) class Client(ApiItem): """Represents a client network device.""" raw: TypedClient @property def access_point_mac(self) -> str: """MAC address of access point.""" return self.raw.get("ap_mac", "") @property def association_time(self) -> int: """When was client associated with controller.""" return self.raw.get("assoc_time", 0) @property def blocked(self) -> bool: """Is client blocked.""" return self.raw.get("blocked", False) @property def device_name(self) -> str: """Device name of client.""" return self.raw.get("device_name", "") @property def essid(self) -> str: """ESSID client is connected to.""" return self.raw.get("essid", "") @property def firmware_version(self) -> str: """Firmware version of client.""" return self.raw.get("fw_version", "") @property def first_seen(self) -> int: """When was client first seen.""" return self.raw.get("first_seen", 0) @property def fixed_ip(self) -> str: """List IP if fixed IP is configured.""" return self.raw.get("fixed_ip", "") @property def hostname(self) -> str: """Hostname of client.""" return self.raw.get("hostname", "") @property def idle_time(self) -> int: """Idle time of client.""" return self.raw.get("idletime", 0) @property def ip(self) -> str: """IP of client.""" return self.raw.get("ip", "") @property def is_guest(self) -> bool: """Is client guest.""" return self.raw.get("is_guest", False) @property def is_wired(self) -> bool: """Is client wired.""" return self.raw.get("is_wired", False) @property def last_seen(self) -> int: """When was client last seen.""" return self.raw.get("last_seen", 0) @property def last_seen_by_access_point(self) -> int: """When was client last seen by access point.""" return self.raw.get("_last_seen_by_uap", 0) @property def last_seen_by_gateway(self) -> int: """When was client last seen by gateway.""" return self.raw.get("_last_seen_by_ugw", 0) @property def last_seen_by_switch(self) -> int: """When was client last seen by network switch.""" return self.raw.get("_last_seen_by_usw", 0) @property def latest_association_time(self) -> int: """When was client last associated with controller.""" return self.raw.get("latest_assoc_time", 0) @property def mac(self) -> str: """MAC address of client.""" return self.raw.get("mac", "") @property def name(self) -> str: """Name of client.""" return self.raw.get("name", "") @property def oui(self) -> str: """Vendor string for client MAC.""" return self.raw.get("oui", "") @property def powersave_enabled(self) -> bool | None: """Powersave functionality enabled for wireless client.""" return self.raw.get("powersave_enabled") @property def site_id(self) -> str: """Site ID client belongs to.""" return self.raw.get("site_id", "") @property def switch_depth(self) -> int | None: """How many layers of switches client is in.""" return self.raw.get("sw_depth") @property def switch_mac(self) -> str: """MAC for switch client is connected to.""" return self.raw.get("sw_mac", "") @property def switch_port(self) -> int | None: """Switch port client is connected to.""" return self.raw.get("sw_port") @property def rx_bytes(self) -> int: """Bytes received over wireless connection.""" return self.raw.get("rx_bytes", 0) @property def rx_bytes_r(self) -> float: """Bytes recently received over wireless connection.""" return cast(float, self.raw.get("rx_bytes-r", 0.0)) @property def tx_bytes(self) -> int: """Bytes transferred over wireless connection.""" return self.raw.get("tx_bytes", 0) @property def tx_bytes_r(self) -> float: """Bytes recently transferred over wireless connection.""" return cast(float, self.raw.get("tx_bytes-r", 0.0)) @property def uptime(self) -> int: """Uptime of client.""" return self.raw.get("uptime", 0) @property def uptime_by_access_point(self) -> int: """Uptime of client observed by access point.""" return self.raw.get("_uptime_by_uap", 0) @property def uptime_by_gateway(self) -> int: """Uptime of client observed by gateway.""" return self.raw.get("_uptime_by_ugw", 0) @property def uptime_by_switch(self) -> int: """Uptime of client observed by network switch.""" return self.raw.get("_uptime_by_usw", 0) @property def wired_rate_mbps(self) -> int: """Wired rate in Mbps.""" return self.raw.get("wired_rate_mbps", 0) @property def wired_rx_bytes(self) -> int: """Bytes received over wired connection.""" return cast(int, self.raw.get("wired-rx_bytes", 0)) @property def wired_rx_bytes_r(self) -> float: """Bytes recently received over wired connection.""" return cast(float, self.raw.get("wired-rx_bytes-r", 0.0)) @property def wired_tx_bytes(self) -> int: """Bytes transferred over wired connection.""" return cast(int, self.raw.get("wired-tx_bytes", 0)) @property def wired_tx_bytes_r(self) -> float: """Bytes recently transferred over wired connection.""" return cast(float, self.raw.get("wired-tx_bytes-r", 0.0))