dronecot 2.3.8__tar.gz → 2.3.10__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 (44) hide show
  1. {dronecot-2.3.8 → dronecot-2.3.10}/CHANGELOG.md +5 -0
  2. {dronecot-2.3.8/dronecot.egg-info → dronecot-2.3.10}/PKG-INFO +1 -1
  3. {dronecot-2.3.8 → dronecot-2.3.10/dronecot.egg-info}/PKG-INFO +1 -1
  4. dronecot-2.3.10/src/dronecot/VERSION +1 -0
  5. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/classes.py +14 -5
  6. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/functions.py +8 -1
  7. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_functions.py +30 -0
  8. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_status.py +23 -1
  9. dronecot-2.3.8/src/dronecot/VERSION +0 -1
  10. {dronecot-2.3.8 → dronecot-2.3.10}/LICENSE +0 -0
  11. {dronecot-2.3.8 → dronecot-2.3.10}/MANIFEST.in +0 -0
  12. {dronecot-2.3.8 → dronecot-2.3.10}/README.md +0 -0
  13. {dronecot-2.3.8 → dronecot-2.3.10}/dronecot.egg-info/SOURCES.txt +0 -0
  14. {dronecot-2.3.8 → dronecot-2.3.10}/dronecot.egg-info/dependency_links.txt +0 -0
  15. {dronecot-2.3.8 → dronecot-2.3.10}/dronecot.egg-info/entry_points.txt +0 -0
  16. {dronecot-2.3.8 → dronecot-2.3.10}/dronecot.egg-info/requires.txt +0 -0
  17. {dronecot-2.3.8 → dronecot-2.3.10}/dronecot.egg-info/top_level.txt +0 -0
  18. {dronecot-2.3.8 → dronecot-2.3.10}/setup.cfg +0 -0
  19. {dronecot-2.3.8 → dronecot-2.3.10}/setup.py +0 -0
  20. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/__init__.py +0 -0
  21. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/ble_capture.py +0 -0
  22. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/ble_parse.py +0 -0
  23. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/bluez_capture.py +0 -0
  24. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/commands.py +0 -0
  25. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/constants.py +0 -0
  26. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/dji_exceptions.py +0 -0
  27. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/dji_functions.py +0 -0
  28. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/dji_text_parser.py +0 -0
  29. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/odid.py +0 -0
  30. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/open_drone_id.py +0 -0
  31. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/rid_normalize.py +0 -0
  32. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/rid_track.py +0 -0
  33. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/udp_rid.py +0 -0
  34. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/wifi_capture.py +0 -0
  35. {dronecot-2.3.8 → dronecot-2.3.10}/src/dronecot/wifi_parse.py +0 -0
  36. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_adsb_vehicle.py +0 -0
  37. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_bluez_capture.py +0 -0
  38. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_dji_listener.py +0 -0
  39. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_mavlink_identity.py +0 -0
  40. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_rid_normalize.py +0 -0
  41. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_rid_track.py +0 -0
  42. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_sensor_detail.py +0 -0
  43. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_serial_crlf.py +0 -0
  44. {dronecot-2.3.8 → dronecot-2.3.10}/tests/test_wifi_parse.py +0 -0
@@ -1,3 +1,8 @@
1
+ ## DroneCOT 2.3.10
2
+
3
+ - Add `SENSOR_BEACON=0` to disable the periodic receiver marker without
4
+ disabling drone tracks.
5
+
1
6
  ## DroneCOT 2.3.8
2
7
 
3
8
  - Add opt-in `SERIAL_CRLF_NORMALIZE` support for receivers that send binary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.3.8
3
+ Version: 2.3.10
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>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.3.8
3
+ Version: 2.3.10
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>
@@ -0,0 +1 @@
1
+ 2.3.10
@@ -118,11 +118,18 @@ class _CRLFNormalizingReceiver:
118
118
  return self._normalizer.feed(self._receiver(size))
119
119
 
120
120
 
121
- def make_status(app_name: str, version: str):
121
+ def make_status(app_name: str, version: str, path: Optional[str] = None):
122
122
  """Return a status writer, or a no-op if this pytak has none."""
123
123
  if _StatusWriter is None:
124
124
  return _NoStatus()
125
- return _StatusWriter(app_name, version=version)
125
+ return _StatusWriter(app_name, path=path, version=version)
126
+
127
+
128
+ def make_worker_status(config):
129
+ """Build the status writer for one independently managed instance."""
130
+ app_name = str(config.get("STATUS_APP", "dronecot") or "dronecot")
131
+ path = str(config.get("STATUS_PATH", "") or "") or None
132
+ return make_status(app_name, dronecot.__version__, path=path)
126
133
 
127
134
 
128
135
  class SerialWorker(pytak.QueueWorker):
@@ -832,7 +839,7 @@ class RIDWorker(pytak.QueueWorker):
832
839
  # every RF source (Wi-Fi, BLE, BlueZ, serial MAVLink, MQTT, UDP) funnels
833
840
  # through here. Instrumenting the capture workers instead would give N
834
841
  # writers racing on one path, and each would see only its own slice.
835
- self.status = make_status("dronecot", dronecot.__version__)
842
+ self.status = make_worker_status(config)
836
843
 
837
844
  def _tracked(self) -> int:
838
845
  """Number of transmitters the aggregator is currently holding."""
@@ -990,10 +997,12 @@ class RXMockWorker(pytak.RXWorker):
990
997
  class DJIWorker(pytak.QueueWorker):
991
998
  """Process DJI Drone ID data from a net queue and emit CoT events."""
992
999
 
993
- def __init__(self, tx_queue: asyncio.Queue, config, net_queue: asyncio.Queue) -> None:
1000
+ def __init__(
1001
+ self, tx_queue: asyncio.Queue, config, net_queue: asyncio.Queue
1002
+ ) -> None:
994
1003
  super().__init__(tx_queue, config)
995
1004
  self.net_queue = net_queue
996
- self.status = make_status("dronecot", dronecot.__version__)
1005
+ self.status = make_worker_status(config)
997
1006
 
998
1007
  async def handle_data(self, data) -> None:
999
1008
  if not data:
@@ -65,6 +65,12 @@ _DJI_Logger = logging.getLogger(__name__)
65
65
  APP_NAME = "dronecot"
66
66
 
67
67
 
68
+ def sensor_beacon_enabled(config: Union[dict, SectionProxy]) -> bool:
69
+ """Return whether the periodic receiver beacon is enabled."""
70
+ value = config.get("SENSOR_BEACON", "1")
71
+ return str(value).strip().lower() not in {"0", "false", "no", "off"}
72
+
73
+
68
74
  def _pacific_timestamp() -> str:
69
75
  """Return a human-readable last-seen timestamp, falling back if tzdata is absent."""
70
76
  try:
@@ -160,7 +166,8 @@ def create_tasks(config: SectionProxy, clitool: pytak.CLITool) -> Set[pytak.Work
160
166
  if config.get("ENABLE_RX_MOCK", "0") == "1":
161
167
  tasks.add(dronecot.RXMockWorker(clitool.rx_queue, config))
162
168
 
163
- tasks.add(dronecot.SensorWorker(clitool.tx_queue, config))
169
+ if sensor_beacon_enabled(config):
170
+ tasks.add(dronecot.SensorWorker(clitool.tx_queue, config))
164
171
 
165
172
  return tasks
166
173
 
@@ -16,9 +16,12 @@
16
16
 
17
17
  """DroneCOT Function Tests."""
18
18
 
19
+ import asyncio
19
20
  import json
20
21
  import os
21
22
  import unittest
23
+ from types import SimpleNamespace
24
+ from unittest.mock import patch
22
25
 
23
26
  import xml.etree.ElementTree as ET
24
27
 
@@ -45,6 +48,33 @@ def load_sample_data(file_path, line=0):
45
48
 
46
49
 
47
50
  class FunctionsTestCase(unittest.TestCase):
51
+ def test_sensor_beacon_switch(self):
52
+ """Receiver beacons default on and accept common false values."""
53
+ enabled = dronecot.functions.sensor_beacon_enabled
54
+ self.assertTrue(enabled({}))
55
+ for value in ("0", "false", "False", "no", "off"):
56
+ with self.subTest(value=value):
57
+ self.assertFalse(enabled({"SENSOR_BEACON": value}))
58
+
59
+ def test_create_tasks_can_omit_sensor_beacon(self):
60
+ """Disabling the receiver beacon keeps drone processing enabled."""
61
+ clitool = SimpleNamespace(tx_queue=object())
62
+ config = {"FEED_URL": "serial:///dev/ttyACM0", "SENSOR_BEACON": "0"}
63
+ loop = asyncio.new_event_loop()
64
+ try:
65
+ asyncio.set_event_loop(loop)
66
+ with patch.object(dronecot, "SerialWorker", return_value="serial"):
67
+ with patch.object(dronecot, "RIDWorker", return_value="tracks"):
68
+ with patch.object(
69
+ dronecot, "SensorWorker", return_value="receiver"
70
+ ) as sensor_worker:
71
+ tasks = dronecot.functions.create_tasks(config, clitool)
72
+ finally:
73
+ asyncio.set_event_loop(None)
74
+ loop.close()
75
+ self.assertEqual(tasks, {"serial", "tracks"})
76
+ sensor_worker.assert_not_called()
77
+
48
78
  """
49
79
  Test class for functions... functions.
50
80
  """
@@ -216,7 +216,10 @@ class TestDJIStatusSurface:
216
216
  def _worker(self, tmp_path):
217
217
  worker = classes.DJIWorker.__new__(classes.DJIWorker)
218
218
  worker.config = {"FEED_URL": "tcp://172.31.100.2:52002"}
219
- worker.net_queue = asyncio.Queue()
219
+ # handle_data does not consume net_queue. Keeping this synthetic worker
220
+ # loop-free also works on Python 3.9 after an earlier asyncio.run()
221
+ # closes and clears the process-wide default loop.
222
+ worker.net_queue = None
220
223
  worker._logger = logging.getLogger("test")
221
224
  worker.status = pytak.StatusWriter(
222
225
  "dronecot-test", path=str(tmp_path / "status.json")
@@ -330,3 +333,22 @@ class TestStatusDegradesVisibly:
330
333
  if classes._StatusWriter is None:
331
334
  pytest.skip("installed pytak has no StatusWriter")
332
335
  assert not isinstance(classes.make_status("x", "0"), classes._NoStatus)
336
+
337
+ def test_instance_status_uses_configured_app_and_path(self, monkeypatch, tmp_path):
338
+ captured = {}
339
+
340
+ def fake_writer(app_name, *, path=None, version=None):
341
+ captured.update(app_name=app_name, path=path, version=version)
342
+ return object()
343
+
344
+ monkeypatch.setattr(classes, "_StatusWriter", fake_writer)
345
+ path = str(tmp_path / "status.json")
346
+ classes.make_worker_status(
347
+ {"STATUS_APP": "dronecot-dronescout", "STATUS_PATH": path}
348
+ )
349
+
350
+ assert captured == {
351
+ "app_name": "dronecot-dronescout",
352
+ "path": path,
353
+ "version": classes.dronecot.__version__,
354
+ }
@@ -1 +0,0 @@
1
- 2.3.8
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes