ul-data-logger-api-sdk 3.0.0__tar.gz → 3.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -271,6 +271,7 @@ tags
271
271
  .envs/*
272
272
  !.envs/.local/
273
273
  !srv/merge_dotenvs.py
274
+ *.env
274
275
 
275
276
 
276
277
  .ipynb_checkpoints/
@@ -291,5 +292,8 @@ _*.ipynb
291
292
 
292
293
  /srv/*.retry
293
294
 
295
+ # Локальный overlay для demo-стенда (пароли, адреса Patroni)
296
+ srv/docker_compose/docker-compose.demo.yml
297
+
294
298
  # Swagger spec
295
299
  /src/conf/*docs.yml
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: ul-data-logger-api-sdk
3
- Version: 3.0.0
3
+ Version: 3.2.0
4
4
  Summary: Data logger API sdk
5
5
  Author: Unic-lab
6
6
  License-Expression: MIT
@@ -0,0 +1,344 @@
1
+ import json
2
+ import time
3
+ from datetime import date, datetime
4
+ from functools import lru_cache
5
+ from typing import Any, Dict, List, Type
6
+ from uuid import UUID
7
+
8
+ from ul_api_utils.internal_api.internal_api import InternalApi
9
+ from ul_api_utils.internal_api.internal_api_response import TPyloadType
10
+ from ul_api_utils.utils.api_method import ApiMethod
11
+
12
+ from data_logger_api_sdk.data_logger_sdk_config import DataLoggerApiSdkConfig
13
+ from data_logger_api_sdk.types.api_base_stations import (
14
+ ApiBaseStationDetailResponse,
15
+ ApiBaseStationDeviceLogsResponse,
16
+ ApiBaseStationDeviceLogsStatResponse,
17
+ ApiBaseStationDevicesResponse,
18
+ ApiBaseStationLogsStatForIntervalsResponse,
19
+ ApiBaseStationLogsStatResponse,
20
+ ApiBaseStationLogResponse,
21
+ ApiBaseStationLogsListResult,
22
+ ApiBaseStationsListResult,
23
+ ApiBaseStationResponse,
24
+ ApiBaseStationSystemLogResponse,
25
+ ApiBaseStationSystemLogsListResult,
26
+ )
27
+ from data_logger_api_sdk.types.api_bs_traffic_stats import ApiBsTrafficStatsResponse, ApiDeviceTrafficStatsQuery
28
+ from data_logger_api_sdk.types.api_device_data_history import ApiDeviceDataHistoryBody, ApiDeviceDataHistoryResponse
29
+ from data_logger_api_sdk.types.api_last_bs_data_by_device_macs import ApiDeviceLastBsDataResponse, ApiDeviceMacsListBodyModel
30
+ from data_logger_api_sdk.types.api_packet_types import ApiPacketTypesListResponse
31
+ from data_logger_api_sdk.types.api_protocol_types import ApiProtocolTypesListResponse
32
+
33
+
34
+ class DataLoggerApiSdk:
35
+
36
+ def __init__(self, config: DataLoggerApiSdkConfig) -> None:
37
+ self._config = config
38
+ self._api = InternalApi(entry_point=self._config.api_url, default_auth_token=self._config.api_token)
39
+
40
+ @lru_cache(maxsize=None) # there should not be leaks memory leaks. flake8 just scared inmemory cache ;)
41
+ def request_payload(
42
+ self,
43
+ _: int,
44
+ method: ApiMethod,
45
+ endpoint: str,
46
+ typed_as: Type[TPyloadType],
47
+ body: str | None,
48
+ query: str | None = None,
49
+ ) -> TPyloadType:
50
+ return (
51
+ self._api.request(
52
+ method=method,
53
+ path=endpoint,
54
+ json=json.loads(body) if body is not None else None,
55
+ q=json.loads(query) if query is not None else None,
56
+ )
57
+ .typed(typed_as)
58
+ .check()
59
+ .payload
60
+ )
61
+
62
+ def _get_cache_post_payload(
63
+ self,
64
+ method: ApiMethod,
65
+ path: str,
66
+ typed: Type[TPyloadType],
67
+ body: str | None = None,
68
+ query: str | None = None,
69
+ ) -> TPyloadType:
70
+ ttl_status = round(time.time() / self._config.cache_ttl_s)
71
+ return self.request_payload(ttl_status, method, path, typed, body, query) # type: ignore # Type[TPyloadType] is hashable
72
+
73
+ def _request_payload_once(
74
+ self,
75
+ method: ApiMethod,
76
+ path: str,
77
+ typed: Type[TPyloadType],
78
+ query: Dict[str, Any] | None = None,
79
+ ) -> TPyloadType:
80
+ response = self._api.request(
81
+ method=method,
82
+ path=path,
83
+ json=None,
84
+ q=query,
85
+ ).typed(typed).check()
86
+ return response.payload
87
+
88
+ def _request_list_payload_once(
89
+ self,
90
+ method: ApiMethod,
91
+ path: str,
92
+ typed: Type[TPyloadType],
93
+ query: Dict[str, Any] | None = None,
94
+ ) -> tuple[TPyloadType, int]:
95
+ response = self._api.request(
96
+ method=method,
97
+ path=path,
98
+ json=None,
99
+ q=query,
100
+ ).typed(typed).check()
101
+ total_count = response.total_count
102
+ assert total_count is not None, "total_count must be present for array payloads"
103
+ return response.payload, total_count
104
+
105
+ def get_protocol_types_list(self, mac: int) -> List[str]:
106
+ return self._get_cache_post_payload(
107
+ ApiMethod.GET,
108
+ f"/devices/mac/{mac}/logs/protocol-types",
109
+ ApiProtocolTypesListResponse,
110
+ ).protocol_types
111
+
112
+ def get_packet_types_list(self, mac: int) -> List[str]:
113
+ return self._get_cache_post_payload(
114
+ ApiMethod.GET,
115
+ f"/devices/mac/{mac}/logs/packet-types",
116
+ ApiPacketTypesListResponse,
117
+ ).packet_types
118
+
119
+ def get_device_data_history(
120
+ self,
121
+ period_from: datetime,
122
+ period_to: datetime,
123
+ mac: int,
124
+ protocol_type: str | None = None,
125
+ packet_type: str | None = None,
126
+ ) -> List[ApiDeviceDataHistoryResponse]:
127
+ api_device_history_body = ApiDeviceDataHistoryBody(
128
+ period_from=period_from,
129
+ period_to=period_to,
130
+ mac=mac,
131
+ protocol_type=protocol_type,
132
+ packet_type=packet_type,
133
+ ).model_dump_json()
134
+ return self._get_cache_post_payload(
135
+ ApiMethod.POST,
136
+ "/device-data-history",
137
+ List[ApiDeviceDataHistoryResponse], # type: ignore
138
+ api_device_history_body,
139
+ )
140
+
141
+ def get_last_bs_data_by_device_macs(
142
+ self,
143
+ macs: set[int],
144
+ ) -> List[ApiDeviceLastBsDataResponse]:
145
+ return self._get_cache_post_payload(
146
+ ApiMethod.POST,
147
+ "/devices/last-bs-data",
148
+ List[ApiDeviceLastBsDataResponse], # type: ignore
149
+ ApiDeviceMacsListBodyModel(macs=macs).model_dump_json(),
150
+ )
151
+
152
+ def get_bs_traffic_stats(
153
+ self,
154
+ base_station_id: UUID,
155
+ start_at: datetime,
156
+ end_at: datetime,
157
+ ) -> List[ApiBsTrafficStatsResponse]:
158
+ api_bs_traffic_stats_query = ApiDeviceTrafficStatsQuery(
159
+ start_at=start_at,
160
+ end_at=end_at,
161
+ )
162
+ return self._get_cache_post_payload(
163
+ ApiMethod.GET,
164
+ f"/base-stations/{base_station_id}/traffic-stats",
165
+ List[ApiBsTrafficStatsResponse], # type: ignore
166
+ None,
167
+ api_bs_traffic_stats_query.model_dump_json(),
168
+ )
169
+
170
+ def get_base_stations(
171
+ self,
172
+ limit: int,
173
+ offset: int = 0,
174
+ serial_number: int | None = None,
175
+ log_stat_days: int = 0,
176
+ ) -> ApiBaseStationsListResult:
177
+ """GET /base-stations — список базовых станций Data Logger."""
178
+ query: Dict[str, Any] = {
179
+ "limit": limit,
180
+ "offset": offset,
181
+ "log_stat_days": log_stat_days,
182
+ }
183
+ if serial_number is not None:
184
+ query["serial_number"] = serial_number
185
+ items, total_count = self._request_list_payload_once(
186
+ ApiMethod.GET,
187
+ "/base-stations",
188
+ List[ApiBaseStationResponse], # type: ignore
189
+ query,
190
+ )
191
+ return ApiBaseStationsListResult(items=items, total_count=total_count)
192
+
193
+ def get_base_station(
194
+ self,
195
+ base_station_id: UUID,
196
+ log_stat_days: int = 0,
197
+ ) -> ApiBaseStationDetailResponse:
198
+ """GET /base-stations/<uuid> — карточка базовой станции Data Logger."""
199
+ return self._request_payload_once(
200
+ ApiMethod.GET,
201
+ f"/base-stations/{base_station_id}",
202
+ ApiBaseStationDetailResponse,
203
+ {"log_stat_days": log_stat_days},
204
+ )
205
+
206
+ def get_base_station_logs(
207
+ self,
208
+ base_station_id: UUID,
209
+ start_at: datetime | None = None,
210
+ end_at: datetime | None = None,
211
+ limit: int = 100,
212
+ offset: int = 0,
213
+ exclude_log_types: List[str] | None = None,
214
+ sort: str | None = None,
215
+ ) -> ApiBaseStationLogsListResult:
216
+ """GET /base-stations/<uuid>/logs — журнал логов базовой станции.
217
+
218
+ exclude_log_types передаётся стандартным filter-параметром
219
+ (например, ['heartbit'] прячет служебные heartbeat-логи).
220
+ """
221
+ query: Dict[str, Any] = {"limit": limit, "offset": offset}
222
+ if start_at is not None:
223
+ query["start_at"] = start_at.isoformat()
224
+ if end_at is not None:
225
+ query["end_at"] = end_at.isoformat()
226
+ if exclude_log_types:
227
+ query["filter"] = json.dumps([{"name": "log_type", "op": "not_in", "val": exclude_log_types}])
228
+ if sort:
229
+ query["sort"] = sort
230
+ items, total_count = self._request_list_payload_once(
231
+ ApiMethod.GET,
232
+ f"/base-stations/{base_station_id}/logs",
233
+ List[ApiBaseStationLogResponse], # type: ignore
234
+ query,
235
+ )
236
+ return ApiBaseStationLogsListResult(items=items, total_count=total_count)
237
+
238
+ def get_base_station_system_logs(
239
+ self,
240
+ base_station_id: UUID,
241
+ start_at: datetime | None = None,
242
+ end_at: datetime | None = None,
243
+ limit: int = 100,
244
+ offset: int = 0,
245
+ ) -> ApiBaseStationSystemLogsListResult:
246
+ """GET /base-stations/<uuid>/system-logs — журнал системных логов базовой станции."""
247
+ query: Dict[str, Any] = {"limit": limit, "offset": offset}
248
+ if start_at is not None:
249
+ query["start_at"] = start_at.isoformat()
250
+ if end_at is not None:
251
+ query["end_at"] = end_at.isoformat()
252
+ items, total_count = self._request_list_payload_once(
253
+ ApiMethod.GET,
254
+ f"/base-stations/{base_station_id}/system-logs",
255
+ List[ApiBaseStationSystemLogResponse], # type: ignore
256
+ query,
257
+ )
258
+ return ApiBaseStationSystemLogsListResult(items=items, total_count=total_count)
259
+
260
+ def get_base_station_logs_stat(
261
+ self,
262
+ base_station_id: UUID,
263
+ day: date,
264
+ ) -> ApiBaseStationLogsStatResponse:
265
+ """GET /base-stations/<uuid>/logs-stat — статистика логов базовой станции за сутки."""
266
+ return self._request_payload_once(
267
+ ApiMethod.GET,
268
+ f"/base-stations/{base_station_id}/logs-stat",
269
+ ApiBaseStationLogsStatResponse,
270
+ {"day": day.isoformat()},
271
+ )
272
+
273
+ def get_base_station_device_logs_stat(
274
+ self,
275
+ base_station_id: UUID,
276
+ start_at: datetime,
277
+ end_at: datetime | None = None,
278
+ ) -> ApiBaseStationDeviceLogsStatResponse:
279
+ """GET /base-stations/<uuid>/device-logs-stat — число логов устройств БС по дням."""
280
+ query: Dict[str, Any] = {"start_at": start_at.isoformat()}
281
+ if end_at is not None:
282
+ query["end_at"] = end_at.isoformat()
283
+ return self._request_payload_once(
284
+ ApiMethod.GET,
285
+ f"/base-stations/{base_station_id}/device-logs-stat",
286
+ ApiBaseStationDeviceLogsStatResponse,
287
+ query,
288
+ )
289
+
290
+ def get_base_station_logs_stat_for_intervals(
291
+ self,
292
+ base_station_id: UUID,
293
+ start_at: datetime,
294
+ end_at: datetime | None = None,
295
+ ) -> ApiBaseStationLogsStatForIntervalsResponse:
296
+ """GET /base-stations/<uuid>/logs-stat-for-intervals — метки времени логов за интервал."""
297
+ query: Dict[str, Any] = {"start_at": start_at.isoformat()}
298
+ if end_at is not None:
299
+ query["end_at"] = end_at.isoformat()
300
+ return self._request_payload_once(
301
+ ApiMethod.GET,
302
+ f"/base-stations/{base_station_id}/logs-stat-for-intervals",
303
+ ApiBaseStationLogsStatForIntervalsResponse,
304
+ query,
305
+ )
306
+
307
+ def get_base_station_devices(
308
+ self,
309
+ base_station_id: UUID,
310
+ start_at: datetime,
311
+ end_at: datetime | None = None,
312
+ limit: int = 100,
313
+ offset: int = 0,
314
+ ) -> ApiBaseStationDevicesResponse:
315
+ """GET /base-stations/<uuid>/devices — устройства, работающие через базовую станцию."""
316
+ query: Dict[str, Any] = {"start_at": start_at.isoformat(), "limit": limit, "offset": offset}
317
+ if end_at is not None:
318
+ query["end_at"] = end_at.isoformat()
319
+ return self._request_payload_once(
320
+ ApiMethod.GET,
321
+ f"/base-stations/{base_station_id}/devices",
322
+ ApiBaseStationDevicesResponse,
323
+ query,
324
+ )
325
+
326
+ def get_base_station_device_logs(
327
+ self,
328
+ base_station_id: UUID,
329
+ start_at: datetime,
330
+ end_at: datetime | None = None,
331
+ limit: int = 100,
332
+ offset: int = 0,
333
+ ) -> ApiBaseStationDeviceLogsResponse:
334
+ """GET /base-stations/<uuid>/device-logs — логи устройств базовой станции."""
335
+ query: Dict[str, Any] = {"start_at": start_at.isoformat(), "limit": limit, "offset": offset}
336
+ if end_at is not None:
337
+ query["end_at"] = end_at.isoformat()
338
+ return self._request_payload_once(
339
+ ApiMethod.GET,
340
+ f"/base-stations/{base_station_id}/device-logs",
341
+ ApiBaseStationDeviceLogsResponse,
342
+ query,
343
+ )
344
+
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ul-data-logger-api-sdk"
7
- version = "3.0.0"
7
+ version = "3.2.0"
8
8
  description = "Data logger API sdk"
9
9
  license = "MIT"
10
10
  requires-python = ">=3.14"
@@ -0,0 +1,190 @@
1
+ from datetime import date, datetime
2
+ from typing import Any, Dict, List, NamedTuple, Tuple
3
+
4
+ from pydantic.types import UUID4
5
+ from ul_api_utils.api_resource.api_response import JsonApiResponsePayload
6
+
7
+ BS__LOG_TYPE__DEVICE_DATA = "device_data"
8
+ BS__LOG_TYPE__HEARTBIT = "heartbit"
9
+
10
+
11
+ class ApiBaseStationsListResult(NamedTuple):
12
+ """Результат спискового вызова BS-endpoint Data Logger: страница элементов и total_count."""
13
+
14
+ items: List["ApiBaseStationResponse"]
15
+ total_count: int
16
+
17
+
18
+ class ApiBaseStationLogsListResult(NamedTuple):
19
+ """Результат вызова GET /base-stations/<uuid>/logs."""
20
+
21
+ items: List["ApiBaseStationLogResponse"]
22
+ total_count: int
23
+
24
+
25
+ class ApiBaseStationSystemLogsListResult(NamedTuple):
26
+ """Результат вызова GET /base-stations/<uuid>/system-logs."""
27
+
28
+ items: List["ApiBaseStationSystemLogResponse"]
29
+ total_count: int
30
+
31
+
32
+ class ApiBaseStationResponse(JsonApiResponsePayload):
33
+ """Элемент ответа GET /base-stations (BsListShortResponse на стороне Data Logger)."""
34
+
35
+ id: UUID4
36
+ date_created: datetime
37
+ date_modified: datetime
38
+ is_alive: bool
39
+
40
+ api_user_modified_id: UUID4 | None = None
41
+ note: str = ""
42
+ permission_id: int
43
+
44
+ type: str
45
+ serial_number: int
46
+
47
+ latest_log_datetime: datetime | None = None
48
+ latest_geo_latitude: float | None = None
49
+ latest_geo_longitude: float | None = None
50
+ latest_soft_version: str | None = None
51
+ latest_geo_is_actual: bool | None = None
52
+ latest_sdr_config_id: UUID4 | None = None
53
+
54
+ logs_stat: List[Tuple[datetime, int]] | None = None
55
+
56
+
57
+ class ApiBaseStationDetailResponse(ApiBaseStationResponse):
58
+ """Ответ GET /base-stations/<uuid> (BsResponse на стороне Data Logger)."""
59
+
60
+ latest_sdr_config: Dict[str, Any] | None = None
61
+ device_counts: List[Tuple[datetime, int]] | None = None
62
+ system_logs_stat: List[Tuple[datetime, int]] | None = None
63
+ environment_spectrums_stat: List[Tuple[datetime, int]] | None = None
64
+ sdr_config_data_list: List[Dict[str, Any] | None] | None = None
65
+
66
+
67
+ class ApiBaseStationLogResponse(JsonApiResponsePayload):
68
+ """Элемент ответа GET /base-stations/<uuid>/logs (ApiBsLogsResponse на стороне Data Logger)."""
69
+
70
+ id: UUID4
71
+ date_created: datetime
72
+
73
+ bs_id: UUID4 | None = None
74
+ protocol: str | None = None
75
+ log_type: str
76
+
77
+ date_sending: datetime
78
+
79
+ latitude: float | None = None
80
+ longitude: float | None = None
81
+ soft_version: str | None = None
82
+ uptime_s: int | None = None
83
+
84
+ external_document_id: UUID4 | None = None
85
+
86
+
87
+ class ApiBaseStationSystemLogResponse(JsonApiResponsePayload):
88
+ """Элемент ответа GET /base-stations/<uuid>/system-logs (ApiBsSystemLogsResponse)."""
89
+
90
+ id: UUID4
91
+ date_created: datetime
92
+
93
+ bs_id: UUID4 | None = None
94
+ bs_log_id: UUID4 | None = None
95
+ date: datetime
96
+ type: str
97
+ mdl: str | None = None
98
+ text: str | None = None
99
+
100
+
101
+ class ApiBaseStationLogsStatResponse(JsonApiResponsePayload):
102
+ """Ответ GET /base-stations/<uuid>/logs-stat (ApiBsLogsStatResponse)."""
103
+
104
+ stats: List[Tuple[datetime, int]]
105
+
106
+
107
+ class ApiBaseStationLogsStatForIntervalsResponse(JsonApiResponsePayload):
108
+ """Ответ GET /base-stations/<uuid>/logs-stat-for-intervals (ApiBsLogsStatForIntervalsGraphResponse)."""
109
+
110
+ stats: List[datetime]
111
+
112
+
113
+ class ApiBaseStationDeviceLogsStatResponse(JsonApiResponsePayload):
114
+ """Ответ GET /base-stations/<uuid>/device-logs-stat: число логов устройств по дням."""
115
+
116
+ stats: List[Tuple[date, int]]
117
+
118
+
119
+ class ApiBaseStationDeviceResponse(JsonApiResponsePayload):
120
+ """Элемент devices ответа GET /base-stations/<uuid>/devices (ApiDeviceResponse без вложенной БС)."""
121
+
122
+ id: UUID4
123
+ date_created: datetime
124
+ date_modified: datetime
125
+ is_alive: bool
126
+
127
+ api_user_modified_id: UUID4
128
+ note: str
129
+
130
+ device_id: UUID4
131
+ device_network_id: UUID4
132
+ device_network_mac: int
133
+
134
+ latest_device_gateway_id: UUID4 | None = None
135
+ latest_device_protocol_id: UUID4
136
+ latest_device_protocol_type: str | None = None
137
+ latest_device_network_sys_type: str | None = None
138
+ latest_log_datetime: datetime
139
+ latest_bs_id: UUID4 | None = None
140
+ latest_signal_rssi: int | None = None
141
+ latest_signal_snr: int | None = None
142
+
143
+ logs_stat: List[Tuple[datetime, int]] | None = None
144
+
145
+
146
+ class ApiBaseStationDevicesResponse(JsonApiResponsePayload):
147
+ """Ответ GET /base-stations/<uuid>/devices (ApiBsDevicesResponse)."""
148
+
149
+ devices: List[ApiBaseStationDeviceResponse]
150
+ device_count: int
151
+
152
+
153
+ class ApiBaseStationDeviceLogResponse(JsonApiResponsePayload):
154
+ """Элемент device_logs ответа GET /base-stations/<uuid>/device-logs (ApiDeviceLogsResponse)."""
155
+
156
+ id: UUID4
157
+ date_created: datetime
158
+
159
+ bs_id: UUID4 | None = None
160
+ device_id: UUID4
161
+
162
+ device_gateway_id: UUID4 | None = None
163
+ device_protocol_id: UUID4
164
+
165
+ signal_rssi: int | None = None
166
+ signal_snr: int | None = None
167
+
168
+ date_sending: datetime
169
+
170
+ is_verified: bool | None = None
171
+ protocol_type: str | None = None
172
+ packet_type_name: str | None = None
173
+ network_id: UUID4 | None = None
174
+ network_sys_type: str | None = None
175
+ decrypted_payload: str | None = None
176
+ raw_payload: str | None = None
177
+
178
+ error_message: str | None = None
179
+ error_type: str | None = None
180
+
181
+ device: Dict[str, Any] | None = None
182
+ external_document_id: UUID4 | None = None
183
+
184
+
185
+ class ApiBaseStationDeviceLogsResponse(JsonApiResponsePayload):
186
+ """Ответ GET /base-stations/<uuid>/device-logs (ApiBsDeviceLogsResponse)."""
187
+
188
+ device_logs: List[ApiBaseStationDeviceLogResponse]
189
+ device_logs_count: int
190
+ device_count: int
@@ -1,123 +0,0 @@
1
- import json
2
- import time
3
- from datetime import datetime
4
- from functools import lru_cache
5
- from typing import List, Type
6
- from uuid import UUID
7
-
8
- from ul_api_utils.internal_api.internal_api import InternalApi
9
- from ul_api_utils.internal_api.internal_api_response import TPyloadType
10
- from ul_api_utils.utils.api_method import ApiMethod
11
-
12
- from data_logger_api_sdk.data_logger_sdk_config import DataLoggerApiSdkConfig
13
- from data_logger_api_sdk.types.api_bs_traffic_stats import ApiBsTrafficStatsResponse, ApiDeviceTrafficStatsQuery
14
- from data_logger_api_sdk.types.api_device_data_history import ApiDeviceDataHistoryBody, ApiDeviceDataHistoryResponse
15
- from data_logger_api_sdk.types.api_last_bs_data_by_device_macs import ApiDeviceLastBsDataResponse, ApiDeviceMacsListBodyModel
16
- from data_logger_api_sdk.types.api_packet_types import ApiPacketTypesListResponse
17
- from data_logger_api_sdk.types.api_protocol_types import ApiProtocolTypesListResponse
18
-
19
-
20
- class DataLoggerApiSdk:
21
-
22
- def __init__(self, config: DataLoggerApiSdkConfig) -> None:
23
- self._config = config
24
- self._api = InternalApi(entry_point=self._config.api_url, default_auth_token=self._config.api_token)
25
-
26
- @lru_cache(maxsize=None) # there should not be leaks memory leaks. flake8 just scared inmemory cache ;)
27
- def request_payload(
28
- self,
29
- _: int,
30
- method: ApiMethod,
31
- endpoint: str,
32
- typed_as: Type[TPyloadType],
33
- body: str | None,
34
- query: str | None = None,
35
- ) -> TPyloadType:
36
- return (
37
- self._api.request(
38
- method=method,
39
- path=endpoint,
40
- json=json.loads(body) if body is not None else None,
41
- q=json.loads(query) if query is not None else None,
42
- )
43
- .typed(typed_as)
44
- .check()
45
- .payload
46
- )
47
-
48
- def _get_cache_post_payload(
49
- self,
50
- method: ApiMethod,
51
- path: str,
52
- typed: Type[TPyloadType],
53
- body: str | None = None,
54
- query: str | None = None,
55
- ) -> TPyloadType:
56
- ttl_status = round(time.time() / self._config.cache_ttl_s)
57
- return self.request_payload(ttl_status, method, path, typed, body, query) # type: ignore # Type[TPyloadType] is hashable
58
-
59
- def get_protocol_types_list(self, mac: int) -> List[str]:
60
- return self._get_cache_post_payload(
61
- ApiMethod.GET,
62
- f"/devices/mac/{mac}/logs/protocol-types",
63
- ApiProtocolTypesListResponse,
64
- ).protocol_types
65
-
66
- def get_packet_types_list(self, mac: int) -> List[str]:
67
- return self._get_cache_post_payload(
68
- ApiMethod.GET,
69
- f"/devices/mac/{mac}/logs/packet-types",
70
- ApiPacketTypesListResponse,
71
- ).packet_types
72
-
73
- def get_device_data_history(
74
- self,
75
- period_from: datetime,
76
- period_to: datetime,
77
- mac: int,
78
- protocol_type: str | None = None,
79
- packet_type: str | None = None,
80
- ) -> List[ApiDeviceDataHistoryResponse]:
81
- api_device_history_body = ApiDeviceDataHistoryBody(
82
- period_from=period_from,
83
- period_to=period_to,
84
- mac=mac,
85
- protocol_type=protocol_type,
86
- packet_type=packet_type,
87
- ).model_dump_json()
88
- return self._get_cache_post_payload(
89
- ApiMethod.POST,
90
- "/device-data-history",
91
- List[ApiDeviceDataHistoryResponse], # type: ignore
92
- api_device_history_body,
93
- )
94
-
95
- def get_last_bs_data_by_device_macs(
96
- self,
97
- macs: set[int],
98
- ) -> List[ApiDeviceLastBsDataResponse]:
99
- return self._get_cache_post_payload(
100
- ApiMethod.POST,
101
- "/devices/last-bs-data",
102
- List[ApiDeviceLastBsDataResponse], # type: ignore
103
- ApiDeviceMacsListBodyModel(macs=macs).model_dump_json(),
104
- )
105
-
106
- def get_bs_traffic_stats(
107
- self,
108
- base_station_id: UUID,
109
- start_at: datetime,
110
- end_at: datetime,
111
- ) -> List[ApiBsTrafficStatsResponse]:
112
- api_bs_traffic_stats_query = ApiDeviceTrafficStatsQuery(
113
- start_at=start_at,
114
- end_at=end_at,
115
- )
116
- return self._get_cache_post_payload(
117
- ApiMethod.GET,
118
- f"/base-stations/{base_station_id}/traffic-stats",
119
- List[ApiBsTrafficStatsResponse], # type: ignore
120
- None,
121
- api_bs_traffic_stats_query.model_dump_json(),
122
- )
123
-