Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor for SigFox devices."""
2 
3 from __future__ import annotations
4 
5 import datetime
6 from http import HTTPStatus
7 import json
8 import logging
9 from urllib.parse import urljoin
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 CONF_NAME
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 SCAN_INTERVAL = datetime.timedelta(seconds=30)
27 API_URL = "https://backend.sigfox.com/api/"
28 CONF_API_LOGIN = "api_login"
29 CONF_API_PASSWORD = "api_password"
30 DEFAULT_NAME = "sigfox"
31 
32 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
33  {
34  vol.Required(CONF_API_LOGIN): cv.string,
35  vol.Required(CONF_API_PASSWORD): cv.string,
36  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
37  }
38 )
39 
40 
42  hass: HomeAssistant,
43  config: ConfigType,
44  add_entities: AddEntitiesCallback,
45  discovery_info: DiscoveryInfoType | None = None,
46 ) -> None:
47  """Set up the sigfox sensor."""
48  api_login = config[CONF_API_LOGIN]
49  api_password = config[CONF_API_PASSWORD]
50  name = config[CONF_NAME]
51  try:
52  sigfox = SigfoxAPI(api_login, api_password)
53  except ValueError:
54  return
55  auth = sigfox.auth
56  devices = sigfox.devices
57 
58  add_entities((SigfoxDevice(device, auth, name) for device in devices), True)
59 
60 
61 def epoch_to_datetime(epoch_time):
62  """Take an ms since epoch and return datetime string."""
63  return datetime.datetime.fromtimestamp(epoch_time).isoformat()
64 
65 
66 class SigfoxAPI:
67  """Class for interacting with the SigFox API."""
68 
69  def __init__(self, api_login, api_password):
70  """Initialise the API object."""
71  self._auth_auth = requests.auth.HTTPBasicAuth(api_login, api_password)
72  if self.check_credentialscheck_credentials():
73  device_types = self.get_device_typesget_device_types()
74  self._devices_devices = self.get_devicesget_devices(device_types)
75 
76  def check_credentials(self):
77  """Check API credentials are valid."""
78  url = urljoin(API_URL, "devicetypes")
79  response = requests.get(url, auth=self._auth_auth, timeout=10)
80  if response.status_code != HTTPStatus.OK:
81  if response.status_code == HTTPStatus.UNAUTHORIZED:
82  _LOGGER.error("Invalid credentials for Sigfox API")
83  else:
84  _LOGGER.error(
85  "Unable to login to Sigfox API, error code %s",
86  str(response.status_code),
87  )
88  raise ValueError("Sigfox integration not set up")
89  return True
90 
91  def get_device_types(self):
92  """Get a list of device types."""
93  url = urljoin(API_URL, "devicetypes")
94  response = requests.get(url, auth=self._auth_auth, timeout=10)
95  return [device["id"] for device in json.loads(response.text)["data"]]
96 
97  def get_devices(self, device_types):
98  """Get the device_id of each device registered."""
99  devices = []
100  for unique_type in device_types:
101  location_url = f"devicetypes/{unique_type}/devices"
102  url = urljoin(API_URL, location_url)
103  response = requests.get(url, auth=self._auth_auth, timeout=10)
104  devices_data = json.loads(response.text)["data"]
105  devices.extend(device["id"] for device in devices_data)
106  return devices
107 
108  @property
109  def auth(self):
110  """Return the API authentication."""
111  return self._auth_auth
112 
113  @property
114  def devices(self):
115  """Return the list of device_id."""
116  return self._devices_devices
117 
118 
120  """Class for single sigfox device."""
121 
122  def __init__(self, device_id, auth, name):
123  """Initialise the device object."""
124  self._device_id_device_id = device_id
125  self._auth_auth = auth
126  self._message_data_message_data = {}
127  self._name_name = f"{name}_{device_id}"
128  self._state_state = None
129 
130  def get_last_message(self):
131  """Return the last message from a device."""
132  device_url = f"devices/{self._device_id}/messages?limit=1"
133  url = urljoin(API_URL, device_url)
134  response = requests.get(url, auth=self._auth_auth, timeout=10)
135  data = json.loads(response.text)["data"][0]
136  payload = bytes.fromhex(data["data"]).decode("utf-8")
137  lat = data["rinfos"][0]["lat"]
138  lng = data["rinfos"][0]["lng"]
139  snr = data["snr"]
140  epoch_time = data["time"]
141  return {
142  "lat": lat,
143  "lng": lng,
144  "payload": payload,
145  "snr": snr,
146  "time": epoch_to_datetime(epoch_time),
147  }
148 
149  def update(self) -> None:
150  """Fetch the latest device message."""
151  self._message_data_message_data = self.get_last_messageget_last_message()
152  self._state_state = self._message_data_message_data["payload"]
153 
154  @property
155  def name(self):
156  """Return the HA name of the sensor."""
157  return self._name_name
158 
159  @property
160  def native_value(self):
161  """Return the payload of the last message."""
162  return self._state_state
163 
164  @property
166  """Return other details about the last message."""
167  return self._message_data_message_data
def __init__(self, api_login, api_password)
Definition: sensor.py:69
def __init__(self, device_id, auth, name)
Definition: sensor.py:122
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:46