Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for retrieving status info from Google Wifi/OnHub routers."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 from datetime import timedelta
7 import logging
8 
9 import requests
10 import voluptuous as vol
11 
13  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
14  SensorEntity,
15  SensorEntityDescription,
16 )
17 from homeassistant.const import (
18  CONF_HOST,
19  CONF_MONITORED_CONDITIONS,
20  CONF_NAME,
21  UnitOfTime,
22 )
23 from homeassistant.core import HomeAssistant
25 from homeassistant.helpers.entity_platform import AddEntitiesCallback
26 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
27 from homeassistant.util import Throttle, dt as dt_util
28 
29 _LOGGER = logging.getLogger(__name__)
30 
31 ATTR_CURRENT_VERSION = "current_version"
32 ATTR_LAST_RESTART = "last_restart"
33 ATTR_LOCAL_IP = "local_ip"
34 ATTR_NEW_VERSION = "new_version"
35 ATTR_STATUS = "status"
36 ATTR_UPTIME = "uptime"
37 
38 DEFAULT_HOST = "testwifi.here"
39 DEFAULT_NAME = "google_wifi"
40 
41 ENDPOINT = "/api/v1/status"
42 
43 MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=1)
44 
45 
46 @dataclass(frozen=True, kw_only=True)
48  """Describes GoogleWifi sensor entity."""
49 
50  primary_key: str
51  sensor_key: str
52 
53 
54 SENSOR_TYPES: tuple[GoogleWifiSensorEntityDescription, ...] = (
56  key=ATTR_CURRENT_VERSION,
57  primary_key="software",
58  sensor_key="softwareVersion",
59  icon="mdi:checkbox-marked-circle-outline",
60  ),
62  key=ATTR_NEW_VERSION,
63  primary_key="software",
64  sensor_key="updateNewVersion",
65  icon="mdi:update",
66  ),
68  key=ATTR_UPTIME,
69  primary_key="system",
70  sensor_key="uptime",
71  native_unit_of_measurement=UnitOfTime.DAYS,
72  icon="mdi:timelapse",
73  ),
75  key=ATTR_LAST_RESTART,
76  primary_key="system",
77  sensor_key="uptime",
78  icon="mdi:restart",
79  ),
81  key=ATTR_LOCAL_IP,
82  primary_key="wan",
83  sensor_key="localIpAddress",
84  icon="mdi:access-point-network",
85  ),
87  key=ATTR_STATUS,
88  primary_key="wan",
89  sensor_key="online",
90  icon="mdi:google",
91  ),
92 )
93 
94 SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
95 
96 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
97  {
98  vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
99  vol.Optional(CONF_MONITORED_CONDITIONS, default=SENSOR_KEYS): vol.All(
100  cv.ensure_list, [vol.In(SENSOR_KEYS)]
101  ),
102  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
103  }
104 )
105 
106 
108  hass: HomeAssistant,
109  config: ConfigType,
110  add_entities: AddEntitiesCallback,
111  discovery_info: DiscoveryInfoType | None = None,
112 ) -> None:
113  """Set up the Google Wifi sensor."""
114  name = config[CONF_NAME]
115  host = config[CONF_HOST]
116  monitored_conditions = config[CONF_MONITORED_CONDITIONS]
117 
118  api = GoogleWifiAPI(host, monitored_conditions)
119  entities = [
120  GoogleWifiSensor(api, name, description)
121  for description in SENSOR_TYPES
122  if description.key in monitored_conditions
123  ]
124  add_entities(entities, True)
125 
126 
128  """Representation of a Google Wifi sensor."""
129 
130  entity_description: GoogleWifiSensorEntityDescription
131 
132  def __init__(
133  self,
134  api: GoogleWifiAPI,
135  name: str,
136  description: GoogleWifiSensorEntityDescription,
137  ) -> None:
138  """Initialize a Google Wifi sensor."""
139  self.entity_descriptionentity_description = description
140  self._api_api = api
141  self._attr_name_attr_name = f"{name}_{description.key}"
142 
143  @property
144  def available(self) -> bool:
145  """Return availability of Google Wifi API."""
146  return self._api_api.available
147 
148  def update(self) -> None:
149  """Get the latest data from the Google Wifi API."""
150  self._api_api.update()
151  if self.availableavailableavailable:
152  self._attr_native_value_attr_native_value = self._api_api.data[self.entity_descriptionentity_description.key]
153  else:
154  self._attr_native_value_attr_native_value = None
155 
156 
158  """Get the latest data and update the states."""
159 
160  def __init__(self, host, conditions):
161  """Initialize the data object."""
162  uri = "http://"
163  resource = f"{uri}{host}{ENDPOINT}"
164  self._request_request = requests.Request("GET", resource).prepare()
165  self.raw_dataraw_data = None
166  self.conditionsconditions = conditions
167  self.datadata = {
168  ATTR_CURRENT_VERSION: None,
169  ATTR_NEW_VERSION: None,
170  ATTR_UPTIME: None,
171  ATTR_LAST_RESTART: None,
172  ATTR_LOCAL_IP: None,
173  ATTR_STATUS: None,
174  }
175  self.availableavailable = True
176  self.updateupdate()
177 
178  @Throttle(MIN_TIME_BETWEEN_UPDATES)
179  def update(self):
180  """Get the latest data from the router."""
181  try:
182  with requests.Session() as sess:
183  response = sess.send(self._request_request, timeout=10)
184  self.raw_dataraw_data = response.json()
185  self.data_formatdata_format()
186  self.availableavailable = True
187  except (ValueError, requests.exceptions.ConnectionError):
188  _LOGGER.warning("Unable to fetch data from Google Wifi")
189  self.availableavailable = False
190  self.raw_dataraw_data = None
191 
192  def data_format(self):
193  """Format raw data into easily accessible dict."""
194  for description in SENSOR_TYPES:
195  if description.key not in self.conditionsconditions:
196  continue
197  attr_key = description.key
198  try:
199  if description.primary_key in self.raw_dataraw_data:
200  sensor_value = self.raw_dataraw_data[description.primary_key][
201  description.sensor_key
202  ]
203  # Format sensor for better readability
204  if attr_key == ATTR_NEW_VERSION and sensor_value == "0.0.0.0":
205  sensor_value = "Latest"
206  elif attr_key == ATTR_UPTIME:
207  sensor_value = round(sensor_value / (3600 * 24), 2)
208  elif attr_key == ATTR_LAST_RESTART:
209  last_restart = dt_util.now() - timedelta(seconds=sensor_value)
210  sensor_value = last_restart.strftime("%Y-%m-%d %H:%M:%S")
211  elif attr_key == ATTR_STATUS:
212  if sensor_value:
213  sensor_value = "Online"
214  else:
215  sensor_value = "Offline"
216  elif (
217  attr_key == ATTR_LOCAL_IP and not self.raw_dataraw_data["wan"]["online"]
218  ):
219  sensor_value = None
220 
221  self.datadata[attr_key] = sensor_value
222  except KeyError:
223  _LOGGER.error(
224  (
225  "Router does not support %s field. "
226  "Please remove %s from monitored_conditions"
227  ),
228  description.sensor_key,
229  attr_key,
230  )
231  self.datadata[attr_key] = None
None __init__(self, GoogleWifiAPI api, str name, GoogleWifiSensorEntityDescription description)
Definition: sensor.py:137
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:112
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40