Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for an exposed aREST RESTful API of a device."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 from http import HTTPStatus
7 import logging
8 
9 import requests
10 import voluptuous as vol
11 
13  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
14  SensorEntity,
15 )
16 from homeassistant.const import (
17  CONF_MONITORED_VARIABLES,
18  CONF_NAME,
19  CONF_RESOURCE,
20  CONF_UNIT_OF_MEASUREMENT,
21  CONF_VALUE_TEMPLATE,
22 )
23 from homeassistant.core import HomeAssistant
24 from homeassistant.exceptions import TemplateError
26 from homeassistant.helpers.entity_platform import AddEntitiesCallback
27 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
28 from homeassistant.util import Throttle
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
33 
34 CONF_FUNCTIONS = "functions"
35 CONF_PINS = "pins"
36 
37 DEFAULT_NAME = "aREST sensor"
38 
39 PIN_VARIABLE_SCHEMA = vol.Schema(
40  {
41  vol.Optional(CONF_NAME): cv.string,
42  vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
43  vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
44  }
45 )
46 
47 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
48  {
49  vol.Required(CONF_RESOURCE): cv.url,
50  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
51  vol.Optional(CONF_PINS, default={}): vol.Schema(
52  {cv.string: PIN_VARIABLE_SCHEMA}
53  ),
54  vol.Optional(CONF_MONITORED_VARIABLES, default={}): vol.Schema(
55  {cv.string: PIN_VARIABLE_SCHEMA}
56  ),
57  }
58 )
59 
60 
62  hass: HomeAssistant,
63  config: ConfigType,
64  add_entities: AddEntitiesCallback,
65  discovery_info: DiscoveryInfoType | None = None,
66 ) -> None:
67  """Set up the aREST sensor."""
68  resource = config[CONF_RESOURCE]
69  var_conf = config[CONF_MONITORED_VARIABLES]
70  pins = config[CONF_PINS]
71 
72  try:
73  response = requests.get(resource, timeout=10).json()
74  except requests.exceptions.MissingSchema:
75  _LOGGER.error(
76  "Missing resource or schema in configuration. Add http:// to your URL"
77  )
78  return
79  except requests.exceptions.ConnectionError:
80  _LOGGER.error("No route to device at %s", resource)
81  return
82 
83  arest = ArestData(resource)
84 
85  def make_renderer(value_template):
86  """Create a renderer based on variable_template value."""
87  if value_template is None:
88  return lambda value: value
89 
90  def _render(value):
91  try:
92  return value_template.async_render({"value": value}, parse_result=False)
93  except TemplateError:
94  _LOGGER.exception("Error parsing value")
95  return value
96 
97  return _render
98 
99  dev = []
100 
101  if var_conf is not None:
102  for variable, var_data in var_conf.items():
103  if variable not in response["variables"]:
104  _LOGGER.error("Variable: %s does not exist", variable)
105  continue
106 
107  renderer = make_renderer(var_data.get(CONF_VALUE_TEMPLATE))
108  dev.append(
109  ArestSensor(
110  arest,
111  resource,
112  config.get(CONF_NAME, response[CONF_NAME]),
113  var_data.get(CONF_NAME, variable),
114  variable=variable,
115  unit_of_measurement=var_data.get(CONF_UNIT_OF_MEASUREMENT),
116  renderer=renderer,
117  )
118  )
119 
120  if pins is not None:
121  for pinnum, pin in pins.items():
122  renderer = make_renderer(pin.get(CONF_VALUE_TEMPLATE))
123  dev.append(
124  ArestSensor(
125  ArestData(resource, pinnum),
126  resource,
127  config.get(CONF_NAME, response[CONF_NAME]),
128  pin.get(CONF_NAME),
129  pin=pinnum,
130  unit_of_measurement=pin.get(CONF_UNIT_OF_MEASUREMENT),
131  renderer=renderer,
132  )
133  )
134 
135  add_entities(dev, True)
136 
137 
139  """Implementation of an aREST sensor for exposed variables."""
140 
141  def __init__(
142  self,
143  arest,
144  resource,
145  location,
146  name,
147  variable=None,
148  pin=None,
149  unit_of_measurement=None,
150  renderer=None,
151  ):
152  """Initialize the sensor."""
153  self.arestarest = arest
154  self._attr_name_attr_name = f"{location.title()} {name.title()}"
155  self._variable_variable = variable
156  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = unit_of_measurement
157  self._renderer_renderer = renderer
158 
159  if pin is not None:
160  request = requests.get(f"{resource}/mode/{pin}/i", timeout=10)
161  if request.status_code != HTTPStatus.OK:
162  _LOGGER.error("Can't set mode of %s", resource)
163 
164  def update(self) -> None:
165  """Get the latest data from aREST API."""
166  self.arestarest.update()
167  self._attr_available_attr_available = self.arestarest.available
168  values = self.arestarest.data
169  if "error" in values:
170  self._attr_native_value_attr_native_value = values["error"]
171  else:
172  self._attr_native_value_attr_native_value = self._renderer_renderer(
173  values.get("value", values.get(self._variable_variable, None))
174  )
175 
176 
177 class ArestData:
178  """The Class for handling the data retrieval for variables."""
179 
180  def __init__(self, resource, pin=None):
181  """Initialize the data object."""
182  self._resource_resource = resource
183  self._pin_pin = pin
184  self.datadata = {}
185  self.availableavailable = True
186 
187  @Throttle(MIN_TIME_BETWEEN_UPDATES)
188  def update(self):
189  """Get the latest data from aREST device."""
190  try:
191  if self._pin_pin is None:
192  response = requests.get(self._resource_resource, timeout=10)
193  self.datadata = response.json()["variables"]
194  else:
195  try:
196  if str(self._pin_pin[0]) == "A":
197  response = requests.get(
198  f"{self._resource}/analog/{self._pin[1:]}", timeout=10
199  )
200  self.datadata = {"value": response.json()["return_value"]}
201  except TypeError:
202  response = requests.get(
203  f"{self._resource}/digital/{self._pin}", timeout=10
204  )
205  self.datadata = {"value": response.json()["return_value"]}
206  self.availableavailable = True
207  except requests.exceptions.ConnectionError:
208  _LOGGER.error("No route to device %s", self._resource_resource)
209  self.availableavailable = False
def __init__(self, resource, pin=None)
Definition: sensor.py:180
def __init__(self, arest, resource, location, name, variable=None, pin=None, unit_of_measurement=None, renderer=None)
Definition: sensor.py:151
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:66
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40