Home Assistant Unofficial Reference 2024.12.1
device_tracker.py
Go to the documentation of this file.
1 """Support for Linksys Smart Wifi routers."""
2 
3 from __future__ import annotations
4 
5 from http import HTTPStatus
6 import logging
7 
8 import requests
9 import voluptuous as vol
10 
12  DOMAIN as DEVICE_TRACKER_DOMAIN,
13  PLATFORM_SCHEMA as DEVICE_TRACKER_PLATFORM_SCHEMA,
14  DeviceScanner,
15 )
16 from homeassistant.const import CONF_HOST
17 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.typing import ConfigType
20 
21 DEFAULT_TIMEOUT = 10
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 PLATFORM_SCHEMA = DEVICE_TRACKER_PLATFORM_SCHEMA.extend(
26  {vol.Required(CONF_HOST): cv.string}
27 )
28 
29 
31  hass: HomeAssistant, config: ConfigType
32 ) -> LinksysSmartWifiDeviceScanner | None:
33  """Validate the configuration and return a Linksys AP scanner."""
34  try:
35  return LinksysSmartWifiDeviceScanner(config[DEVICE_TRACKER_DOMAIN])
36  except ConnectionError:
37  return None
38 
39 
40 class LinksysSmartWifiDeviceScanner(DeviceScanner):
41  """Class which queries a Linksys Access Point."""
42 
43  def __init__(self, config):
44  """Initialize the scanner."""
45  self.hosthost = config[CONF_HOST]
46  self.last_resultslast_results = {}
47 
48  # Check if the access point is accessible
49  response = self._make_request_make_request()
50  if response.status_code != HTTPStatus.OK:
51  raise ConnectionError("Cannot connect to Linksys Access Point")
52 
53  def scan_devices(self):
54  """Scan for new devices and return a list with device IDs (MACs)."""
55  self._update_info_update_info()
56 
57  return self.last_resultslast_results.keys()
58 
59  def get_device_name(self, device):
60  """Return the name (if known) of the device."""
61  return self.last_resultslast_results.get(device)
62 
63  def _update_info(self):
64  """Check for connected devices."""
65  _LOGGER.debug("Checking Linksys Smart Wifi")
66 
67  self.last_resultslast_results = {}
68  response = self._make_request_make_request()
69  if response.status_code != HTTPStatus.OK:
70  _LOGGER.error(
71  "Got HTTP status code %d when getting device list", response.status_code
72  )
73  return False
74  try:
75  data = response.json()
76  result = data["responses"][0]
77  devices = result["output"]["devices"]
78  for device in devices:
79  if not (macs := device["knownMACAddresses"]):
80  _LOGGER.warning("Skipping device without known MAC address")
81  continue
82  mac = macs[-1]
83  if not device["connections"]:
84  _LOGGER.debug("Device %s is not connected", mac)
85  continue
86 
87  name = None
88  for prop in device["properties"]:
89  if prop["name"] == "userDeviceName":
90  name = prop["value"]
91  if not name:
92  name = device.get("friendlyName", device["deviceID"])
93 
94  _LOGGER.debug("Device %s is connected", mac)
95  self.last_resultslast_results[mac] = name
96  except (KeyError, IndexError):
97  _LOGGER.exception("Router returned unexpected response")
98  return False
99  return True
100 
101  def _make_request(self):
102  # Weirdly enough, this doesn't seem to require authentication
103  data = [
104  {
105  "request": {"sinceRevision": 0},
106  "action": "http://linksys.com/jnap/devicelist/GetDevices",
107  }
108  ]
109  headers = {"X-JNAP-Action": "http://linksys.com/jnap/core/Transaction"}
110  return requests.post(
111  f"http://{self.host}/JNAP/",
112  timeout=DEFAULT_TIMEOUT,
113  headers=headers,
114  json=data,
115  )
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
LinksysSmartWifiDeviceScanner|None get_scanner(HomeAssistant hass, ConfigType config)