1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00
ha-core/homeassistant/components/linksys_smart/device_tracker.py
epenet caff3e0630
Add type hints to get_scanner/async_get_scanner (#63836)
Co-authored-by: epenet <epenet@users.noreply.github.com>
2022-01-10 23:42:39 +01:00

111 lines
3.6 KiB
Python

"""Support for Linksys Smart Wifi routers."""
from __future__ import annotations
from http import HTTPStatus
import logging
import requests
import voluptuous as vol
from homeassistant.components.device_tracker import (
DOMAIN,
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
DeviceScanner,
)
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType
DEFAULT_TIMEOUT = 10
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend({vol.Required(CONF_HOST): cv.string})
def get_scanner(hass: HomeAssistant, config: ConfigType) -> DeviceScanner | None:
"""Validate the configuration and return a Linksys AP scanner."""
try:
return LinksysSmartWifiDeviceScanner(config[DOMAIN])
except ConnectionError:
return None
class LinksysSmartWifiDeviceScanner(DeviceScanner):
"""This class queries a Linksys Access Point."""
def __init__(self, config):
"""Initialize the scanner."""
self.host = config[CONF_HOST]
self.last_results = {}
# Check if the access point is accessible
response = self._make_request()
if response.status_code != HTTPStatus.OK:
raise ConnectionError("Cannot connect to Linksys Access Point")
def scan_devices(self):
"""Scan for new devices and return a list with device IDs (MACs)."""
self._update_info()
return self.last_results.keys()
def get_device_name(self, device):
"""Return the name (if known) of the device."""
return self.last_results.get(device)
def _update_info(self):
"""Check for connected devices."""
_LOGGER.info("Checking Linksys Smart Wifi")
self.last_results = {}
response = self._make_request()
if response.status_code != HTTPStatus.OK:
_LOGGER.error(
"Got HTTP status code %d when getting device list", response.status_code
)
return False
try:
data = response.json()
result = data["responses"][0]
devices = result["output"]["devices"]
for device in devices:
if not (macs := device["knownMACAddresses"]):
_LOGGER.warning("Skipping device without known MAC address")
continue
mac = macs[-1]
if not device["connections"]:
_LOGGER.debug("Device %s is not connected", mac)
continue
name = None
for prop in device["properties"]:
if prop["name"] == "userDeviceName":
name = prop["value"]
if not name:
name = device.get("friendlyName", device["deviceID"])
_LOGGER.debug("Device %s is connected", mac)
self.last_results[mac] = name
except (KeyError, IndexError):
_LOGGER.exception("Router returned unexpected response")
return False
return True
def _make_request(self):
# Weirdly enough, this doesn't seem to require authentication
data = [
{
"request": {"sinceRevision": 0},
"action": "http://linksys.com/jnap/devicelist/GetDevices",
}
]
headers = {"X-JNAP-Action": "http://linksys.com/jnap/core/Transaction"}
return requests.post(
f"http://{self.host}/JNAP/",
timeout=DEFAULT_TIMEOUT,
headers=headers,
json=data,
)