Home Assistant Unofficial Reference 2024.12.1
device_tracker.py
Go to the documentation of this file.
1 """Support for Swisscom routers (Internet-Box)."""
2 
3 from __future__ import annotations
4 
5 from contextlib import suppress
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 _LOGGER = logging.getLogger(__name__)
22 
23 DEFAULT_IP = "192.168.1.1"
24 
25 PLATFORM_SCHEMA = DEVICE_TRACKER_PLATFORM_SCHEMA.extend(
26  {vol.Optional(CONF_HOST, default=DEFAULT_IP): cv.string}
27 )
28 
29 
31  hass: HomeAssistant, config: ConfigType
32 ) -> SwisscomDeviceScanner | None:
33  """Return the Swisscom device scanner."""
34  scanner = SwisscomDeviceScanner(config[DEVICE_TRACKER_DOMAIN])
35 
36  return scanner if scanner.success_init else None
37 
38 
39 class SwisscomDeviceScanner(DeviceScanner):
40  """Class which queries a router running Swisscom Internet-Box firmware."""
41 
42  def __init__(self, config):
43  """Initialize the scanner."""
44  self.hosthost = config[CONF_HOST]
45  self.last_resultslast_results = {}
46 
47  # Test the router is accessible.
48  data = self.get_swisscom_dataget_swisscom_data()
49  self.success_initsuccess_init = data is not None
50 
51  def scan_devices(self):
52  """Scan for new devices and return a list with found device IDs."""
53  self._update_info_update_info()
54  return [client["mac"] for client in self.last_resultslast_results]
55 
56  def get_device_name(self, device):
57  """Return the name of the given device or None if we don't know."""
58  if not self.last_resultslast_results:
59  return None
60  for client in self.last_resultslast_results:
61  if client["mac"] == device:
62  return client["host"]
63  return None
64 
65  def _update_info(self):
66  """Ensure the information from the Swisscom router is up to date.
67 
68  Return boolean if scanning successful.
69  """
70  if not self.success_initsuccess_init:
71  return False
72 
73  _LOGGER.debug("Loading data from Swisscom Internet Box")
74  if not (data := self.get_swisscom_dataget_swisscom_data()):
75  return False
76 
77  active_clients = [client for client in data.values() if client["status"]]
78  self.last_resultslast_results = active_clients
79  return True
80 
81  def get_swisscom_data(self):
82  """Retrieve data from Swisscom and return parsed result."""
83  url = f"http://{self.host}/ws"
84  headers = {"Content-Type": "application/x-sah-ws-4-call+json"}
85  data = """
86  {"service":"Devices", "method":"get",
87  "parameters":{"expression":"lan and not self"}}"""
88 
89  devices = {}
90 
91  try:
92  request = requests.post(url, headers=headers, data=data, timeout=10)
93  except (
94  requests.exceptions.ConnectionError,
95  requests.exceptions.Timeout,
96  requests.exceptions.ConnectTimeout,
97  ):
98  _LOGGER.debug("No response from Swisscom Internet Box")
99  return devices
100 
101  if "status" not in request.json():
102  _LOGGER.debug("No status in response from Swisscom Internet Box")
103  return devices
104 
105  for device in request.json()["status"]:
106  with suppress(KeyError, requests.exceptions.RequestException):
107  devices[device["Key"]] = {
108  "ip": device["IPAddress"],
109  "mac": device["PhysAddress"],
110  "host": device["Name"],
111  "status": device["Active"],
112  }
113  return devices
SwisscomDeviceScanner|None get_scanner(HomeAssistant hass, ConfigType config)