Home Assistant Unofficial Reference 2024.12.1
device_tracker.py
Go to the documentation of this file.
1 """Support for the Hitron CODA-4582U, provided by Rogers."""
2 
3 from __future__ import annotations
4 
5 from collections import namedtuple
6 from http import HTTPStatus
7 import logging
8 
9 import requests
10 import voluptuous as vol
11 
13  DOMAIN as DEVICE_TRACKER_DOMAIN,
14  PLATFORM_SCHEMA as DEVICE_TRACKER_PLATFORM_SCHEMA,
15  DeviceScanner,
16 )
17 from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_TYPE, CONF_USERNAME
18 from homeassistant.core import HomeAssistant
20 from homeassistant.helpers.typing import ConfigType
21 
22 _LOGGER = logging.getLogger(__name__)
23 
24 DEFAULT_TYPE = "rogers"
25 
26 PLATFORM_SCHEMA = DEVICE_TRACKER_PLATFORM_SCHEMA.extend(
27  {
28  vol.Required(CONF_HOST): cv.string,
29  vol.Required(CONF_USERNAME): cv.string,
30  vol.Required(CONF_PASSWORD): cv.string,
31  vol.Optional(CONF_TYPE, default=DEFAULT_TYPE): cv.string,
32  }
33 )
34 
35 
37  _hass: HomeAssistant, config: ConfigType
38 ) -> HitronCODADeviceScanner | None:
39  """Validate the configuration and return a Hitron CODA-4582U scanner."""
40  scanner = HitronCODADeviceScanner(config[DEVICE_TRACKER_DOMAIN])
41 
42  return scanner if scanner.success_init else None
43 
44 
45 Device = namedtuple("Device", ["mac", "name"]) # noqa: PYI024
46 
47 
48 class HitronCODADeviceScanner(DeviceScanner):
49  """Scanner for devices using the CODA's web interface."""
50 
51  def __init__(self, config):
52  """Initialize the scanner."""
53  self.last_resultslast_results = []
54  host = config[CONF_HOST]
55  self._url_url = f"http://{host}/data/getConnectInfo.asp"
56  self._loginurl_loginurl = f"http://{host}/goform/login"
57 
58  self._username_username = config.get(CONF_USERNAME)
59  self._password_password = config.get(CONF_PASSWORD)
60 
61  if config.get(CONF_TYPE) == "shaw":
62  self._type_type = "pwd"
63  else:
64  self._type_type = "pws"
65 
66  self._userid_userid = None
67 
68  self.success_initsuccess_init = self._update_info_update_info()
69 
70  def scan_devices(self):
71  """Scan for new devices and return a list with found device IDs."""
72  self._update_info_update_info()
73 
74  return [device.mac for device in self.last_resultslast_results]
75 
76  def get_device_name(self, device):
77  """Return the name of the device with the given MAC address."""
78  return next(
79  (result.name for result in self.last_resultslast_results if result.mac == device), None
80  )
81 
82  def _login(self):
83  """Log in to the router. This is required for subsequent api calls."""
84  _LOGGER.debug("Logging in to CODA")
85 
86  try:
87  data = [("user", self._username_username), (self._type_type, self._password_password)]
88  res = requests.post(self._loginurl_loginurl, data=data, timeout=10)
89  except requests.exceptions.Timeout:
90  _LOGGER.error("Connection to the router timed out at URL %s", self._url_url)
91  return False
92  if res.status_code != HTTPStatus.OK:
93  _LOGGER.error("Connection failed with http code %s", res.status_code)
94  return False
95  try:
96  self._userid_userid = res.cookies["userid"]
97  except KeyError:
98  _LOGGER.error("Failed to log in to router")
99  return False
100  return True
101 
102  def _update_info(self):
103  """Get ARP from router."""
104  _LOGGER.debug("Fetching")
105 
106  if self._userid_userid is None and not self._login_login():
107  _LOGGER.error("Could not obtain a user ID from the router")
108  return False
109  last_results = []
110 
111  # doing a request
112  try:
113  res = requests.get(self._url_url, timeout=10, cookies={"userid": self._userid_userid})
114  except requests.exceptions.Timeout:
115  _LOGGER.error("Connection to the router timed out at URL %s", self._url_url)
116  return False
117  if res.status_code != HTTPStatus.OK:
118  _LOGGER.error("Connection failed with http code %s", res.status_code)
119  return False
120  try:
121  result = res.json()
122  except ValueError:
123  # If json decoder could not parse the response
124  _LOGGER.error("Failed to parse response from router")
125  return False
126 
127  # parsing response
128  for info in result:
129  mac = info["macAddr"]
130  name = info["hostName"]
131  # No address = no item :)
132  if mac is None:
133  continue
134 
135  last_results.append(Device(mac.upper(), name))
136 
137  self.last_resultslast_results = last_results
138 
139  _LOGGER.debug("Request successful")
140  return True
HitronCODADeviceScanner|None get_scanner(HomeAssistant _hass, ConfigType config)