Home Assistant Unofficial Reference 2024.12.1
account.py
Go to the documentation of this file.
1 """StarLine Account."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from datetime import datetime, timedelta
7 from typing import Any
8 
9 from starline import StarlineApi, StarlineDevice
10 
11 from homeassistant.config_entries import ConfigEntry
12 from homeassistant.core import HomeAssistant, callback
13 from homeassistant.helpers.device_registry import DeviceInfo
14 from homeassistant.helpers.event import async_track_time_interval
15 from homeassistant.util import dt as dt_util
16 
17 from .const import (
18  _LOGGER,
19  DATA_EXPIRES,
20  DATA_SLID_TOKEN,
21  DATA_SLNET_TOKEN,
22  DATA_USER_ID,
23  DEFAULT_SCAN_INTERVAL,
24  DEFAULT_SCAN_OBD_INTERVAL,
25  DOMAIN,
26 )
27 
28 
29 def _parse_datetime(dt_str: str | None) -> str | None:
30  if dt_str is None or (parsed := dt_util.parse_datetime(dt_str)) is None:
31  return None
32  return parsed.replace(tzinfo=dt_util.UTC).isoformat()
33 
34 
36  """StarLine Account class."""
37 
38  def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
39  """Initialize StarLine account."""
40  self._hass: HomeAssistant = hass
41  self._config_entry: ConfigEntry = config_entry
42  self._update_interval_update_interval: int = DEFAULT_SCAN_INTERVAL
43  self._update_obd_interval_update_obd_interval: int = DEFAULT_SCAN_OBD_INTERVAL
44  self._unsubscribe_auto_updater_unsubscribe_auto_updater: Callable | None = None
45  self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater: Callable | None = None
46  self._api: StarlineApi = StarlineApi(
47  config_entry.data[DATA_USER_ID], config_entry.data[DATA_SLNET_TOKEN]
48  )
49 
50  def _check_slnet_token(self, interval: int) -> None:
51  """Check SLNet token expiration and update if needed."""
52  now = datetime.now().timestamp()
53  slnet_token_expires = self._config_entry.data[DATA_EXPIRES]
54 
55  if now + interval > slnet_token_expires:
56  self._update_slnet_token_update_slnet_token()
57 
58  def _update_slnet_token(self) -> None:
59  """Update SLNet token."""
60  slid_token = self._config_entry.data[DATA_SLID_TOKEN]
61 
62  try:
63  slnet_token, slnet_token_expires, user_id = self._api.get_user_id(
64  slid_token
65  )
66  self._api.set_slnet_token(slnet_token)
67  self._api.set_user_id(user_id)
68  self._hass.add_job(
69  self._save_slnet_token_save_slnet_token,
70  {
71  **self._config_entry.data,
72  DATA_SLNET_TOKEN: slnet_token,
73  DATA_EXPIRES: slnet_token_expires,
74  DATA_USER_ID: user_id,
75  },
76  )
77  except Exception as err: # noqa: BLE001
78  _LOGGER.error("Error updating SLNet token: %s", err)
79 
80  @callback
81  def _save_slnet_token(self, data) -> None:
82  self._hass.config_entries.async_update_entry(
83  self._config_entry,
84  data=data,
85  )
86 
87  def _update_data(self):
88  """Update StarLine data."""
89  self._check_slnet_token_check_slnet_token(self._update_interval_update_interval)
90  self._api.update()
91 
92  def _update_obd_data(self):
93  """Update StarLine OBD data."""
94  self._check_slnet_token_check_slnet_token(self._update_obd_interval_update_obd_interval)
95  self._api.update_obd()
96 
97  @property
98  def api(self) -> StarlineApi:
99  """Return the instance of the API."""
100  return self._api
101 
102  async def update(self, unused=None):
103  """Update StarLine data."""
104  await self._hass.async_add_executor_job(self._update_data_update_data)
105 
106  async def update_obd(self, unused=None):
107  """Update StarLine OBD data."""
108  await self._hass.async_add_executor_job(self._update_obd_data_update_obd_data)
109 
110  def set_update_interval(self, interval: int) -> None:
111  """Set StarLine API update interval."""
112  _LOGGER.debug("Setting update interval: %ds", interval)
113  self._update_interval_update_interval = interval
114  if self._unsubscribe_auto_updater_unsubscribe_auto_updater is not None:
115  self._unsubscribe_auto_updater_unsubscribe_auto_updater()
116 
117  delta = timedelta(seconds=interval)
118  self._unsubscribe_auto_updater_unsubscribe_auto_updater = async_track_time_interval(
119  self._hass, self.updateupdate, delta
120  )
121 
122  def set_update_obd_interval(self, interval: int) -> None:
123  """Set StarLine API OBD update interval."""
124  _LOGGER.debug("Setting OBD update interval: %ds", interval)
125  self._update_obd_interval_update_obd_interval = interval
126  if self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater is not None:
127  self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater()
128 
129  delta = timedelta(seconds=interval)
130  self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater = async_track_time_interval(
131  self._hass, self.update_obdupdate_obd, delta
132  )
133 
134  def unload(self):
135  """Unload StarLine API."""
136  _LOGGER.debug("Unloading StarLine API")
137  if self._unsubscribe_auto_updater_unsubscribe_auto_updater is not None:
138  self._unsubscribe_auto_updater_unsubscribe_auto_updater()
139  self._unsubscribe_auto_updater_unsubscribe_auto_updater = None
140  if self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater is not None:
141  self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater()
142  self._unsubscribe_auto_obd_updater_unsubscribe_auto_obd_updater = None
143 
144  @staticmethod
145  def device_info(device: StarlineDevice) -> DeviceInfo:
146  """Device information for entities."""
147  return DeviceInfo(
148  identifiers={(DOMAIN, device.device_id)},
149  manufacturer="StarLine",
150  model=device.typename,
151  name=device.name,
152  sw_version=device.fw_version,
153  configuration_url="https://starline-online.ru/",
154  )
155 
156  @staticmethod
157  def gps_attrs(device: StarlineDevice) -> dict[str, Any]:
158  """Attributes for device tracker."""
159  return {
160  "updated": dt_util.utc_from_timestamp(device.position["ts"]).isoformat(),
161  "online": device.online,
162  }
163 
164  @staticmethod
165  def balance_attrs(device: StarlineDevice) -> dict[str, Any]:
166  """Attributes for balance sensor."""
167  return {
168  "operator": device.balance.get("operator"),
169  "state": device.balance.get("state"),
170  "updated": _parse_datetime(device.balance.get("ts")),
171  }
172 
173  @staticmethod
174  def gsm_attrs(device: StarlineDevice) -> dict[str, Any]:
175  """Attributes for GSM sensor."""
176  return {
177  "raw": device.gsm_level,
178  "imei": device.imei,
179  "phone": device.phone,
180  "online": device.online,
181  }
182 
183  @staticmethod
184  def engine_attrs(device: StarlineDevice) -> dict[str, Any]:
185  """Attributes for engine switch."""
186  return {
187  "autostart": device.car_state.get("r_start"),
188  "ignition": device.car_state.get("run"),
189  }
190 
191  @staticmethod
192  def errors_attrs(device: StarlineDevice) -> dict[str, Any]:
193  """Attributes for errors sensor."""
194  return {"errors": device.errors.get("errors")}
dict[str, Any] errors_attrs(StarlineDevice device)
Definition: account.py:192
dict[str, Any] engine_attrs(StarlineDevice device)
Definition: account.py:184
DeviceInfo device_info(StarlineDevice device)
Definition: account.py:145
dict[str, Any] gps_attrs(StarlineDevice device)
Definition: account.py:157
dict[str, Any] gsm_attrs(StarlineDevice device)
Definition: account.py:174
None __init__(self, HomeAssistant hass, ConfigEntry config_entry)
Definition: account.py:38
dict[str, Any] balance_attrs(StarlineDevice device)
Definition: account.py:165
str|None _parse_datetime(str|None dt_str)
Definition: account.py:29
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
Definition: event.py:1679