dronecot 2.2.0__tar.gz → 2.2.2__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.
Files changed (33) hide show
  1. {dronecot-2.2.0 → dronecot-2.2.2}/CHANGELOG.md +10 -0
  2. {dronecot-2.2.0/dronecot.egg-info → dronecot-2.2.2}/PKG-INFO +25 -3
  3. {dronecot-2.2.0 → dronecot-2.2.2}/README.md +23 -0
  4. {dronecot-2.2.0 → dronecot-2.2.2/dronecot.egg-info}/PKG-INFO +25 -3
  5. {dronecot-2.2.0 → dronecot-2.2.2}/dronecot.egg-info/requires.txt +1 -2
  6. {dronecot-2.2.0 → dronecot-2.2.2}/setup.cfg +1 -2
  7. dronecot-2.2.2/src/dronecot/VERSION +1 -0
  8. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/functions.py +107 -127
  9. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/open_drone_id.py +2 -4
  10. {dronecot-2.2.0 → dronecot-2.2.2}/tests/test_functions.py +8 -8
  11. dronecot-2.2.0/src/dronecot/VERSION +0 -1
  12. {dronecot-2.2.0 → dronecot-2.2.2}/LICENSE +0 -0
  13. {dronecot-2.2.0 → dronecot-2.2.2}/MANIFEST.in +0 -0
  14. {dronecot-2.2.0 → dronecot-2.2.2}/dronecot.egg-info/SOURCES.txt +0 -0
  15. {dronecot-2.2.0 → dronecot-2.2.2}/dronecot.egg-info/dependency_links.txt +0 -0
  16. {dronecot-2.2.0 → dronecot-2.2.2}/dronecot.egg-info/entry_points.txt +0 -0
  17. {dronecot-2.2.0 → dronecot-2.2.2}/dronecot.egg-info/top_level.txt +0 -0
  18. {dronecot-2.2.0 → dronecot-2.2.2}/setup.py +0 -0
  19. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/__init__.py +0 -0
  20. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/ble_capture.py +0 -0
  21. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/ble_parse.py +0 -0
  22. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/classes.py +0 -0
  23. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/commands.py +0 -0
  24. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/constants.py +0 -0
  25. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/dji_exceptions.py +0 -0
  26. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/dji_functions.py +0 -0
  27. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/dji_text_parser.py +0 -0
  28. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/odid.py +0 -0
  29. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/rid_normalize.py +0 -0
  30. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/udp_rid.py +0 -0
  31. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/wifi_capture.py +0 -0
  32. {dronecot-2.2.0 → dronecot-2.2.2}/src/dronecot/wifi_parse.py +0 -0
  33. {dronecot-2.2.0 → dronecot-2.2.2}/tests/test_rid_normalize.py +0 -0
@@ -1,3 +1,13 @@
1
+ ## DroneCOT 2.2.2
2
+
3
+ - Fix UTC timestamp handling on Python 3.9 and 3.10.
4
+
5
+ ## DroneCOT 2.2.1
6
+
7
+ - Use PyTAK shared CoT event, point, detail, remarks, and serialization helpers.
8
+ - Replace `pytz` timestamp handling with standard-library timezone handling.
9
+ - Require `pytak >= 7.3.12`.
10
+
1
11
  ## DroneCOT 2.2.0
2
12
 
3
13
  - Add `SensorWorker`: periodic `a-f-G-E-S-E` sensor CoT heartbeat, emitted every `SENSOR_KEEPALIVE_PERIOD` seconds (default 30).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Display Drones in Team Awareness Kit (TAK).
5
5
  Home-page: https://github.com/snstac/dronecot
6
6
  Maintainer: Greg Albrecht <oss@undef.net>
@@ -22,14 +22,13 @@ Classifier: Operating System :: OS Independent
22
22
  Requires-Python: <4,>=3.9
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
- Requires-Dist: pytak>=7.3.0
25
+ Requires-Dist: pytak>=7.3.12
26
26
  Requires-Dist: paho-mqtt<2.0.0
27
27
  Requires-Dist: bitstruct
28
28
  Requires-Dist: pymavlink
29
29
  Requires-Dist: pyserial
30
30
  Requires-Dist: cryptography
31
31
  Requires-Dist: aiohttp
32
- Requires-Dist: pytz
33
32
  Requires-Dist: aiomqtt
34
33
  Provides-Extra: with-takproto
35
34
  Requires-Dist: takproto>=2.0.0; extra == "with-takproto"
@@ -244,6 +243,29 @@ Contact: [info@snstac.com](mailto:info@snstac.com) · [snstac.com](https://www.s
244
243
 
245
244
  ---
246
245
 
246
+ ## The snstac TAK sensor ecosystem
247
+
248
+ Different sensor, same workflow — pick the gateway for your application; most have a
249
+ matching Cockpit plugin for browser-based management:
250
+
251
+ | Application | Gateway | Cockpit plugin |
252
+ |---|---|---|
253
+ | Aircraft via ADS-B (1090 MHz / 978 MHz UAT) | [adsbcot](https://github.com/snstac/adsbcot) | [cockpit-adsbcot](https://github.com/snstac/cockpit-adsbcot) |
254
+ | Ships & vessels via AIS | [aiscot](https://github.com/snstac/aiscot) | [cockpit-aiscot](https://github.com/snstac/cockpit-aiscot), [cockpit-aiscatcher](https://github.com/snstac/cockpit-aiscatcher) |
255
+ | Drone / UAS Remote ID (counter-UAS) | [dronecot](https://github.com/snstac/dronecot) | [cockpit-dronecot](https://github.com/snstac/cockpit-dronecot) |
256
+ | Own position via GPS/GNSS | [lincot](https://github.com/snstac/lincot) | [cockpit-lincot](https://github.com/snstac/cockpit-lincot), [cockpit-gps](https://github.com/snstac/cockpit-gps) |
257
+ | Radio direction finding (KrakenSDR) | [kraktak](https://github.com/snstac/kraktak) | — |
258
+ | APRS amateur radio | [aprscot](https://github.com/snstac/aprscot) | — |
259
+ | Weather stations | [windtak](https://github.com/snstac/windtak) | — |
260
+ | CoT routing / TAK Server bridging | [charontak](https://github.com/snstac/charontak) | — |
261
+
262
+ All gateways are built on [PyTAK](https://github.com/snstac/pytak), speak
263
+ **Cursor on Target (CoT)** to **ATAK, WinTAK, iTAK, TAK Server, and Mesh SA**, ship as
264
+ signed Debian/RPM packages at [snstac.github.io/packages](https://snstac.github.io/packages),
265
+ and come pre-installed on [AryaOS](https://github.com/snstac/aryaos), the
266
+ situational-awareness OS for Raspberry Pi.
267
+
268
+
247
269
  ## License
248
270
 
249
271
  ### DroneCOT
@@ -196,6 +196,29 @@ Contact: [info@snstac.com](mailto:info@snstac.com) · [snstac.com](https://www.s
196
196
 
197
197
  ---
198
198
 
199
+ ## The snstac TAK sensor ecosystem
200
+
201
+ Different sensor, same workflow — pick the gateway for your application; most have a
202
+ matching Cockpit plugin for browser-based management:
203
+
204
+ | Application | Gateway | Cockpit plugin |
205
+ |---|---|---|
206
+ | Aircraft via ADS-B (1090 MHz / 978 MHz UAT) | [adsbcot](https://github.com/snstac/adsbcot) | [cockpit-adsbcot](https://github.com/snstac/cockpit-adsbcot) |
207
+ | Ships & vessels via AIS | [aiscot](https://github.com/snstac/aiscot) | [cockpit-aiscot](https://github.com/snstac/cockpit-aiscot), [cockpit-aiscatcher](https://github.com/snstac/cockpit-aiscatcher) |
208
+ | Drone / UAS Remote ID (counter-UAS) | [dronecot](https://github.com/snstac/dronecot) | [cockpit-dronecot](https://github.com/snstac/cockpit-dronecot) |
209
+ | Own position via GPS/GNSS | [lincot](https://github.com/snstac/lincot) | [cockpit-lincot](https://github.com/snstac/cockpit-lincot), [cockpit-gps](https://github.com/snstac/cockpit-gps) |
210
+ | Radio direction finding (KrakenSDR) | [kraktak](https://github.com/snstac/kraktak) | — |
211
+ | APRS amateur radio | [aprscot](https://github.com/snstac/aprscot) | — |
212
+ | Weather stations | [windtak](https://github.com/snstac/windtak) | — |
213
+ | CoT routing / TAK Server bridging | [charontak](https://github.com/snstac/charontak) | — |
214
+
215
+ All gateways are built on [PyTAK](https://github.com/snstac/pytak), speak
216
+ **Cursor on Target (CoT)** to **ATAK, WinTAK, iTAK, TAK Server, and Mesh SA**, ship as
217
+ signed Debian/RPM packages at [snstac.github.io/packages](https://snstac.github.io/packages),
218
+ and come pre-installed on [AryaOS](https://github.com/snstac/aryaos), the
219
+ situational-awareness OS for Raspberry Pi.
220
+
221
+
199
222
  ## License
200
223
 
201
224
  ### DroneCOT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Display Drones in Team Awareness Kit (TAK).
5
5
  Home-page: https://github.com/snstac/dronecot
6
6
  Maintainer: Greg Albrecht <oss@undef.net>
@@ -22,14 +22,13 @@ Classifier: Operating System :: OS Independent
22
22
  Requires-Python: <4,>=3.9
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
- Requires-Dist: pytak>=7.3.0
25
+ Requires-Dist: pytak>=7.3.12
26
26
  Requires-Dist: paho-mqtt<2.0.0
27
27
  Requires-Dist: bitstruct
28
28
  Requires-Dist: pymavlink
29
29
  Requires-Dist: pyserial
30
30
  Requires-Dist: cryptography
31
31
  Requires-Dist: aiohttp
32
- Requires-Dist: pytz
33
32
  Requires-Dist: aiomqtt
34
33
  Provides-Extra: with-takproto
35
34
  Requires-Dist: takproto>=2.0.0; extra == "with-takproto"
@@ -244,6 +243,29 @@ Contact: [info@snstac.com](mailto:info@snstac.com) · [snstac.com](https://www.s
244
243
 
245
244
  ---
246
245
 
246
+ ## The snstac TAK sensor ecosystem
247
+
248
+ Different sensor, same workflow — pick the gateway for your application; most have a
249
+ matching Cockpit plugin for browser-based management:
250
+
251
+ | Application | Gateway | Cockpit plugin |
252
+ |---|---|---|
253
+ | Aircraft via ADS-B (1090 MHz / 978 MHz UAT) | [adsbcot](https://github.com/snstac/adsbcot) | [cockpit-adsbcot](https://github.com/snstac/cockpit-adsbcot) |
254
+ | Ships & vessels via AIS | [aiscot](https://github.com/snstac/aiscot) | [cockpit-aiscot](https://github.com/snstac/cockpit-aiscot), [cockpit-aiscatcher](https://github.com/snstac/cockpit-aiscatcher) |
255
+ | Drone / UAS Remote ID (counter-UAS) | [dronecot](https://github.com/snstac/dronecot) | [cockpit-dronecot](https://github.com/snstac/cockpit-dronecot) |
256
+ | Own position via GPS/GNSS | [lincot](https://github.com/snstac/lincot) | [cockpit-lincot](https://github.com/snstac/cockpit-lincot), [cockpit-gps](https://github.com/snstac/cockpit-gps) |
257
+ | Radio direction finding (KrakenSDR) | [kraktak](https://github.com/snstac/kraktak) | — |
258
+ | APRS amateur radio | [aprscot](https://github.com/snstac/aprscot) | — |
259
+ | Weather stations | [windtak](https://github.com/snstac/windtak) | — |
260
+ | CoT routing / TAK Server bridging | [charontak](https://github.com/snstac/charontak) | — |
261
+
262
+ All gateways are built on [PyTAK](https://github.com/snstac/pytak), speak
263
+ **Cursor on Target (CoT)** to **ATAK, WinTAK, iTAK, TAK Server, and Mesh SA**, ship as
264
+ signed Debian/RPM packages at [snstac.github.io/packages](https://snstac.github.io/packages),
265
+ and come pre-installed on [AryaOS](https://github.com/snstac/aryaos), the
266
+ situational-awareness OS for Raspberry Pi.
267
+
268
+
247
269
  ## License
248
270
 
249
271
  ### DroneCOT
@@ -1,11 +1,10 @@
1
- pytak>=7.3.0
1
+ pytak>=7.3.12
2
2
  paho-mqtt<2.0.0
3
3
  bitstruct
4
4
  pymavlink
5
5
  pyserial
6
6
  cryptography
7
7
  aiohttp
8
- pytz
9
8
  aiomqtt
10
9
 
11
10
  [ble]
@@ -48,14 +48,13 @@ package_dir =
48
48
  dronecot = src/dronecot
49
49
  python_requires = >=3.9, <4
50
50
  install_requires =
51
- pytak >= 7.3.0
51
+ pytak >= 7.3.12
52
52
  paho-mqtt < 2.0.0
53
53
  bitstruct
54
54
  pymavlink
55
55
  pyserial
56
56
  cryptography
57
57
  aiohttp
58
- pytz
59
58
  aiomqtt
60
59
 
61
60
  [options.packages.find]
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -29,11 +29,11 @@ import xml.etree.ElementTree as ET
29
29
  from configparser import SectionProxy
30
30
  from typing import Optional, Set, Union
31
31
  from urllib.parse import urlparse
32
+ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
32
33
 
33
34
  import pytak
34
35
  import dronecot
35
36
  from datetime import datetime
36
- import pytz
37
37
 
38
38
  from .dji_functions import parse_frame, parse_data
39
39
  from .dji_text_parser import dji_parse_text_line
@@ -65,6 +65,15 @@ _DJI_Logger = logging.getLogger(__name__)
65
65
  APP_NAME = "dronecot"
66
66
 
67
67
 
68
+ def _pacific_timestamp() -> str:
69
+ """Return a human-readable last-seen timestamp, falling back if tzdata is absent."""
70
+ try:
71
+ tzinfo = ZoneInfo("US/Pacific")
72
+ except ZoneInfoNotFoundError:
73
+ tzinfo = datetime.now().astimezone().tzinfo
74
+ return datetime.now(tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
75
+
76
+
68
77
  def _dji_feed_uses_text(config, feed_url: str, parsed) -> bool:
69
78
  """Return True if the configured DJI feed uses AntSDR text CSV format."""
70
79
  feed_format = str(config.get("FEED_FORMAT", "")).lower()
@@ -138,6 +147,31 @@ def create_tasks(config: SectionProxy, clitool: pytak.CLITool) -> Set[pytak.Work
138
147
  return tasks
139
148
 
140
149
 
150
+ def _cot_event_with_detail(
151
+ *,
152
+ uid: str,
153
+ cot_type: str,
154
+ stale,
155
+ lat,
156
+ lon,
157
+ hae,
158
+ ce,
159
+ le,
160
+ detail: ET.Element,
161
+ config: Union[SectionProxy, dict],
162
+ cot_host_id: str,
163
+ ) -> ET.Element:
164
+ """Build a standard CoT event around DroneCOT-specific detail children."""
165
+ return pytak.cot_event(
166
+ uid=uid,
167
+ cot_type=cot_type,
168
+ stale=stale,
169
+ point=pytak.cot_point(lat=lat, lon=lon, ce=ce, hae=hae, le=le),
170
+ detail=pytak.cot_detail(*list(detail), flow_tag_host_id=cot_host_id),
171
+ access=config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS),
172
+ )
173
+
174
+
141
175
  def rid_op_to_cot_xml( # NOQA pylint: disable=too-many-locals,too-many-branches,too-many-statements
142
176
  data: dict,
143
177
  config: Union[SectionProxy, dict, None] = None,
@@ -218,26 +252,19 @@ def rid_op_to_cot_xml( # NOQA pylint: disable=too-many-locals,too-many-branches
218
252
  remarks.text = _remarks
219
253
  detail.append(remarks)
220
254
 
221
- cot_d = {
222
- "lat": str(lat),
223
- "lon": str(lon),
224
- "ce": str(data.get("HorizAccuracy", "9999999.0")),
225
- "le": str(data.get("VertAccuracy", "9999999.0")),
226
- "hae": str(data.get("OperatorAltitudeGeo", "9999999.0")),
227
- "uid": cot_uid,
228
- "cot_type": cot_type,
229
- "stale": cot_stale,
230
- }
231
- cot = pytak.gen_cot_xml(**cot_d)
232
- cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
233
-
234
- _detail = cot.findall("detail")[0]
235
- flowtags = _detail.findall("_flow-tags_")
236
- detail.extend(flowtags)
237
- cot.remove(_detail)
238
- cot.append(detail)
239
-
240
- return cot
255
+ return _cot_event_with_detail(
256
+ uid=cot_uid,
257
+ cot_type=cot_type,
258
+ stale=cot_stale,
259
+ lat=lat,
260
+ lon=lon,
261
+ ce=str(data.get("HorizAccuracy", "9999999.0")),
262
+ le=str(data.get("VertAccuracy", "9999999.0")),
263
+ hae=str(data.get("OperatorAltitudeGeo", "9999999.0")),
264
+ detail=detail,
265
+ config=config,
266
+ cot_host_id=cot_host_id,
267
+ )
241
268
 
242
269
 
243
270
  def rid_uas_to_cot_xml( # NOQA pylint: disable=too-many-locals,too-many-branches,too-many-statements
@@ -287,8 +314,7 @@ def rid_uas_to_cot_xml( # NOQA pylint: disable=too-many-locals,too-many-branche
287
314
  cot_host_id: str = config.get("COT_HOST_ID", pytak.DEFAULT_HOST_ID)
288
315
 
289
316
  # Show last-seen time in Pacific Time Zone in remarks
290
- pacific = pytz.timezone('US/Pacific')
291
- pacific_time = datetime.now(pacific).strftime('%Y-%m-%d %H:%M:%S %Z')
317
+ pacific_time = _pacific_timestamp()
292
318
  remarks_fields.append(f"Last Seen (Pacific): {pacific_time}")
293
319
 
294
320
  remarks_fields.append(f"Remote ID: {uasid}")
@@ -405,26 +431,19 @@ def rid_uas_to_cot_xml( # NOQA pylint: disable=too-many-locals,too-many-branche
405
431
  remarks.text = _remarks
406
432
  detail.append(remarks)
407
433
 
408
- cot_d = {
409
- "lat": str(lat),
410
- "lon": str(lon),
411
- "ce": str(data.get("HorizAccuracy", "9999999.0")),
412
- "le": str(data.get("VertAccuracy", "9999999.0")),
413
- "hae": str(data.get("AltitudeGeo", "9999999.0")),
414
- "uid": cot_uid,
415
- "cot_type": cot_type,
416
- "stale": cot_stale,
417
- }
418
- cot = pytak.gen_cot_xml(**cot_d)
419
- cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
420
-
421
- _detail = cot.findall("detail")[0]
422
- flowtags = _detail.findall("_flow-tags_")
423
- detail.extend(flowtags)
424
- cot.remove(_detail)
425
- cot.append(detail)
426
-
427
- return cot
434
+ return _cot_event_with_detail(
435
+ uid=cot_uid,
436
+ cot_type=cot_type,
437
+ stale=cot_stale,
438
+ lat=lat,
439
+ lon=lon,
440
+ ce=str(data.get("HorizAccuracy", "9999999.0")),
441
+ le=str(data.get("VertAccuracy", "9999999.0")),
442
+ hae=str(data.get("AltitudeGeo", "9999999.0")),
443
+ detail=detail,
444
+ config=config,
445
+ cot_host_id=cot_host_id,
446
+ )
428
447
 
429
448
 
430
449
  def sensor_status_to_cot( # NOQA pylint: disable=too-many-locals,too-many-branches,too-many-statements
@@ -494,26 +513,19 @@ def sensor_status_to_cot( # NOQA pylint: disable=too-many-locals,too-many-branc
494
513
  remarks.text = _remarks
495
514
  detail.append(remarks)
496
515
 
497
- cot_d = {
498
- "lat": str(lat),
499
- "lon": str(lon),
500
- "ce": str(data.get("HorizAccuracy", "9999999.0")),
501
- "le": str(data.get("VertAccuracy", "9999999.0")),
502
- "hae": hae,
503
- "uid": cot_uid,
504
- "cot_type": cot_type,
505
- "stale": cot_stale,
506
- }
507
- cot = pytak.gen_cot_xml(**cot_d)
508
- cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
509
-
510
- _detail = cot.findall("detail")[0]
511
- flowtags = _detail.findall("_flow-tags_")
512
- detail.extend(flowtags)
513
- cot.remove(_detail)
514
- cot.append(detail)
515
-
516
- return cot
516
+ return _cot_event_with_detail(
517
+ uid=cot_uid,
518
+ cot_type=cot_type,
519
+ stale=cot_stale,
520
+ lat=lat,
521
+ lon=lon,
522
+ ce=str(data.get("HorizAccuracy", "9999999.0")),
523
+ le=str(data.get("VertAccuracy", "9999999.0")),
524
+ hae=hae,
525
+ detail=detail,
526
+ config=config,
527
+ cot_host_id=cot_host_id,
528
+ )
517
529
 
518
530
 
519
531
  def xml_to_cot(
@@ -521,9 +533,7 @@ def xml_to_cot(
521
533
  ) -> Optional[bytes]:
522
534
  """Return a CoT XML object as an XML string, using the given func."""
523
535
  cot: Optional[ET.Element] = getattr(dronecot.functions, func)(data, config)
524
- return (
525
- b"\n".join([pytak.DEFAULT_XML_DECLARATION, ET.tostring(cot)]) if cot else None
526
- )
536
+ return pytak.serialize_cot(cot) if cot is not None else None
527
537
 
528
538
 
529
539
  def cot_to_xml(
@@ -703,27 +713,19 @@ def gen_dji_cot( # NOQA pylint: disable=too-many-locals,too-many-branches,too-m
703
713
  detail.append(cuas)
704
714
  detail.append(crumbs)
705
715
 
706
- cot_d = {
707
- "lat": str(lat),
708
- "lon": str(lon),
709
- "ce": ce,
710
- "le": str(data.get("nac_v", "9999999.0")),
711
- "hae": str(data.get("alt_geom", "9999999.0")),
712
- "uid": cot_uid,
713
- "cot_type": cot_type,
714
- "stale": cot_stale,
715
- }
716
- cot = pytak.gen_cot_xml(**cot_d)
717
- cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
718
-
719
- _detail = cot.find("detail")
720
- if _detail is not None:
721
- flowtags = _detail.findall("_flow-tags_")
722
- detail.extend(flowtags)
723
- cot.remove(_detail)
724
- cot.append(detail)
725
-
726
- return cot
716
+ return _cot_event_with_detail(
717
+ uid=cot_uid,
718
+ cot_type=cot_type,
719
+ stale=cot_stale,
720
+ lat=lat,
721
+ lon=lon,
722
+ ce=ce,
723
+ le=str(data.get("nac_v", "9999999.0")),
724
+ hae=str(data.get("alt_geom", "9999999.0")),
725
+ detail=detail,
726
+ config=config,
727
+ cot_host_id=cot_host_id,
728
+ )
727
729
 
728
730
 
729
731
  def dji_sensor_to_cot(
@@ -762,27 +764,19 @@ def dji_sensor_to_cot(
762
764
  detail.append(contact)
763
765
  detail.append(cuas)
764
766
 
765
- cot_d = {
766
- "lat": str(lat),
767
- "lon": str(lon),
768
- "ce": str(config.get("SENSOR_CE", DEFAULT_DJI_SENSOR_CE)),
769
- "le": str(config.get("SENSOR_LE", DEFAULT_DJI_SENSOR_LE)),
770
- "hae": str(config.get("SENSOR_HAE", DEFAULT_DJI_SENSOR_HAE)),
771
- "uid": cot_uid,
772
- "cot_type": cot_type,
773
- "stale": cot_stale,
774
- }
775
- cot = pytak.gen_cot_xml(**cot_d)
776
- cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
777
-
778
- _detail = cot.find("detail")
779
- if _detail is not None:
780
- flowtags = _detail.findall("_flow-tags_")
781
- detail.extend(flowtags)
782
- cot.remove(_detail)
783
- cot.append(detail)
784
-
785
- return cot
767
+ return _cot_event_with_detail(
768
+ uid=cot_uid,
769
+ cot_type=cot_type,
770
+ stale=cot_stale,
771
+ lat=lat,
772
+ lon=lon,
773
+ ce=str(config.get("SENSOR_CE", DEFAULT_DJI_SENSOR_CE)),
774
+ le=str(config.get("SENSOR_LE", DEFAULT_DJI_SENSOR_LE)),
775
+ hae=str(config.get("SENSOR_HAE", DEFAULT_DJI_SENSOR_HAE)),
776
+ detail=detail,
777
+ config=config,
778
+ cot_host_id=cot_host_id,
779
+ )
786
780
 
787
781
 
788
782
  def dji_uas_to_cot(data, config: Union[SectionProxy, dict, None] = None) -> Optional[ET.Element]:
@@ -874,26 +868,12 @@ def gen_sensor_cot(
874
868
  cuas.set("sensor_id", sensor_id)
875
869
  cuas.set("type", payload_type)
876
870
 
877
- detail = ET.Element("detail")
878
- detail.append(contact)
879
- detail.append(cuas)
880
-
881
- cot = pytak.gen_cot_xml(
882
- lat=str(lat),
883
- lon=str(lon),
884
- hae=str(hae),
885
- ce=ce,
886
- le=le,
871
+ return pytak.cot_event(
887
872
  uid=f"SENSOR.{sensor_id}",
888
873
  cot_type=cot_type,
889
874
  stale=cot_stale,
875
+ point=pytak.cot_point(lat=lat, lon=lon, hae=hae, ce=ce, le=le),
876
+ detail=pytak.cot_detail(contact, cuas, flow_tag=False),
877
+ how="m-g",
878
+ access=config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS),
890
879
  )
891
- cot.set("how", "m-g")
892
- cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
893
-
894
- _detail = cot.find("detail")
895
- if _detail is not None:
896
- cot.remove(_detail)
897
- cot.append(detail)
898
-
899
- return cot
@@ -27,8 +27,6 @@
27
27
  import datetime
28
28
  import struct
29
29
 
30
- import pytz
31
-
32
30
  from bitstruct import *
33
31
 
34
32
 
@@ -356,7 +354,7 @@ def parse_System(payload):
356
354
  if Timestamp != float("NaN") and Timestamp != 0:
357
355
  pl["Timestamp"] = (
358
356
  datetime.datetime.fromtimestamp(
359
- (int(Timestamp) + 1546300800), pytz.UTC
357
+ (int(Timestamp) + 1546300800), datetime.timezone.utc
360
358
  ).strftime("%Y-%m-%d %H:%M %Z"),
361
359
  )
362
360
  pl["TimestampRaw"] = Timestamp
@@ -411,7 +409,7 @@ def parse_AuthPage(payload, page):
411
409
 
412
410
  if Timestamp != float("NaN") and Timestamp != 0:
413
411
  pl["Timestamp"] = datetime.datetime.fromtimestamp(
414
- (int(Timestamp) + 1546300800), pytz.UTC
412
+ (int(Timestamp) + 1546300800), datetime.timezone.utc
415
413
  ).strftime("%Y-%m-%d %H:%M %Z")
416
414
 
417
415
  AuthData = payload[AuthPage_start_byte + 16 : AuthPage_start_byte + 16 + 17]
@@ -71,8 +71,8 @@ class FunctionsTestCase(unittest.TestCase):
71
71
 
72
72
  point = cot_xml.find("point")
73
73
  self.assertIsNotNone(point)
74
- self.assertEqual(point.get("lat"), "37.759979")
75
- self.assertEqual(point.get("lon"), "-122.497734")
74
+ self.assertEqual(point.get("lat"), "37.7599")
75
+ self.assertEqual(point.get("lon"), "-122.4977")
76
76
  self.assertEqual(point.get("ce"), "12")
77
77
  self.assertEqual(point.get("le"), "5")
78
78
  self.assertEqual(point.get("hae"), "28.0")
@@ -111,8 +111,8 @@ class FunctionsTestCase(unittest.TestCase):
111
111
 
112
112
  point = cot_xml.find("point")
113
113
  self.assertIsNotNone(point)
114
- self.assertEqual(point.get("lat"), "37.759979")
115
- self.assertEqual(point.get("lon"), "-122.497734")
114
+ self.assertEqual(point.get("lat"), "37.7599")
115
+ self.assertEqual(point.get("lon"), "-122.4977")
116
116
  self.assertEqual(point.get("ce"), "12")
117
117
  self.assertEqual(point.get("le"), "5")
118
118
  self.assertEqual(point.get("hae"), "28.0")
@@ -151,8 +151,8 @@ class FunctionsTestCase(unittest.TestCase):
151
151
 
152
152
  point = cot_xml.find("point")
153
153
  self.assertIsNotNone(point)
154
- self.assertEqual(point.get("lat"), "37.759979")
155
- self.assertEqual(point.get("lon"), "-122.497734")
154
+ self.assertEqual(point.get("lat"), "37.7599")
155
+ self.assertEqual(point.get("lon"), "-122.4977")
156
156
  self.assertEqual(point.get("ce"), "12")
157
157
  self.assertEqual(point.get("le"), "5")
158
158
  self.assertEqual(point.get("hae"), "28.0")
@@ -191,8 +191,8 @@ class FunctionsTestCase(unittest.TestCase):
191
191
 
192
192
  point = cot_xml.find("point")
193
193
  self.assertIsNotNone(point)
194
- self.assertEqual(point.get("lat"), "37.7599788")
195
- self.assertEqual(point.get("lon"), "-122.4977338")
194
+ self.assertEqual(point.get("lat"), "37.7599")
195
+ self.assertEqual(point.get("lon"), "-122.4977")
196
196
  self.assertEqual(point.get("ce"), "12")
197
197
  self.assertEqual(point.get("le"), "5")
198
198
  self.assertEqual(point.get("hae"), "28.0")
@@ -1 +0,0 @@
1
- 2.2.0
File without changes
File without changes
File without changes
File without changes