Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Washington State Department of Transportation (WSDOT) data."""
2 
3 from __future__ import annotations
4 
5 from datetime import datetime, timedelta, timezone
6 from http import HTTPStatus
7 import logging
8 import re
9 from typing import Any
10 
11 import requests
12 import voluptuous as vol
13 
15  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
16  SensorEntity,
17 )
18 from homeassistant.const import ATTR_NAME, CONF_API_KEY, CONF_ID, CONF_NAME, UnitOfTime
19 from homeassistant.core import HomeAssistant
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 ATTR_ACCESS_CODE = "AccessCode"
27 ATTR_AVG_TIME = "AverageTime"
28 ATTR_CURRENT_TIME = "CurrentTime"
29 ATTR_DESCRIPTION = "Description"
30 ATTR_TIME_UPDATED = "TimeUpdated"
31 ATTR_TRAVEL_TIME_ID = "TravelTimeID"
32 
33 ATTRIBUTION = "Data provided by WSDOT"
34 
35 CONF_TRAVEL_TIMES = "travel_time"
36 
37 ICON = "mdi:car"
38 
39 RESOURCE = (
40  "http://www.wsdot.wa.gov/Traffic/api/TravelTimes/"
41  "TravelTimesREST.svc/GetTravelTimeAsJson"
42 )
43 
44 SCAN_INTERVAL = timedelta(minutes=3)
45 
46 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
47  {
48  vol.Required(CONF_API_KEY): cv.string,
49  vol.Required(CONF_TRAVEL_TIMES): [
50  {vol.Required(CONF_ID): cv.string, vol.Optional(CONF_NAME): cv.string}
51  ],
52  }
53 )
54 
55 
57  hass: HomeAssistant,
58  config: ConfigType,
59  add_entities: AddEntitiesCallback,
60  discovery_info: DiscoveryInfoType | None = None,
61 ) -> None:
62  """Set up the WSDOT sensor."""
63  sensors = []
64  for travel_time in config[CONF_TRAVEL_TIMES]:
65  name = travel_time.get(CONF_NAME) or travel_time.get(CONF_ID)
66  sensors.append(
68  name, config.get(CONF_API_KEY), travel_time.get(CONF_ID)
69  )
70  )
71 
72  add_entities(sensors, True)
73 
74 
76  """Sensor that reads the WSDOT web API.
77 
78  WSDOT provides ferry schedules, toll rates, weather conditions,
79  mountain pass conditions, and more. Subclasses of this
80  can read them and make them available.
81  """
82 
83  _attr_icon = ICON
84 
85  def __init__(self, name, access_code):
86  """Initialize the sensor."""
87  self._data_data = {}
88  self._access_code_access_code = access_code
89  self._name_name = name
90  self._state_state = None
91 
92  @property
93  def name(self):
94  """Return the name of the sensor."""
95  return self._name_name
96 
97  @property
98  def native_value(self):
99  """Return the state of the sensor."""
100  return self._state_state
101 
102 
104  """Travel time sensor from WSDOT."""
105 
106  _attr_attribution = ATTRIBUTION
107  _attr_native_unit_of_measurement = UnitOfTime.MINUTES
108 
109  def __init__(self, name, access_code, travel_time_id):
110  """Construct a travel time sensor."""
111  self._travel_time_id_travel_time_id = travel_time_id
112  WashingtonStateTransportSensor.__init__(self, name, access_code)
113 
114  def update(self) -> None:
115  """Get the latest data from WSDOT."""
116  params = {
117  ATTR_ACCESS_CODE: self._access_code_access_code,
118  ATTR_TRAVEL_TIME_ID: self._travel_time_id_travel_time_id,
119  }
120 
121  response = requests.get(RESOURCE, params, timeout=10)
122  if response.status_code != HTTPStatus.OK:
123  _LOGGER.warning("Invalid response from WSDOT API")
124  else:
125  self._data_data_data = response.json()
126  self._state_state_state = self._data_data_data.get(ATTR_CURRENT_TIME)
127 
128  @property
129  def extra_state_attributes(self) -> dict[str, Any] | None:
130  """Return other details about the sensor state."""
131  if self._data_data_data is not None:
132  attrs = {}
133  for key in (
134  ATTR_AVG_TIME,
135  ATTR_NAME,
136  ATTR_DESCRIPTION,
137  ATTR_TRAVEL_TIME_ID,
138  ):
139  attrs[key] = self._data_data_data.get(key)
140  attrs[ATTR_TIME_UPDATED] = _parse_wsdot_timestamp(
141  self._data_data_data.get(ATTR_TIME_UPDATED)
142  )
143  return attrs
144  return None
145 
146 
147 def _parse_wsdot_timestamp(timestamp):
148  """Convert WSDOT timestamp to datetime."""
149  if not timestamp:
150  return None
151  # ex: Date(1485040200000-0800)
152  milliseconds, tzone = re.search(r"Date\‍((\d+)([+-]\d\d)\d\d\‍)", timestamp).groups()
153  return datetime.fromtimestamp(
154  int(milliseconds) / 1000, tz=timezone(timedelta(hours=int(tzone)))
155  )
def __init__(self, name, access_code, travel_time_id)
Definition: sensor.py:109
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40
def _parse_wsdot_timestamp(timestamp)
Definition: sensor.py:147
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:61