ReticulumTelemetryHub 0.1.0__py3-none-any.whl → 0.143.0__py3-none-any.whl
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.
- reticulum_telemetry_hub/api/__init__.py +23 -0
- reticulum_telemetry_hub/api/models.py +323 -0
- reticulum_telemetry_hub/api/service.py +836 -0
- reticulum_telemetry_hub/api/storage.py +528 -0
- reticulum_telemetry_hub/api/storage_base.py +156 -0
- reticulum_telemetry_hub/api/storage_models.py +118 -0
- reticulum_telemetry_hub/atak_cot/__init__.py +49 -0
- reticulum_telemetry_hub/atak_cot/base.py +277 -0
- reticulum_telemetry_hub/atak_cot/chat.py +506 -0
- reticulum_telemetry_hub/atak_cot/detail.py +235 -0
- reticulum_telemetry_hub/atak_cot/event.py +181 -0
- reticulum_telemetry_hub/atak_cot/pytak_client.py +569 -0
- reticulum_telemetry_hub/atak_cot/tak_connector.py +848 -0
- reticulum_telemetry_hub/config/__init__.py +25 -0
- reticulum_telemetry_hub/config/constants.py +7 -0
- reticulum_telemetry_hub/config/manager.py +515 -0
- reticulum_telemetry_hub/config/models.py +215 -0
- reticulum_telemetry_hub/embedded_lxmd/__init__.py +5 -0
- reticulum_telemetry_hub/embedded_lxmd/embedded.py +418 -0
- reticulum_telemetry_hub/internal_api/__init__.py +21 -0
- reticulum_telemetry_hub/internal_api/bus.py +344 -0
- reticulum_telemetry_hub/internal_api/core.py +690 -0
- reticulum_telemetry_hub/internal_api/v1/__init__.py +74 -0
- reticulum_telemetry_hub/internal_api/v1/enums.py +109 -0
- reticulum_telemetry_hub/internal_api/v1/manifest.json +8 -0
- reticulum_telemetry_hub/internal_api/v1/schemas.py +478 -0
- reticulum_telemetry_hub/internal_api/versioning.py +63 -0
- reticulum_telemetry_hub/lxmf_daemon/Handlers.py +122 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMF.py +252 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMPeer.py +898 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMRouter.py +4227 -0
- reticulum_telemetry_hub/lxmf_daemon/LXMessage.py +1006 -0
- reticulum_telemetry_hub/lxmf_daemon/LXStamper.py +490 -0
- reticulum_telemetry_hub/lxmf_daemon/__init__.py +10 -0
- reticulum_telemetry_hub/lxmf_daemon/_version.py +1 -0
- reticulum_telemetry_hub/lxmf_daemon/lxmd.py +1655 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/fields/field_telemetry_stream.py +6 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/__init__.py +3 -0
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/appearance.py +19 -19
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/peer.py +17 -13
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/__init__.py +65 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/acceleration.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/ambient_light.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/angular_velocity.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/battery.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/connection_map.py +258 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/generic.py +841 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/gravity.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/humidity.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/information.py +42 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/location.py +110 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/lxmf_propagation.py +429 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/magnetic_field.py +68 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/physical_link.py +53 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/pressure.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/proximity.py +37 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/received.py +75 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/rns_transport.py +209 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor.py +65 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor_enum.py +27 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/sensor_mapping.py +58 -0
- reticulum_telemetry_hub/lxmf_telemetry/model/persistance/sensors/temperature.py +37 -0
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/sensors/time.py +36 -32
- {lxmf_telemetry → reticulum_telemetry_hub/lxmf_telemetry}/model/persistance/telemeter.py +26 -23
- reticulum_telemetry_hub/lxmf_telemetry/sampler.py +229 -0
- reticulum_telemetry_hub/lxmf_telemetry/telemeter_manager.py +409 -0
- reticulum_telemetry_hub/lxmf_telemetry/telemetry_controller.py +804 -0
- reticulum_telemetry_hub/northbound/__init__.py +5 -0
- reticulum_telemetry_hub/northbound/app.py +195 -0
- reticulum_telemetry_hub/northbound/auth.py +119 -0
- reticulum_telemetry_hub/northbound/gateway.py +310 -0
- reticulum_telemetry_hub/northbound/internal_adapter.py +302 -0
- reticulum_telemetry_hub/northbound/models.py +213 -0
- reticulum_telemetry_hub/northbound/routes_chat.py +123 -0
- reticulum_telemetry_hub/northbound/routes_files.py +119 -0
- reticulum_telemetry_hub/northbound/routes_rest.py +345 -0
- reticulum_telemetry_hub/northbound/routes_subscribers.py +150 -0
- reticulum_telemetry_hub/northbound/routes_topics.py +178 -0
- reticulum_telemetry_hub/northbound/routes_ws.py +107 -0
- reticulum_telemetry_hub/northbound/serializers.py +72 -0
- reticulum_telemetry_hub/northbound/services.py +373 -0
- reticulum_telemetry_hub/northbound/websocket.py +855 -0
- reticulum_telemetry_hub/reticulum_server/__main__.py +2237 -0
- reticulum_telemetry_hub/reticulum_server/command_manager.py +1268 -0
- reticulum_telemetry_hub/reticulum_server/command_text.py +399 -0
- reticulum_telemetry_hub/reticulum_server/constants.py +1 -0
- reticulum_telemetry_hub/reticulum_server/event_log.py +357 -0
- reticulum_telemetry_hub/reticulum_server/internal_adapter.py +358 -0
- reticulum_telemetry_hub/reticulum_server/outbound_queue.py +312 -0
- reticulum_telemetry_hub/reticulum_server/services.py +422 -0
- reticulumtelemetryhub-0.143.0.dist-info/METADATA +181 -0
- reticulumtelemetryhub-0.143.0.dist-info/RECORD +97 -0
- {reticulumtelemetryhub-0.1.0.dist-info → reticulumtelemetryhub-0.143.0.dist-info}/WHEEL +1 -1
- reticulumtelemetryhub-0.143.0.dist-info/licenses/LICENSE +277 -0
- lxmf_telemetry/model/fields/field_telemetry_stream.py +0 -7
- lxmf_telemetry/model/persistance/__init__.py +0 -3
- lxmf_telemetry/model/persistance/sensors/location.py +0 -69
- lxmf_telemetry/model/persistance/sensors/magnetic_field.py +0 -36
- lxmf_telemetry/model/persistance/sensors/sensor.py +0 -44
- lxmf_telemetry/model/persistance/sensors/sensor_enum.py +0 -24
- lxmf_telemetry/model/persistance/sensors/sensor_mapping.py +0 -9
- lxmf_telemetry/telemetry_controller.py +0 -124
- reticulum_server/main.py +0 -182
- reticulumtelemetryhub-0.1.0.dist-info/METADATA +0 -15
- reticulumtelemetryhub-0.1.0.dist-info/RECORD +0 -19
- {lxmf_telemetry → reticulum_telemetry_hub}/__init__.py +0 -0
- {lxmf_telemetry/model/persistance/sensors → reticulum_telemetry_hub/lxmf_telemetry}/__init__.py +0 -0
- {reticulum_server → reticulum_telemetry_hub/reticulum_server}/__init__.py +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""SQLAlchemy model for the Magnetic Field sensor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import Float, ForeignKey
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from .sensor import Sensor
|
|
11
|
+
from .sensor_enum import SID_MAGNETIC_FIELD
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MagneticField(Sensor):
|
|
15
|
+
__tablename__ = "MagneticField"
|
|
16
|
+
|
|
17
|
+
id: Mapped[int] = mapped_column(ForeignKey("Sensor.id"), primary_key=True)
|
|
18
|
+
x: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
19
|
+
y: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
20
|
+
z: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
stale_time: float | None = 1,
|
|
25
|
+
data: Any | None = None,
|
|
26
|
+
active: bool = False,
|
|
27
|
+
synthesized: bool = False,
|
|
28
|
+
last_update: float = 0,
|
|
29
|
+
last_read: float = 0,
|
|
30
|
+
) -> None:
|
|
31
|
+
super().__init__(
|
|
32
|
+
stale_time=stale_time,
|
|
33
|
+
data=data,
|
|
34
|
+
active=active,
|
|
35
|
+
synthesized=synthesized,
|
|
36
|
+
last_update=last_update,
|
|
37
|
+
last_read=last_read,
|
|
38
|
+
)
|
|
39
|
+
self.sid = SID_MAGNETIC_FIELD
|
|
40
|
+
|
|
41
|
+
def pack(self): # type: ignore[override]
|
|
42
|
+
if self.x is None and self.y is None and self.z is None:
|
|
43
|
+
return None
|
|
44
|
+
return [self.x, self.y, self.z]
|
|
45
|
+
|
|
46
|
+
def unpack(self, packed: Any): # type: ignore[override]
|
|
47
|
+
if packed is None:
|
|
48
|
+
self.x = None
|
|
49
|
+
self.y = None
|
|
50
|
+
self.z = None
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
self.x = packed[0]
|
|
55
|
+
self.y = packed[1]
|
|
56
|
+
self.z = packed[2]
|
|
57
|
+
except (IndexError, TypeError):
|
|
58
|
+
self.x = None
|
|
59
|
+
self.y = None
|
|
60
|
+
self.z = None
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
return {"x": self.x, "y": self.y, "z": self.z}
|
|
64
|
+
|
|
65
|
+
__mapper_args__ = {
|
|
66
|
+
"polymorphic_identity": SID_MAGNETIC_FIELD,
|
|
67
|
+
"with_polymorphic": "*",
|
|
68
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""SQLAlchemy model for the Physical Link sensor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import Float, ForeignKey
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from .sensor import Sensor
|
|
11
|
+
from .sensor_enum import SID_PHYSICAL_LINK
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PhysicalLink(Sensor):
|
|
15
|
+
__tablename__ = "PhysicalLink"
|
|
16
|
+
|
|
17
|
+
id: Mapped[int] = mapped_column(ForeignKey("Sensor.id"), primary_key=True)
|
|
18
|
+
rssi: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
19
|
+
snr: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
20
|
+
q: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
21
|
+
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
super().__init__(stale_time=5)
|
|
24
|
+
self.sid = SID_PHYSICAL_LINK
|
|
25
|
+
|
|
26
|
+
def pack(self): # type: ignore[override]
|
|
27
|
+
if self.rssi is None and self.snr is None and self.q is None:
|
|
28
|
+
return None
|
|
29
|
+
return [self.rssi, self.snr, self.q]
|
|
30
|
+
|
|
31
|
+
def unpack(self, packed: Any): # type: ignore[override]
|
|
32
|
+
if packed is None:
|
|
33
|
+
self.rssi = None
|
|
34
|
+
self.snr = None
|
|
35
|
+
self.q = None
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
self.rssi = packed[0]
|
|
40
|
+
self.snr = packed[1]
|
|
41
|
+
self.q = packed[2]
|
|
42
|
+
except (IndexError, TypeError):
|
|
43
|
+
self.rssi = None
|
|
44
|
+
self.snr = None
|
|
45
|
+
self.q = None
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
return {"rssi": self.rssi, "snr": self.snr, "q": self.q}
|
|
49
|
+
|
|
50
|
+
__mapper_args__ = {
|
|
51
|
+
"polymorphic_identity": SID_PHYSICAL_LINK,
|
|
52
|
+
"with_polymorphic": "*",
|
|
53
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""SQLAlchemy model for the Pressure sensor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import Float, ForeignKey
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from .sensor import Sensor
|
|
11
|
+
from .sensor_enum import SID_PRESSURE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Pressure(Sensor):
|
|
15
|
+
__tablename__ = "Pressure"
|
|
16
|
+
|
|
17
|
+
id: Mapped[int] = mapped_column(ForeignKey("Sensor.id"), primary_key=True)
|
|
18
|
+
mbar: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
super().__init__(stale_time=5)
|
|
22
|
+
self.sid = SID_PRESSURE
|
|
23
|
+
|
|
24
|
+
def pack(self): # type: ignore[override]
|
|
25
|
+
return self.mbar
|
|
26
|
+
|
|
27
|
+
def unpack(self, packed: Any): # type: ignore[override]
|
|
28
|
+
if packed is None:
|
|
29
|
+
self.mbar = None
|
|
30
|
+
return None
|
|
31
|
+
self.mbar = packed
|
|
32
|
+
return {"mbar": self.mbar}
|
|
33
|
+
|
|
34
|
+
__mapper_args__ = {
|
|
35
|
+
"polymorphic_identity": SID_PRESSURE,
|
|
36
|
+
"with_polymorphic": "*",
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""SQLAlchemy model for the Proximity sensor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import Boolean, ForeignKey
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from .sensor import Sensor
|
|
11
|
+
from .sensor_enum import SID_PROXIMITY
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Proximity(Sensor):
|
|
15
|
+
__tablename__ = "Proximity"
|
|
16
|
+
|
|
17
|
+
id: Mapped[int] = mapped_column(ForeignKey("Sensor.id"), primary_key=True)
|
|
18
|
+
triggered: Mapped[Optional[bool]] = mapped_column(Boolean, nullable=True)
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
super().__init__(stale_time=1)
|
|
22
|
+
self.sid = SID_PROXIMITY
|
|
23
|
+
|
|
24
|
+
def pack(self): # type: ignore[override]
|
|
25
|
+
return self.triggered
|
|
26
|
+
|
|
27
|
+
def unpack(self, packed: Any): # type: ignore[override]
|
|
28
|
+
if packed is None:
|
|
29
|
+
self.triggered = None
|
|
30
|
+
return None
|
|
31
|
+
self.triggered = packed
|
|
32
|
+
return {"triggered": self.triggered}
|
|
33
|
+
|
|
34
|
+
__mapper_args__ = {
|
|
35
|
+
"polymorphic_identity": SID_PROXIMITY,
|
|
36
|
+
"with_polymorphic": "*",
|
|
37
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""SQLAlchemy model for the Received sensor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import Float, ForeignKey, LargeBinary
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from .sensor import Sensor
|
|
11
|
+
from .sensor_enum import SID_RECEIVED
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Received(Sensor):
|
|
15
|
+
__tablename__ = "Received"
|
|
16
|
+
|
|
17
|
+
id: Mapped[int] = mapped_column(ForeignKey("Sensor.id"), primary_key=True)
|
|
18
|
+
by: Mapped[Optional[bytes]] = mapped_column(LargeBinary, nullable=True)
|
|
19
|
+
via: Mapped[Optional[bytes]] = mapped_column(LargeBinary, nullable=True)
|
|
20
|
+
geodesic_distance: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
21
|
+
euclidian_distance: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
super().__init__(stale_time=5)
|
|
25
|
+
self.sid = SID_RECEIVED
|
|
26
|
+
|
|
27
|
+
def pack(self): # type: ignore[override]
|
|
28
|
+
if (
|
|
29
|
+
self.by is None
|
|
30
|
+
and self.via is None
|
|
31
|
+
and self.geodesic_distance is None
|
|
32
|
+
and self.euclidian_distance is None
|
|
33
|
+
):
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
return [
|
|
37
|
+
self.by,
|
|
38
|
+
self.via,
|
|
39
|
+
self.geodesic_distance,
|
|
40
|
+
self.euclidian_distance,
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def unpack(self, packed: Any): # type: ignore[override]
|
|
44
|
+
if packed is None:
|
|
45
|
+
self.by = None
|
|
46
|
+
self.via = None
|
|
47
|
+
self.geodesic_distance = None
|
|
48
|
+
self.euclidian_distance = None
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
self.by = packed[0]
|
|
53
|
+
self.via = packed[1]
|
|
54
|
+
self.geodesic_distance = packed[2]
|
|
55
|
+
self.euclidian_distance = packed[3]
|
|
56
|
+
except (IndexError, TypeError):
|
|
57
|
+
self.by = None
|
|
58
|
+
self.via = None
|
|
59
|
+
self.geodesic_distance = None
|
|
60
|
+
self.euclidian_distance = None
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
"by": self.by,
|
|
65
|
+
"via": self.via,
|
|
66
|
+
"distance": {
|
|
67
|
+
"geodesic": self.geodesic_distance,
|
|
68
|
+
"euclidian": self.euclidian_distance,
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
__mapper_args__ = {
|
|
73
|
+
"polymorphic_identity": SID_RECEIVED,
|
|
74
|
+
"with_polymorphic": "*",
|
|
75
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""SQLAlchemy model for Reticulum transport telemetry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from msgpack import packb, unpackb
|
|
8
|
+
from sqlalchemy import Boolean, Float, ForeignKey, Integer, LargeBinary
|
|
9
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
10
|
+
|
|
11
|
+
from .sensor import Sensor
|
|
12
|
+
from .sensor_enum import SID_RNS_TRANSPORT
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _encode_payload(payload: Any) -> bytes | None:
|
|
16
|
+
"""Serialize ``payload`` with msgpack if it is not ``None``."""
|
|
17
|
+
|
|
18
|
+
if payload is None:
|
|
19
|
+
return None
|
|
20
|
+
return packb(payload, use_bin_type=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _decode_payload(blob: bytes | None) -> Any:
|
|
24
|
+
"""Deserialize msgpack ``blob`` into Python objects."""
|
|
25
|
+
|
|
26
|
+
if blob is None:
|
|
27
|
+
return None
|
|
28
|
+
return unpackb(blob, strict_map_key=False)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RNSTransport(Sensor):
|
|
32
|
+
"""Telemetry sensor describing the local Reticulum transport state."""
|
|
33
|
+
|
|
34
|
+
__tablename__ = "RNSTransport"
|
|
35
|
+
|
|
36
|
+
SID = SID_RNS_TRANSPORT
|
|
37
|
+
|
|
38
|
+
id: Mapped[int] = mapped_column(
|
|
39
|
+
ForeignKey("Sensor.id", ondelete="CASCADE"), primary_key=True
|
|
40
|
+
)
|
|
41
|
+
transport_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
42
|
+
transport_identity: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
43
|
+
transport_uptime: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
44
|
+
traffic_rxb: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
45
|
+
traffic_txb: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
46
|
+
speed_rx: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
47
|
+
speed_tx: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
48
|
+
speed_rx_inst: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
49
|
+
speed_tx_inst: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
50
|
+
memory_used: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
51
|
+
interface_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
52
|
+
link_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
53
|
+
|
|
54
|
+
interfaces_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
55
|
+
path_table_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
56
|
+
ifstats_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
57
|
+
extra_blob: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
|
58
|
+
|
|
59
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
60
|
+
if "stale_time" not in kwargs:
|
|
61
|
+
kwargs["stale_time"] = 60
|
|
62
|
+
super().__init__(*args, **kwargs)
|
|
63
|
+
self.sid = self.SID
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def interfaces(self) -> Any:
|
|
67
|
+
"""Return the decoded interface list or mapping."""
|
|
68
|
+
|
|
69
|
+
return _decode_payload(self.interfaces_blob)
|
|
70
|
+
|
|
71
|
+
@interfaces.setter
|
|
72
|
+
def interfaces(self, value: Any) -> None:
|
|
73
|
+
self.interfaces_blob = _encode_payload(value)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def path_table(self) -> Any:
|
|
77
|
+
return _decode_payload(self.path_table_blob)
|
|
78
|
+
|
|
79
|
+
@path_table.setter
|
|
80
|
+
def path_table(self, value: Any) -> None:
|
|
81
|
+
self.path_table_blob = _encode_payload(value)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def ifstats(self) -> Any:
|
|
85
|
+
return _decode_payload(self.ifstats_blob)
|
|
86
|
+
|
|
87
|
+
@ifstats.setter
|
|
88
|
+
def ifstats(self, value: Any) -> None:
|
|
89
|
+
self.ifstats_blob = _encode_payload(value)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def extras(self) -> Any:
|
|
93
|
+
return _decode_payload(self.extra_blob)
|
|
94
|
+
|
|
95
|
+
@extras.setter
|
|
96
|
+
def extras(self, value: Any) -> None:
|
|
97
|
+
self.extra_blob = _encode_payload(value)
|
|
98
|
+
|
|
99
|
+
def _populate_counts(self, interfaces: Any) -> None:
|
|
100
|
+
if self.interface_count is not None or interfaces is None:
|
|
101
|
+
return
|
|
102
|
+
if hasattr(interfaces, "__len__"):
|
|
103
|
+
try:
|
|
104
|
+
self.interface_count = len(interfaces) # type: ignore[arg-type]
|
|
105
|
+
return
|
|
106
|
+
except TypeError:
|
|
107
|
+
pass
|
|
108
|
+
try:
|
|
109
|
+
self.interface_count = sum(1 for _ in interfaces) # type: ignore[arg-type]
|
|
110
|
+
except TypeError:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
def pack(self) -> dict[str, Any] | None: # type: ignore[override]
|
|
114
|
+
extras = self.extras or {}
|
|
115
|
+
payload: dict[str, Any] = dict(extras)
|
|
116
|
+
|
|
117
|
+
interfaces = self.interfaces
|
|
118
|
+
path_table = self.path_table
|
|
119
|
+
ifstats = self.ifstats
|
|
120
|
+
|
|
121
|
+
if ifstats is not None and interfaces is not None:
|
|
122
|
+
if isinstance(ifstats, dict) and "interfaces" not in ifstats:
|
|
123
|
+
merged = dict(ifstats)
|
|
124
|
+
merged["interfaces"] = interfaces
|
|
125
|
+
ifstats = merged
|
|
126
|
+
elif interfaces is not None:
|
|
127
|
+
ifstats = {"interfaces": interfaces}
|
|
128
|
+
|
|
129
|
+
interface_count = self.interface_count
|
|
130
|
+
if interface_count is None and isinstance(interfaces, list):
|
|
131
|
+
interface_count = len(interfaces)
|
|
132
|
+
|
|
133
|
+
payload.update(
|
|
134
|
+
{
|
|
135
|
+
"transport_enabled": bool(self.transport_enabled),
|
|
136
|
+
"transport_identity": (
|
|
137
|
+
bytes(self.transport_identity) if self.transport_identity else None
|
|
138
|
+
),
|
|
139
|
+
"transport_uptime": self.transport_uptime,
|
|
140
|
+
"traffic_rxb": self.traffic_rxb,
|
|
141
|
+
"traffic_txb": self.traffic_txb,
|
|
142
|
+
"speed_rx": self.speed_rx,
|
|
143
|
+
"speed_tx": self.speed_tx,
|
|
144
|
+
"speed_rx_inst": self.speed_rx_inst,
|
|
145
|
+
"speed_tx_inst": self.speed_tx_inst,
|
|
146
|
+
"memory_used": self.memory_used,
|
|
147
|
+
"interface_count": interface_count,
|
|
148
|
+
"link_count": self.link_count,
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if interfaces is not None:
|
|
153
|
+
payload["interfaces"] = interfaces
|
|
154
|
+
if path_table is not None:
|
|
155
|
+
payload["path_table"] = path_table
|
|
156
|
+
if ifstats is not None:
|
|
157
|
+
payload["ifstats"] = ifstats
|
|
158
|
+
|
|
159
|
+
return payload
|
|
160
|
+
|
|
161
|
+
def unpack(self, packed: Any) -> Any: # type: ignore[override]
|
|
162
|
+
if packed is None or not isinstance(packed, dict):
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
data = dict(packed)
|
|
166
|
+
|
|
167
|
+
interfaces = data.pop("interfaces", None)
|
|
168
|
+
ifstats = data.pop("ifstats", None)
|
|
169
|
+
path_table = data.pop("path_table", None)
|
|
170
|
+
|
|
171
|
+
if interfaces is None and isinstance(ifstats, dict):
|
|
172
|
+
maybe_interfaces = ifstats.get("interfaces")
|
|
173
|
+
if maybe_interfaces is not None:
|
|
174
|
+
interfaces = maybe_interfaces
|
|
175
|
+
|
|
176
|
+
self.interfaces = interfaces
|
|
177
|
+
self.ifstats = ifstats
|
|
178
|
+
self.path_table = path_table
|
|
179
|
+
|
|
180
|
+
self.transport_enabled = bool(
|
|
181
|
+
data.pop("transport_enabled", self.transport_enabled)
|
|
182
|
+
)
|
|
183
|
+
self.transport_identity = data.pop(
|
|
184
|
+
"transport_identity", self.transport_identity
|
|
185
|
+
)
|
|
186
|
+
self.transport_uptime = data.pop("transport_uptime", self.transport_uptime)
|
|
187
|
+
self.traffic_rxb = data.pop("traffic_rxb", self.traffic_rxb)
|
|
188
|
+
self.traffic_txb = data.pop("traffic_txb", self.traffic_txb)
|
|
189
|
+
self.speed_rx = data.pop("speed_rx", self.speed_rx)
|
|
190
|
+
self.speed_tx = data.pop("speed_tx", self.speed_tx)
|
|
191
|
+
self.speed_rx_inst = data.pop("speed_rx_inst", self.speed_rx_inst)
|
|
192
|
+
self.speed_tx_inst = data.pop("speed_tx_inst", self.speed_tx_inst)
|
|
193
|
+
self.memory_used = data.pop("memory_used", self.memory_used)
|
|
194
|
+
self.interface_count = data.pop("interface_count", self.interface_count)
|
|
195
|
+
self.link_count = data.pop("link_count", self.link_count)
|
|
196
|
+
|
|
197
|
+
self._populate_counts(interfaces)
|
|
198
|
+
|
|
199
|
+
self.extras = data if data else None
|
|
200
|
+
|
|
201
|
+
return packed
|
|
202
|
+
|
|
203
|
+
__mapper_args__ = {
|
|
204
|
+
"polymorphic_identity": SID_RNS_TRANSPORT,
|
|
205
|
+
"with_polymorphic": "*",
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
__all__ = ["RNSTransport"]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from sqlalchemy import Column, ForeignKey, Integer, Float, Boolean, BLOB
|
|
2
|
+
from msgpack import packb, unpackb
|
|
3
|
+
from sqlalchemy.orm import DeclarativeMeta, relationship, Mapped, mapped_column
|
|
4
|
+
|
|
5
|
+
from .. import Base
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SensorDeclarativeMeta(DeclarativeMeta):
|
|
9
|
+
"""Custom declarative metaclass that fills in mapper defaults."""
|
|
10
|
+
|
|
11
|
+
def __init__(cls, classname, bases, dict_, **kwargs):
|
|
12
|
+
if classname != "Sensor":
|
|
13
|
+
mapper_args = dict_.get("__mapper_args__")
|
|
14
|
+
if mapper_args is None or "polymorphic_identity" not in mapper_args:
|
|
15
|
+
mapper_args = dict(mapper_args or {})
|
|
16
|
+
mapper_args.setdefault("with_polymorphic", "*")
|
|
17
|
+
mapper_args.setdefault("polymorphic_identity", classname)
|
|
18
|
+
cls.__mapper_args__ = mapper_args
|
|
19
|
+
super().__init__(classname, bases, dict_, **kwargs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Sensor(Base, metaclass=SensorDeclarativeMeta):
|
|
23
|
+
__tablename__ = "Sensor"
|
|
24
|
+
|
|
25
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
26
|
+
sid = Column(Integer, nullable=False, default=0x00)
|
|
27
|
+
stale_time = Column(Float, nullable=True)
|
|
28
|
+
data = Column(BLOB, nullable=True)
|
|
29
|
+
synthesized = Column(Boolean, default=False)
|
|
30
|
+
telemeter_id: Mapped[int] = mapped_column(ForeignKey("Telemeter.id"))
|
|
31
|
+
telemeter = relationship("Telemeter", back_populates="sensors")
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
stale_time=None,
|
|
36
|
+
data=None,
|
|
37
|
+
active=False,
|
|
38
|
+
synthesized=False,
|
|
39
|
+
last_update=0,
|
|
40
|
+
last_read=0,
|
|
41
|
+
):
|
|
42
|
+
self.stale_time = stale_time
|
|
43
|
+
self.data = data
|
|
44
|
+
self.active = active
|
|
45
|
+
self.synthesized = synthesized
|
|
46
|
+
self.last_update = last_update
|
|
47
|
+
self.last_read = last_read
|
|
48
|
+
|
|
49
|
+
def packb(self):
|
|
50
|
+
return packb(self.pack())
|
|
51
|
+
|
|
52
|
+
def unpackb(self, packed):
|
|
53
|
+
return unpackb(self.unpack(packed))
|
|
54
|
+
|
|
55
|
+
def pack(self):
|
|
56
|
+
return self.data
|
|
57
|
+
|
|
58
|
+
def unpack(self, packed):
|
|
59
|
+
return packed
|
|
60
|
+
|
|
61
|
+
__mapper_args__ = {
|
|
62
|
+
"polymorphic_identity": "Sensor",
|
|
63
|
+
"with_polymorphic": "*",
|
|
64
|
+
"polymorphic_on": sid,
|
|
65
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
SID_NONE = 0x00
|
|
2
|
+
SID_TIME = 0x01
|
|
3
|
+
SID_LOCATION = 0x02
|
|
4
|
+
SID_PRESSURE = 0x03
|
|
5
|
+
SID_BATTERY = 0x04
|
|
6
|
+
SID_PHYSICAL_LINK = 0x05
|
|
7
|
+
SID_ACCELERATION = 0x06
|
|
8
|
+
SID_TEMPERATURE = 0x07
|
|
9
|
+
SID_HUMIDITY = 0x08
|
|
10
|
+
SID_MAGNETIC_FIELD = 0x09
|
|
11
|
+
SID_AMBIENT_LIGHT = 0x0A
|
|
12
|
+
SID_GRAVITY = 0x0B
|
|
13
|
+
SID_ANGULAR_VELOCITY = 0x0C
|
|
14
|
+
SID_PROXIMITY = 0x0E
|
|
15
|
+
SID_INFORMATION = 0x0F
|
|
16
|
+
SID_RECEIVED = 0x10
|
|
17
|
+
SID_POWER_CONSUMPTION = 0x11
|
|
18
|
+
SID_POWER_PRODUCTION = 0x12
|
|
19
|
+
SID_PROCESSOR = 0x13
|
|
20
|
+
SID_RAM = 0x14
|
|
21
|
+
SID_NVM = 0x15
|
|
22
|
+
SID_TANK = 0x16
|
|
23
|
+
SID_FUEL = 0x17
|
|
24
|
+
SID_LXMF_PROPAGATION = 0x18
|
|
25
|
+
SID_RNS_TRANSPORT = 0x19
|
|
26
|
+
SID_CONNECTION_MAP = 0x1A
|
|
27
|
+
SID_CUSTOM = 0xFF
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from .sensor_enum import *
|
|
2
|
+
from .acceleration import Acceleration
|
|
3
|
+
from .ambient_light import AmbientLight
|
|
4
|
+
from .angular_velocity import AngularVelocity
|
|
5
|
+
from .battery import Battery
|
|
6
|
+
from .gravity import Gravity
|
|
7
|
+
from .humidity import Humidity
|
|
8
|
+
from .information import Information
|
|
9
|
+
from .location import Location
|
|
10
|
+
from .magnetic_field import MagneticField
|
|
11
|
+
from .physical_link import PhysicalLink
|
|
12
|
+
from .pressure import Pressure
|
|
13
|
+
from .proximity import Proximity
|
|
14
|
+
from .received import Received
|
|
15
|
+
from .temperature import Temperature
|
|
16
|
+
from .time import Time
|
|
17
|
+
from .connection_map import ConnectionMap
|
|
18
|
+
from .generic import (
|
|
19
|
+
Custom,
|
|
20
|
+
Fuel,
|
|
21
|
+
NonVolatileMemory,
|
|
22
|
+
PowerConsumption,
|
|
23
|
+
PowerProduction,
|
|
24
|
+
Processor,
|
|
25
|
+
RandomAccessMemory,
|
|
26
|
+
Tank,
|
|
27
|
+
)
|
|
28
|
+
from .lxmf_propagation import LXMFPropagation
|
|
29
|
+
from .rns_transport import RNSTransport
|
|
30
|
+
|
|
31
|
+
sid_mapping = {
|
|
32
|
+
SID_TIME: Time,
|
|
33
|
+
SID_LOCATION: Location,
|
|
34
|
+
SID_PRESSURE: Pressure,
|
|
35
|
+
SID_BATTERY: Battery,
|
|
36
|
+
SID_PHYSICAL_LINK: PhysicalLink,
|
|
37
|
+
SID_ACCELERATION: Acceleration,
|
|
38
|
+
SID_TEMPERATURE: Temperature,
|
|
39
|
+
SID_HUMIDITY: Humidity,
|
|
40
|
+
SID_MAGNETIC_FIELD: MagneticField,
|
|
41
|
+
SID_AMBIENT_LIGHT: AmbientLight,
|
|
42
|
+
SID_GRAVITY: Gravity,
|
|
43
|
+
SID_ANGULAR_VELOCITY: AngularVelocity,
|
|
44
|
+
SID_PROXIMITY: Proximity,
|
|
45
|
+
SID_INFORMATION: Information,
|
|
46
|
+
SID_RECEIVED: Received,
|
|
47
|
+
SID_POWER_CONSUMPTION: PowerConsumption,
|
|
48
|
+
SID_POWER_PRODUCTION: PowerProduction,
|
|
49
|
+
SID_PROCESSOR: Processor,
|
|
50
|
+
SID_RAM: RandomAccessMemory,
|
|
51
|
+
SID_NVM: NonVolatileMemory,
|
|
52
|
+
SID_TANK: Tank,
|
|
53
|
+
SID_FUEL: Fuel,
|
|
54
|
+
SID_LXMF_PROPAGATION: LXMFPropagation,
|
|
55
|
+
SID_RNS_TRANSPORT: RNSTransport,
|
|
56
|
+
SID_CONNECTION_MAP: ConnectionMap,
|
|
57
|
+
SID_CUSTOM: Custom,
|
|
58
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""SQLAlchemy model for the Temperature sensor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import Float, ForeignKey
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from .sensor import Sensor
|
|
11
|
+
from .sensor_enum import SID_TEMPERATURE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Temperature(Sensor):
|
|
15
|
+
__tablename__ = "Temperature"
|
|
16
|
+
|
|
17
|
+
id: Mapped[int] = mapped_column(ForeignKey("Sensor.id"), primary_key=True)
|
|
18
|
+
c: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
super().__init__(stale_time=5)
|
|
22
|
+
self.sid = SID_TEMPERATURE
|
|
23
|
+
|
|
24
|
+
def pack(self): # type: ignore[override]
|
|
25
|
+
return self.c
|
|
26
|
+
|
|
27
|
+
def unpack(self, packed: Any): # type: ignore[override]
|
|
28
|
+
if packed is None:
|
|
29
|
+
self.c = None
|
|
30
|
+
return None
|
|
31
|
+
self.c = packed
|
|
32
|
+
return {"c": self.c}
|
|
33
|
+
|
|
34
|
+
__mapper_args__ = {
|
|
35
|
+
"polymorphic_identity": SID_TEMPERATURE,
|
|
36
|
+
"with_polymorphic": "*",
|
|
37
|
+
}
|