1 """Data update coordinator for Traccar Server."""
3 from __future__
import annotations
6 from datetime
import datetime
7 from logging
import DEBUG
as LOG_LEVEL_DEBUG
8 from typing
import TYPE_CHECKING, Any, TypedDict
10 from pytraccar
import (
25 from .const
import DOMAIN, EVENTS, LOGGER
26 from .helpers
import get_device, get_first_geofence
30 """Traccar Server coordinator data."""
33 geofence: GeofenceModel |
None
34 position: PositionModel
35 attributes: dict[str, Any]
38 type TraccarServerCoordinatorData = dict[int, TraccarServerCoordinatorDataDevice]
42 """Class to manage fetching Traccar Server data."""
44 config_entry: ConfigEntry
53 skip_accuracy_filter_for: list[str],
54 custom_attributes: list[str],
56 """Initialize global Traccar Server data updater."""
68 self.
_geofences_geofences: list[GeofenceModel] = []
73 """Fetch data from Traccar Server."""
74 LOGGER.debug(
"Updating device data")
75 data: TraccarServerCoordinatorData = {}
81 ) = await asyncio.gather(
82 self.
clientclient.get_devices(),
83 self.
clientclient.get_positions(),
84 self.
clientclient.get_geofences(),
86 except TraccarException
as ex:
87 raise UpdateFailed(f
"Error while updating device data: {ex}")
from ex
90 assert isinstance(devices, list[DeviceModel])
91 assert isinstance(positions, list[PositionModel])
92 assert isinstance(geofences, list[GeofenceModel])
96 if self.
loggerlogger.isEnabledFor(LOG_LEVEL_DEBUG):
97 self.
loggerlogger.debug(
"Received devices: %s", devices)
98 self.
loggerlogger.debug(
"Received positions: %s", positions)
100 for position
in positions:
101 device_id = position[
"deviceId"]
102 if (device :=
get_device(device_id, devices))
is None:
104 "Device %s not found for position: %s",
117 "Skipping position update %s for %s due to accuracy filter",
127 position[
"geofenceIds"]
or [],
129 "position": position,
136 """Handle subscription data."""
137 self.
loggerlogger.debug(
"Received subscription data: %s", data)
139 update_devices = set()
140 for device
in data.get(
"devices")
or []:
141 if (device_id := device[
"id"])
not in self.
datadata:
142 self.
loggerlogger.debug(
"Device %s not found in data", device_id)
148 device, self.
datadata[device_id][
"position"]
153 self.
datadata[device_id][
"device"] = device
154 self.
datadata[device_id][
"attributes"] = attr
155 update_devices.add(device_id)
157 for position
in data.get(
"positions")
or []:
158 if (device_id := position[
"deviceId"])
not in self.
datadata:
160 "Device %s for position %s not found in data",
169 self.
datadata[device_id][
"device"], position
173 "Skipping position update %s for %s due to accuracy filter",
179 self.
datadata[device_id][
"position"] = position
180 self.
datadata[device_id][
"attributes"] = attr
183 position[
"geofenceIds"]
or [],
185 update_devices.add(device_id)
187 for device_id
in update_devices:
191 """Import events from Traccar."""
192 start_time = dt_util.utcnow().replace(tzinfo=
None)
198 events = await self.
clientclient.get_reports_events(
200 start_time=start_time,
202 event_types=self.
eventsevents,
209 device = self.
datadata[event[
"deviceId"]][
"device"]
210 self.
hasshass.bus.async_fire(
218 f
"traccar_{EVENTS[event['type']]}",
220 "device_traccar_id": event[
"deviceId"],
221 "device_name": device[
"name"]
if device
else None,
222 "type": event[
"type"],
223 "serverTime": event[
"eventTime"],
224 "attributes": event[
"attributes"],
229 """Subscribe to events."""
232 except TraccarException
as ex:
235 LOGGER.error(
"Error while subscribing to Traccar: %s", ex)
237 await asyncio.sleep(10)
243 position: PositionModel,
244 ) -> dict[str, Any] |
None:
245 """Return a dictionary of custom attributes if not filtered by accuracy configuration."""
247 skip_accuracy_filter =
False
251 skip_accuracy_filter =
True
252 attr[custom_attr] = device[
"attributes"].
get(
254 position[
"attributes"].
get(custom_attr,
None),
257 accuracy = position[
"accuracy"]
or 0.0
259 not skip_accuracy_filter
TraccarServerCoordinatorData _async_update_data(self)
None handle_subscription_data(self, SubscriptionData data)
dict[str, Any]|None _return_custom_attributes_if_not_filtered_by_accuracy_configuration(self, DeviceModel device, PositionModel position)
None import_events(self, datetime _)
_should_log_subscription_error
None __init__(self, HomeAssistant hass, ApiClient client, *list[str] events, float max_accuracy, list[str] skip_accuracy_filter_for, list[str] custom_attributes)
DeviceEntry get_device(HomeAssistant hass, str unique_id)
web.Response get(self, web.Request request, str config_key)
GeofenceModel|None get_first_geofence(list[GeofenceModel] geofences, list[int] target)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)