mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-06 12:10:59 +10:00
Compare commits
112
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
948c867db5 | ||
|
|
3a0c08b695 | ||
|
|
4bc1078cfb | ||
|
|
9af3d74023 | ||
|
|
23065a145e | ||
|
|
7bdff8ff70 | ||
|
|
05e6a7e5eb | ||
|
|
35b0590408 | ||
|
|
cbddf07bc8 | ||
|
|
c360815c0e | ||
|
|
871194517d | ||
|
|
5a1d2b9ef4 | ||
|
|
9cd3a86938 | ||
|
|
748668b8e9 | ||
|
|
f0a30f72ff | ||
|
|
778d1ad90f | ||
|
|
1ae4a538f5 | ||
|
|
c756bbe2c1 | ||
|
|
4c3ba612bb | ||
|
|
20eaefe706 | ||
|
|
75b7a7df4f | ||
|
|
59025e4820 | ||
|
|
8110887be2 | ||
|
|
5f734dabf9 | ||
|
|
039ad42758 | ||
|
|
593909c26b | ||
|
|
0a7b3c723e | ||
|
|
29a61dc75c | ||
|
|
eead467ce6 | ||
|
|
f3ae02c425 | ||
|
|
d9cb74e4dd | ||
|
|
7ff6641f97 | ||
|
|
cd5d608e8d | ||
|
|
f7548e7c25 | ||
|
|
b960121464 | ||
|
|
b074645586 | ||
|
|
811dd427dd | ||
|
|
7f5184281d | ||
|
|
1c06b702dc | ||
|
|
59b4993861 | ||
|
|
4446b0f1a2 | ||
|
|
64fd61706d | ||
|
|
da61a0db7d | ||
|
|
8e99ffbe7e | ||
|
|
8877608858 | ||
|
|
9a63eb729d | ||
|
|
0bb89644f9 | ||
|
|
aef13bef89 | ||
|
|
6d31915e57 | ||
|
|
aa89faa619 | ||
|
|
ee796c75c9 | ||
|
|
a0a7209229 | ||
|
|
16f2ff8c9e | ||
|
|
894a2f43bd | ||
|
|
e470723811 | ||
|
|
13ee648e3d | ||
|
|
bec00b0a57 | ||
|
|
025630937c | ||
|
|
527e2dc7f7 | ||
|
|
095838f375 | ||
|
|
e66fcde7e3 | ||
|
|
dc478d2163 | ||
|
|
f85ed3e59e | ||
|
|
acb7ac6e3d | ||
|
|
c6282d3a3c | ||
|
|
3f35f2c6d7 | ||
|
|
6847f232a7 | ||
|
|
f586824435 | ||
|
|
b0d7440bad | ||
|
|
8dc82c45b8 | ||
|
|
96538b492a | ||
|
|
41df7ef0bf | ||
|
|
e49dab1f08 | ||
|
|
74f7d9ce3c | ||
|
|
6c74052ec3 | ||
|
|
fb7d34afb9 | ||
|
|
814773f50e | ||
|
|
621aeb29b5 | ||
|
|
6f1d611c34 | ||
|
|
fc871f42e4 | ||
|
|
9da0ee9c51 | ||
|
|
61bab08d9e | ||
|
|
e85ebc6859 | ||
|
|
c9fc1edb4f | ||
|
|
483bb33749 | ||
|
|
e22a22b325 | ||
|
|
35e4a877ee | ||
|
|
a6f6175535 | ||
|
|
3e5f95f8ed | ||
|
|
edb975d886 | ||
|
|
8d649e9a0b | ||
|
|
5c1e5f3dd6 | ||
|
|
6bde1d5fbb | ||
|
|
bc311616a3 | ||
|
|
9fdeec173e | ||
|
|
b24a6676a6 | ||
|
|
a3f39de4b9 | ||
|
|
d235d3f933 | ||
|
|
67726f9e3a | ||
|
|
3cbc9c91cb | ||
|
|
5c2afbf8ce | ||
|
|
f1ca363efe | ||
|
|
814dc2db1b | ||
|
|
fbeabe29ed | ||
|
|
7235afec2f | ||
|
|
ef7036e9ed | ||
|
|
c997e3bb65 | ||
|
|
028f781ea5 | ||
|
|
86cdff463b | ||
|
|
778090a269 | ||
|
|
14e9cb0fc3 | ||
|
|
aa506ce4ab |
@@ -8,17 +8,21 @@
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"installTools": true,
|
||||
"version": "3.14"
|
||||
"version": "3.13"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools",
|
||||
"platformio.platformio-ide",
|
||||
"Jason2866.esp-decoder",
|
||||
"pioarduino.pioarduino-ide",
|
||||
"Trunk.io"
|
||||
],
|
||||
"unwantedRecommendations": ["ms-azuretools.vscode-docker"],
|
||||
"unwantedRecommendations": [
|
||||
"ms-azuretools.vscode-docker",
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"settings": {
|
||||
"extensions.ignoreRecommendations": true
|
||||
}
|
||||
|
||||
@@ -193,22 +193,26 @@ Writers go through `setNodeStatus`, `updatePosition`, `updateTelemetry` (which d
|
||||
|
||||
Every code path that drops a node from the header table must also evict the satellites. The single chokepoint is `eraseNodeSatellites(NodeNum)`; it's already called from `getOrCreateMeshNode`'s oldest-boring eviction, `removeNodeByNum`, both branches of `resetNodes`, `cleanupMeshDB`, `addFromContact`'s ignored-branch, and `AdminModule`'s `set_ignored_node`. Add new eviction sites here, not by calling `.erase()` directly.
|
||||
|
||||
### Gradient sync (opt-in via special nonces)
|
||||
### Sync flow: thin NodeInfo + post-COMPLETE_ID replay (no opt-in)
|
||||
|
||||
`client_capabilities` is **not** a thing in this branch. Phone clients opt into the new sync flow by sending one of two values in the `ToRadio.want_config_id`:
|
||||
There is no capability flag and no special "gradient" nonce. The **default** sync flow is:
|
||||
|
||||
- `SPECIAL_NONCE_GRADIENT_SYNC` (69422) — full config + thin NodeInfo + replay phases.
|
||||
- `SPECIAL_NONCE_GRADIENT_ONLY_NODES` (69423) — skip config segments, NodeInfo + replay only.
|
||||
1. Config / module-config / channel / metadata segments (same as before).
|
||||
2. `STATE_SEND_OWN_NODEINFO` — **our own** NodeInfo, still bundled with our position and device_metrics (because the replay snapshot excludes our own NodeNum). Emitted via `ConvertToNodeInfo(lite)`.
|
||||
3. `STATE_SEND_OTHER_NODEINFOS` — every other peer's NodeInfo, **always thin** (no `position`, no `device_metrics`). Emitted via `ConvertToNodeInfoThin(lite)`.
|
||||
4. `STATE_SEND_FILEMANIFEST` → `STATE_SEND_COMPLETE_ID` — the phone sees `config_complete_id` and treats sync as done.
|
||||
5. `STATE_SEND_PACKETS` — live mesh packets, with a trailing replay drain interleaved. The replay drain walks four cached satellite stores in order (positions → telemetry → environment → status) and emits each cached entry as an ordinary `MeshPacket` on the matching portnum (`POSITION_APP`, `TELEMETRY_APP` device + environment variants, `NODE_STATUS_APP`). These are indistinguishable on the wire from live mesh traffic, so clients need no special handling — any code that already updates UI on `POSITION_APP` etc. works.
|
||||
|
||||
`PhoneAPI::clientWantsGradientSync()` is the single switch. When true, `STATE_SEND_OTHER_NODEINFOS` is followed by:
|
||||
`PhoneAPI::sendConfigComplete()` arms `replayPhase = REPLAY_PHASE_POSITIONS` for default/full sync and `SPECIAL_NONCE_ONLY_NODES`, while `SPECIAL_NONCE_ONLY_CONFIG` skips replay. The drain runs inside `STATE_SEND_PACKETS` via `popReplayPacket()`, lower priority than live traffic. When all four phases drain, `replayPhase` flips back to `REPLAY_PHASE_IDLE` and the snapshot vectors get `shrink_to_fit`ed.
|
||||
|
||||
```text
|
||||
STATE_REPLAY_POSITIONS → STATE_REPLAY_TELEMETRY → STATE_REPLAY_ENVIRONMENT → STATE_REPLAY_STATUS
|
||||
```
|
||||
STM32WL and any other build with all four `MESHTASTIC_EXCLUDE_*DB` flags set produces zero replay packets — `popReplayPacket` advances through each phase in microseconds without emitting anything.
|
||||
|
||||
Each replay phase walks the corresponding satellite map and emits synthetic `MeshPacket`s on the matching portnum (`POSITION_APP`, `TELEMETRY_APP` for both device + environment variants, `STATUS_MESSAGE_APP`). Legacy clients (no special nonce) get the bundled-NodeInfo path with position/device_metrics joined back in by `ConvertToNodeInfo(lite, pos*, dm*)` — wire bytes are byte-identical to pre-v25 for them.
|
||||
Special nonces that still mean something:
|
||||
|
||||
`ConvertToNodeInfoThin(lite)` is the gradient-sync emitter (no position/telemetry).
|
||||
- `SPECIAL_NONCE_ONLY_CONFIG` (69420) — skip node sync entirely, just config.
|
||||
- `SPECIAL_NONCE_ONLY_NODES` (69421) — skip config segments, jump straight to `STATE_SEND_OWN_NODEINFO`. Still gets the post-COMPLETE_ID replay drain.
|
||||
|
||||
There are no other reserved nonces; everything else is a fresh random `want_config_id` from the client.
|
||||
|
||||
### v24 → v25 migration
|
||||
|
||||
|
||||
@@ -56,3 +56,9 @@ CMakeLists.txt
|
||||
.python3
|
||||
.claude/scheduled_tasks.lock
|
||||
userPrefs.jsonc.mcp-session-bak
|
||||
|
||||
# Fake-NodeDB fixture pipeline (bin/regen-fake-nodedbs.sh)
|
||||
# JSONL seeds are committed (test/fixtures/nodedb/seed_v25_*.jsonl);
|
||||
# compiled .proto outputs are ephemeral build artifacts.
|
||||
build/fixtures/
|
||||
bin/_generated/
|
||||
|
||||
+8
-1
@@ -11,7 +11,7 @@ lint:
|
||||
- checkov@3.2.528
|
||||
- renovate@43.150.0
|
||||
- prettier@3.8.3
|
||||
- trufflehog@3.95.2
|
||||
- trufflehog@3.95.3
|
||||
- yamllint@1.38.0
|
||||
- bandit@1.9.4
|
||||
- trivy@0.70.0
|
||||
@@ -34,6 +34,13 @@ lint:
|
||||
- linters: [ALL]
|
||||
paths:
|
||||
- bin/**
|
||||
# Fake-NodeDB fixture JSONL files contain deterministic synthetic
|
||||
# public_key_hex (64-char hex) values that gitleaks misidentifies as
|
||||
# generic-api-key. These are not secrets — they're test fixtures
|
||||
# produced by bin/gen-fake-nodedb-seed.py with a fixed RNG seed.
|
||||
- linters: [gitleaks]
|
||||
paths:
|
||||
- test/fixtures/nodedb/seed_v25_*.jsonl
|
||||
runtimes:
|
||||
enabled:
|
||||
- python@3.14.4
|
||||
|
||||
Vendored
+4
-4
@@ -1,10 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
"Jason2866.esp-decoder",
|
||||
"pioarduino.pioarduino-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
"ms-vscode.cpptools-extension-pack",
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Post-process protoc-generated Python files to live under a local namespace.
|
||||
|
||||
Called by bin/regen-py-protos.sh. Walks the generated *_pb2.py files in the
|
||||
target directory and rewrites every `meshtastic` reference (imports, dotted
|
||||
attribute access) to use the new namespace (e.g., `meshtastic_v25`).
|
||||
|
||||
Why: the .proto files declare `package meshtastic;`, so protoc emits
|
||||
`from meshtastic import mesh_pb2 as ...` lines. That would shadow the PyPI
|
||||
`meshtastic` package which other parts of the mcp-server depend on. Renaming
|
||||
to a local namespace keeps both available.
|
||||
|
||||
Usage:
|
||||
_rewrite_proto_namespace.py <generated_dir> <new_namespace>
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def rewrite(dir_path: pathlib.Path, new_ns: str) -> int:
|
||||
# Standard protoc import forms:
|
||||
# from meshtastic.X_pb2 import ... (rare, for direct symbol pulls)
|
||||
# from meshtastic import X_pb2 as ... (common, the cross-file ref)
|
||||
# import meshtastic.X_pb2 (also possible)
|
||||
pattern_dotted_from = re.compile(r"^from meshtastic\.", re.MULTILINE)
|
||||
pattern_bare_from = re.compile(r"^from meshtastic import ", re.MULTILINE)
|
||||
pattern_dotted_import = re.compile(r"^import meshtastic\.", re.MULTILINE)
|
||||
|
||||
count = 0
|
||||
for p in dir_path.glob("*.py"):
|
||||
text = p.read_text(encoding="utf-8")
|
||||
new = pattern_dotted_from.sub(f"from {new_ns}.", text)
|
||||
new = pattern_bare_from.sub(f"from {new_ns} import ", new)
|
||||
new = pattern_dotted_import.sub(f"import {new_ns}.", new)
|
||||
# NOTE: we deliberately leave `meshtastic/X.proto` source-filename
|
||||
# references inside descriptor strings alone. The descriptor pool is
|
||||
# keyed by source filename (independent of Python package layout), so
|
||||
# those don't collide with the PyPI package's descriptors.
|
||||
if new != text:
|
||||
p.write_text(new, encoding="utf-8")
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if len(argv) != 2:
|
||||
print("usage: _rewrite_proto_namespace.py <generated_dir> <new_namespace>", file=sys.stderr)
|
||||
return 2
|
||||
dir_path = pathlib.Path(argv[0])
|
||||
new_ns = argv[1]
|
||||
if not dir_path.is_dir():
|
||||
print(f"directory not found: {dir_path}", file=sys.stderr)
|
||||
return 2
|
||||
n = rewrite(dir_path, new_ns)
|
||||
print(f"rewrote {n} file(s) in {dir_path} → namespace {new_ns}", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
+1
-1
@@ -38,4 +38,4 @@ cp bin/device-install.* $OUTDIR/
|
||||
cp bin/device-update.* $OUTDIR/
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Station G3 motherboard with a Raspberry Pi Zero 2W as the MCU daughterboard.
|
||||
# Verify spidev / I2C device paths for your OS — they may differ.
|
||||
Meta:
|
||||
name: Station G3
|
||||
support: community
|
||||
compatible:
|
||||
- raspberry-pi
|
||||
|
||||
Lora:
|
||||
Module: sx1262
|
||||
IRQ: 22 # BCM pin — wiki spec
|
||||
Reset: 16 # BCM pin — wiki spec
|
||||
Busy: 24 # BCM pin — wiki spec
|
||||
# CS: 8 # BCM 8 = SPI0 CE0 (default); uncomment only to override
|
||||
DIO2_AS_RF_SWITCH: true
|
||||
DIO3_TCXO_VOLTAGE: true
|
||||
spidev: spidev0.0
|
||||
# SX126X_MAX_POWER: 19 # matches Station G2 firmware cap; raise carefully per PA jumper mode
|
||||
Executable
+439
@@ -0,0 +1,439 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic seed-data generator for the fake NodeDB fixture pipeline.
|
||||
|
||||
Writes a JSONL file describing N fake-but-realistic Meshtastic peers.
|
||||
The output is hand-editable and committed; a sibling compile step
|
||||
(bin/seed-json-to-proto.py) turns it into a binary `meshtastic_NodeDatabase`
|
||||
v25 protobuf with fresh "now-relative" timestamps.
|
||||
|
||||
Determinism contract:
|
||||
Same --seed -> byte-identical JSONL output, regardless of wall clock.
|
||||
All timestamps are stored as `*_offset_sec` (seconds before "now"); the
|
||||
compile step resolves them to absolute epochs at compile time.
|
||||
|
||||
Structural fields covered:
|
||||
* NodeInfoLite header: num, long_name, short_name, hw_model, role,
|
||||
public_key, snr, channel, hops_away, next_hop, bitfield flags
|
||||
* PositionLite: lat/long Gaussian around --centroid, altitude, source
|
||||
* DeviceMetrics: battery/voltage/util/uptime
|
||||
* EnvironmentMetrics: temp/humidity/pressure/iaq
|
||||
* StatusMessage: error_code (usually zero)
|
||||
|
||||
Active-board allow-list:
|
||||
hw_model values are restricted to the intersection of
|
||||
(a) variants with `custom_meshtastic_support_level = 1` in
|
||||
variants/*/*/platformio.ini, AND
|
||||
(b) values present in the `HardwareModel` enum in mesh.proto.
|
||||
See HW_MODEL_WEIGHTS below. Deprecated boards (legacy TLORA / Heltec V1-2 /
|
||||
classic TBEAM / TBEAM_V0P7 / Nano G1 / etc.) and fuzzer-only sentinels
|
||||
(PORTDUINO, ANDROID_SIM, DIY_V1, ...) are excluded.
|
||||
|
||||
Active-role allow-list:
|
||||
Excludes ROUTER_CLIENT (deprecated v2.3.15) and REPEATER (deprecated v2.7.11).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as _dt
|
||||
import json
|
||||
import math
|
||||
import pathlib
|
||||
import random
|
||||
import sys
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Active-board allow-list (intersection of tier-1 variants + HardwareModel enum).
|
||||
# Refresh by running:
|
||||
# for f in $(find variants -name 'platformio.ini' | xargs grep -lE 'custom_meshtastic_support_level = 1'); do
|
||||
# grep custom_meshtastic_hw_model_slug $f | awk -F= '{print $2}' | tr -d ' ';
|
||||
# done | sort -u | comm -12 - <(python3 -c "from meshtastic.protobuf.mesh_pb2 import HardwareModel; print('\\n'.join(HardwareModel.keys()))" | sort)
|
||||
# --------------------------------------------------------------------------
|
||||
HW_MODEL_WEIGHTS: dict[str, float] = {
|
||||
"HELTEC_V3": 14.0,
|
||||
"T_DECK": 9.0,
|
||||
"HELTEC_V4": 8.0,
|
||||
"RAK4631": 8.0,
|
||||
"HELTEC_MESH_POCKET": 6.0,
|
||||
"TRACKER_T1000_E": 5.0,
|
||||
"HELTEC_MESH_NODE_T114": 5.0,
|
||||
"T_DECK_PRO": 5.0,
|
||||
"LILYGO_TBEAM_S3_CORE": 4.0,
|
||||
"HELTEC_WIRELESS_PAPER": 4.0,
|
||||
"HELTEC_WSL_V3": 3.0,
|
||||
"T_ECHO": 3.0,
|
||||
"HELTEC_WIRELESS_TRACKER": 3.0,
|
||||
"HELTEC_WIRELESS_TRACKER_V2": 2.0,
|
||||
"HELTEC_VISION_MASTER_E290": 2.0,
|
||||
"HELTEC_MESH_SOLAR": 2.0,
|
||||
"SEEED_WIO_TRACKER_L1": 2.0,
|
||||
"T_LORA_PAGER": 1.5,
|
||||
"HELTEC_VISION_MASTER_E213": 1.5,
|
||||
"T_ECHO_PLUS": 1.0,
|
||||
"MUZI_BASE": 1.0,
|
||||
"WISMESH_TAP_V2": 1.0,
|
||||
"THINKNODE_M2": 1.0,
|
||||
"THINKNODE_M5": 1.0,
|
||||
"TLORA_T3_S3": 1.0,
|
||||
# Long tail (uniform low weight across remaining tier-1 boards):
|
||||
"HELTEC_V4_R8": 0.3,
|
||||
"HELTEC_VISION_MASTER_T190": 0.3,
|
||||
"HELTEC_HT62": 0.3,
|
||||
"HELTEC_MESH_NODE_T096": 0.3,
|
||||
"M5STACK_C6L": 0.3,
|
||||
"MINI_EPAPER_S3": 0.3,
|
||||
"MUZI_R1_NEO": 0.3,
|
||||
"NOMADSTAR_METEOR_PRO": 0.3,
|
||||
"RAK3312": 0.3,
|
||||
"RAK3401": 0.3,
|
||||
"SEEED_SOLAR_NODE": 0.3,
|
||||
"SEEED_WIO_TRACKER_L1_EINK": 0.3,
|
||||
"SENSECAP_INDICATOR": 0.3,
|
||||
"TBEAM_1_WATT": 0.3,
|
||||
"THINKNODE_M1": 0.3,
|
||||
"THINKNODE_M3": 0.3,
|
||||
"THINKNODE_M6": 0.3,
|
||||
"T_ECHO_LITE": 0.3,
|
||||
"WISMESH_TAG": 0.3,
|
||||
"WISMESH_TAP": 0.3,
|
||||
"XIAO_NRF52_KIT": 0.3,
|
||||
"CROWPANEL": 0.3,
|
||||
}
|
||||
|
||||
# Non-deprecated roles only.
|
||||
ROLE_WEIGHTS: dict[str, float] = {
|
||||
"CLIENT": 75.0,
|
||||
"CLIENT_MUTE": 5.0,
|
||||
"ROUTER": 7.0,
|
||||
"TRACKER": 3.0,
|
||||
"SENSOR": 2.0,
|
||||
"CLIENT_HIDDEN": 2.0,
|
||||
"ROUTER_LATE": 2.0,
|
||||
"CLIENT_BASE": 2.0,
|
||||
"TAK": 1.0,
|
||||
"TAK_TRACKER": 0.5,
|
||||
"LOST_AND_FOUND": 0.5,
|
||||
}
|
||||
|
||||
# Name pools — 60 firsts × 60 lasts = 3600 combinations.
|
||||
FIRSTS = [
|
||||
"Quick", "Brave", "Silent", "Wild", "Lone", "Bright", "Red", "Blue",
|
||||
"Green", "Black", "White", "Iron", "Steel", "Copper", "Silver", "Gold",
|
||||
"Stone", "River", "Forest", "Mountain", "Canyon", "Desert", "Storm", "Sky",
|
||||
"Solar", "Lunar", "Dawn", "Dusk", "Misty", "Frosty", "Sunny", "Shady",
|
||||
"Happy", "Sleepy", "Drowsy", "Sneaky", "Sharp", "Smooth", "Rough", "Loud",
|
||||
"Soft", "Slow", "Fast", "Tall", "Short", "Old", "New", "Tiny",
|
||||
"Giant", "Hidden", "Lost", "Found", "Wandering", "Roving", "Drifting", "Floating",
|
||||
"Burning", "Frozen", "Whispering", "Howling",
|
||||
]
|
||||
LASTS = [
|
||||
"Phoenix", "Lion", "Bear", "Wolf", "Hawk", "Eagle", "Fox", "Lynx",
|
||||
"Cougar", "Coyote", "Raven", "Owl", "Crow", "Falcon", "Heron", "Crane",
|
||||
"Otter", "Badger", "Bison", "Elk", "Moose", "Stag", "Doe", "Hare",
|
||||
"Marmot", "Mole", "Beaver", "Squirrel", "Mustang", "Bronco", "Pony", "Colt",
|
||||
"Cobra", "Viper", "Mamba", "Adder", "Gecko", "Iguana", "Tortoise", "Turtle",
|
||||
"Salmon", "Trout", "Bass", "Pike", "Shark", "Whale", "Dolphin", "Seal",
|
||||
"Cactus", "Yucca", "Sage", "Juniper", "Pine", "Cedar", "Aspen", "Oak",
|
||||
"Bluff", "Mesa", "Arroyo", "Ridge",
|
||||
]
|
||||
|
||||
# Brief callsign pool for licensed-looking suffixes.
|
||||
CALLSIGN_PREFIXES = ["KX", "WD", "N5", "KE", "AB", "W5", "K1", "KQ", "AE", "NM"]
|
||||
|
||||
# Only emojis that fit in 4 UTF-8 bytes (no variation selectors). short_name's
|
||||
# nanopb max_size:5 (incl. NUL) limits content to 4 bytes. ❄️ / ☀️ would be
|
||||
# 6 bytes due to U+FE0F variation selector — explicitly excluded.
|
||||
EMOJI_SHORTNAMES = ["🦊", "🐺", "🦅", "🐢", "🌵", "🔥", "🌙",
|
||||
"🌊", "🗻", "🌲", "🦌", "🐝", "🦂", "🦉",
|
||||
"🦇", "🦋"]
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
NUM_RESERVED = 4 # firmware reserves 0..3 (per NodeDB constants)
|
||||
NUM_MAX_EXCLUSIVE = 0x80000000 # restrict to positive int32 range for readability
|
||||
|
||||
|
||||
def _weighted_choice(rng: random.Random, weights: dict[str, float]) -> str:
|
||||
"""Deterministic weighted pick. Uses sorted keys so dict order is fixed."""
|
||||
keys = sorted(weights.keys())
|
||||
totals = [weights[k] for k in keys]
|
||||
return rng.choices(keys, weights=totals, k=1)[0]
|
||||
|
||||
|
||||
def _gen_long_name(rng: random.Random, is_licensed: bool) -> str:
|
||||
base = f"{rng.choice(FIRSTS)} {rng.choice(LASTS)}"
|
||||
if is_licensed:
|
||||
prefix = rng.choice(CALLSIGN_PREFIXES)
|
||||
# Two trailing alpha chars after the digit; keep within 25 - len(base) - 1
|
||||
suffix = f" {prefix}{rng.randint(0,9)}{rng.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ')}{rng.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ')}"
|
||||
# nanopb max_size:25 means C string fits 24 bytes + NUL.
|
||||
if len(base) + len(suffix) <= 24:
|
||||
base = base + suffix
|
||||
# Hard cap to 24 chars (nanopb max_size:25 minus NUL).
|
||||
return base[:24]
|
||||
|
||||
|
||||
def _gen_short_name(rng: random.Random, long_name: str) -> str:
|
||||
# 10% emoji-only short_name
|
||||
if rng.random() < 0.10:
|
||||
return rng.choice(EMOJI_SHORTNAMES)
|
||||
first_char = long_name[0].upper() if long_name else "X"
|
||||
alphanums = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
return first_char + "".join(rng.choices(alphanums, k=3))
|
||||
|
||||
|
||||
def _gen_hops_away(rng: random.Random) -> int:
|
||||
# Geometric-ish: 0→55%, 1→25%, 2→12%, 3→5%, 4→2%, 5+→1%
|
||||
r = rng.random()
|
||||
if r < 0.55:
|
||||
return 0
|
||||
if r < 0.80:
|
||||
return 1
|
||||
if r < 0.92:
|
||||
return 2
|
||||
if r < 0.97:
|
||||
return 3
|
||||
if r < 0.99:
|
||||
return 4
|
||||
return rng.randint(5, 7)
|
||||
|
||||
|
||||
def _gen_position(
|
||||
rng: random.Random,
|
||||
centroid_lat: float,
|
||||
centroid_lon: float,
|
||||
spread_km: float,
|
||||
last_heard_offset_sec: int,
|
||||
) -> dict:
|
||||
# 1 deg ≈ 111 km at the equator; we use this as a flat approximation.
|
||||
lat = centroid_lat + rng.gauss(0.0, spread_km / 111.0)
|
||||
lon = centroid_lon + rng.gauss(0.0, spread_km / 111.0)
|
||||
altitude = max(0, round(rng.gauss(1376.0, 250.0))) # T or C valley floor + relief
|
||||
# Position was reported up to 300s before last_heard.
|
||||
time_offset_sec = last_heard_offset_sec + rng.randint(0, 300)
|
||||
return {
|
||||
"latitude": round(lat, 6),
|
||||
"longitude": round(lon, 6),
|
||||
"altitude": altitude,
|
||||
"time_offset_sec": time_offset_sec,
|
||||
"location_source": "LOC_INTERNAL",
|
||||
}
|
||||
|
||||
|
||||
def _gen_telemetry(rng: random.Random) -> dict:
|
||||
# 5% plugged-in (battery_level == 101); rest uniform [10..100].
|
||||
if rng.random() < 0.05:
|
||||
battery_level = 101
|
||||
voltage = 4.20
|
||||
else:
|
||||
battery_level = rng.randint(10, 100)
|
||||
voltage = round(3.3 + (battery_level / 100.0) * 0.9, 3)
|
||||
# Beta distributions for low/right-skewed metrics; randomly draw via gammavariate.
|
||||
def _beta(a: float, b: float) -> float:
|
||||
x = rng.gammavariate(a, 1.0)
|
||||
y = rng.gammavariate(b, 1.0)
|
||||
return x / (x + y)
|
||||
channel_utilization = round(_beta(2.0, 15.0) * 100.0, 2)
|
||||
air_util_tx = round(_beta(1.5, 20.0) * 10.0, 3)
|
||||
uptime_seconds = int(rng.expovariate(1.0 / 86400.0))
|
||||
return {
|
||||
"battery_level": battery_level,
|
||||
"voltage": voltage,
|
||||
"channel_utilization": channel_utilization,
|
||||
"air_util_tx": air_util_tx,
|
||||
"uptime_seconds": uptime_seconds,
|
||||
}
|
||||
|
||||
|
||||
def _gen_environment(rng: random.Random) -> dict:
|
||||
return {
|
||||
"temperature": round(rng.gauss(22.0, 8.0), 2),
|
||||
"relative_humidity": round(min(100.0, max(0.0, rng.gauss(55.0, 20.0))), 2),
|
||||
"barometric_pressure": round(rng.gauss(1013.0, 8.0), 2),
|
||||
"iaq": int(min(500, max(0, round(rng.gauss(50.0, 30.0))))),
|
||||
}
|
||||
|
||||
|
||||
def _gen_status(rng: random.Random) -> dict:
|
||||
# `StatusMessage` (mesh.proto:1445) has a single free-form `string status`.
|
||||
# Most peers report a healthy short status; occasional alert string.
|
||||
healthy = ["OK", "online", "active", "running", "ready", "nominal"]
|
||||
alert = ["low-batt", "no-gps", "weak-signal", "rebooted", "offline-soon"]
|
||||
if rng.random() < 0.92:
|
||||
return {"status": rng.choice(healthy)}
|
||||
return {"status": rng.choice(alert)}
|
||||
|
||||
|
||||
def _gen_node(
|
||||
rng: random.Random,
|
||||
num: int,
|
||||
centroid_lat: float,
|
||||
centroid_lon: float,
|
||||
spread_km: float,
|
||||
coverage: dict[str, float],
|
||||
last_heard_mean_sec: int,
|
||||
last_heard_max_sec: int,
|
||||
) -> dict:
|
||||
is_licensed = rng.random() < 0.05
|
||||
long_name = _gen_long_name(rng, is_licensed)
|
||||
short_name = _gen_short_name(rng, long_name)
|
||||
hw_model = _weighted_choice(rng, HW_MODEL_WEIGHTS)
|
||||
role = _weighted_choice(rng, ROLE_WEIGHTS)
|
||||
has_public_key = rng.random() < 0.92
|
||||
public_key_hex = (
|
||||
"".join(f"{rng.randint(0,255):02x}" for _ in range(32)) if has_public_key else ""
|
||||
)
|
||||
snr = round(max(-20.0, min(12.0, rng.gauss(6.0, 4.0))), 2)
|
||||
channel = 0 if rng.random() < 0.90 else rng.randint(1, 7)
|
||||
hops_away = _gen_hops_away(rng)
|
||||
next_hop = rng.randint(0, 255) if hops_away > 0 else 0
|
||||
last_heard_offset_sec = int(min(rng.expovariate(1.0 / last_heard_mean_sec), last_heard_max_sec))
|
||||
|
||||
bitfield = {
|
||||
"has_user": True,
|
||||
"is_favorite": rng.random() < 0.08,
|
||||
"is_muted": rng.random() < 0.03,
|
||||
"via_mqtt": rng.random() < 0.12,
|
||||
"is_ignored": rng.random() < 0.01,
|
||||
"is_licensed": is_licensed,
|
||||
"has_is_unmessagable": True,
|
||||
"is_unmessagable": rng.random() < 0.02,
|
||||
"is_key_manually_verified": rng.random() < 0.04,
|
||||
}
|
||||
|
||||
node: dict = {
|
||||
"num": f"0x{num:08x}",
|
||||
"long_name": long_name,
|
||||
"short_name": short_name,
|
||||
"hw_model": hw_model,
|
||||
"role": role,
|
||||
"public_key_hex": public_key_hex,
|
||||
"snr": snr,
|
||||
"channel": channel,
|
||||
"hops_away": hops_away,
|
||||
"next_hop": next_hop,
|
||||
"last_heard_offset_sec": last_heard_offset_sec,
|
||||
"bitfield": bitfield,
|
||||
"position": (
|
||||
_gen_position(rng, centroid_lat, centroid_lon, spread_km, last_heard_offset_sec)
|
||||
if rng.random() < coverage["position"]
|
||||
else None
|
||||
),
|
||||
"telemetry": _gen_telemetry(rng) if rng.random() < coverage["telemetry"] else None,
|
||||
"environment": _gen_environment(rng) if rng.random() < coverage["environment"] else None,
|
||||
"status": _gen_status(rng) if rng.random() < coverage["status"] else None,
|
||||
}
|
||||
return node
|
||||
|
||||
|
||||
def _parse_my_node_num(s: str | None) -> int | None:
|
||||
if s is None:
|
||||
return None
|
||||
s = s.strip()
|
||||
if s.startswith("0x") or s.startswith("0X"):
|
||||
return int(s, 16)
|
||||
return int(s)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
p = argparse.ArgumentParser(
|
||||
description="Deterministic JSONL seed for the fake NodeDB fixture.",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p.add_argument("--count", type=int, required=True, help="Number of fake nodes to emit.")
|
||||
p.add_argument("--seed", type=int, required=True, help="Deterministic seed.")
|
||||
p.add_argument("--out", required=True, help="Output JSONL path.")
|
||||
p.add_argument(
|
||||
"--centroid",
|
||||
default="33.1284,-107.2528",
|
||||
help="LAT,LON centroid (default: Truth or Consequences, NM).",
|
||||
)
|
||||
p.add_argument("--spread-km", type=float, default=60.0, help="Gaussian std-dev in km.")
|
||||
p.add_argument("--position-coverage", type=float, default=0.85)
|
||||
p.add_argument("--telemetry-coverage", type=float, default=0.70)
|
||||
p.add_argument("--environment-coverage", type=float, default=0.25)
|
||||
p.add_argument("--status-coverage", type=float, default=0.40)
|
||||
p.add_argument("--my-node-num", default=None, help="Exclude this NodeNum from generated set (hex or dec).")
|
||||
p.add_argument("--last-heard-mean-sec", type=int, default=3600)
|
||||
p.add_argument("--last-heard-max-sec", type=int, default=7 * 86400)
|
||||
args = p.parse_args(argv)
|
||||
|
||||
if args.count <= 0:
|
||||
print("--count must be positive", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
try:
|
||||
centroid_lat, centroid_lon = (float(s) for s in args.centroid.split(","))
|
||||
except ValueError:
|
||||
print(f"--centroid must be LAT,LON; got {args.centroid!r}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
my_node_num = _parse_my_node_num(args.my_node_num)
|
||||
|
||||
rng = random.Random(args.seed)
|
||||
|
||||
# 1) Generate a unique deterministic set of NodeNums.
|
||||
nums: set[int] = set()
|
||||
while len(nums) < args.count:
|
||||
n = rng.randrange(NUM_RESERVED, NUM_MAX_EXCLUSIVE)
|
||||
if my_node_num is not None and n == my_node_num:
|
||||
continue
|
||||
nums.add(n)
|
||||
ordered_nums = sorted(nums) # sort to fix output order independent of set hash
|
||||
|
||||
# 2) Per-node generation (in num order, single RNG continues).
|
||||
coverage = {
|
||||
"position": args.position_coverage,
|
||||
"telemetry": args.telemetry_coverage,
|
||||
"environment": args.environment_coverage,
|
||||
"status": args.status_coverage,
|
||||
}
|
||||
nodes = [
|
||||
_gen_node(
|
||||
rng,
|
||||
n,
|
||||
centroid_lat,
|
||||
centroid_lon,
|
||||
args.spread_km,
|
||||
coverage,
|
||||
args.last_heard_mean_sec,
|
||||
args.last_heard_max_sec,
|
||||
)
|
||||
for n in ordered_nums
|
||||
]
|
||||
|
||||
# 3) Write JSONL.
|
||||
out_path = pathlib.Path(args.out)
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
# `generated_at_iso` is informational; it does NOT affect determinism because
|
||||
# we derive it from the seed, not from wall clock. (Same seed -> same string.)
|
||||
generated_at = _dt.datetime.fromtimestamp(args.seed, tz=_dt.timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
meta = {
|
||||
"_meta": {
|
||||
"version": 25,
|
||||
"seed": args.seed,
|
||||
"count": args.count,
|
||||
"centroid": [centroid_lat, centroid_lon],
|
||||
"spread_km": args.spread_km,
|
||||
"generated_at_iso": generated_at,
|
||||
"my_node_num_excluded": (None if my_node_num is None else f"0x{my_node_num:08x}"),
|
||||
"coverage": coverage,
|
||||
"last_heard_mean_sec": args.last_heard_mean_sec,
|
||||
"last_heard_max_sec": args.last_heard_max_sec,
|
||||
}
|
||||
}
|
||||
with out_path.open("w", encoding="utf-8") as f:
|
||||
# `ensure_ascii=False` so emoji short_names survive. `sort_keys=True` for
|
||||
# determinism (insertion order varies by Python version otherwise).
|
||||
f.write(json.dumps(meta, ensure_ascii=False, sort_keys=True) + "\n")
|
||||
for node in nodes:
|
||||
f.write(json.dumps(node, ensure_ascii=False, sort_keys=True) + "\n")
|
||||
|
||||
print(f"wrote {args.count} nodes to {out_path} ({out_path.stat().st_size} bytes)", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
@@ -293,9 +293,12 @@ if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||
board_arch = infer_architecture(env.BoardConfig())
|
||||
should_skip_manifest = board_arch is None
|
||||
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
mtjson_deps = [] if should_skip_manifest else ["buildprog"]
|
||||
if not should_skip_manifest and platform.name == "espressif32":
|
||||
# Most platforms can generate the manifest as part of the default 'buildprog' target.
|
||||
# Typically this passes success/failure properly.
|
||||
mtjson_deps = ["buildprog"]
|
||||
if platform.name == "espressif32":
|
||||
# On ESP32, we need to explicitly depend upon the binary to prevent fake-success upon failure.
|
||||
mtjson_deps = ["$BUILD_DIR/${PROGNAME}.bin"]
|
||||
# Build littlefs image as part of mtjson target
|
||||
# Equivalent to `pio run -t buildfs`
|
||||
target_lfs = env.DataToBin(
|
||||
@@ -309,7 +312,8 @@ if should_skip_manifest:
|
||||
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
dependencies=mtjson_deps,
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
dependencies=[],
|
||||
actions=[skip_manifest],
|
||||
title="Meshtastic Manifest (skipped)",
|
||||
description="mtjson generation is skipped for native environments",
|
||||
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerate the fake-NodeDB fixtures: produces 250 / 500 / 1000 / 2000-node
|
||||
# JSONL seed files + their compiled v25 protobufs.
|
||||
#
|
||||
# Layout:
|
||||
# test/fixtures/nodedb/seed_v25_<N>.jsonl — COMMITTED, hand-editable.
|
||||
# build/fixtures/nodedb/nodes_v25_<N>.proto — .gitignored, build artifact.
|
||||
# Drop into /prefs/nodes.proto.
|
||||
#
|
||||
# Daily use: ./bin/regen-fake-nodedbs.sh
|
||||
# - Recompiles protos from committed seeds (fresh wall-clock timestamps).
|
||||
# Intentional seed bump: REGEN_SEEDS=yes ./bin/regen-fake-nodedbs.sh
|
||||
# - Overwrites the committed JSONL files with freshly-seeded data.
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# 1) Make sure the Python protobuf bindings exist (in-tree generation; .gitignored).
|
||||
if [[ ! -d bin/_generated/meshtastic ]]; then
|
||||
echo "regenerating Python protobuf bindings (one-time)..."
|
||||
./bin/regen-py-protos.sh
|
||||
fi
|
||||
|
||||
# 2) Pick a Python interpreter that has the meshtastic deps installed.
|
||||
# Prefer the mcp-server venv (most likely to be set up by the operator).
|
||||
PY="python3"
|
||||
for cand in mcp-server/.venv/bin/python3 .venv/bin/python3; do
|
||||
if [[ -x "$cand" ]]; then
|
||||
PY="$cand"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# 3) Pinned seeds per size — bump only when you intentionally want different
|
||||
# structural data committed. Parallel arrays so the script works on
|
||||
# macOS bash 3.2 (no `declare -A`).
|
||||
SIZES=(250 500 1000 2000)
|
||||
SEEDS=(20260511 20260512 20260513 20260514)
|
||||
|
||||
REGEN_SEEDS="${REGEN_SEEDS:-no}"
|
||||
|
||||
mkdir -p build/fixtures/nodedb test/fixtures/nodedb
|
||||
|
||||
for i in 0 1 2 3; do
|
||||
n="${SIZES[$i]}"
|
||||
seed="${SEEDS[$i]}"
|
||||
jsonl=$(printf "test/fixtures/nodedb/seed_v25_%04d.jsonl" "$n")
|
||||
proto=$(printf "build/fixtures/nodedb/nodes_v25_%04d.proto" "$n")
|
||||
|
||||
if [[ "$REGEN_SEEDS" == "yes" || ! -f "$jsonl" ]]; then
|
||||
$PY bin/gen-fake-nodedb-seed.py \
|
||||
--count "$n" \
|
||||
--seed "$seed" \
|
||||
--out "$jsonl" \
|
||||
--centroid 33.1284,-107.2528 \
|
||||
--spread-km 60 \
|
||||
--position-coverage 0.85 \
|
||||
--telemetry-coverage 0.70 \
|
||||
--environment-coverage 0.25 \
|
||||
--status-coverage 0.40
|
||||
echo " seed: $jsonl ($(wc -c < "$jsonl") bytes)"
|
||||
fi
|
||||
|
||||
$PY bin/seed-json-to-proto.py --in "$jsonl" --out "$proto"
|
||||
echo " proto: $proto ($(wc -c < "$proto") bytes)"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Done. To load on Portduino native:"
|
||||
echo " cp build/fixtures/nodedb/nodes_v25_1000.proto ~/.portduino/default/prefs/nodes.proto"
|
||||
echo ""
|
||||
echo "To push to a hardware device:"
|
||||
echo " Use the mcp-server tool: push_fake_nodedb(size=1000, target=\"hardware\", port=\"/dev/cu.usbmodemXXXX\", confirm=True)"
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerate Python protobuf bindings from the in-tree `protobufs/` submodule
|
||||
# into `bin/_generated/`. Called by bin/regen-fake-nodedbs.sh; also useful as
|
||||
# a standalone refresh after any change to a .proto file.
|
||||
#
|
||||
# Output is .gitignored — bindings are a build artifact.
|
||||
#
|
||||
# Namespace rewrite:
|
||||
# The .proto files declare `package meshtastic;`, which makes protoc emit
|
||||
# imports like `from meshtastic import mesh_pb2`. That conflicts with the
|
||||
# PyPI `meshtastic` package (which the mcp-server relies on for its
|
||||
# SerialInterface/BLEInterface transport). We post-process the generated
|
||||
# files to live under `meshtastic_v25` instead — both the directory layout
|
||||
# and all internal imports — so they coexist cleanly with the PyPI package.
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if ! command -v protoc >/dev/null 2>&1; then
|
||||
echo "ERROR: protoc not found in PATH." >&2
|
||||
echo " macOS: brew install protobuf" >&2
|
||||
echo " Ubuntu/Debian: apt install protobuf-compiler" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUT=bin/_generated
|
||||
LOCAL_NS=meshtastic_v25
|
||||
|
||||
rm -rf "$OUT"
|
||||
mkdir -p "$OUT"
|
||||
|
||||
# 1) Generate from the in-tree protos. nanopb.proto first so its descriptor
|
||||
# is available for the [(nanopb).*] options on other messages.
|
||||
protoc \
|
||||
--proto_path=protobufs \
|
||||
--python_out="$OUT" \
|
||||
protobufs/nanopb.proto \
|
||||
protobufs/meshtastic/*.proto
|
||||
|
||||
# 2) Move the generated `meshtastic/` directory to `meshtastic_v25/`.
|
||||
mv "$OUT/meshtastic" "$OUT/$LOCAL_NS"
|
||||
|
||||
# 3) Rewrite internal imports: any reference to `meshtastic.X_pb2` or
|
||||
# `from meshtastic import X_pb2` becomes `meshtastic_v25.*`.
|
||||
python3 bin/_rewrite_proto_namespace.py "$OUT/$LOCAL_NS" "$LOCAL_NS"
|
||||
|
||||
# 4) Make the package importable.
|
||||
touch "$OUT/__init__.py"
|
||||
touch "$OUT/$LOCAL_NS/__init__.py"
|
||||
|
||||
echo "regenerated Python protobuf bindings -> $OUT/$LOCAL_NS/ (namespace: $LOCAL_NS)" >&2
|
||||
Executable
+342
@@ -0,0 +1,342 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compile a committed seed JSONL into a binary meshtastic_NodeDatabase v25 proto.
|
||||
|
||||
The input is produced by `bin/gen-fake-nodedb-seed.py`. Timestamps in the JSONL
|
||||
are stored as `*_offset_sec` (seconds before "now"); this script resolves them
|
||||
to absolute epochs using `--now-epoch` (default: current wall clock).
|
||||
|
||||
Output is a raw `pb_encode`-compatible binary that can be dropped at
|
||||
`/prefs/nodes.proto` on the device (Portduino prefs dir or hardware via
|
||||
XModem) and loaded by `NodeDB::loadFromDisk` at boot.
|
||||
|
||||
Wire format reference:
|
||||
protobufs/meshtastic/deviceonly.proto (NodeDatabase, NodeInfoLite, sat entries)
|
||||
src/mesh/NodeDB.h:467-484 (bitfield bit positions)
|
||||
src/mesh/NodeDB.cpp:1523-1524 (pb_decode entry point)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
# Prefer the in-tree generated Python protobuf bindings (bin/_generated/meshtastic_v25/)
|
||||
# because the firmware branch's protos (v25 NodeDatabase satellite arrays, slim
|
||||
# NodeInfoLite) are typically newer than what the PyPI `meshtastic` package
|
||||
# ships. Run `bin/regen-py-protos.sh` to (re)generate.
|
||||
#
|
||||
# Namespace note: the local bindings live under `meshtastic_v25` (NOT `meshtastic`)
|
||||
# to avoid shadowing the PyPI `meshtastic` package — bin/regen-py-protos.sh
|
||||
# post-processes the protoc output to rename the package.
|
||||
_HERE = pathlib.Path(__file__).resolve().parent
|
||||
_LOCAL_PROTO_DIR = _HERE / "_generated"
|
||||
if _LOCAL_PROTO_DIR.is_dir():
|
||||
sys.path.insert(0, str(_LOCAL_PROTO_DIR))
|
||||
|
||||
try:
|
||||
from meshtastic_v25.deviceonly_pb2 import ( # type: ignore[import-not-found]
|
||||
NodeDatabase,
|
||||
NodeInfoLite,
|
||||
NodePositionEntry,
|
||||
NodeTelemetryEntry,
|
||||
NodeEnvironmentEntry,
|
||||
NodeStatusEntry,
|
||||
PositionLite,
|
||||
)
|
||||
from meshtastic_v25.mesh_pb2 import HardwareModel, Position, StatusMessage # type: ignore[import-not-found]
|
||||
from meshtastic_v25.config_pb2 import Config # type: ignore[import-not-found]
|
||||
from meshtastic_v25.telemetry_pb2 import DeviceMetrics, EnvironmentMetrics # type: ignore[import-not-found]
|
||||
except ImportError as local_err:
|
||||
# Fall back to the PyPI package if in-tree bindings haven't been generated.
|
||||
# Will fail the v25 assertion below if the PyPI package predates the
|
||||
# satellite-DB schema, but at least gives a clear "run regen-py-protos.sh"
|
||||
# error message instead of an opaque ImportError.
|
||||
try:
|
||||
from meshtastic.protobuf.deviceonly_pb2 import (
|
||||
NodeDatabase,
|
||||
NodeInfoLite,
|
||||
NodePositionEntry,
|
||||
NodeTelemetryEntry,
|
||||
NodeEnvironmentEntry,
|
||||
NodeStatusEntry,
|
||||
PositionLite,
|
||||
)
|
||||
from meshtastic.protobuf.mesh_pb2 import HardwareModel, Position, StatusMessage
|
||||
from meshtastic.protobuf.config_pb2 import Config
|
||||
from meshtastic.protobuf.telemetry_pb2 import DeviceMetrics, EnvironmentMetrics
|
||||
except ImportError as pypi_err:
|
||||
print(
|
||||
"ERROR: could not import meshtastic protobuf bindings.\n"
|
||||
" In-tree generation: run `bin/regen-py-protos.sh` (requires protoc).\n"
|
||||
" PyPI fallback: `pip install meshtastic` (may lag firmware branch).\n"
|
||||
f" local error (meshtastic_v25): {local_err}\n"
|
||||
f" pypi error (meshtastic.protobuf): {pypi_err}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Fail loudly if bindings predate v25 (no satellite arrays).
|
||||
assert (
|
||||
hasattr(NodeDatabase, "DESCRIPTOR")
|
||||
and "positions" in NodeDatabase.DESCRIPTOR.fields_by_name
|
||||
), (
|
||||
"Loaded meshtastic bindings are older than v25 (NodeDatabase.positions missing). "
|
||||
"Run `bin/regen-py-protos.sh` against the in-tree protobufs/ submodule."
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bitfield bit positions (mirror src/mesh/NodeDB.h:467-484).
|
||||
# ---------------------------------------------------------------------------
|
||||
BIT_IS_KEY_MANUALLY_VERIFIED = 0
|
||||
BIT_IS_MUTED = 1
|
||||
BIT_VIA_MQTT = 2
|
||||
BIT_IS_FAVORITE = 3
|
||||
BIT_IS_IGNORED = 4
|
||||
BIT_HAS_USER = 5
|
||||
BIT_IS_LICENSED = 6
|
||||
BIT_IS_UNMESSAGABLE = 7
|
||||
BIT_HAS_IS_UNMESSAGABLE = 8
|
||||
|
||||
BITFIELD_LAYOUT = (
|
||||
# JSON key bit position
|
||||
("is_key_manually_verified", BIT_IS_KEY_MANUALLY_VERIFIED),
|
||||
("is_muted", BIT_IS_MUTED),
|
||||
("via_mqtt", BIT_VIA_MQTT),
|
||||
("is_favorite", BIT_IS_FAVORITE),
|
||||
("is_ignored", BIT_IS_IGNORED),
|
||||
("has_user", BIT_HAS_USER),
|
||||
("is_licensed", BIT_IS_LICENSED),
|
||||
("is_unmessagable", BIT_IS_UNMESSAGABLE),
|
||||
("has_is_unmessagable", BIT_HAS_IS_UNMESSAGABLE),
|
||||
)
|
||||
|
||||
|
||||
def _pack_bitfield(bf: dict[str, bool]) -> int:
|
||||
out = 0
|
||||
for key, shift in BITFIELD_LAYOUT:
|
||||
if bf.get(key, False):
|
||||
out |= (1 << shift)
|
||||
return out
|
||||
|
||||
|
||||
def _validate_node(node: dict[str, Any]) -> None:
|
||||
"""Friendly errors so hand-editors get clear feedback."""
|
||||
if "num" not in node or not isinstance(node["num"], str):
|
||||
raise ValueError(f"node missing/invalid 'num' (must be hex string): {node!r}")
|
||||
if "long_name" not in node:
|
||||
raise ValueError(f"node {node['num']}: missing 'long_name'")
|
||||
if len(node["long_name"]) > 24:
|
||||
raise ValueError(
|
||||
f"node {node['num']}: long_name {node['long_name']!r} is "
|
||||
f"{len(node['long_name'])} chars; max 24 (nanopb max_size:25 minus NUL)"
|
||||
)
|
||||
if "short_name" in node:
|
||||
# short_name max_size:5 (incl. NUL) → 4 bytes of content.
|
||||
# Char count is irrelevant — emojis with variation selectors (e.g. ❄️ = 6 B)
|
||||
# would slip past a `len(str) > 4` check. Always measure bytes.
|
||||
b = node["short_name"].encode("utf-8")
|
||||
if len(b) > 4:
|
||||
raise ValueError(
|
||||
f"node {node['num']}: short_name {node['short_name']!r} is "
|
||||
f"{len(b)} bytes UTF-8; max 4 (nanopb max_size:5 minus NUL)"
|
||||
)
|
||||
pk = node.get("public_key_hex", "")
|
||||
if pk and len(pk) != 64:
|
||||
raise ValueError(
|
||||
f"node {node['num']}: public_key_hex must be 64 hex chars or empty; "
|
||||
f"got {len(pk)} chars"
|
||||
)
|
||||
if pk:
|
||||
try:
|
||||
bytes.fromhex(pk)
|
||||
except ValueError as e:
|
||||
raise ValueError(f"node {node['num']}: public_key_hex is not valid hex: {e}")
|
||||
|
||||
|
||||
def _resolve_time(
|
||||
node: dict[str, Any],
|
||||
field_absolute: str,
|
||||
field_offset: str,
|
||||
now_epoch: int,
|
||||
) -> int:
|
||||
"""If `field_absolute` is set, use it; else compute `now_epoch - offset`."""
|
||||
if field_absolute in node and node[field_absolute] is not None:
|
||||
return int(node[field_absolute])
|
||||
offset = node.get(field_offset, 0)
|
||||
return max(0, int(now_epoch) - int(offset))
|
||||
|
||||
|
||||
def _build_node_info_lite(node: dict[str, Any], now_epoch: int) -> NodeInfoLite:
|
||||
_validate_node(node)
|
||||
info = NodeInfoLite()
|
||||
info.num = int(node["num"], 16) if isinstance(node["num"], str) else int(node["num"])
|
||||
info.long_name = node.get("long_name", "")
|
||||
info.short_name = node.get("short_name", "")
|
||||
# Enum lookups will raise ValueError on unknown names — that's exactly what we want.
|
||||
info.hw_model = HardwareModel.Value(node.get("hw_model", "UNSET"))
|
||||
info.role = Config.DeviceConfig.Role.Value(node.get("role", "CLIENT"))
|
||||
pk_hex = node.get("public_key_hex", "")
|
||||
if pk_hex:
|
||||
info.public_key = bytes.fromhex(pk_hex)
|
||||
info.snr = float(node.get("snr", 0.0))
|
||||
info.channel = int(node.get("channel", 0))
|
||||
if "hops_away" in node:
|
||||
# `optional uint32 hops_away = 9;` — in Python protobuf, assigning the
|
||||
# field implicitly sets HasField("hops_away") to True. No has_hops_away
|
||||
# setter exists (unlike the C++ nanopb-generated header).
|
||||
info.hops_away = int(node["hops_away"])
|
||||
info.next_hop = int(node.get("next_hop", 0))
|
||||
info.last_heard = _resolve_time(node, "last_heard", "last_heard_offset_sec", now_epoch)
|
||||
info.bitfield = _pack_bitfield(node.get("bitfield", {}))
|
||||
return info
|
||||
|
||||
|
||||
def _build_position_entry(num: int, pos: dict[str, Any], now_epoch: int) -> NodePositionEntry:
|
||||
entry = NodePositionEntry()
|
||||
entry.num = num
|
||||
pl = PositionLite()
|
||||
# Firmware stores lat/long as int32 in 1e-7 degrees.
|
||||
pl.latitude_i = int(round(float(pos["latitude"]) * 1e7))
|
||||
pl.longitude_i = int(round(float(pos["longitude"]) * 1e7))
|
||||
pl.altitude = int(pos.get("altitude", 0))
|
||||
pl.time = _resolve_time(pos, "time", "time_offset_sec", now_epoch)
|
||||
pl.location_source = Position.LocSource.Value(pos.get("location_source", "LOC_UNSET"))
|
||||
entry.position.CopyFrom(pl)
|
||||
return entry
|
||||
|
||||
|
||||
def _build_telemetry_entry(num: int, tel: dict[str, Any]) -> NodeTelemetryEntry:
|
||||
entry = NodeTelemetryEntry()
|
||||
entry.num = num
|
||||
dm = DeviceMetrics()
|
||||
if "battery_level" in tel:
|
||||
dm.battery_level = int(tel["battery_level"])
|
||||
if "voltage" in tel:
|
||||
dm.voltage = float(tel["voltage"])
|
||||
if "channel_utilization" in tel:
|
||||
dm.channel_utilization = float(tel["channel_utilization"])
|
||||
if "air_util_tx" in tel:
|
||||
dm.air_util_tx = float(tel["air_util_tx"])
|
||||
if "uptime_seconds" in tel:
|
||||
dm.uptime_seconds = int(tel["uptime_seconds"])
|
||||
entry.device_metrics.CopyFrom(dm)
|
||||
return entry
|
||||
|
||||
|
||||
def _build_environment_entry(num: int, env: dict[str, Any]) -> NodeEnvironmentEntry:
|
||||
entry = NodeEnvironmentEntry()
|
||||
entry.num = num
|
||||
em = EnvironmentMetrics()
|
||||
if "temperature" in env:
|
||||
em.temperature = float(env["temperature"])
|
||||
if "relative_humidity" in env:
|
||||
em.relative_humidity = float(env["relative_humidity"])
|
||||
if "barometric_pressure" in env:
|
||||
em.barometric_pressure = float(env["barometric_pressure"])
|
||||
if "iaq" in env:
|
||||
em.iaq = int(env["iaq"])
|
||||
entry.environment_metrics.CopyFrom(em)
|
||||
return entry
|
||||
|
||||
|
||||
def _build_status_entry(num: int, status: dict[str, Any]) -> NodeStatusEntry:
|
||||
# `StatusMessage` (mesh.proto:1445) has a single `string status` field.
|
||||
entry = NodeStatusEntry()
|
||||
entry.num = num
|
||||
sm = StatusMessage()
|
||||
if "status" in status:
|
||||
sm.status = str(status["status"])
|
||||
entry.status.CopyFrom(sm)
|
||||
return entry
|
||||
|
||||
|
||||
def compile_jsonl_to_proto(jsonl_path: pathlib.Path, now_epoch: int) -> bytes:
|
||||
"""Read a seed JSONL and return the encoded NodeDatabase bytes."""
|
||||
lines = jsonl_path.read_text(encoding="utf-8").splitlines()
|
||||
if not lines:
|
||||
raise ValueError(f"{jsonl_path} is empty")
|
||||
meta_line = lines[0]
|
||||
meta_obj = json.loads(meta_line)
|
||||
meta = meta_obj.get("_meta", {})
|
||||
version = meta.get("version")
|
||||
if version != 25:
|
||||
raise ValueError(
|
||||
f"{jsonl_path}: meta version is {version!r}; this compiler "
|
||||
f"requires version=25. Regenerate the seed with the matching tooling."
|
||||
)
|
||||
|
||||
db = NodeDatabase()
|
||||
db.version = 25
|
||||
|
||||
for ln, raw in enumerate(lines[1:], start=2):
|
||||
raw = raw.strip()
|
||||
if not raw:
|
||||
continue
|
||||
try:
|
||||
node = json.loads(raw)
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError(f"{jsonl_path}:{ln} JSON parse error: {e}")
|
||||
|
||||
num = int(node["num"], 16) if isinstance(node["num"], str) else int(node["num"])
|
||||
|
||||
# Header
|
||||
info = _build_node_info_lite(node, now_epoch)
|
||||
db.nodes.append(info)
|
||||
|
||||
# Satellites (nullable)
|
||||
if node.get("position"):
|
||||
db.positions.append(_build_position_entry(num, node["position"], now_epoch))
|
||||
if node.get("telemetry"):
|
||||
db.telemetry.append(_build_telemetry_entry(num, node["telemetry"]))
|
||||
if node.get("environment"):
|
||||
db.environment.append(_build_environment_entry(num, node["environment"]))
|
||||
if node.get("status"):
|
||||
db.status.append(_build_status_entry(num, node["status"]))
|
||||
|
||||
return db.SerializeToString()
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
p = argparse.ArgumentParser(
|
||||
description="Compile a seed JSONL into a binary v25 NodeDatabase proto.",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p.add_argument("--in", dest="in_path", required=True, help="Input seed JSONL.")
|
||||
p.add_argument("--out", required=True, help="Output binary .proto path.")
|
||||
p.add_argument(
|
||||
"--now-epoch",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Pin 'now' to this Unix epoch (for byte-identical CI). Default: time.time().",
|
||||
)
|
||||
args = p.parse_args(argv)
|
||||
|
||||
in_path = pathlib.Path(args.in_path)
|
||||
if not in_path.is_file():
|
||||
print(f"input not found: {in_path}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
now_epoch = args.now_epoch if args.now_epoch is not None else int(time.time())
|
||||
|
||||
try:
|
||||
encoded = compile_jsonl_to_proto(in_path, now_epoch)
|
||||
except ValueError as e:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
return 3
|
||||
|
||||
out_path = pathlib.Path(args.out)
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
out_path.write_bytes(encoded)
|
||||
print(
|
||||
f"compiled {in_path} -> {out_path} ({len(encoded)} bytes, now_epoch={now_epoch})",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
@@ -7,7 +7,7 @@
|
||||
"extra_flags": [
|
||||
"-D CDEBYTE_EORA_S3",
|
||||
"-D ARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-D ARDUINO_USB_MODE=0",
|
||||
"-D ARDUINO_USB_MODE=1",
|
||||
"-D ARDUINO_RUNNING_CORE=1",
|
||||
"-D ARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-D BOARD_HAS_PSRAM"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DHELTEC_WIRELESS_TRACKER",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "station-g3"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "BQ Station G3",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "",
|
||||
"vendor": "BQ Consulting"
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_1W",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_S3_CORE",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"extra_flags": [
|
||||
"-DLILYGO_T3S3_V1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DUNPHONE_SPIN=9",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x640000,
|
||||
app1, app, ota_1, 0x650000,0x640000,
|
||||
spiffs, data, spiffs, 0xc90000,0x360000,
|
||||
coredump, data, coredump,0xFF0000,0x10000,
|
||||
|
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x330000,
|
||||
app1, app, ota_1, 0x340000,0x330000,
|
||||
spiffs, data, spiffs, 0x670000,0x180000,
|
||||
coredump, data, coredump,0x7F0000,0x10000,
|
||||
|
@@ -70,17 +70,6 @@ def esp32_create_combined_bin(source, target, env):
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
||||
|
||||
esp32_kind = env.GetProjectOption("custom_esp32_kind")
|
||||
if esp32_kind == "esp32":
|
||||
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
|
||||
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
|
||||
env.Append(
|
||||
LINKFLAGS=[
|
||||
"-Wl,--wrap=esp_flash_chip_gd",
|
||||
"-Wl,--wrap=esp_flash_chip_issi",
|
||||
"-Wl,--wrap=esp_flash_chip_winbond",
|
||||
]
|
||||
)
|
||||
else:
|
||||
# For newer ESP32 targets, using newlib nano works better.
|
||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||
# Enable Newlib Nano formatting to save space
|
||||
# ...but allow printf float support (compromise)
|
||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
|
||||
# force linker response file instead of command line arguments
|
||||
|
||||
Import("env")
|
||||
|
||||
|
||||
def wrap_with_tempfile(command_key):
|
||||
command = env.get(command_key)
|
||||
if not command or not isinstance(command, str):
|
||||
return
|
||||
if "TEMPFILE(" in command:
|
||||
return
|
||||
env.Replace(**{command_key: "${TEMPFILE('%s')}" % command})
|
||||
|
||||
|
||||
# Force SCons to spill long commands into response files on this target.
|
||||
env.Replace(MAXLINELENGTH=8192)
|
||||
|
||||
for key in ("LINKCOM", "CXXLINKCOM", "SHLINKCOM", "SHCXXLINKCOM"):
|
||||
wrap_with_tempfile(key)
|
||||
@@ -0,0 +1,382 @@
|
||||
"""Fake NodeDB fixture push — Portduino file copy + hardware XModem upload.
|
||||
|
||||
The fixture pipeline is two-stage:
|
||||
1. `bin/gen-fake-nodedb-seed.py` produces a deterministic JSONL describing N
|
||||
fake-but-realistic peers. Committed under `test/fixtures/nodedb/`.
|
||||
2. `bin/seed-json-to-proto.py` compiles JSONL → binary v25 NodeDatabase
|
||||
protobuf with fresh wall-clock timestamps.
|
||||
|
||||
This module exposes `push_fake_nodedb(...)`, the MCP tool that:
|
||||
- target="portduino": compiles the JSONL into the device's prefs dir on
|
||||
the local filesystem (`~/.portduino/<config>/prefs/nodes.proto`).
|
||||
- target="hardware": compiles to a temp file, then streams it over the
|
||||
XModem protocol (via the meshtastic SerialInterface/BLEInterface +
|
||||
`meshtastic.xmodempacket` pubsub topic) to `/prefs/nodes.proto` on the
|
||||
device. Triggers a reboot so the firmware loads the new state on next
|
||||
boot.
|
||||
|
||||
XModem wire details (mirrors firmware impl at src/xmodem.cpp:115-260):
|
||||
* 128-byte chunks; final chunk padded to 128 B with 0x1A (SUB) bytes.
|
||||
* CRC16-CCITT (poly 0x1021, init 0x0000).
|
||||
* SOH/seq=0 carries the destination filename in `buffer.bytes`. ACK if
|
||||
`FSCom.open(filename, FILE_O_WRITE)` succeeds; NAK otherwise.
|
||||
* SOH/seq≥1 carries a 128-byte chunk. ACK = advance; NAK = retransmit.
|
||||
* EOT after the last chunk flushes + closes the file on-device.
|
||||
|
||||
Hardware push requires `confirm=True` (mirrors factory_reset / erase_and_flash
|
||||
in the .github/copilot-instructions.md "never do these without asking" list).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import hashlib
|
||||
import pathlib
|
||||
import queue
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from typing import Any, Literal
|
||||
|
||||
from .connection import connect, is_tcp_port
|
||||
|
||||
# Resolve repo root so the tool works regardless of mcp-server cwd.
|
||||
_REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
_SEED_DIR = _REPO_ROOT / "test" / "fixtures" / "nodedb"
|
||||
_COMPILE_SCRIPT = _REPO_ROOT / "bin" / "seed-json-to-proto.py"
|
||||
|
||||
_DEFAULT_NODES_FILENAME = "/prefs/nodes.proto"
|
||||
_XMODEM_CHUNK = 128
|
||||
_XMODEM_SUB = 0x1A
|
||||
_ACK_TIMEOUT_INIT_S = 5.0
|
||||
_ACK_TIMEOUT_CHUNK_S = 2.0
|
||||
_MAX_CHUNK_RETRIES = 5
|
||||
|
||||
_VALID_SIZES = (250, 500, 1000, 2000)
|
||||
|
||||
|
||||
class FixtureError(RuntimeError):
|
||||
"""Raised for any fixture-push failure (compile, transport, ack timeout, …)."""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CRC16-CCITT (poly 0x1021, init 0x0000). Matches the firmware's `crc16_ccitt`.
|
||||
# Hand-rolled to avoid the optional `crcmod` dep.
|
||||
# ---------------------------------------------------------------------------
|
||||
def _crc16_ccitt(data: bytes, *, init: int = 0x0000) -> int:
|
||||
crc = init
|
||||
for b in data:
|
||||
crc ^= b << 8
|
||||
for _ in range(8):
|
||||
if crc & 0x8000:
|
||||
crc = ((crc << 1) ^ 0x1021) & 0xFFFF
|
||||
else:
|
||||
crc = (crc << 1) & 0xFFFF
|
||||
return crc
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Compile step — shells out to bin/seed-json-to-proto.py so the MCP module
|
||||
# doesn't have to duplicate the proto-encoding logic.
|
||||
# ---------------------------------------------------------------------------
|
||||
def _compile_proto(jsonl_path: pathlib.Path, out_path: pathlib.Path) -> None:
|
||||
if not _COMPILE_SCRIPT.is_file():
|
||||
raise FixtureError(f"compile script missing at {_COMPILE_SCRIPT}")
|
||||
cmd = [
|
||||
sys.executable,
|
||||
str(_COMPILE_SCRIPT),
|
||||
"--in",
|
||||
str(jsonl_path),
|
||||
"--out",
|
||||
str(out_path),
|
||||
]
|
||||
try:
|
||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
raise FixtureError(
|
||||
f"seed-json-to-proto.py failed (exit {exc.returncode}):\n"
|
||||
f" stdout: {exc.stdout}\n stderr: {exc.stderr}"
|
||||
) from exc
|
||||
|
||||
|
||||
def _resolve_seed_jsonl(size: int, custom: str | None) -> pathlib.Path:
|
||||
if custom is not None:
|
||||
p = pathlib.Path(custom).expanduser().resolve()
|
||||
if not p.is_file():
|
||||
raise FixtureError(f"custom_seed_jsonl not found: {p}")
|
||||
return p
|
||||
p = _SEED_DIR / f"seed_v25_{size:04d}.jsonl"
|
||||
if not p.is_file():
|
||||
raise FixtureError(
|
||||
f"missing committed seed at {p}. "
|
||||
f"Run `./bin/regen-fake-nodedbs.sh` to generate it."
|
||||
)
|
||||
return p
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Portduino push — file copy into ~/.portduino/<config>/prefs/
|
||||
# ---------------------------------------------------------------------------
|
||||
def _portduino_prefs_dir(config_name: str) -> pathlib.Path:
|
||||
home = pathlib.Path.home()
|
||||
return home / ".portduino" / config_name / "prefs"
|
||||
|
||||
|
||||
def _push_portduino(
|
||||
size: int,
|
||||
jsonl: pathlib.Path,
|
||||
portduino_config: str,
|
||||
backup_existing: bool,
|
||||
) -> dict[str, Any]:
|
||||
prefs = _portduino_prefs_dir(portduino_config)
|
||||
prefs.mkdir(parents=True, exist_ok=True)
|
||||
target = prefs / "nodes.proto"
|
||||
backed_up_to: str | None = None
|
||||
if backup_existing and target.is_file():
|
||||
ts = int(time.time())
|
||||
backup = prefs / f"nodes.proto.bak.{ts}"
|
||||
shutil.move(str(target), str(backup))
|
||||
backed_up_to = str(backup)
|
||||
_compile_proto(jsonl, target)
|
||||
raw = target.read_bytes()
|
||||
return {
|
||||
"transport": "portduino",
|
||||
"path": str(target),
|
||||
"bytes": len(raw),
|
||||
"sha256": hashlib.sha256(raw).hexdigest(),
|
||||
"jsonl_source": str(jsonl),
|
||||
"backed_up_to": backed_up_to,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hardware push — XModem over BLE/serial via the meshtastic Python interface.
|
||||
# ---------------------------------------------------------------------------
|
||||
@dataclasses.dataclass
|
||||
class _AckEvent:
|
||||
control: int
|
||||
seq: int
|
||||
|
||||
|
||||
def _wait_for_response(q: "queue.Queue[_AckEvent]", timeout_s: float) -> _AckEvent:
|
||||
try:
|
||||
return q.get(timeout=timeout_s)
|
||||
except queue.Empty as exc:
|
||||
raise FixtureError(
|
||||
f"XModem response timeout after {timeout_s:.1f}s — device not responding"
|
||||
) from exc
|
||||
|
||||
|
||||
def _push_hardware(
|
||||
size: int,
|
||||
jsonl: pathlib.Path,
|
||||
port: str | None,
|
||||
reboot_after: bool,
|
||||
) -> dict[str, Any]:
|
||||
# Lazy imports so the module loads even when the meshtastic deps aren't
|
||||
# available (e.g. CI in a Python env without the package installed).
|
||||
try:
|
||||
from meshtastic.protobuf import mesh_pb2, xmodem_pb2
|
||||
from pubsub import pub
|
||||
except ImportError as exc: # pragma: no cover — dep missing
|
||||
raise FixtureError(
|
||||
f"hardware push requires the meshtastic + pypubsub packages: {exc}"
|
||||
) from exc
|
||||
|
||||
if is_tcp_port(port):
|
||||
raise FixtureError(
|
||||
"hardware push over TCP/portduino is not supported — use "
|
||||
"target='portduino' to drop the fixture directly into the prefs dir."
|
||||
)
|
||||
|
||||
# Compile the fixture to a temp file with fresh timestamps.
|
||||
with tempfile.NamedTemporaryFile(suffix=".proto", delete=False) as tf:
|
||||
proto_path = pathlib.Path(tf.name)
|
||||
try:
|
||||
_compile_proto(jsonl, proto_path)
|
||||
payload = proto_path.read_bytes()
|
||||
finally:
|
||||
proto_path.unlink(missing_ok=True)
|
||||
|
||||
sha256 = hashlib.sha256(payload).hexdigest()
|
||||
total_bytes = len(payload)
|
||||
|
||||
# Subscribe to XModem responses BEFORE we open the interface, so we don't
|
||||
# race the first ACK that arrives during the SOH/seq=0 handshake.
|
||||
#
|
||||
# NB: the signature MUST declare every kwarg pypubsub will see for this
|
||||
# topic, or pubsub locks the topic spec to a smaller set (whichever
|
||||
# subscribe arrives first) and then *rejects* the meshtastic library's
|
||||
# publish call with `SenderUnknownMsgDataError: unknown ... interface`.
|
||||
# The meshtastic lib publishes both `packet=` and `interface=`
|
||||
# (mesh_interface.py:1389-1395), so both must appear here.
|
||||
response_q: "queue.Queue[_AckEvent]" = queue.Queue()
|
||||
|
||||
def _on_xmodem(packet: Any = None, interface: Any = None, **_kw: Any) -> None:
|
||||
if packet is None:
|
||||
return
|
||||
response_q.put(_AckEvent(control=int(packet.control), seq=int(packet.seq)))
|
||||
|
||||
pub.subscribe(_on_xmodem, "meshtastic.xmodempacket")
|
||||
|
||||
chunks_sent = 0
|
||||
retried = 0
|
||||
rebooted = False
|
||||
|
||||
XMC = xmodem_pb2.XModem.Control
|
||||
try:
|
||||
with connect(port=port) as iface:
|
||||
# 1) Send the filename (SOH, seq=0).
|
||||
init_pkt = xmodem_pb2.XModem(
|
||||
control=XMC.Value("SOH"),
|
||||
seq=0,
|
||||
buffer=_DEFAULT_NODES_FILENAME.encode("utf-8"),
|
||||
)
|
||||
iface._sendToRadio(mesh_pb2.ToRadio(xmodemPacket=init_pkt))
|
||||
ack = _wait_for_response(response_q, _ACK_TIMEOUT_INIT_S)
|
||||
if ack.control != XMC.Value("ACK"):
|
||||
raise FixtureError(
|
||||
f"device refused filename {_DEFAULT_NODES_FILENAME!r} "
|
||||
f"(got control={ack.control}, expected ACK). "
|
||||
f"Filesystem full or permissions issue?"
|
||||
)
|
||||
|
||||
# 2) Stream the payload in 128 B chunks.
|
||||
for offset in range(0, total_bytes, _XMODEM_CHUNK):
|
||||
chunk = payload[offset : offset + _XMODEM_CHUNK]
|
||||
if len(chunk) < _XMODEM_CHUNK:
|
||||
# Pad final chunk to 128 B with SUB. The trailing 0x1A bytes
|
||||
# become part of the file on-device, but nanopb ignores
|
||||
# bytes past the end of the top-level message.
|
||||
chunk = chunk + bytes([_XMODEM_SUB] * (_XMODEM_CHUNK - len(chunk)))
|
||||
seq = ((offset // _XMODEM_CHUNK) + 1) % 256
|
||||
# Retry loop on NAK / timeout.
|
||||
attempts = 0
|
||||
while True:
|
||||
pkt = xmodem_pb2.XModem(
|
||||
control=XMC.Value("SOH"),
|
||||
seq=seq,
|
||||
buffer=chunk,
|
||||
crc16=_crc16_ccitt(chunk),
|
||||
)
|
||||
iface._sendToRadio(mesh_pb2.ToRadio(xmodemPacket=pkt))
|
||||
ack = _wait_for_response(response_q, _ACK_TIMEOUT_CHUNK_S)
|
||||
if ack.control == XMC.Value("ACK"):
|
||||
chunks_sent += 1
|
||||
break
|
||||
if ack.control == XMC.Value("NAK"):
|
||||
attempts += 1
|
||||
retried += 1
|
||||
if attempts >= _MAX_CHUNK_RETRIES:
|
||||
# Abort: send CAN so the firmware removes the half-
|
||||
# written file via FSCom.remove(filename).
|
||||
iface._sendToRadio(
|
||||
mesh_pb2.ToRadio(
|
||||
xmodemPacket=xmodem_pb2.XModem(
|
||||
control=XMC.Value("CAN")
|
||||
)
|
||||
)
|
||||
)
|
||||
raise FixtureError(
|
||||
f"chunk seq={seq} NAK'd {attempts} times; "
|
||||
f"aborted transfer (file removed on-device)."
|
||||
)
|
||||
continue # retry the same chunk
|
||||
raise FixtureError(
|
||||
f"unexpected XModem control={ack.control} on seq={seq}"
|
||||
)
|
||||
|
||||
# 3) Tell the device we're done.
|
||||
iface._sendToRadio(
|
||||
mesh_pb2.ToRadio(
|
||||
xmodemPacket=xmodem_pb2.XModem(control=XMC.Value("EOT"))
|
||||
)
|
||||
)
|
||||
ack = _wait_for_response(response_q, _ACK_TIMEOUT_CHUNK_S)
|
||||
if ack.control != XMC.Value("ACK"):
|
||||
raise FixtureError(f"EOT not ACKed (got control={ack.control})")
|
||||
|
||||
# 4) Reboot so loadFromDisk picks up the new file.
|
||||
if reboot_after:
|
||||
iface.localNode.reboot(secs=1)
|
||||
rebooted = True
|
||||
finally:
|
||||
try:
|
||||
pub.unsubscribe(_on_xmodem, "meshtastic.xmodempacket")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"transport": "hardware",
|
||||
"port": port,
|
||||
"filename_on_device": _DEFAULT_NODES_FILENAME,
|
||||
"bytes": total_bytes,
|
||||
"chunks_sent": chunks_sent,
|
||||
"retried": retried,
|
||||
"sha256": sha256,
|
||||
"jsonl_source": str(jsonl),
|
||||
"rebooted": rebooted,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public entry point — registered as an MCP tool in server.py.
|
||||
# ---------------------------------------------------------------------------
|
||||
def push_fake_nodedb(
|
||||
size: int,
|
||||
target: Literal["portduino", "hardware"] = "portduino",
|
||||
*,
|
||||
port: str | None = None,
|
||||
portduino_config: str = "default",
|
||||
backup_existing: bool = True,
|
||||
confirm: bool = False,
|
||||
reboot_after: bool = True,
|
||||
custom_seed_jsonl: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Compile a fresh-timestamp NodeDatabase fixture and push it to a device.
|
||||
|
||||
Args:
|
||||
size: 250, 500, 1000, or 2000 — selects which committed seed JSONL to use.
|
||||
target: "portduino" (file copy to ~/.portduino/<config>/prefs/) or
|
||||
"hardware" (XModem upload to /prefs/nodes.proto + reboot).
|
||||
port: required for target="hardware". Serial path (e.g. /dev/cu.usbmodemXXXX)
|
||||
or BLE identifier. TCP endpoints are rejected — use target="portduino"
|
||||
instead.
|
||||
portduino_config: which Portduino instance dir under ~/.portduino/. Default "default".
|
||||
backup_existing: portduino only. Move nodes.proto -> nodes.proto.bak.<ts>
|
||||
if present, so you can roll back.
|
||||
confirm: required True for target="hardware" (writes flash + reboots).
|
||||
reboot_after: hardware only. If True, send a 1-second reboot after the
|
||||
final ACK so loadFromDisk picks up the new file at next boot.
|
||||
custom_seed_jsonl: override the committed JSONL. Use to push a hand-edited
|
||||
test scenario.
|
||||
|
||||
Returns:
|
||||
dict with transport, bytes, sha256, etc. — depends on target.
|
||||
|
||||
"""
|
||||
if size not in _VALID_SIZES:
|
||||
raise FixtureError(
|
||||
f"size must be one of {_VALID_SIZES}; got {size!r}. "
|
||||
f"Add a new committed seed if you need a different cardinality."
|
||||
)
|
||||
|
||||
jsonl = _resolve_seed_jsonl(size, custom_seed_jsonl)
|
||||
|
||||
if target == "portduino":
|
||||
return _push_portduino(size, jsonl, portduino_config, backup_existing)
|
||||
|
||||
if target == "hardware":
|
||||
if not confirm:
|
||||
raise FixtureError(
|
||||
"hardware push writes flash and triggers a reboot — pass confirm=True."
|
||||
)
|
||||
if not port:
|
||||
raise FixtureError(
|
||||
"target='hardware' requires a port (e.g. /dev/cu.usbmodemXXXX)."
|
||||
)
|
||||
return _push_hardware(size, jsonl, port, reboot_after)
|
||||
|
||||
raise FixtureError(f"unknown target {target!r}; expected 'portduino' or 'hardware'")
|
||||
@@ -15,6 +15,7 @@ from . import (
|
||||
admin,
|
||||
boards,
|
||||
devices,
|
||||
fixtures,
|
||||
flash,
|
||||
hw_tools,
|
||||
info,
|
||||
@@ -963,3 +964,45 @@ def recorder_export(
|
||||
dest_dir=dest_dir,
|
||||
streams=streams,
|
||||
)
|
||||
|
||||
|
||||
# ---------- Fixture / test-data push --------------------------------------
|
||||
|
||||
|
||||
@app.tool()
|
||||
def push_fake_nodedb(
|
||||
size: int,
|
||||
target: str = "portduino",
|
||||
port: str | None = None,
|
||||
portduino_config: str = "default",
|
||||
backup_existing: bool = True,
|
||||
confirm: bool = False,
|
||||
reboot_after: bool = True,
|
||||
custom_seed_jsonl: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Push a fake-NodeDB v25 fixture (250/500/1000/2000 nodes) onto a device.
|
||||
|
||||
Two transports:
|
||||
target="portduino" — file copy to ~/.portduino/<portduino_config>/prefs/nodes.proto.
|
||||
Fast, no device connection needed.
|
||||
target="hardware" — XModem upload over serial/BLE to /prefs/nodes.proto.
|
||||
Requires `port` + `confirm=True`. Triggers a reboot
|
||||
so loadFromDisk picks up the new file at next boot.
|
||||
|
||||
Compiles a fresh-timestamp proto from the committed JSONL seed under
|
||||
test/fixtures/nodedb/seed_v25_<N>.jsonl each invocation, so the loaded
|
||||
NodeDB always looks "recent" to the connecting phone. Structural data
|
||||
(names, IDs, positions, telemetries) is deterministic per the seed.
|
||||
|
||||
Override the JSONL via `custom_seed_jsonl` to push a hand-edited scenario.
|
||||
"""
|
||||
return fixtures.push_fake_nodedb(
|
||||
size=size,
|
||||
target=target, # type: ignore[arg-type]
|
||||
port=port,
|
||||
portduino_config=portduino_config,
|
||||
backup_existing=backup_existing,
|
||||
confirm=confirm,
|
||||
reboot_after=reboot_after,
|
||||
custom_seed_jsonl=custom_seed_jsonl,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
"""Tests for the fake-NodeDB fixture pipeline (bin/gen-fake-nodedb-seed.py
|
||||
+ bin/seed-json-to-proto.py + mcp-server fixtures.push_fake_nodedb).
|
||||
|
||||
Lives under tests/unit/ because none of these touch real hardware — they
|
||||
shell out to the bin/ scripts and decode the resulting protobufs in-process.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
SEED_GEN = REPO_ROOT / "bin" / "gen-fake-nodedb-seed.py"
|
||||
COMPILE = REPO_ROOT / "bin" / "seed-json-to-proto.py"
|
||||
FIXTURES_DIR = REPO_ROOT / "test" / "fixtures" / "nodedb"
|
||||
|
||||
# Ensure the locally-generated Python protobuf bindings are importable.
|
||||
# These live under `meshtastic_v25` (not `meshtastic`) so they don't shadow
|
||||
# the PyPI `meshtastic` package that the rest of the mcp-server depends on.
|
||||
_BINDINGS_DIR = REPO_ROOT / "bin" / "_generated"
|
||||
if _BINDINGS_DIR.is_dir() and str(_BINDINGS_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_BINDINGS_DIR))
|
||||
|
||||
try:
|
||||
from meshtastic_v25.deviceonly_pb2 import (
|
||||
NodeDatabase, # type: ignore[import-not-found]
|
||||
)
|
||||
except ImportError:
|
||||
NodeDatabase = None # type: ignore[assignment]
|
||||
|
||||
|
||||
def _require_v25_bindings() -> None:
|
||||
if NodeDatabase is None:
|
||||
pytest.skip(
|
||||
"v25 Python protobuf bindings missing; run `./bin/regen-py-protos.sh`."
|
||||
)
|
||||
if "positions" not in NodeDatabase.DESCRIPTOR.fields_by_name:
|
||||
pytest.skip(
|
||||
"Loaded NodeDatabase predates v25 — run `./bin/regen-py-protos.sh`."
|
||||
)
|
||||
|
||||
|
||||
def _run(cmd: list[str]) -> None:
|
||||
subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed generator: deterministic for given --seed (no wall-clock dependence).
|
||||
# ---------------------------------------------------------------------------
|
||||
def test_seed_generator_is_deterministic(tmp_path: pathlib.Path) -> None:
|
||||
a = tmp_path / "a.jsonl"
|
||||
b = tmp_path / "b.jsonl"
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SEED_GEN),
|
||||
"--count",
|
||||
"100",
|
||||
"--seed",
|
||||
"42",
|
||||
"--out",
|
||||
str(a),
|
||||
]
|
||||
)
|
||||
# Sleep so any sneaky wall-clock leak in the generator would surface as
|
||||
# a byte diff between the two runs.
|
||||
time.sleep(0.8)
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SEED_GEN),
|
||||
"--count",
|
||||
"100",
|
||||
"--seed",
|
||||
"42",
|
||||
"--out",
|
||||
str(b),
|
||||
]
|
||||
)
|
||||
assert a.read_bytes() == b.read_bytes()
|
||||
|
||||
|
||||
def test_seed_generator_meta_line(tmp_path: pathlib.Path) -> None:
|
||||
out = tmp_path / "seed.jsonl"
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SEED_GEN),
|
||||
"--count",
|
||||
"50",
|
||||
"--seed",
|
||||
"1",
|
||||
"--out",
|
||||
str(out),
|
||||
]
|
||||
)
|
||||
lines = out.read_text(encoding="utf-8").splitlines()
|
||||
assert len(lines) == 51 # 1 meta + 50 nodes
|
||||
meta = json.loads(lines[0])
|
||||
assert "_meta" in meta
|
||||
assert meta["_meta"]["version"] == 25
|
||||
assert meta["_meta"]["count"] == 50
|
||||
assert meta["_meta"]["seed"] == 1
|
||||
|
||||
|
||||
def test_seed_only_uses_active_hardware_and_roles(tmp_path: pathlib.Path) -> None:
|
||||
"""Confirm no deprecated roles + no off-list HW models leak through."""
|
||||
out = tmp_path / "seed.jsonl"
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SEED_GEN),
|
||||
"--count",
|
||||
"500",
|
||||
"--seed",
|
||||
"7",
|
||||
"--out",
|
||||
str(out),
|
||||
]
|
||||
)
|
||||
forbidden_roles = {"ROUTER_CLIENT", "REPEATER"}
|
||||
forbidden_hw = {
|
||||
"TLORA_V1",
|
||||
"TLORA_V2",
|
||||
"TLORA_V1_1P3",
|
||||
"TLORA_V2_1_1P6",
|
||||
"TLORA_V2_1_1P8",
|
||||
"HELTEC_V1",
|
||||
"HELTEC_V2_0",
|
||||
"HELTEC_V2_1",
|
||||
"TBEAM",
|
||||
"TBEAM_V0P7",
|
||||
"NANO_G1",
|
||||
"NANO_G1_EXPLORER",
|
||||
"NANO_G2_ULTRA",
|
||||
"STATION_G1",
|
||||
"STATION_G2",
|
||||
"PORTDUINO",
|
||||
"ANDROID_SIM",
|
||||
"DIY_V1",
|
||||
"LORA_RELAY_V1",
|
||||
"NRF52840_PCA10059",
|
||||
"NRF52_UNKNOWN",
|
||||
"DR_DEV",
|
||||
"GENIEBLOCKS",
|
||||
"M5STACK",
|
||||
"RP2040_LORA",
|
||||
"PPR",
|
||||
}
|
||||
for raw in out.read_text(encoding="utf-8").splitlines()[1:]:
|
||||
node = json.loads(raw)
|
||||
assert node["role"] not in forbidden_roles, f"deprecated role: {node['role']}"
|
||||
assert (
|
||||
node["hw_model"] not in forbidden_hw
|
||||
), f"non-tier-1 HW: {node['hw_model']}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Compile step + committed seeds.
|
||||
# ---------------------------------------------------------------------------
|
||||
@pytest.mark.parametrize("size", [250, 500, 1000, 2000])
|
||||
def test_committed_seed_compiles_and_decodes(size: int, tmp_path: pathlib.Path) -> None:
|
||||
_require_v25_bindings()
|
||||
proto = tmp_path / "out.proto"
|
||||
jsonl = FIXTURES_DIR / f"seed_v25_{size:04d}.jsonl"
|
||||
if not jsonl.is_file():
|
||||
pytest.skip(f"{jsonl} not present — run ./bin/regen-fake-nodedbs.sh")
|
||||
_run([sys.executable, str(COMPILE), "--in", str(jsonl), "--out", str(proto)])
|
||||
|
||||
db = NodeDatabase()
|
||||
db.ParseFromString(proto.read_bytes())
|
||||
assert db.version == 25
|
||||
assert len(db.nodes) == size
|
||||
nums = {n.num for n in db.nodes}
|
||||
assert len(nums) == size, "node numbers must be unique"
|
||||
assert all(n.long_name and n.short_name for n in db.nodes)
|
||||
assert all(len(n.long_name) <= 24 for n in db.nodes) # max_size:25 - NUL
|
||||
|
||||
# Coverage sanity (±10pp tolerance for binomial fluctuation).
|
||||
def in_range(actual: int, expected_ratio: float, tol_pp: float = 0.10) -> bool:
|
||||
lo = max(0, int((expected_ratio - tol_pp) * size))
|
||||
hi = min(size, int((expected_ratio + tol_pp) * size))
|
||||
return lo <= actual <= hi
|
||||
|
||||
assert in_range(len(db.positions), 0.85)
|
||||
assert in_range(len(db.telemetry), 0.70)
|
||||
assert in_range(len(db.environment), 0.25)
|
||||
assert in_range(len(db.status), 0.40)
|
||||
|
||||
|
||||
def test_compile_freshens_timestamps(tmp_path: pathlib.Path) -> None:
|
||||
"""Same JSONL compiled twice → identical structure, different timestamps."""
|
||||
_require_v25_bindings()
|
||||
jsonl = FIXTURES_DIR / "seed_v25_0250.jsonl"
|
||||
if not jsonl.is_file():
|
||||
pytest.skip("250-node seed not present — run ./bin/regen-fake-nodedbs.sh")
|
||||
a = tmp_path / "a.proto"
|
||||
b = tmp_path / "b.proto"
|
||||
_run([sys.executable, str(COMPILE), "--in", str(jsonl), "--out", str(a)])
|
||||
time.sleep(1.2)
|
||||
_run([sys.executable, str(COMPILE), "--in", str(jsonl), "--out", str(b)])
|
||||
|
||||
da = NodeDatabase()
|
||||
db_ = NodeDatabase()
|
||||
da.ParseFromString(a.read_bytes())
|
||||
db_.ParseFromString(b.read_bytes())
|
||||
|
||||
# Zero out timestamp fields and confirm everything else is byte-identical.
|
||||
for d in (da, db_):
|
||||
for n in d.nodes:
|
||||
n.last_heard = 0
|
||||
for p in d.positions:
|
||||
p.position.time = 0
|
||||
assert da.SerializeToString() == db_.SerializeToString()
|
||||
|
||||
# Re-load fresh copies to confirm timestamps actually moved.
|
||||
aa = NodeDatabase()
|
||||
bb = NodeDatabase()
|
||||
aa.ParseFromString(a.read_bytes())
|
||||
bb.ParseFromString(b.read_bytes())
|
||||
aa_max = max(n.last_heard for n in aa.nodes if n.last_heard)
|
||||
bb_max = max(n.last_heard for n in bb.nodes if n.last_heard)
|
||||
assert bb_max >= aa_max
|
||||
assert bb_max - aa_max < 5 # within a few seconds
|
||||
|
||||
|
||||
def test_compile_pinned_now_epoch_is_byte_identical(tmp_path: pathlib.Path) -> None:
|
||||
"""With --now-epoch pinned, two compiles produce identical bytes."""
|
||||
_require_v25_bindings()
|
||||
jsonl = FIXTURES_DIR / "seed_v25_0250.jsonl"
|
||||
if not jsonl.is_file():
|
||||
pytest.skip("250-node seed not present")
|
||||
a = tmp_path / "a.proto"
|
||||
b = tmp_path / "b.proto"
|
||||
for o in (a, b):
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
str(COMPILE),
|
||||
"--in",
|
||||
str(jsonl),
|
||||
"--now-epoch",
|
||||
"1700000000",
|
||||
"--out",
|
||||
str(o),
|
||||
]
|
||||
)
|
||||
assert a.read_bytes() == b.read_bytes()
|
||||
|
||||
|
||||
def test_compile_timestamps_are_recent(tmp_path: pathlib.Path) -> None:
|
||||
_require_v25_bindings()
|
||||
jsonl = FIXTURES_DIR / "seed_v25_0250.jsonl"
|
||||
if not jsonl.is_file():
|
||||
pytest.skip("250-node seed not present")
|
||||
out = tmp_path / "out.proto"
|
||||
_run([sys.executable, str(COMPILE), "--in", str(jsonl), "--out", str(out)])
|
||||
db = NodeDatabase()
|
||||
db.ParseFromString(out.read_bytes())
|
||||
now = int(time.time())
|
||||
# No timestamp older than 7 days, none in the future.
|
||||
for n in db.nodes:
|
||||
if n.last_heard:
|
||||
assert now - 7 * 86400 <= n.last_heard <= now
|
||||
# At least half should be within the last hour
|
||||
# (matches expovariate(mean=3600s)).
|
||||
recent = sum(1 for n in db.nodes if n.last_heard and n.last_heard >= now - 3600)
|
||||
assert recent >= 0.4 * len(db.nodes)
|
||||
|
||||
|
||||
def test_compile_hand_edit_round_trip(tmp_path: pathlib.Path) -> None:
|
||||
"""Edit one JSONL line, recompile, confirm edit appears in the proto."""
|
||||
_require_v25_bindings()
|
||||
src = FIXTURES_DIR / "seed_v25_0250.jsonl"
|
||||
if not src.is_file():
|
||||
pytest.skip("250-node seed not present")
|
||||
dst = tmp_path / "edited.jsonl"
|
||||
lines = src.read_text(encoding="utf-8").splitlines()
|
||||
|
||||
# Find a node that already has telemetry so the index relationship is
|
||||
# easy to assert on the other side.
|
||||
edit_idx = None
|
||||
for i, raw in enumerate(lines[1:], start=1):
|
||||
node = json.loads(raw)
|
||||
if node.get("telemetry") is not None:
|
||||
edit_idx = i
|
||||
break
|
||||
assert edit_idx is not None, "expected at least one node with telemetry"
|
||||
|
||||
node = json.loads(lines[edit_idx])
|
||||
target_num = int(node["num"], 16)
|
||||
node["long_name"] = "Hand Edited Node"
|
||||
node["telemetry"] = {
|
||||
"battery_level": 42,
|
||||
"voltage": 3.71,
|
||||
"channel_utilization": 0.0,
|
||||
"air_util_tx": 0.0,
|
||||
"uptime_seconds": 1,
|
||||
}
|
||||
lines[edit_idx] = json.dumps(node, ensure_ascii=False, sort_keys=True)
|
||||
dst.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
out = tmp_path / "out.proto"
|
||||
_run([sys.executable, str(COMPILE), "--in", str(dst), "--out", str(out)])
|
||||
db = NodeDatabase()
|
||||
db.ParseFromString(out.read_bytes())
|
||||
edited = next((n for n in db.nodes if n.num == target_num), None)
|
||||
assert edited is not None
|
||||
assert edited.long_name == "Hand Edited Node"
|
||||
tel = next((t for t in db.telemetry if t.num == target_num), None)
|
||||
assert tel is not None
|
||||
assert tel.device_metrics.battery_level == 42
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Misc smoke checks on the module surface.
|
||||
# ---------------------------------------------------------------------------
|
||||
def test_crc16_ccitt_matches_known_vectors() -> None:
|
||||
"""Sanity-check the hand-rolled CRC16-CCITT matches well-known vectors.
|
||||
|
||||
Test vectors from the XModem-CRC spec (init=0, poly=0x1021):
|
||||
crc16("123456789") = 0x31C3
|
||||
crc16("") = 0x0000
|
||||
"""
|
||||
from meshtastic_mcp.fixtures import _crc16_ccitt
|
||||
|
||||
assert _crc16_ccitt(b"") == 0x0000
|
||||
assert _crc16_ccitt(b"123456789") == 0x31C3
|
||||
|
||||
|
||||
def test_push_fake_nodedb_rejects_invalid_size() -> None:
|
||||
from meshtastic_mcp.fixtures import FixtureError, push_fake_nodedb
|
||||
|
||||
with pytest.raises(FixtureError, match="size must be one of"):
|
||||
push_fake_nodedb(size=999, target="portduino") # type: ignore[arg-type]
|
||||
|
||||
|
||||
def test_push_fake_nodedb_hardware_requires_confirm() -> None:
|
||||
from meshtastic_mcp.fixtures import FixtureError, push_fake_nodedb
|
||||
|
||||
with pytest.raises(FixtureError, match="confirm=True"):
|
||||
push_fake_nodedb(size=250, target="hardware", port="/dev/cu.fake")
|
||||
|
||||
|
||||
def test_push_fake_nodedb_hardware_requires_port() -> None:
|
||||
from meshtastic_mcp.fixtures import FixtureError, push_fake_nodedb
|
||||
|
||||
with pytest.raises(FixtureError, match="requires a port"):
|
||||
push_fake_nodedb(size=250, target="hardware", confirm=True)
|
||||
|
||||
|
||||
def test_push_fake_nodedb_hardware_rejects_tcp_port() -> None:
|
||||
from meshtastic_mcp.fixtures import FixtureError, push_fake_nodedb
|
||||
|
||||
with pytest.raises(FixtureError, match="not supported"):
|
||||
push_fake_nodedb(
|
||||
size=250, target="hardware", confirm=True, port="tcp://localhost:4403"
|
||||
)
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = tbeam
|
||||
default_envs = heltec-v3
|
||||
|
||||
extra_configs =
|
||||
variants/*/*.ini
|
||||
@@ -49,6 +49,7 @@ build_flags = -Wno-missing-field-initializers
|
||||
-DRADIOLIB_EXCLUDE_PAGER=1
|
||||
-DRADIOLIB_EXCLUDE_FSK4=1
|
||||
-DRADIOLIB_EXCLUDE_APRS=1
|
||||
-DRADIOLIB_EXCLUDE_ADSB=1
|
||||
-DRADIOLIB_EXCLUDE_LORAWAN=1
|
||||
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
||||
-DMESHTASTIC_EXCLUDE_REPLYBOT=1
|
||||
@@ -137,7 +138,7 @@ lib_deps =
|
||||
# renovate: datasource=github-tags depName=Adafruit GFX packageName=adafruit/Adafruit-GFX-Library
|
||||
https://github.com/adafruit/Adafruit-GFX-Library/archive/refs/tags/1.12.6.zip
|
||||
# renovate: datasource=github-tags depName=NeoPixel packageName=adafruit/Adafruit_NeoPixel
|
||||
https://github.com/adafruit/Adafruit_NeoPixel/archive/refs/tags/1.15.4.zip
|
||||
https://github.com/adafruit/Adafruit_NeoPixel/archive/1.15.5.zip
|
||||
# renovate: datasource=github-tags depName=Adafruit SSD1306 packageName=adafruit/Adafruit_SSD1306
|
||||
https://github.com/adafruit/Adafruit_SSD1306/archive/refs/tags/2.5.16.zip
|
||||
# renovate: datasource=github-tags depName=Adafruit BMP280 packageName=adafruit/Adafruit_BMP280_Library
|
||||
|
||||
+1
-1
Submodule protobufs updated: 559f3c12dd...108919393a
@@ -158,9 +158,8 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||
#include <RAK13800_W5100S.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_WIFI
|
||||
|
||||
+1
-1
@@ -277,7 +277,7 @@ void fsInit()
|
||||
*/
|
||||
void setupSDCard()
|
||||
{
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) && !defined(HAS_SD_MMC)
|
||||
concurrency::LockGuard g(spiLock);
|
||||
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) {
|
||||
|
||||
+26
-31
@@ -6,7 +6,6 @@
|
||||
#include "SPILock.h"
|
||||
#include "SafeFile.h"
|
||||
#include "gps/RTC.h"
|
||||
#include "graphics/draw/MessageRenderer.h"
|
||||
#include <cstring> // memcpy
|
||||
|
||||
#ifndef MESSAGE_TEXT_POOL_SIZE
|
||||
@@ -181,13 +180,8 @@ const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &pa
|
||||
|
||||
bool isDM = (sm.dest != 0 && sm.dest != NODENUM_BROADCAST);
|
||||
|
||||
if (packet.from == 0) {
|
||||
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
|
||||
sm.ackStatus = AckStatus::NONE;
|
||||
} else {
|
||||
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
|
||||
sm.ackStatus = AckStatus::ACKED;
|
||||
}
|
||||
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
|
||||
sm.ackStatus = (packet.from == 0) ? AckStatus::NONE : AckStatus::ACKED;
|
||||
|
||||
addLiveMessage(sm);
|
||||
|
||||
@@ -372,26 +366,25 @@ void MessageStore::clearAllMessages()
|
||||
#endif
|
||||
}
|
||||
|
||||
// Internal helper: erase first or last message matching a predicate
|
||||
template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deque, Predicate pred, bool fromBack = false)
|
||||
// Internal helpers for targeted erasure.
|
||||
template <typename Predicate> static bool eraseFirstMatch(std::deque<StoredMessage> &deque, Predicate pred)
|
||||
{
|
||||
if (fromBack) {
|
||||
// Iterate from the back and erase all matches from the end
|
||||
for (auto it = deque.rbegin(); it != deque.rend();) {
|
||||
if (pred(*it)) {
|
||||
it = std::deque<StoredMessage>::reverse_iterator(deque.erase(std::next(it).base()));
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
for (auto it = deque.begin(); it != deque.end(); ++it) {
|
||||
if (pred(*it)) {
|
||||
deque.erase(it);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Manual forward search to erase all matches
|
||||
for (auto it = deque.begin(); it != deque.end();) {
|
||||
if (pred(*it)) {
|
||||
it = deque.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Predicate> static void eraseAllMatches(std::deque<StoredMessage> &deque, Predicate pred)
|
||||
{
|
||||
for (auto it = deque.begin(); it != deque.end();) {
|
||||
if (pred(*it)) {
|
||||
it = deque.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,7 +392,9 @@ template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deq
|
||||
// Delete oldest message (RAM + persisted queue)
|
||||
void MessageStore::deleteOldestMessage()
|
||||
{
|
||||
eraseIf(liveMessages, [](StoredMessage &) { return true; });
|
||||
if (!liveMessages.empty()) {
|
||||
liveMessages.pop_front();
|
||||
}
|
||||
saveToFlash();
|
||||
}
|
||||
|
||||
@@ -407,14 +402,14 @@ void MessageStore::deleteOldestMessage()
|
||||
void MessageStore::deleteOldestMessageInChannel(uint8_t channel)
|
||||
{
|
||||
auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; };
|
||||
eraseIf(liveMessages, pred);
|
||||
eraseFirstMatch(liveMessages, pred);
|
||||
saveToFlash();
|
||||
}
|
||||
|
||||
void MessageStore::deleteAllMessagesInChannel(uint8_t channel)
|
||||
{
|
||||
auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; };
|
||||
eraseIf(liveMessages, pred, false /* delete ALL, not just first */);
|
||||
eraseAllMatches(liveMessages, pred);
|
||||
saveToFlash();
|
||||
}
|
||||
|
||||
@@ -427,7 +422,7 @@ void MessageStore::deleteAllMessagesWithPeer(uint32_t peer)
|
||||
uint32_t other = (m.sender == local) ? m.dest : m.sender;
|
||||
return other == peer;
|
||||
};
|
||||
eraseIf(liveMessages, pred, false);
|
||||
eraseAllMatches(liveMessages, pred);
|
||||
saveToFlash();
|
||||
}
|
||||
|
||||
@@ -440,7 +435,7 @@ void MessageStore::deleteOldestMessageWithPeer(uint32_t peer)
|
||||
uint32_t other = (m.sender == nodeDB->getNodeNum()) ? m.dest : m.sender;
|
||||
return other == peer;
|
||||
};
|
||||
eraseIf(liveMessages, pred);
|
||||
eraseFirstMatch(liveMessages, pred);
|
||||
saveToFlash();
|
||||
}
|
||||
|
||||
|
||||
@@ -124,9 +124,6 @@ class MessageStore
|
||||
// Allocate text into pool (used by sender-side code)
|
||||
static uint16_t storeText(const char *src, size_t len);
|
||||
|
||||
// Used when loading from flash to rebuild the text pool
|
||||
static uint16_t rebuildTextFromFlash(const char *src, size_t len);
|
||||
|
||||
private:
|
||||
std::deque<StoredMessage> liveMessages; // Single in-RAM message buffer (also used for persistence)
|
||||
std::string filename; // Flash filename for persistence
|
||||
|
||||
+136
-84
@@ -24,6 +24,13 @@
|
||||
#include "meshUtils.h"
|
||||
#include "power/PowerHAL.h"
|
||||
#include "sleep.h"
|
||||
#ifdef ARCH_ESP32
|
||||
// #include <driver/adc.h>
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_cali_scheme.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include <esp_err.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
#include "api/WiFiServerAPI.h"
|
||||
@@ -63,9 +70,8 @@
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#endif
|
||||
@@ -77,21 +83,86 @@
|
||||
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
|
||||
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1 is default
|
||||
static const adc1_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_unit_t unit = ADC_UNIT_1;
|
||||
#else // ADC2
|
||||
static const adc2_channel_t adc_channel = ADC_CHANNEL;
|
||||
#else // ADC2
|
||||
static const adc_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_unit_t unit = ADC_UNIT_2;
|
||||
RTC_NOINIT_ATTR uint64_t RTC_reg_b;
|
||||
|
||||
#endif // BAT_MEASURE_ADC_UNIT
|
||||
|
||||
esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
|
||||
static adc_oneshot_unit_handle_t adc_handle = nullptr;
|
||||
static adc_cali_handle_t adc_cali_handle = nullptr;
|
||||
static bool adc_calibrated = false;
|
||||
#ifndef ADC_ATTENUATION
|
||||
static const adc_atten_t atten = ADC_ATTEN_DB_12;
|
||||
#else
|
||||
static const adc_atten_t atten = ADC_ATTENUATION;
|
||||
#endif
|
||||
#ifdef ADC_BITWIDTH
|
||||
static const adc_bitwidth_t adc_width = ADC_BITWIDTH;
|
||||
#else
|
||||
static const adc_bitwidth_t adc_width = ADC_BITWIDTH_DEFAULT;
|
||||
#endif
|
||||
|
||||
static int adcBitWidthToBits(adc_bitwidth_t width)
|
||||
{
|
||||
switch (width) {
|
||||
case ADC_BITWIDTH_9:
|
||||
return 9;
|
||||
case ADC_BITWIDTH_10:
|
||||
return 10;
|
||||
case ADC_BITWIDTH_11:
|
||||
return 11;
|
||||
case ADC_BITWIDTH_12:
|
||||
return 12;
|
||||
#ifdef ADC_BITWIDTH_13
|
||||
case ADC_BITWIDTH_13:
|
||||
return 13;
|
||||
#endif
|
||||
default:
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
static bool initAdcCalibration()
|
||||
{
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
LOG_INFO("ADC calibration: curve fitting enabled");
|
||||
return true;
|
||||
}
|
||||
if (ret != ESP_ERR_NOT_SUPPORTED) {
|
||||
LOG_WARN("ADC calibration: curve fitting failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
.default_vref = DEFAULT_VREF,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
LOG_INFO("ADC calibration: line fitting enabled");
|
||||
return true;
|
||||
}
|
||||
if (ret != ESP_ERR_NOT_SUPPORTED) {
|
||||
LOG_WARN("ADC calibration: line fitting failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_INFO("ADC calibration not supported; using approximate scaling");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // BATTERY_PIN && ARCH_ESP32
|
||||
|
||||
#ifdef EXT_PWR_DETECT
|
||||
@@ -367,8 +438,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
scaled *= operativeAdcMultiplier;
|
||||
#elif defined(ARCH_ESP32) // ADC block for espressif platforms
|
||||
raw = espAdcRead();
|
||||
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
|
||||
scaled *= operativeAdcMultiplier;
|
||||
int voltage_mv = 0;
|
||||
if (adc_calibrated && adc_cali_handle) {
|
||||
if (adc_cali_raw_to_voltage(adc_cali_handle, raw, &voltage_mv) != ESP_OK) {
|
||||
LOG_WARN("ADC calibration read failed; using raw value");
|
||||
voltage_mv = 0;
|
||||
}
|
||||
}
|
||||
if (voltage_mv == 0) {
|
||||
// Fallback approximate conversion without calibration
|
||||
const int bits = adcBitWidthToBits(adc_width);
|
||||
const float max_code = powf(2.0f, bits) - 1.0f;
|
||||
voltage_mv = (int)((raw / max_code) * DEFAULT_VREF);
|
||||
}
|
||||
scaled = voltage_mv * operativeAdcMultiplier;
|
||||
#else // block for all other platforms
|
||||
#ifdef ARCH_NRF52
|
||||
concurrency::LockGuard saadcGuard(concurrency::nrf52SaadcLock);
|
||||
@@ -410,51 +493,22 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
uint32_t raw = 0;
|
||||
uint8_t raw_c = 0; // raw reading counter
|
||||
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
if (!adc_handle) {
|
||||
LOG_ERROR("ADC oneshot handle not initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
int val_ = adc1_get_raw(adc_channel);
|
||||
if (val_ >= 0) { // save only valid readings
|
||||
raw += val_;
|
||||
int val = 0;
|
||||
esp_err_t err = adc_oneshot_read(adc_handle, adc_channel, &val);
|
||||
if (err == ESP_OK) {
|
||||
raw += val;
|
||||
raw_c++;
|
||||
}
|
||||
// delayMicroseconds(100);
|
||||
}
|
||||
#else // ADC2
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3
|
||||
// ADC2 wifi bug workaround not required, breaks compile
|
||||
// On ESP32S3, ADC2 can take turns with Wifi (?)
|
||||
|
||||
int32_t adc_buf;
|
||||
esp_err_t read_result;
|
||||
|
||||
// Multiple samples
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
adc_buf = 0;
|
||||
read_result = -1;
|
||||
|
||||
read_result = adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
|
||||
if (read_result == ESP_OK) {
|
||||
raw += adc_buf;
|
||||
raw_c++; // Count valid samples
|
||||
} else {
|
||||
LOG_DEBUG("An attempt to sample ADC2 failed");
|
||||
LOG_DEBUG("ADC read failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
#else // Other ESP32
|
||||
int32_t adc_buf = 0;
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
// ADC2 wifi bug workaround, see
|
||||
// https://github.com/espressif/arduino-esp32/issues/102
|
||||
WRITE_PERI_REG(SENS_SAR_READ_CTRL2_REG, RTC_reg_b);
|
||||
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV);
|
||||
adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
|
||||
raw += adc_buf;
|
||||
raw_c++;
|
||||
}
|
||||
#endif // BAT_MEASURE_ADC_UNIT
|
||||
|
||||
#endif // End BAT_MEASURE_ADC_UNIT
|
||||
return (raw / (raw_c < 1 ? 1 : raw_c));
|
||||
}
|
||||
#endif
|
||||
@@ -666,42 +720,31 @@ bool Power::analogInit()
|
||||
#ifdef ARCH_STM32WL
|
||||
analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS);
|
||||
#elif defined(ARCH_ESP32) // ESP32 needs special analog stuff
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = unit,
|
||||
};
|
||||
|
||||
#ifndef ADC_WIDTH // max resolution by default
|
||||
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
|
||||
#else
|
||||
static const adc_bits_width_t width = ADC_WIDTH;
|
||||
#endif
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
adc1_config_width(width);
|
||||
adc1_config_channel_atten(adc_channel, atten);
|
||||
#else // ADC2
|
||||
adc2_config_channel_atten(adc_channel, atten);
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S3
|
||||
// ADC2 wifi bug workaround
|
||||
// Not required with ESP32S3, breaks compile
|
||||
RTC_reg_b = READ_PERI_REG(SENS_SAR_READ_CTRL2_REG);
|
||||
#endif
|
||||
#endif
|
||||
// calibrate ADC
|
||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_characs);
|
||||
// show ADC characterization base
|
||||
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||
LOG_INFO("ADC config based on Two Point values stored in eFuse");
|
||||
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
||||
LOG_INFO("ADC config based on reference voltage stored in eFuse");
|
||||
if (!adc_handle) {
|
||||
esp_err_t err = adc_oneshot_new_unit(&init_config, &adc_handle);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("ADC oneshot init failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
// ESP32S3
|
||||
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
|
||||
LOG_INFO("ADC config based on Two Point values and fitting curve "
|
||||
"coefficients stored in eFuse");
|
||||
|
||||
adc_oneshot_chan_cfg_t chan_cfg = {
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
};
|
||||
|
||||
esp_err_t err = adc_oneshot_config_channel(adc_handle, adc_channel, &chan_cfg);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("ADC channel config failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
LOG_INFO("ADC config based on default reference voltage");
|
||||
}
|
||||
#endif // ARCH_ESP32
|
||||
|
||||
adc_calibrated = initAdcCalibration();
|
||||
#endif // ARCH_ESP32
|
||||
|
||||
// NRF52 ADC init moved to powerHAL_init in nrf52 platform
|
||||
|
||||
@@ -904,7 +947,16 @@ void Power::readPowerStatus()
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent);
|
||||
if (millis() > lastLogTime + 50 * 1000) {
|
||||
|
||||
// Log battery-presence transitions once; skip OptUnknown so we don't lie before the first probe.
|
||||
static OptionalBool prevHasBattery = OptUnknown;
|
||||
if (hasBattery != OptUnknown && hasBattery != prevHasBattery) {
|
||||
LOG_INFO("Power: battery hardware %s", hasBattery == OptTrue ? "detected" : "absent (USB-only)");
|
||||
prevHasBattery = hasBattery;
|
||||
}
|
||||
|
||||
// Periodic telemetry only emits when a battery is actually present (otherwise values are constant -1/0).
|
||||
if (hasBattery == OptTrue && !Throttle::isWithinTimespanMs(lastLogTime, 50 * 1000)) {
|
||||
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(),
|
||||
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
||||
lastLogTime = millis();
|
||||
|
||||
@@ -230,9 +230,9 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
meshtastic_LogRecord logRecord = meshtastic_LogRecord_init_zero;
|
||||
logRecord.level = getLogLevel(logLevel);
|
||||
vsprintf(logRecord.message, format, arg);
|
||||
vsnprintf(logRecord.message, sizeof(logRecord.message), format, arg);
|
||||
if (thread)
|
||||
strcpy(logRecord.source, thread->ThreadName.c_str());
|
||||
strlcpy(logRecord.source, thread->ThreadName.c_str(), sizeof(logRecord.source));
|
||||
logRecord.time = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||
|
||||
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[meshtastic_LogRecord_size]);
|
||||
|
||||
@@ -561,5 +561,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define HAS_SCREEN 0
|
||||
#endif
|
||||
|
||||
#ifndef USE_ETHERNET_DEFAULT
|
||||
#define USE_ETHERNET_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
#include "RF95Configuration.h"
|
||||
|
||||
+10
-142
@@ -65,7 +65,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "mesh/Default.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#include "modules/WaypointModule.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
@@ -231,6 +230,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
|
||||
#endif
|
||||
// Store the message and set the expiration timestamp
|
||||
strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255);
|
||||
NotificationRenderer::parseBannerMessageWithFonts(NotificationRenderer::alertBannerMessage);
|
||||
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
|
||||
NotificationRenderer::alertBannerUntil =
|
||||
(banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs;
|
||||
@@ -240,9 +240,9 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
|
||||
NotificationRenderer::alertBannerCallback = banner_overlay_options.bannerCallback;
|
||||
NotificationRenderer::curSelected = banner_overlay_options.InitialSelected;
|
||||
NotificationRenderer::pauseBanner = false;
|
||||
NotificationRenderer::current_notification_type = notificationTypeEnum::selection_picker;
|
||||
NotificationRenderer::current_notification_type = banner_overlay_options.notificationType;
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
ui->setTargetFPS(60);
|
||||
updateUiFrame(ui);
|
||||
}
|
||||
@@ -264,7 +264,7 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct
|
||||
NotificationRenderer::current_notification_type = notificationTypeEnum::node_picker;
|
||||
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
ui->setTargetFPS(60);
|
||||
updateUiFrame(ui);
|
||||
}
|
||||
@@ -288,7 +288,7 @@ void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t
|
||||
NotificationRenderer::currentNumber = 0;
|
||||
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
ui->setTargetFPS(60);
|
||||
updateUiFrame(ui);
|
||||
}
|
||||
@@ -311,7 +311,7 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
|
||||
|
||||
// Set the overlay using the same pattern as other notification types
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
ui->setTargetFPS(60);
|
||||
updateUiFrame(ui);
|
||||
}
|
||||
@@ -696,7 +696,7 @@ void Screen::setup()
|
||||
static OverlayCallback overlays[] = {
|
||||
graphics::UIRenderer::drawNavigationBar // Custom indicator icons for each frame
|
||||
};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 1);
|
||||
|
||||
// Enable UTF-8 to display mapping
|
||||
dispdev->setFontTableLookupFunction(customFontTableLookup);
|
||||
@@ -1313,7 +1313,7 @@ void Screen::setFrames(FrameFocus focus)
|
||||
|
||||
// Add overlays: frame icons and alert banner)
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
|
||||
prevFrame = -1; // Force drawFavoriteNode to pick a new node (because our list just changed)
|
||||
|
||||
@@ -1643,138 +1643,6 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handles when message is received; will jump to text message frame.
|
||||
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
if (showingNormalScreen) {
|
||||
if (packet->from == 0) {
|
||||
// Outgoing message (likely sent from phone)
|
||||
devicestate.has_rx_text_message = false;
|
||||
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
||||
hiddenFrames.textMessage = true;
|
||||
hasUnreadMessage = false; // Clear unread state when user replies
|
||||
|
||||
setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
|
||||
} else {
|
||||
// Incoming message
|
||||
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
||||
hasUnreadMessage = true; // Enables mail icon in the header
|
||||
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view (no-op during text_input)
|
||||
|
||||
// Only wake/force display if the configuration allows it
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
setOn(true); // Wake up the screen first
|
||||
forceDisplay(); // Forces screen redraw
|
||||
}
|
||||
// === Prepare banner/popup content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const meshtastic_Channel channel =
|
||||
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||
const char *longName = nodeInfoLiteHasUser(node) ? node->long_name : nullptr;
|
||||
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
|
||||
char banner[256];
|
||||
|
||||
bool isAlert = false;
|
||||
|
||||
if (moduleConfig.external_notification.alert_bell || moduleConfig.external_notification.alert_bell_vibra ||
|
||||
moduleConfig.external_notification.alert_bell_buzzer)
|
||||
// Check for bell character to determine if this message is an alert
|
||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||
if (msgRaw[i] == ASCII_BELL) {
|
||||
isAlert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
||||
// 'mute' preferences set to any specific node or channel.
|
||||
// If on-screen keyboard is active, show a transient popup over keyboard instead of interrupting it
|
||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||
// Wake and force redraw so popup is visible immediately
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
setOn(true);
|
||||
forceDisplay();
|
||||
}
|
||||
|
||||
// Build popup: title = message source name, content = message text (sanitized)
|
||||
// Title
|
||||
char titleBuf[64] = {0};
|
||||
if (longName && longName[0]) {
|
||||
// Sanitize sender name
|
||||
std::string t = sanitizeString(longName);
|
||||
strncpy(titleBuf, t.c_str(), sizeof(titleBuf) - 1);
|
||||
} else {
|
||||
strncpy(titleBuf, "Message", sizeof(titleBuf) - 1);
|
||||
}
|
||||
|
||||
// Content: payload bytes may not be null-terminated, remove ASCII_BELL and sanitize
|
||||
char content[256] = {0};
|
||||
{
|
||||
std::string raw;
|
||||
raw.reserve(packet->decoded.payload.size);
|
||||
for (size_t i = 0; i < packet->decoded.payload.size; ++i) {
|
||||
char c = msgRaw[i];
|
||||
if (c == ASCII_BELL)
|
||||
continue; // strip bell
|
||||
raw.push_back(c);
|
||||
}
|
||||
std::string sanitized = sanitizeString(raw);
|
||||
strncpy(content, sanitized.c_str(), sizeof(content) - 1);
|
||||
}
|
||||
|
||||
NotificationRenderer::showKeyboardMessagePopupWithTitle(titleBuf, content, 3000);
|
||||
|
||||
// Maintain existing buzzer behavior on M5 if applicable
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||
(!isBroadcast(packet->to) && isToUs(packet))) {
|
||||
playLongBeep();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// No keyboard active: use regular banner flow, respecting mute settings
|
||||
if (isAlert) {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "Alert Received");
|
||||
}
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
|
||||
if (longName && longName[0]) {
|
||||
if (currentResolution == ScreenResolution::UltraLow) {
|
||||
strcpy(banner, "New Message");
|
||||
} else {
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
}
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
screen->setOn(true);
|
||||
screen->showSimpleBanner(banner, 1500);
|
||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||
(!isBroadcast(packet->to) && isToUs(packet))) {
|
||||
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
|
||||
// - packet contains an alert and alert bell buzzer is enabled
|
||||
// - packet is a non-broadcast that is addressed to this node
|
||||
playLongBeep();
|
||||
}
|
||||
#else
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Triggered by MeshModules
|
||||
int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
{
|
||||
@@ -1820,7 +1688,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||
NotificationRenderer::inEvent = *event;
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
setFastFramerate(); // Draw ASAP
|
||||
updateUiFrame(ui);
|
||||
return 0;
|
||||
@@ -1835,7 +1703,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
if (NotificationRenderer::isOverlayBannerShowing()) {
|
||||
NotificationRenderer::inEvent = *event;
|
||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
ui->setOverlays(overlays, 2);
|
||||
setFastFramerate(); // Draw ASAP
|
||||
updateUiFrame(ui);
|
||||
|
||||
|
||||
@@ -609,7 +609,6 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
// Handle observer events
|
||||
int handleStatusUpdate(const meshtastic::Status *arg);
|
||||
int handleTextMessage(const meshtastic_MeshPacket *packet);
|
||||
int handleUIFrameEvent(const UIFrameEvent *arg);
|
||||
int handleInputEvent(const InputEvent *arg);
|
||||
int handleAdminMessage(AdminModule_ObserverData *arg);
|
||||
|
||||
@@ -88,6 +88,16 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// nRF52 flash optimization: re-route FONT_LARGE_LOCAL to the 16pt glyph so
|
||||
// the display-tier dispatch below picks up 16pt everywhere it would have used
|
||||
// 24pt. Drops the ~9.6 KB ArialMT_Plain_24 table from the linked binary.
|
||||
// Set MESHTASTIC_LARGE_FONT_24PT=1 in build_flags to opt out per variant
|
||||
// (undefined or 0 keeps the optimization on).
|
||||
#if defined(ARCH_NRF52) && (!defined(MESHTASTIC_LARGE_FONT_24PT) || MESHTASTIC_LARGE_FONT_24PT == 0)
|
||||
#undef FONT_LARGE_LOCAL
|
||||
#define FONT_LARGE_LOCAL FONT_MEDIUM_LOCAL
|
||||
#endif
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||
defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)) && \
|
||||
|
||||
@@ -57,6 +57,70 @@ BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOp
|
||||
return bannerOptions;
|
||||
}
|
||||
|
||||
const StoredMessage *getNewestMessageForActiveThread()
|
||||
{
|
||||
const auto &messages = messageStore.getMessages();
|
||||
if (messages.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto mode = graphics::MessageRenderer::getThreadMode();
|
||||
const int channel = graphics::MessageRenderer::getThreadChannel();
|
||||
const uint32_t peer = graphics::MessageRenderer::getThreadPeer();
|
||||
const uint32_t localNode = nodeDB->getNodeNum();
|
||||
|
||||
if (mode == graphics::MessageRenderer::ThreadMode::ALL) {
|
||||
return &messages.back();
|
||||
}
|
||||
|
||||
for (auto it = messages.rbegin(); it != messages.rend(); ++it) {
|
||||
const StoredMessage &m = *it;
|
||||
|
||||
if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
|
||||
if (m.type == MessageType::BROADCAST && static_cast<int>(m.channelIndex) == channel) {
|
||||
return &m;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
|
||||
if (m.type != MessageType::DM_TO_US) {
|
||||
continue;
|
||||
}
|
||||
const uint32_t other = (m.sender == localNode) ? m.dest : m.sender;
|
||||
if (other == peer) {
|
||||
return &m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void launchReplyForMessage(const StoredMessage &message, bool freetext)
|
||||
{
|
||||
if (message.type == MessageType::BROADCAST || message.dest == NODENUM_BROADCAST) {
|
||||
if (freetext) {
|
||||
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, message.channelIndex);
|
||||
} else {
|
||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, message.channelIndex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t localNode = nodeDB->getNodeNum();
|
||||
const uint32_t peer = (message.sender == localNode) ? message.dest : message.sender;
|
||||
if (peer == 0 || peer == NODENUM_BROADCAST) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (freetext) {
|
||||
cannedMessageModule->LaunchFreetextWithDestination(peer);
|
||||
} else {
|
||||
cannedMessageModule->LaunchWithDestination(peer);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
menuHandler::screenMenus menuHandler::menuQueue = MenuNone;
|
||||
@@ -594,9 +658,12 @@ void menuHandler::messageResponseMenu()
|
||||
|
||||
#ifdef HAS_I2S
|
||||
} else if (selected == Aloud) {
|
||||
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
|
||||
const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes);
|
||||
audioThread->readAloud(msg);
|
||||
if (const StoredMessage *latest = getNewestMessageForActiveThread()) {
|
||||
const char *msg = MessageStore::getText(*latest);
|
||||
if (msg && msg[0]) {
|
||||
audioThread->readAloud(msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@@ -656,20 +723,12 @@ void menuHandler::replyMenu()
|
||||
|
||||
// Preset reply
|
||||
if (selected == ReplyPreset) {
|
||||
|
||||
if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
|
||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, ch);
|
||||
|
||||
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
|
||||
cannedMessageModule->LaunchWithDestination(peer);
|
||||
|
||||
} else {
|
||||
// Fallback for last received message
|
||||
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
|
||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
|
||||
} else {
|
||||
cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from);
|
||||
}
|
||||
} else if (const StoredMessage *latest = getNewestMessageForActiveThread()) {
|
||||
launchReplyForMessage(*latest, false);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -677,20 +736,12 @@ void menuHandler::replyMenu()
|
||||
|
||||
// Freetext reply
|
||||
if (selected == ReplyFreetext) {
|
||||
|
||||
if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
|
||||
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, ch);
|
||||
|
||||
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
|
||||
cannedMessageModule->LaunchFreetextWithDestination(peer);
|
||||
|
||||
} else {
|
||||
// Fallback for last received message
|
||||
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
|
||||
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
|
||||
} else {
|
||||
cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from);
|
||||
}
|
||||
} else if (const StoredMessage *latest = getNewestMessageForActiveThread()) {
|
||||
launchReplyForMessage(*latest, true);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -66,6 +66,110 @@ uint32_t pow_of_10(uint32_t n)
|
||||
return ret;
|
||||
}
|
||||
|
||||
char graphics::NotificationRenderer::alertBannerLines[MAX_LINES + 1][64] = {};
|
||||
uint8_t graphics::NotificationRenderer::alertBannerLineCount = 0;
|
||||
graphics::NotificationRenderer::BannerFont graphics::NotificationRenderer::alertBannerLineFonts[MAX_LINES + 1] = {};
|
||||
|
||||
static inline graphics::NotificationRenderer::BannerFont parseFontTagPrefix(const char *&p)
|
||||
{
|
||||
// Tags must be at the start of the line:
|
||||
// [S] small, [M] medium, [L] large
|
||||
if (p && p[0] == '[' && p[2] == ']' && p[1] != '\0') {
|
||||
char t = p[1];
|
||||
if (t == 'S') {
|
||||
p += 3;
|
||||
return graphics::NotificationRenderer::BANNER_FONT_SMALL;
|
||||
}
|
||||
if (t == 'M') {
|
||||
p += 3;
|
||||
return graphics::NotificationRenderer::BANNER_FONT_MEDIUM;
|
||||
}
|
||||
if (t == 'L') {
|
||||
p += 3;
|
||||
return graphics::NotificationRenderer::BANNER_FONT_LARGE;
|
||||
}
|
||||
}
|
||||
return graphics::NotificationRenderer::BANNER_FONT_DEFAULT;
|
||||
}
|
||||
|
||||
static inline const uint8_t *fontForBannerLine(graphics::NotificationRenderer::BannerFont f)
|
||||
{
|
||||
switch (f) {
|
||||
case graphics::NotificationRenderer::BANNER_FONT_SMALL:
|
||||
return FONT_SMALL;
|
||||
case graphics::NotificationRenderer::BANNER_FONT_MEDIUM:
|
||||
return FONT_MEDIUM;
|
||||
case graphics::NotificationRenderer::BANNER_FONT_LARGE:
|
||||
return FONT_LARGE;
|
||||
case graphics::NotificationRenderer::BANNER_FONT_DEFAULT:
|
||||
default:
|
||||
return FONT_SMALL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t effectiveLineHeightForBannerLine(graphics::NotificationRenderer::BannerFont f)
|
||||
{
|
||||
uint8_t height = FONT_HEIGHT_SMALL;
|
||||
switch (f) {
|
||||
case graphics::NotificationRenderer::BANNER_FONT_MEDIUM:
|
||||
height = FONT_HEIGHT_MEDIUM;
|
||||
break;
|
||||
case graphics::NotificationRenderer::BANNER_FONT_LARGE:
|
||||
height = FONT_HEIGHT_LARGE;
|
||||
break;
|
||||
case graphics::NotificationRenderer::BANNER_FONT_SMALL:
|
||||
case graphics::NotificationRenderer::BANNER_FONT_DEFAULT:
|
||||
default:
|
||||
height = FONT_HEIGHT_SMALL;
|
||||
break;
|
||||
}
|
||||
return (height > 3) ? (height - 3) : height;
|
||||
}
|
||||
|
||||
void graphics::NotificationRenderer::parseBannerMessageWithFonts(const char *message)
|
||||
{
|
||||
alertBannerLineCount = 0;
|
||||
for (uint8_t i = 0; i < (MAX_LINES + 1); i++) {
|
||||
alertBannerLines[i][0] = '\0';
|
||||
alertBannerLineFonts[i] = BANNER_FONT_DEFAULT;
|
||||
}
|
||||
|
||||
if (!message || !message[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *p = message;
|
||||
|
||||
while (*p && alertBannerLineCount < (MAX_LINES + 1)) {
|
||||
const char *lineStart = p;
|
||||
while (*p && *p != '\n') {
|
||||
p++;
|
||||
}
|
||||
|
||||
char tmp[64] = {0};
|
||||
size_t len = (size_t)(p - lineStart);
|
||||
if (len > (sizeof(tmp) - 1)) {
|
||||
len = sizeof(tmp) - 1;
|
||||
}
|
||||
memcpy(tmp, lineStart, len);
|
||||
tmp[len] = '\0';
|
||||
|
||||
// Tag at start
|
||||
const char *tp = tmp;
|
||||
BannerFont f = parseFontTagPrefix(tp);
|
||||
alertBannerLineFonts[alertBannerLineCount] = f;
|
||||
|
||||
// Store stripped text
|
||||
strncpy(alertBannerLines[alertBannerLineCount], tp, sizeof(alertBannerLines[0]) - 1);
|
||||
alertBannerLines[alertBannerLineCount][sizeof(alertBannerLines[0]) - 1] = '\0';
|
||||
alertBannerLineCount++;
|
||||
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used on boot when a certificate is being created
|
||||
void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -375,23 +479,37 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
||||
const char *lineStarts[MAX_LINES + 1] = {0};
|
||||
uint16_t lineCount = 0;
|
||||
char lineBuffer[40] = {0};
|
||||
bool useTaggedTextBanner =
|
||||
(current_notification_type == notificationTypeEnum::text_banner && alertBannerOptions == 0 && alertBannerLineCount > 0);
|
||||
|
||||
// Parse lines
|
||||
char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
|
||||
lineStarts[lineCount] = alertBannerMessage;
|
||||
if (useTaggedTextBanner) {
|
||||
lineCount = std::min<uint8_t>(alertBannerLineCount, MAX_LINES);
|
||||
for (uint16_t i = 0; i < lineCount; i++) {
|
||||
lineStarts[i] = alertBannerLines[i];
|
||||
lineLengths[i] = strlen(lineStarts[i]);
|
||||
display->setFont(fontForBannerLine(alertBannerLineFonts[i]));
|
||||
lineWidths[i] = display->getStringWidth(lineStarts[i], lineLengths[i], true);
|
||||
if (lineWidths[i] > maxWidth)
|
||||
maxWidth = lineWidths[i];
|
||||
}
|
||||
} else {
|
||||
char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
|
||||
lineStarts[lineCount] = alertBannerMessage;
|
||||
|
||||
while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
|
||||
lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
|
||||
lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
|
||||
if (lineStarts[lineCount + 1][0] == '\n')
|
||||
lineStarts[lineCount + 1] += 1;
|
||||
lineWidths[lineCount] = display->getStringWidth(lineStarts[lineCount], lineLengths[lineCount], true);
|
||||
if (lineWidths[lineCount] > maxWidth)
|
||||
maxWidth = lineWidths[lineCount];
|
||||
lineCount++;
|
||||
while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
|
||||
lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
|
||||
lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
|
||||
if (lineStarts[lineCount + 1][0] == '\n')
|
||||
lineStarts[lineCount + 1] += 1;
|
||||
lineWidths[lineCount] = display->getStringWidth(lineStarts[lineCount], lineLengths[lineCount], true);
|
||||
if (lineWidths[lineCount] > maxWidth)
|
||||
maxWidth = lineWidths[lineCount];
|
||||
lineCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Measure option widths
|
||||
display->setFont(FONT_SMALL);
|
||||
for (int i = 0; i < alertBannerOptions; i++) {
|
||||
optionWidths[i] = display->getStringWidth(optionsArrayPtr[i], strlen(optionsArrayPtr[i]), true);
|
||||
if (optionWidths[i] > maxWidth)
|
||||
@@ -505,6 +623,10 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
bool needs_bell = false;
|
||||
uint16_t lineWidths[totalLines] = {0};
|
||||
uint16_t lineLengths[totalLines] = {0};
|
||||
BannerFont lineFonts[totalLines] = {};
|
||||
uint8_t lineEffectiveHeights[totalLines] = {0};
|
||||
const char *renderLines[totalLines] = {0};
|
||||
bool useTaggedBannerFonts = (current_notification_type == notificationTypeEnum::text_banner && alertBannerOptions == 0);
|
||||
|
||||
if (maxWidth != 0)
|
||||
is_picker = true;
|
||||
@@ -517,11 +639,22 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
uint16_t widestLineWithBars = 0;
|
||||
|
||||
while (lines[lineCount] != nullptr) {
|
||||
auto newlinePointer = strchr(lines[lineCount], '\n');
|
||||
const char *renderText = lines[lineCount];
|
||||
BannerFont lineFont = BANNER_FONT_DEFAULT;
|
||||
if (useTaggedBannerFonts && lineCount < alertBannerLineCount) {
|
||||
renderText = alertBannerLines[lineCount];
|
||||
lineFont = alertBannerLineFonts[lineCount];
|
||||
}
|
||||
renderLines[lineCount] = renderText;
|
||||
lineFonts[lineCount] = lineFont;
|
||||
lineEffectiveHeights[lineCount] = effectiveLineHeightForBannerLine(lineFont);
|
||||
display->setFont(fontForBannerLine(lineFont));
|
||||
|
||||
auto newlinePointer = strchr(renderText, '\n');
|
||||
if (newlinePointer)
|
||||
lineLengths[lineCount] = (newlinePointer - lines[lineCount]); // Check for newlines first
|
||||
else // if the newline wasn't found, then pull string length from strlen
|
||||
lineLengths[lineCount] = strlen(lines[lineCount]);
|
||||
lineLengths[lineCount] = (newlinePointer - renderText);
|
||||
else
|
||||
lineLengths[lineCount] = strlen(renderText);
|
||||
|
||||
if (current_notification_type == notificationTypeEnum::node_picker) {
|
||||
char measureBuffer[64] = {0};
|
||||
@@ -533,7 +666,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
|
||||
// Consider extra width for signal bars on lines that contain "Signal:"
|
||||
uint16_t potentialWidth = lineWidths[lineCount];
|
||||
if (graphics::bannerSignalBars >= 0 && strncmp(lines[lineCount], "Signal:", 7) == 0) {
|
||||
if (graphics::bannerSignalBars >= 0 && strncmp(renderText, "Signal:", 7) == 0) {
|
||||
const int totalBars = 5;
|
||||
const int barWidth = 3;
|
||||
const int barSpacing = 2;
|
||||
@@ -569,8 +702,21 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
|
||||
uint16_t screenHeight = display->height();
|
||||
uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
|
||||
uint8_t visibleTotalLines = std::min<uint8_t>(lineCount, (screenHeight - vPadding * 2) / effectiveLineHeight);
|
||||
uint16_t contentHeight = visibleTotalLines * effectiveLineHeight;
|
||||
uint8_t visibleTotalLines = 0;
|
||||
uint16_t contentHeight = 0;
|
||||
const uint16_t availableHeight = (screenHeight > (vPadding * 2)) ? (screenHeight - vPadding * 2) : 0;
|
||||
for (uint8_t i = 0; i < lineCount; i++) {
|
||||
uint8_t thisLineHeight = lineEffectiveHeights[i] ? lineEffectiveHeights[i] : effectiveLineHeight;
|
||||
if (contentHeight + thisLineHeight > availableHeight) {
|
||||
break;
|
||||
}
|
||||
contentHeight += thisLineHeight;
|
||||
visibleTotalLines++;
|
||||
}
|
||||
if (visibleTotalLines == 0 && lineCount > 0) {
|
||||
visibleTotalLines = 1;
|
||||
contentHeight = lineEffectiveHeights[0] ? lineEffectiveHeights[0] : effectiveLineHeight;
|
||||
}
|
||||
uint16_t boxHeight = contentHeight + vPadding * 2;
|
||||
if (visibleTotalLines == 1) {
|
||||
boxHeight += (currentResolution == ScreenResolution::High) ? 4 : 3;
|
||||
@@ -616,15 +762,18 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
|
||||
// Draw Content
|
||||
int16_t lineY = boxTop + vPadding;
|
||||
for (int i = 0; i < lineCount; i++) {
|
||||
for (int i = 0; i < visibleTotalLines; i++) {
|
||||
display->setFont(fontForBannerLine(lineFonts[i]));
|
||||
int16_t thisLineHeight = lineEffectiveHeights[i] ? lineEffectiveHeights[i] : effectiveLineHeight;
|
||||
int16_t textX = boxLeft + (boxWidth - lineWidths[i]) / 2;
|
||||
if (needs_bell && i == 0) {
|
||||
int bellY = lineY + (FONT_HEIGHT_SMALL - 8) / 2;
|
||||
int fontHeight = thisLineHeight + 3;
|
||||
int bellY = lineY + (fontHeight - 8) / 2;
|
||||
display->drawXbm(textX - 10, bellY, 8, 8, bell_alert);
|
||||
display->drawXbm(textX + lineWidths[i] + 2, bellY, 8, 8, bell_alert);
|
||||
}
|
||||
char lineBuffer[lineLengths[i] + 1];
|
||||
strncpy(lineBuffer, lines[i], lineLengths[i]);
|
||||
strncpy(lineBuffer, renderLines[i], lineLengths[i]);
|
||||
lineBuffer[lineLengths[i]] = '\0';
|
||||
// Determine if this is a pop-up or a pick list
|
||||
if (alertBannerOptions > 0 && i == 0) {
|
||||
@@ -658,7 +807,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
display->drawString(textX, lineY - yOffset, lineBuffer);
|
||||
}
|
||||
display->setColor(WHITE);
|
||||
lineY += (effectiveLineHeight - 2 - background_yOffset);
|
||||
lineY += (thisLineHeight - 2 - background_yOffset);
|
||||
} else {
|
||||
// Pop-up
|
||||
// If this is the Signal line, center text + bars as one group
|
||||
@@ -716,7 +865,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
display->drawString(textX, lineY, lineBuffer);
|
||||
}
|
||||
}
|
||||
lineY += (effectiveLineHeight);
|
||||
lineY += thisLineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,12 @@ class NotificationRenderer
|
||||
|
||||
static bool pauseBanner;
|
||||
|
||||
enum BannerFont : uint8_t { BANNER_FONT_DEFAULT = 0, BANNER_FONT_SMALL, BANNER_FONT_MEDIUM, BANNER_FONT_LARGE };
|
||||
|
||||
static char alertBannerLines[MAX_LINES + 1][64]; // parsed text per line
|
||||
static uint8_t alertBannerLineCount;
|
||||
static BannerFont alertBannerLineFonts[MAX_LINES + 1];
|
||||
static void parseBannerMessageWithFonts(const char *message);
|
||||
static void resetBanner();
|
||||
static void showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs);
|
||||
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
Shows the latest incoming text message, as well as sender.
|
||||
Both broadcast and direct messages will be shown here, from all channels.
|
||||
|
||||
This module doesn't doesn't use the devicestate.rx_text_message,' as this is overwritten to contain outgoing messages
|
||||
This module doesn't collect its own text message. Instead, the WindowManager stores the most recent incoming text message.
|
||||
This is available to any interested modules (SingeMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
|
||||
This is available to any interested modules (SingleMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
|
||||
|
||||
We do still receive notifications from the text message module though,
|
||||
to know when a new message has arrived, and trigger the update.
|
||||
@@ -46,4 +45,4 @@ class AllMessageApplet : public Applet
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
/*
|
||||
|
||||
Shows the latest incoming *Direct Message* (DM), as well as sender.
|
||||
This compliments the threaded message applets
|
||||
This complements the threaded message applets
|
||||
|
||||
This module doesn't doesn't use the devicestate.rx_text_message,' as this is overwritten to contain outgoing messages
|
||||
This module doesn't collect its own text message. Instead, the WindowManager stores the most recent incoming text message.
|
||||
This is available to any interested modules (SingeMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
|
||||
This is available to any interested modules (SingleMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
|
||||
|
||||
We do still receive notifications from the text message module though,
|
||||
to know when a new message has arrived, and trigger the update.
|
||||
@@ -46,4 +45,4 @@ class DMApplet : public Applet
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -525,7 +525,7 @@ int InkHUD::Events::beforeReboot(void *unused)
|
||||
// Callback when a new text message is received
|
||||
// Caches the most recently received message, for use by applets
|
||||
// Rx does not trigger a save to flash, however the data *will* be saved alongside other during shutdown, etc.
|
||||
// Note: this is different from devicestate.rx_text_message, which may contain an *outgoing* message
|
||||
// Note: this is intentionally separate from device-state message fields.
|
||||
int InkHUD::Events::onReceiveTextMessage(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
// Short circuit: don't store outgoing messages
|
||||
|
||||
@@ -53,10 +53,11 @@ void InkHUD::MessageStore::saveToFlash()
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.timestamp), sizeof(m.timestamp)); // Write timestamp. 4 bytes
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.sender), sizeof(m.sender)); // Write sender NodeId. 4 Bytes
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.channelIndex), sizeof(m.channelIndex)); // Write channel index. 1 Byte
|
||||
f.write(reinterpret_cast<const uint8_t *>(m.text.c_str()), min(MAX_MESSAGE_SIZE, m.text.size())); // Write message text
|
||||
f.write('\0'); // Append null term
|
||||
LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast<uint32_t>(i), min(MAX_MESSAGE_SIZE, m.text.size()),
|
||||
m.text.c_str());
|
||||
f.write(reinterpret_cast<const uint8_t *>(m.text.c_str()),
|
||||
min((size_t)MAX_MESSAGE_SIZE, m.text.size())); // Write message text
|
||||
f.write('\0'); // Append null term
|
||||
LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast<uint32_t>(i),
|
||||
min((size_t)MAX_MESSAGE_SIZE, m.text.size()), m.text.c_str());
|
||||
}
|
||||
|
||||
// Release firmware's SPI lock, because SafeFile::close needs it
|
||||
|
||||
@@ -121,8 +121,7 @@ class Persistence
|
||||
|
||||
// Most recently received text message
|
||||
// Value is updated by InkHUD::WindowManager, as a courtesy to applets
|
||||
// Note: different from devicestate.rx_text_message,
|
||||
// which may contain an *outgoing message* to broadcast
|
||||
// InkHUD keeps its own latest-message cache for applets.
|
||||
struct LatestMessage {
|
||||
MessageStore::Message broadcast; // Most recent message received broadcast
|
||||
MessageStore::Message dm; // Most recent received DM
|
||||
|
||||
@@ -464,7 +464,7 @@ Most recently received text message
|
||||
|
||||
Collected here, so various user applets don't all have to store their own copy of this info.
|
||||
|
||||
We are unable to use `devicestate.rx_text_message` for this purpose, because:
|
||||
We keep this separate latest-message cache for this purpose, because:
|
||||
|
||||
- it is cleared by an outgoing text message
|
||||
- we want to store both a recent broadcast and a recent DM
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
|
||||
|
||||
// REVISIT esp_adc_cal.h
|
||||
// "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h"
|
||||
#include <esp_adc_cal.h>
|
||||
#include <soc/adc_channel.h>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ TLoraPagerKeyboard::TLoraPagerKeyboard()
|
||||
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(UINT8_MAX),
|
||||
next_key(UINT8_MAX), last_tap(0L), char_idx(0), tap_interval(0)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
ledcAttach(KB_BL_PIN, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
#else
|
||||
ledcSetup(LEDC_BACKLIGHT_CHANNEL, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
@@ -108,7 +108,7 @@ void TLoraPagerKeyboard::setBacklight(bool on)
|
||||
uint32_t _brightness = 0;
|
||||
if (on)
|
||||
_brightness = brightness;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
ledcWrite(KB_BL_PIN, _brightness);
|
||||
#else
|
||||
ledcWrite(LEDC_BACKLIGHT_CHANNEL, _brightness);
|
||||
|
||||
+1
-1
@@ -245,7 +245,7 @@ const char *getDeviceName()
|
||||
uint32_t timeLastPowered = 0;
|
||||
|
||||
static OSThread *powerFSMthread;
|
||||
OSThread *ambientLightingThread;
|
||||
AmbientLightingThread *ambientLightingThread;
|
||||
|
||||
RadioLibHal *RadioLibHAL = NULL;
|
||||
|
||||
|
||||
+162
-57
@@ -25,6 +25,7 @@
|
||||
#include "mesh/generated/meshtastic/deviceonly_legacy.pb.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include "xmodem.h"
|
||||
#include <ErriezCRC32.h>
|
||||
#include <algorithm>
|
||||
#include <pb_decode.h>
|
||||
@@ -81,6 +82,14 @@ static unsigned char userprefs_admin_key_1[] = USERPREFS_USE_ADMIN_KEY_1;
|
||||
static unsigned char userprefs_admin_key_2[] = USERPREFS_USE_ADMIN_KEY_2;
|
||||
#endif
|
||||
|
||||
// Weak empty variant initialization function.
|
||||
// May be redefined by variant files.
|
||||
void variantDefaultConfig() __attribute__((weak));
|
||||
void variantDefaultConfig() {}
|
||||
|
||||
void variantDefaultModuleConfig() __attribute__((weak));
|
||||
void variantDefaultModuleConfig() {}
|
||||
|
||||
#ifdef HELTEC_MESH_NODE_T114
|
||||
|
||||
uint32_t read8(uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
|
||||
@@ -152,6 +161,25 @@ uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_
|
||||
|
||||
#endif
|
||||
|
||||
// When armed by loadFromDisk, the decode callback writes satellite entries
|
||||
// straight into these maps instead of the temp vectors. Nullptr = legacy
|
||||
// push_back-to-vector path for backup/restore and other decoders.
|
||||
namespace
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
std::map<NodeNum, meshtastic_PositionLite> *s_decodePositionsTarget = nullptr;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
std::map<NodeNum, meshtastic_DeviceMetrics> *s_decodeTelemetryTarget = nullptr;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
std::map<NodeNum, meshtastic_EnvironmentMetrics> *s_decodeEnvironmentTarget = nullptr;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_STATUSDB
|
||||
std::map<NodeNum, meshtastic_StatusMessage> *s_decodeStatusTarget = nullptr;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
|
||||
{
|
||||
const auto *iter = reinterpret_cast<const pb_field_iter_t *>(field);
|
||||
@@ -166,10 +194,10 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (istream) {
|
||||
meshtastic_NodeInfoLite node;
|
||||
if (istream && istream->bytes_left) {
|
||||
meshtastic_NodeInfoLite node = meshtastic_NodeInfoLite_init_zero;
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeInfoLite> *>(iter->pData);
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeInfoLite_fields, &node))
|
||||
if (pb_decode(istream, meshtastic_NodeInfoLite_fields, &node))
|
||||
vec->push_back(node);
|
||||
}
|
||||
return true;
|
||||
@@ -184,11 +212,19 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (istream) {
|
||||
meshtastic_NodePositionEntry entry;
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodePositionEntry> *>(iter->pData);
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodePositionEntry_fields, &entry))
|
||||
if (istream && istream->bytes_left) {
|
||||
meshtastic_NodePositionEntry entry = meshtastic_NodePositionEntry_init_zero;
|
||||
if (pb_decode(istream, meshtastic_NodePositionEntry_fields, &entry)) {
|
||||
#if !MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
if (s_decodePositionsTarget) {
|
||||
if (entry.has_position)
|
||||
(*s_decodePositionsTarget)[entry.num] = entry.position;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodePositionEntry> *>(iter->pData);
|
||||
vec->push_back(entry);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -202,11 +238,19 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (istream) {
|
||||
meshtastic_NodeTelemetryEntry entry;
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeTelemetryEntry> *>(iter->pData);
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeTelemetryEntry_fields, &entry))
|
||||
if (istream && istream->bytes_left) {
|
||||
meshtastic_NodeTelemetryEntry entry = meshtastic_NodeTelemetryEntry_init_zero;
|
||||
if (pb_decode(istream, meshtastic_NodeTelemetryEntry_fields, &entry)) {
|
||||
#if !MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
if (s_decodeTelemetryTarget) {
|
||||
if (entry.has_device_metrics)
|
||||
(*s_decodeTelemetryTarget)[entry.num] = entry.device_metrics;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeTelemetryEntry> *>(iter->pData);
|
||||
vec->push_back(entry);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -220,11 +264,19 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (istream) {
|
||||
meshtastic_NodeStatusEntry entry;
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeStatusEntry> *>(iter->pData);
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeStatusEntry_fields, &entry))
|
||||
if (istream && istream->bytes_left) {
|
||||
meshtastic_NodeStatusEntry entry = meshtastic_NodeStatusEntry_init_zero;
|
||||
if (pb_decode(istream, meshtastic_NodeStatusEntry_fields, &entry)) {
|
||||
#if !MESHTASTIC_EXCLUDE_STATUSDB
|
||||
if (s_decodeStatusTarget) {
|
||||
if (entry.has_status)
|
||||
(*s_decodeStatusTarget)[entry.num] = entry.status;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeStatusEntry> *>(iter->pData);
|
||||
vec->push_back(entry);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -238,11 +290,19 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (istream) {
|
||||
meshtastic_NodeEnvironmentEntry entry;
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeEnvironmentEntry> *>(iter->pData);
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeEnvironmentEntry_fields, &entry))
|
||||
if (istream && istream->bytes_left) {
|
||||
meshtastic_NodeEnvironmentEntry entry = meshtastic_NodeEnvironmentEntry_init_zero;
|
||||
if (pb_decode(istream, meshtastic_NodeEnvironmentEntry_fields, &entry)) {
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
if (s_decodeEnvironmentTarget) {
|
||||
if (entry.has_environment_metrics)
|
||||
(*s_decodeEnvironmentTarget)[entry.num] = entry.environment_metrics;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
auto *vec = static_cast<std::vector<meshtastic_NodeEnvironmentEntry> *>(iter->pData);
|
||||
vec->push_back(entry);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -251,6 +311,42 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDB::armNodeDatabaseDecodeTargets()
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
nodePositions.clear();
|
||||
s_decodePositionsTarget = &nodePositions;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
nodeTelemetry.clear();
|
||||
s_decodeTelemetryTarget = &nodeTelemetry;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
nodeEnvironment.clear();
|
||||
s_decodeEnvironmentTarget = &nodeEnvironment;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_STATUSDB
|
||||
nodeStatus.clear();
|
||||
s_decodeStatusTarget = &nodeStatus;
|
||||
#endif
|
||||
}
|
||||
|
||||
void NodeDB::disarmNodeDatabaseDecodeTargets()
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
s_decodePositionsTarget = nullptr;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
s_decodeTelemetryTarget = nullptr;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
s_decodeEnvironmentTarget = nullptr;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_STATUSDB
|
||||
s_decodeStatusTarget = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||
*/
|
||||
@@ -865,6 +961,10 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
config.network.wifi_enabled = USERPREFS_NETWORK_WIFI_ENABLED;
|
||||
#endif
|
||||
|
||||
#if USE_ETHERNET_DEFAULT
|
||||
config.network.eth_enabled = true;
|
||||
#endif
|
||||
|
||||
#ifdef USERPREFS_NETWORK_WIFI_SSID
|
||||
strncpy(config.network.wifi_ssid, USERPREFS_NETWORK_WIFI_SSID, sizeof(config.network.wifi_ssid));
|
||||
#endif
|
||||
@@ -904,6 +1004,8 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
#endif
|
||||
|
||||
initConfigIntervals();
|
||||
variantDefaultConfig();
|
||||
variantDefaultModuleConfig();
|
||||
}
|
||||
|
||||
void NodeDB::initConfigIntervals()
|
||||
@@ -1161,7 +1263,6 @@ void NodeDB::resetNodes(bool keepFavorites)
|
||||
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite());
|
||||
}
|
||||
(void)ourNum;
|
||||
devicestate.has_rx_text_message = false;
|
||||
devicestate.has_rx_waypoint = false;
|
||||
saveNodeDatabaseToDisk();
|
||||
saveDeviceStateToDisk();
|
||||
@@ -1368,7 +1469,6 @@ void NodeDB::installDefaultDeviceState()
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||
devicestate.has_rx_waypoint = false;
|
||||
devicestate.has_rx_text_message = false;
|
||||
|
||||
generatePacketId(); // FIXME - ugly way to init current_packet_id;
|
||||
|
||||
@@ -1510,6 +1610,19 @@ void NodeDB::loadFromDisk()
|
||||
}
|
||||
|
||||
#endif
|
||||
// Arm the direct-into-map decode so satellite entries skip the temp vectors.
|
||||
{
|
||||
concurrency::LockGuard guard(&satelliteMutex);
|
||||
armNodeDatabaseDecodeTargets();
|
||||
}
|
||||
struct Disarm {
|
||||
NodeDB &self;
|
||||
~Disarm() { self.disarmNodeDatabaseDecodeTargets(); }
|
||||
} disarm{*this};
|
||||
|
||||
// Avoid push_back's power-of-2 capacity growth wasting RAM at small N.
|
||||
nodeDatabase.nodes.reserve(MAX_NUM_NODES);
|
||||
|
||||
auto state = loadProto(nodeDatabaseFileName, getMaxNodesAllocatedSize(), sizeof(meshtastic_NodeDatabase),
|
||||
&meshtastic_NodeDatabase_msg, &nodeDatabase);
|
||||
if (nodeDatabase.version < DEVICESTATE_MIN_VER) {
|
||||
@@ -1523,50 +1636,33 @@ void NodeDB::loadFromDisk()
|
||||
} else {
|
||||
meshNodes = &nodeDatabase.nodes;
|
||||
numMeshNodes = nodeDatabase.nodes.size();
|
||||
// Hydrate the satellite maps; the on-disk vectors stay empty in steady
|
||||
// state and are repopulated only at save time.
|
||||
concurrency::LockGuard guard(&satelliteMutex);
|
||||
// Counts computed outside LOG_INFO() so cppcheck doesn't choke on #if in macro args.
|
||||
const unsigned posCount =
|
||||
#if !MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
nodePositions.clear();
|
||||
nodePositions.reserve(nodeDatabase.positions.size());
|
||||
for (const auto &entry : nodeDatabase.positions) {
|
||||
if (entry.has_position)
|
||||
nodePositions[entry.num] = entry.position;
|
||||
}
|
||||
nodeDatabase.positions.clear();
|
||||
nodeDatabase.positions.shrink_to_fit();
|
||||
(unsigned)nodePositions.size();
|
||||
#else
|
||||
0u;
|
||||
#endif
|
||||
const unsigned telCount =
|
||||
#if !MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
nodeTelemetry.clear();
|
||||
nodeTelemetry.reserve(nodeDatabase.telemetry.size());
|
||||
for (const auto &entry : nodeDatabase.telemetry) {
|
||||
if (entry.has_device_metrics)
|
||||
nodeTelemetry[entry.num] = entry.device_metrics;
|
||||
}
|
||||
nodeDatabase.telemetry.clear();
|
||||
nodeDatabase.telemetry.shrink_to_fit();
|
||||
(unsigned)nodeTelemetry.size();
|
||||
#else
|
||||
0u;
|
||||
#endif
|
||||
const unsigned envCount =
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
nodeEnvironment.clear();
|
||||
nodeEnvironment.reserve(nodeDatabase.environment.size());
|
||||
for (const auto &entry : nodeDatabase.environment) {
|
||||
if (entry.has_environment_metrics)
|
||||
nodeEnvironment[entry.num] = entry.environment_metrics;
|
||||
}
|
||||
nodeDatabase.environment.clear();
|
||||
nodeDatabase.environment.shrink_to_fit();
|
||||
(unsigned)nodeEnvironment.size();
|
||||
#else
|
||||
0u;
|
||||
#endif
|
||||
const unsigned statusCount =
|
||||
#if !MESHTASTIC_EXCLUDE_STATUSDB
|
||||
nodeStatus.clear();
|
||||
nodeStatus.reserve(nodeDatabase.status.size());
|
||||
for (const auto &entry : nodeDatabase.status) {
|
||||
if (entry.has_status)
|
||||
nodeStatus[entry.num] = entry.status;
|
||||
}
|
||||
nodeDatabase.status.clear();
|
||||
nodeDatabase.status.shrink_to_fit();
|
||||
(unsigned)nodeStatus.size();
|
||||
#else
|
||||
0u;
|
||||
#endif
|
||||
LOG_INFO("Loaded saved nodedatabase version %d, with nodes count: %d", nodeDatabase.version, nodeDatabase.nodes.size());
|
||||
LOG_INFO("Loaded saved nodedatabase v%d: %d nodes, %u pos, %u tel, %u env, %u status", nodeDatabase.version,
|
||||
nodeDatabase.nodes.size(), posCount, telCount, envCount, statusCount);
|
||||
}
|
||||
|
||||
if (numMeshNodes > MAX_NUM_NODES) {
|
||||
@@ -1844,6 +1940,15 @@ bool NodeDB::saveNodeDatabaseToDisk()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Defer (don't fail) while xmodem holds the prefs file handle. Returning false
|
||||
// would propagate through saveToDisk() and trigger fsFormat() mid-transfer.
|
||||
#ifdef FSCom
|
||||
if (xModem.isBusy()) {
|
||||
LOG_DEBUG("Deferring NodeDB save: xmodem transfer in progress");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FSCom
|
||||
spiLock->lock();
|
||||
FSCom.mkdir("/prefs");
|
||||
|
||||
+12
-7
@@ -4,9 +4,9 @@
|
||||
#include <Arduino.h>
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <pb_encode.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "MeshTypes.h"
|
||||
@@ -170,19 +170,19 @@ class NodeDB
|
||||
Observable<const meshtastic::NodeStatus *> newStatus;
|
||||
pb_size_t numMeshNodes;
|
||||
|
||||
// Satellite per-NodeNum maps for data we used to inline into NodeInfoLite,
|
||||
// gated by MESHTASTIC_EXCLUDE_*DB so STM32WL can omit them.
|
||||
// Satellite per-NodeNum maps. std::map avoids unordered_map's bucket-array
|
||||
// preallocation; O(log N) lookup is fine at these sizes.
|
||||
#if !MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
std::unordered_map<NodeNum, meshtastic_PositionLite> nodePositions;
|
||||
std::map<NodeNum, meshtastic_PositionLite> nodePositions;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
std::unordered_map<NodeNum, meshtastic_DeviceMetrics> nodeTelemetry;
|
||||
std::map<NodeNum, meshtastic_DeviceMetrics> nodeTelemetry;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
std::unordered_map<NodeNum, meshtastic_EnvironmentMetrics> nodeEnvironment;
|
||||
std::map<NodeNum, meshtastic_EnvironmentMetrics> nodeEnvironment;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_STATUSDB
|
||||
std::unordered_map<NodeNum, meshtastic_StatusMessage> nodeStatus;
|
||||
std::map<NodeNum, meshtastic_StatusMessage> nodeStatus;
|
||||
#endif
|
||||
|
||||
bool keyIsLowEntropy = false;
|
||||
@@ -429,6 +429,11 @@ class NodeDB
|
||||
// the legacy descriptor and copies entries into the v25 layout. Caller
|
||||
// is responsible for save / install-default on the result.
|
||||
bool migrateLegacyNodeDatabase();
|
||||
|
||||
// Route satellite-store decode entries straight into our maps instead of
|
||||
// temp vectors. Must be paired — disarm before any other NodeDatabase decode.
|
||||
void armNodeDatabaseDecodeTargets();
|
||||
void disarmNodeDatabaseDecodeTargets();
|
||||
};
|
||||
|
||||
extern NodeDB *nodeDB;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#include "Throttle.h"
|
||||
|
||||
#define PACKETHISTORY_MAX \
|
||||
max((u_int32_t)(MAX_NUM_NODES * 2.0), \
|
||||
(u_int32_t)100) // x2..3 Should suffice. Empirical setup. 16B per record malloc'ed, but no less than 100
|
||||
max((uint32_t)(MAX_NUM_NODES * 2.0), \
|
||||
(uint32_t)100) // x2..3 Should suffice. Empirical setup. 16B per record malloc'ed, but no less than 100
|
||||
|
||||
#define RECENT_WARN_AGE (10 * 60 * 1000L) // Warn if the packet that gets removed was more recent than 10 min
|
||||
|
||||
|
||||
+158
-189
@@ -62,7 +62,7 @@ void PhoneAPI::handleStartConfig()
|
||||
onConfigStart();
|
||||
|
||||
// even if we were already connected - restart our state machine
|
||||
if (config_nonce == SPECIAL_NONCE_ONLY_NODES || config_nonce == SPECIAL_NONCE_GRADIENT_ONLY_NODES) {
|
||||
if (config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
||||
// If client only wants node info, jump directly to sending nodes
|
||||
state = STATE_SEND_OWN_NODEINFO;
|
||||
LOG_INFO("Client only wants node info, skipping other config");
|
||||
@@ -138,6 +138,7 @@ void PhoneAPI::close()
|
||||
replayTelemetryIndex = 0;
|
||||
replayEnvironmentIndex = 0;
|
||||
replayStatusIndex = 0;
|
||||
replayPhase = REPLAY_PHASE_IDLE;
|
||||
}
|
||||
packetForPhone = NULL;
|
||||
filesManifest.clear();
|
||||
@@ -320,7 +321,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
nodeInfoForPhone.num = 0;
|
||||
}
|
||||
}
|
||||
if (config_nonce == SPECIAL_NONCE_ONLY_NODES || config_nonce == SPECIAL_NONCE_GRADIENT_ONLY_NODES) {
|
||||
if (config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
||||
// If client only wants node info, jump directly to sending nodes
|
||||
state = STATE_SEND_OTHER_NODEINFOS;
|
||||
onNowHasData(0);
|
||||
@@ -535,135 +536,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
// Just in case we stored a different user.id in the past, but should never happen going forward
|
||||
sprintf(infoToSend.user.id, "!%08x", infoToSend.num);
|
||||
|
||||
// Logging this really slows down sending nodes on initial connection because the serial console is so slow, so only
|
||||
// uncomment if you really need to:
|
||||
// LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
||||
// nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
||||
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||
fromRadioScratch.node_info = infoToSend;
|
||||
prefetchNodeInfos();
|
||||
} else {
|
||||
LOG_DEBUG("Done sending %d of %d nodeinfos millis=%u", readIndex, nodeDB->getNumMeshNodes(), millis());
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
nodeInfoMutex.lock();
|
||||
nodeInfoQueue.clear();
|
||||
// Replay states no-op for legacy clients / excluded DBs.
|
||||
state = STATE_REPLAY_POSITIONS;
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_REPLAY_POSITIONS: {
|
||||
if (replayPositionOrder.empty() && replayPositionIndex == 0)
|
||||
beginReplayPositions();
|
||||
prefetchReplayPositions();
|
||||
|
||||
meshtastic_MeshPacket pkt = {};
|
||||
bool havePkt = false;
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
if (!replayQueue.empty()) {
|
||||
pkt = replayQueue.front();
|
||||
replayQueue.pop_front();
|
||||
havePkt = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (havePkt) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = pkt;
|
||||
} else {
|
||||
LOG_DEBUG("Done replaying positions count=%u millis=%u", (unsigned)replayPositionIndex, millis());
|
||||
state = STATE_REPLAY_TELEMETRY;
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_REPLAY_TELEMETRY: {
|
||||
if (replayTelemetryOrder.empty() && replayTelemetryIndex == 0)
|
||||
beginReplayTelemetry();
|
||||
prefetchReplayTelemetry();
|
||||
|
||||
meshtastic_MeshPacket pkt = {};
|
||||
bool havePkt = false;
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
if (!replayQueue.empty()) {
|
||||
pkt = replayQueue.front();
|
||||
replayQueue.pop_front();
|
||||
havePkt = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (havePkt) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = pkt;
|
||||
} else {
|
||||
LOG_DEBUG("Done replaying telemetry count=%u millis=%u", (unsigned)replayTelemetryIndex, millis());
|
||||
state = STATE_REPLAY_ENVIRONMENT;
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_REPLAY_ENVIRONMENT: {
|
||||
if (replayEnvironmentOrder.empty() && replayEnvironmentIndex == 0)
|
||||
beginReplayEnvironment();
|
||||
prefetchReplayEnvironment();
|
||||
|
||||
meshtastic_MeshPacket pkt = {};
|
||||
bool havePkt = false;
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
if (!replayQueue.empty()) {
|
||||
pkt = replayQueue.front();
|
||||
replayQueue.pop_front();
|
||||
havePkt = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (havePkt) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = pkt;
|
||||
} else {
|
||||
LOG_DEBUG("Done replaying environment count=%u millis=%u", (unsigned)replayEnvironmentIndex, millis());
|
||||
state = STATE_REPLAY_STATUS;
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_REPLAY_STATUS: {
|
||||
if (replayStatusOrder.empty() && replayStatusIndex == 0)
|
||||
beginReplayStatus();
|
||||
prefetchReplayStatus();
|
||||
|
||||
meshtastic_MeshPacket pkt = {};
|
||||
bool havePkt = false;
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
if (!replayQueue.empty()) {
|
||||
pkt = replayQueue.front();
|
||||
replayQueue.pop_front();
|
||||
havePkt = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (havePkt) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = pkt;
|
||||
} else {
|
||||
LOG_DEBUG("Done replaying status count=%u millis=%u", (unsigned)replayStatusIndex, millis());
|
||||
replayPositionOrder.clear();
|
||||
replayPositionOrder.shrink_to_fit();
|
||||
replayTelemetryOrder.clear();
|
||||
replayTelemetryOrder.shrink_to_fit();
|
||||
replayEnvironmentOrder.clear();
|
||||
replayEnvironmentOrder.shrink_to_fit();
|
||||
replayStatusOrder.clear();
|
||||
replayStatusOrder.shrink_to_fit();
|
||||
nodeInfoMutex.unlock();
|
||||
// Satellite-DB replay (positions/telemetry/environment/status) now happens
|
||||
// *after* config_complete_id, interleaved with live traffic in STATE_SEND_PACKETS.
|
||||
state = STATE_SEND_FILEMANIFEST;
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
@@ -673,8 +555,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
case STATE_SEND_FILEMANIFEST: {
|
||||
LOG_DEBUG("FromRadio=STATE_SEND_FILEMANIFEST");
|
||||
// ONLY_NODES variants skip the manifest.
|
||||
if (config_state == filesManifest.size() || config_nonce == SPECIAL_NONCE_ONLY_NODES ||
|
||||
config_nonce == SPECIAL_NONCE_GRADIENT_ONLY_NODES) {
|
||||
if (config_state == filesManifest.size() || config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
||||
config_state = 0;
|
||||
filesManifest.clear();
|
||||
// Skip to complete packet
|
||||
@@ -719,6 +600,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = *packetForPhone;
|
||||
releasePhonePacket();
|
||||
} else if (replayPending()) {
|
||||
// No live packet pending — feed the phone one cached satellite-DB packet.
|
||||
// popReplayPacket advances through positions->telemetry->environment->status,
|
||||
// and flips replayPhase back to IDLE when everything has been drained.
|
||||
meshtastic_MeshPacket replayPkt;
|
||||
if (popReplayPacket(replayPkt)) {
|
||||
printPacket("replay packet to phone", &replayPkt);
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = replayPkt;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -743,10 +634,19 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
void PhoneAPI::sendConfigComplete()
|
||||
{
|
||||
LOG_INFO("Config Send Complete millis=%u", millis());
|
||||
const bool shouldReplaySatellites = (config_nonce != SPECIAL_NONCE_ONLY_CONFIG);
|
||||
// The phone sees config_complete_id first (treats sync as done), then the cached
|
||||
// satellite-DB packets (positions / telemetry / environment / status) trickle in
|
||||
// afterward as ordinary mesh packets (except SPECIAL_NONCE_ONLY_CONFIG, which
|
||||
// skips node/satellite sync entirely). Any client that handles live POSITION_APP /
|
||||
// TELEMETRY_APP / NODE_STATUS_APP packets handles these identically. STM32WL and
|
||||
// other builds that compile the satellite DBs out produce no replay packets and
|
||||
// the phase advances to IDLE in microseconds.
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
|
||||
fromRadioScratch.config_complete_id = config_nonce;
|
||||
config_nonce = 0;
|
||||
state = STATE_SEND_PACKETS;
|
||||
replayPhase = shouldReplaySatellites ? REPLAY_PHASE_POSITIONS : REPLAY_PHASE_IDLE;
|
||||
if (api_type == TYPE_BLE) {
|
||||
service->api_state = service->STATE_BLE;
|
||||
} else if (api_type == TYPE_WIFI) {
|
||||
@@ -787,7 +687,8 @@ void PhoneAPI::prefetchNodeInfos()
|
||||
{
|
||||
bool added = false;
|
||||
bool wasEmpty = false;
|
||||
const bool gradient = clientWantsGradientSync();
|
||||
// Other-node NodeInfos always go out thin (no bundled position/device_metrics).
|
||||
// The post-config_complete_id replay drain delivers those as ordinary mesh packets.
|
||||
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
@@ -797,8 +698,7 @@ void PhoneAPI::prefetchNodeInfos()
|
||||
if (!nextNode)
|
||||
break;
|
||||
|
||||
auto info =
|
||||
gradient ? TypeConversions::ConvertToNodeInfoThin(nextNode) : TypeConversions::ConvertToNodeInfo(nextNode);
|
||||
auto info = TypeConversions::ConvertToNodeInfoThin(nextNode);
|
||||
bool isUs = info.num == nodeDB->getNodeNum();
|
||||
info.hops_away = isUs ? 0 : info.hops_away;
|
||||
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
||||
@@ -820,11 +720,20 @@ void PhoneAPI::prefetchNodeInfos()
|
||||
|
||||
meshtastic_MeshPacket PhoneAPI::makeReplayPositionPacket(NodeNum num, const meshtastic_PositionLite &pos)
|
||||
{
|
||||
// Shape this exactly like a fresh live broadcast Position from the peer so the
|
||||
// phone runs it through its normal "live position broadcast" handler path.
|
||||
// to=ourNum would read as a DM-from-peer and never lands in node detail UI.
|
||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||
pkt.from = num;
|
||||
pkt.to = nodeDB->getNodeNum();
|
||||
pkt.to = NODENUM_BROADCAST;
|
||||
pkt.id = generatePacketId();
|
||||
pkt.rx_time = pos.time;
|
||||
pkt.channel = 0;
|
||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
||||
pkt.hop_start = pkt.hop_limit;
|
||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
// Mark as if heard over the air, not internally generated
|
||||
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
||||
pkt.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
pkt.decoded.portnum = meshtastic_PortNum_POSITION_APP;
|
||||
meshtastic_Position fullPos = TypeConversions::ConvertToPosition(pos);
|
||||
@@ -838,11 +747,18 @@ meshtastic_MeshPacket PhoneAPI::makeReplayTelemetryPacket(NodeNum num, const mes
|
||||
{
|
||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||
pkt.from = num;
|
||||
pkt.to = nodeDB->getNodeNum();
|
||||
pkt.to = NODENUM_BROADCAST;
|
||||
pkt.id = generatePacketId();
|
||||
// No native timestamp on telemetry packets here; use last_heard.
|
||||
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||
pkt.rx_time = header ? header->last_heard : 0;
|
||||
pkt.channel = 0;
|
||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
||||
pkt.hop_start = pkt.hop_limit;
|
||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
// Mark as if heard over the air, not internally generated — iOS client filters
|
||||
// TRANSPORT_INTERNAL packets out of broadcast peer state updates.
|
||||
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
||||
pkt.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
pkt.decoded.portnum = meshtastic_PortNum_TELEMETRY_APP;
|
||||
meshtastic_Telemetry fullTel = meshtastic_Telemetry_init_default;
|
||||
@@ -858,16 +774,12 @@ meshtastic_MeshPacket PhoneAPI::makeReplayTelemetryPacket(NodeNum num, const mes
|
||||
void PhoneAPI::beginReplayPositions()
|
||||
{
|
||||
#if MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
// Build excluded entirely - leave the order list empty so the state arm
|
||||
// Build excluded entirely - leave the order list empty so the phase
|
||||
// immediately drains and advances.
|
||||
replayPositionOrder.clear();
|
||||
replayPositionIndex = 0;
|
||||
#else
|
||||
if (!clientWantsGradientSync()) {
|
||||
replayPositionOrder.clear();
|
||||
replayPositionIndex = 0;
|
||||
return;
|
||||
}
|
||||
// Caller (popReplayPacket) only invokes us when replayPhase is armed.
|
||||
// Snapshot the keyset at phase start so concurrent inserts/erases on the
|
||||
// map don't invalidate iteration. Skip our own node - the phone already
|
||||
// got our position bundled in STATE_SEND_OWN_NODEINFO.
|
||||
@@ -882,8 +794,6 @@ void PhoneAPI::prefetchReplayPositions()
|
||||
#if MESHTASTIC_EXCLUDE_POSITIONDB
|
||||
return;
|
||||
#else
|
||||
if (!clientWantsGradientSync())
|
||||
return;
|
||||
bool added = false;
|
||||
bool wasEmpty = false;
|
||||
{
|
||||
@@ -909,11 +819,6 @@ void PhoneAPI::beginReplayTelemetry()
|
||||
replayTelemetryOrder.clear();
|
||||
replayTelemetryIndex = 0;
|
||||
#else
|
||||
if (!clientWantsGradientSync()) {
|
||||
replayTelemetryOrder.clear();
|
||||
replayTelemetryIndex = 0;
|
||||
return;
|
||||
}
|
||||
replayTelemetryOrder = nodeDB->snapshotTelemetryNodeNums(nodeDB->getNodeNum());
|
||||
replayTelemetryIndex = 0;
|
||||
LOG_INFO("Begin telemetry replay: %u entries millis=%u", (unsigned)replayTelemetryOrder.size(), millis());
|
||||
@@ -925,8 +830,6 @@ void PhoneAPI::prefetchReplayTelemetry()
|
||||
#if MESHTASTIC_EXCLUDE_TELEMETRYDB
|
||||
return;
|
||||
#else
|
||||
if (!clientWantsGradientSync())
|
||||
return;
|
||||
bool added = false;
|
||||
bool wasEmpty = false;
|
||||
{
|
||||
@@ -950,10 +853,17 @@ meshtastic_MeshPacket PhoneAPI::makeReplayEnvironmentPacket(uint32_t num, const
|
||||
{
|
||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||
pkt.from = num;
|
||||
pkt.to = nodeDB->getNodeNum();
|
||||
pkt.to = NODENUM_BROADCAST;
|
||||
pkt.id = generatePacketId();
|
||||
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||
pkt.rx_time = header ? header->last_heard : 0;
|
||||
pkt.channel = 0;
|
||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
||||
pkt.hop_start = pkt.hop_limit;
|
||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
// Mark as if heard over the air, not internally generated — iOS client filters
|
||||
// TRANSPORT_INTERNAL packets out of broadcast peer state updates.
|
||||
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
||||
pkt.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
pkt.decoded.portnum = meshtastic_PortNum_TELEMETRY_APP;
|
||||
meshtastic_Telemetry fullTel = meshtastic_Telemetry_init_default;
|
||||
@@ -972,11 +882,6 @@ void PhoneAPI::beginReplayEnvironment()
|
||||
replayEnvironmentOrder.clear();
|
||||
replayEnvironmentIndex = 0;
|
||||
#else
|
||||
if (!clientWantsGradientSync()) {
|
||||
replayEnvironmentOrder.clear();
|
||||
replayEnvironmentIndex = 0;
|
||||
return;
|
||||
}
|
||||
replayEnvironmentOrder = nodeDB->snapshotEnvironmentNodeNums(nodeDB->getNodeNum());
|
||||
replayEnvironmentIndex = 0;
|
||||
LOG_INFO("Begin environment replay: %u entries millis=%u", (unsigned)replayEnvironmentOrder.size(), millis());
|
||||
@@ -988,8 +893,6 @@ void PhoneAPI::prefetchReplayEnvironment()
|
||||
#if MESHTASTIC_EXCLUDE_ENVIRONMENTDB
|
||||
return;
|
||||
#else
|
||||
if (!clientWantsGradientSync())
|
||||
return;
|
||||
bool added = false;
|
||||
bool wasEmpty = false;
|
||||
{
|
||||
@@ -1013,11 +916,17 @@ meshtastic_MeshPacket PhoneAPI::makeReplayStatusPacket(uint32_t num, const mesht
|
||||
{
|
||||
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_default;
|
||||
pkt.from = num;
|
||||
pkt.to = nodeDB->getNodeNum();
|
||||
pkt.to = NODENUM_BROADCAST;
|
||||
pkt.id = generatePacketId();
|
||||
// StatusMessage has no native timestamp; use last_heard.
|
||||
const meshtastic_NodeInfoLite *header = nodeDB->getMeshNode(num);
|
||||
pkt.rx_time = header ? header->last_heard : 0;
|
||||
pkt.channel = 0;
|
||||
pkt.hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
|
||||
pkt.hop_start = pkt.hop_limit;
|
||||
pkt.priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
// Mark as if heard over the air, not internally generated — client filters
|
||||
pkt.transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA;
|
||||
pkt.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
pkt.decoded.portnum = meshtastic_PortNum_NODE_STATUS_APP;
|
||||
size_t len =
|
||||
@@ -1032,11 +941,6 @@ void PhoneAPI::beginReplayStatus()
|
||||
replayStatusOrder.clear();
|
||||
replayStatusIndex = 0;
|
||||
#else
|
||||
if (!clientWantsGradientSync()) {
|
||||
replayStatusOrder.clear();
|
||||
replayStatusIndex = 0;
|
||||
return;
|
||||
}
|
||||
replayStatusOrder = nodeDB->snapshotStatusNodeNums(nodeDB->getNodeNum());
|
||||
replayStatusIndex = 0;
|
||||
LOG_INFO("Begin status replay: %u entries millis=%u", (unsigned)replayStatusOrder.size(), millis());
|
||||
@@ -1048,8 +952,6 @@ void PhoneAPI::prefetchReplayStatus()
|
||||
#if MESHTASTIC_EXCLUDE_STATUSDB
|
||||
return;
|
||||
#else
|
||||
if (!clientWantsGradientSync())
|
||||
return;
|
||||
bool added = false;
|
||||
bool wasEmpty = false;
|
||||
{
|
||||
@@ -1069,6 +971,94 @@ void PhoneAPI::prefetchReplayStatus()
|
||||
#endif
|
||||
}
|
||||
|
||||
// Pop one cached satellite-DB packet from the active replay phase.
|
||||
// Phases drain in order: positions -> telemetry -> environment -> status.
|
||||
// When the current phase's cursor is exhausted (queue empty AND no more entries
|
||||
// to snapshot), advance to the next phase. When all four phases are done,
|
||||
// flip replayPhase back to IDLE and release the snapshot vectors.
|
||||
//
|
||||
// Returns true if a packet was placed in `out`; false if everything is drained.
|
||||
bool PhoneAPI::popReplayPacket(meshtastic_MeshPacket &out)
|
||||
{
|
||||
while (replayPhase != REPLAY_PHASE_IDLE) {
|
||||
// Prime the active phase: seed the snapshot vector on first entry,
|
||||
// top up replayQueue from the snapshot up to kReplayPrefetchDepth.
|
||||
switch (replayPhase) {
|
||||
case REPLAY_PHASE_POSITIONS:
|
||||
if (replayPositionOrder.empty() && replayPositionIndex == 0)
|
||||
beginReplayPositions();
|
||||
prefetchReplayPositions();
|
||||
break;
|
||||
case REPLAY_PHASE_TELEMETRY:
|
||||
if (replayTelemetryOrder.empty() && replayTelemetryIndex == 0)
|
||||
beginReplayTelemetry();
|
||||
prefetchReplayTelemetry();
|
||||
break;
|
||||
case REPLAY_PHASE_ENVIRONMENT:
|
||||
if (replayEnvironmentOrder.empty() && replayEnvironmentIndex == 0)
|
||||
beginReplayEnvironment();
|
||||
prefetchReplayEnvironment();
|
||||
break;
|
||||
case REPLAY_PHASE_STATUS:
|
||||
if (replayStatusOrder.empty() && replayStatusIndex == 0)
|
||||
beginReplayStatus();
|
||||
prefetchReplayStatus();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
if (!replayQueue.empty()) {
|
||||
out = replayQueue.front();
|
||||
replayQueue.pop_front();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Queue empty AND no more entries to feed it — phase is exhausted.
|
||||
advanceReplayPhase();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PhoneAPI::advanceReplayPhase()
|
||||
{
|
||||
switch (replayPhase) {
|
||||
case REPLAY_PHASE_POSITIONS:
|
||||
LOG_DEBUG("Replay drain: positions done (count=%u) millis=%u", (unsigned)replayPositionIndex, millis());
|
||||
replayPhase = REPLAY_PHASE_TELEMETRY;
|
||||
break;
|
||||
case REPLAY_PHASE_TELEMETRY:
|
||||
LOG_DEBUG("Replay drain: telemetry done (count=%u) millis=%u", (unsigned)replayTelemetryIndex, millis());
|
||||
replayPhase = REPLAY_PHASE_ENVIRONMENT;
|
||||
break;
|
||||
case REPLAY_PHASE_ENVIRONMENT:
|
||||
LOG_DEBUG("Replay drain: environment done (count=%u) millis=%u", (unsigned)replayEnvironmentIndex, millis());
|
||||
replayPhase = REPLAY_PHASE_STATUS;
|
||||
break;
|
||||
case REPLAY_PHASE_STATUS:
|
||||
LOG_INFO("Replay drain complete (status count=%u) millis=%u", (unsigned)replayStatusIndex, millis());
|
||||
replayPositionOrder.clear();
|
||||
replayPositionOrder.shrink_to_fit();
|
||||
replayTelemetryOrder.clear();
|
||||
replayTelemetryOrder.shrink_to_fit();
|
||||
replayEnvironmentOrder.clear();
|
||||
replayEnvironmentOrder.shrink_to_fit();
|
||||
replayStatusOrder.clear();
|
||||
replayStatusOrder.shrink_to_fit();
|
||||
replayPositionIndex = 0;
|
||||
replayTelemetryIndex = 0;
|
||||
replayEnvironmentIndex = 0;
|
||||
replayStatusIndex = 0;
|
||||
replayPhase = REPLAY_PHASE_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PhoneAPI::releaseMqttClientProxyPhonePacket()
|
||||
{
|
||||
if (mqttClientProxyMessageForPhone) {
|
||||
@@ -1115,31 +1105,6 @@ bool PhoneAPI::available()
|
||||
PREFETCH_NODEINFO:
|
||||
prefetchNodeInfos();
|
||||
return true;
|
||||
case STATE_REPLAY_POSITIONS: {
|
||||
// Prime the iterator if we haven't yet, then top up the queue.
|
||||
if (replayPositionOrder.empty() && replayPositionIndex == 0)
|
||||
beginReplayPositions();
|
||||
prefetchReplayPositions();
|
||||
return true; // Always advance state machine; arm itself transitions when drained
|
||||
}
|
||||
case STATE_REPLAY_TELEMETRY: {
|
||||
if (replayTelemetryOrder.empty() && replayTelemetryIndex == 0)
|
||||
beginReplayTelemetry();
|
||||
prefetchReplayTelemetry();
|
||||
return true;
|
||||
}
|
||||
case STATE_REPLAY_ENVIRONMENT: {
|
||||
if (replayEnvironmentOrder.empty() && replayEnvironmentIndex == 0)
|
||||
beginReplayEnvironment();
|
||||
prefetchReplayEnvironment();
|
||||
return true;
|
||||
}
|
||||
case STATE_REPLAY_STATUS: {
|
||||
if (replayStatusOrder.empty() && replayStatusIndex == 0)
|
||||
beginReplayStatus();
|
||||
prefetchReplayStatus();
|
||||
return true;
|
||||
}
|
||||
case STATE_SEND_PACKETS: {
|
||||
if (!queueStatusPacketForPhone)
|
||||
queueStatusPacketForPhone = service->getQueueStatusForPhone();
|
||||
@@ -1171,7 +1136,11 @@ bool PhoneAPI::available()
|
||||
if (!packetForPhone)
|
||||
packetForPhone = service->getForPhone();
|
||||
hasPacket = !!packetForPhone;
|
||||
return hasPacket;
|
||||
if (hasPacket)
|
||||
return true;
|
||||
// Trailing replay drain — feeds cached satellite-DB packets alongside
|
||||
// (lower priority than) live traffic.
|
||||
return replayPending();
|
||||
}
|
||||
default:
|
||||
LOG_ERROR("PhoneAPI::available unexpected state %d", state);
|
||||
|
||||
+22
-15
@@ -26,9 +26,6 @@
|
||||
|
||||
#define SPECIAL_NONCE_ONLY_CONFIG 69420
|
||||
#define SPECIAL_NONCE_ONLY_NODES 69421 // ( ͡° ͜ʖ ͡°)
|
||||
// Gradient sync: phone sends one of these to opt into thin-header + replay.
|
||||
#define SPECIAL_NONCE_GRADIENT_SYNC 69422
|
||||
#define SPECIAL_NONCE_GRADIENT_ONLY_NODES 69423
|
||||
|
||||
/**
|
||||
* Provides our protobuf based API which phone/PC clients can use to talk to our device
|
||||
@@ -52,15 +49,22 @@ class PhoneAPI
|
||||
STATE_SEND_CONFIG, // Replacement for the old Radioconfig
|
||||
STATE_SEND_MODULECONFIG, // Send Module specific config
|
||||
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to to the client
|
||||
// Drain satellite DBs as synthetic POSITION_APP / TELEMETRY_APP /
|
||||
// NODE_STATUS_APP packets when the phone opted into gradient sync.
|
||||
STATE_REPLAY_POSITIONS,
|
||||
STATE_REPLAY_TELEMETRY,
|
||||
STATE_REPLAY_ENVIRONMENT,
|
||||
STATE_REPLAY_STATUS,
|
||||
STATE_SEND_FILEMANIFEST, // Send file manifest
|
||||
STATE_SEND_FILEMANIFEST, // Send file manifest
|
||||
STATE_SEND_COMPLETE_ID,
|
||||
STATE_SEND_PACKETS // send packets or debug strings
|
||||
STATE_SEND_PACKETS // live mesh packets + any cached satellite-DB replay that trails sync completion
|
||||
};
|
||||
|
||||
// Satellite-DB replay (positions / telemetry / environment / status) used to live
|
||||
// as four top-level states between STATE_SEND_OTHER_NODEINFOS and STATE_SEND_FILEMANIFEST.
|
||||
// It now drains *after* config_complete_id has been emitted: the phone considers the
|
||||
// initial sync done as soon as headers + manifest are delivered, and the cached
|
||||
// position/telemetry/etc. trickle in alongside live mesh traffic inside STATE_SEND_PACKETS.
|
||||
enum ReplayPhase : uint8_t {
|
||||
REPLAY_PHASE_IDLE = 0, // not replaying (legacy clients, no-op DBs, or replay finished)
|
||||
REPLAY_PHASE_POSITIONS,
|
||||
REPLAY_PHASE_TELEMETRY,
|
||||
REPLAY_PHASE_ENVIRONMENT,
|
||||
REPLAY_PHASE_STATUS,
|
||||
};
|
||||
|
||||
State state = STATE_SEND_NOTHING;
|
||||
@@ -114,6 +118,7 @@ class PhoneAPI
|
||||
size_t replayTelemetryIndex = 0;
|
||||
size_t replayEnvironmentIndex = 0;
|
||||
size_t replayStatusIndex = 0;
|
||||
ReplayPhase replayPhase = REPLAY_PHASE_IDLE; // armed by sendConfigComplete() for full/default sync
|
||||
|
||||
meshtastic_ToRadio toRadioScratch = {
|
||||
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||
@@ -164,10 +169,6 @@ class PhoneAPI
|
||||
|
||||
bool isConnected() { return state != STATE_SEND_NOTHING; }
|
||||
bool isSendingPackets() { return state == STATE_SEND_PACKETS; }
|
||||
bool clientWantsGradientSync() const
|
||||
{
|
||||
return config_nonce == SPECIAL_NONCE_GRADIENT_SYNC || config_nonce == SPECIAL_NONCE_GRADIENT_ONLY_NODES;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Our fromradio packet while it is being assembled
|
||||
@@ -229,6 +230,12 @@ class PhoneAPI
|
||||
meshtastic_MeshPacket makeReplayEnvironmentPacket(uint32_t num, const meshtastic_EnvironmentMetrics &env);
|
||||
meshtastic_MeshPacket makeReplayStatusPacket(uint32_t num, const meshtastic_StatusMessage &status);
|
||||
|
||||
// Post-sync replay drain: pop one cached packet from the active phase, advancing
|
||||
// through positions -> telemetry -> environment -> status until everything is drained.
|
||||
bool popReplayPacket(meshtastic_MeshPacket &out);
|
||||
void advanceReplayPhase();
|
||||
bool replayPending() const { return replayPhase != REPLAY_PHASE_IDLE; }
|
||||
|
||||
void releaseMqttClientProxyPhonePacket();
|
||||
|
||||
void releaseClientNotification();
|
||||
|
||||
@@ -37,6 +37,7 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
|
||||
info.position.altitude = position->altitude;
|
||||
info.position.location_source = position->location_source;
|
||||
info.position.time = position->time;
|
||||
info.position.precision_bits = position->precision_bits;
|
||||
}
|
||||
if (nodeInfoLiteHasUser(lite)) {
|
||||
info.has_user = true;
|
||||
@@ -71,6 +72,7 @@ meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Positi
|
||||
lite.altitude = position.altitude;
|
||||
lite.location_source = position.location_source;
|
||||
lite.time = position.time;
|
||||
lite.precision_bits = position.precision_bits;
|
||||
|
||||
return lite;
|
||||
}
|
||||
@@ -89,6 +91,11 @@ meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite l
|
||||
position.altitude = lite.altitude;
|
||||
position.location_source = lite.location_source;
|
||||
position.time = lite.time;
|
||||
// Preserve the peer's broadcast precision; falls back to 0 for entries cached
|
||||
// before the precision_bits field existed in PositionLite (pre-migration data).
|
||||
// iOS treats 0 as "unspecified precision" and won't render the pin — so for
|
||||
// unset values, declare full precision so the stored lat/lon renders as a point.
|
||||
position.precision_bits = lite.precision_bits == 0 ? 32 : lite.precision_bits;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
#include "ServerAPI.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,9 @@ PB_BIND(meshtastic_AdminMessage_InputEvent, meshtastic_AdminMessage_InputEvent,
|
||||
PB_BIND(meshtastic_AdminMessage_OTAEvent, meshtastic_AdminMessage_OTAEvent, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_LockdownAuth, meshtastic_LockdownAuth, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_HamParameters, meshtastic_HamParameters, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -130,6 +130,41 @@ typedef struct _meshtastic_AdminMessage_OTAEvent {
|
||||
meshtastic_AdminMessage_OTAEvent_ota_hash_t ota_hash;
|
||||
} meshtastic_AdminMessage_OTAEvent;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_LockdownAuth_passphrase_t;
|
||||
/* Lockdown passphrase delivery payload.
|
||||
|
||||
One message handles three operations distinguished by content:
|
||||
- Provision (first-time): passphrase set, lock_now=false. Firmware
|
||||
generates DEK, wraps with passphrase-derived KEK, persists.
|
||||
- Unlock: passphrase set, lock_now=false. Firmware verifies
|
||||
passphrase against stored DEK, unlocks storage, authorizes the
|
||||
connection that delivered this packet.
|
||||
- Lock now: lock_now=true, passphrase ignored. Firmware revokes
|
||||
all client auth and reboots into the locked state.
|
||||
|
||||
Firmware decides between provision and unlock based on its own state
|
||||
(whether a DEK file already exists). Clients do not need to track
|
||||
which case applies. */
|
||||
typedef struct _meshtastic_LockdownAuth {
|
||||
/* Passphrase bytes (1-32). Empty when lock_now is true.
|
||||
Capped to 32 to match the proto cap on related security fields. */
|
||||
meshtastic_LockdownAuth_passphrase_t passphrase;
|
||||
/* Optional override of the boot-count token TTL granted on success.
|
||||
0 = use firmware default (TOKEN_DEFAULT_BOOTS).
|
||||
On reboot the firmware decrements this; when it reaches 0 the
|
||||
device boots fully locked and requires a fresh passphrase. */
|
||||
uint32_t boots_remaining;
|
||||
/* Optional wall-clock expiry for the unlock token, as absolute
|
||||
Unix-epoch seconds. 0 = no time limit (only the boot-count TTL
|
||||
applies). On boot, if the device RTC is set and now > this value,
|
||||
the token is treated as expired. */
|
||||
uint32_t valid_until_epoch;
|
||||
/* If true, ignore passphrase fields, immediately revoke all
|
||||
connection-level admin authorization, and reboot the device into
|
||||
the locked state. Always honoured regardless of current lock state. */
|
||||
bool lock_now;
|
||||
} meshtastic_LockdownAuth;
|
||||
|
||||
/* Parameters for setting up Meshtastic for ameteur radio usage */
|
||||
typedef struct _meshtastic_HamParameters {
|
||||
/* Amateur radio call sign, eg. KD2ABC */
|
||||
@@ -384,6 +419,15 @@ typedef struct _meshtastic_AdminMessage {
|
||||
meshtastic_AdminMessage_OTAEvent ota_request;
|
||||
/* Parameters and sensor configuration */
|
||||
meshtastic_SensorConfig sensor_config;
|
||||
/* Lockdown passphrase delivery / unlock / lock-now command for hardened
|
||||
firmware builds (see MESHTASTIC_LOCKDOWN). Used to provision the
|
||||
passphrase on first boot, unlock encrypted storage on subsequent
|
||||
reboots, re-verify on already-unlocked devices to authorize a new
|
||||
client connection, or immediately re-lock the device.
|
||||
|
||||
Replaces the earlier scheme that repurposed SecurityConfig.private_key
|
||||
to carry passphrase bytes; that hack is retired. */
|
||||
meshtastic_LockdownAuth lockdown_auth;
|
||||
};
|
||||
/* The node generates this key and sends it with any get_x_response packets.
|
||||
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
|
||||
@@ -429,6 +473,7 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
#define meshtastic_KeyVerificationAdmin_message_type_ENUMTYPE meshtastic_KeyVerificationAdmin_MessageType
|
||||
|
||||
|
||||
@@ -441,6 +486,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
|
||||
#define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_AdminMessage_OTAEvent_init_default {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||
#define meshtastic_LockdownAuth_init_default {{0, {0}}, 0, 0, 0}
|
||||
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
||||
#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0}
|
||||
@@ -453,6 +499,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
|
||||
#define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_AdminMessage_OTAEvent_init_zero {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||
#define meshtastic_LockdownAuth_init_zero {{0, {0}}, 0, 0, 0}
|
||||
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
||||
#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0}
|
||||
@@ -470,6 +517,10 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_InputEvent_touch_y_tag 4
|
||||
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_tag 1
|
||||
#define meshtastic_AdminMessage_OTAEvent_ota_hash_tag 2
|
||||
#define meshtastic_LockdownAuth_passphrase_tag 1
|
||||
#define meshtastic_LockdownAuth_boots_remaining_tag 2
|
||||
#define meshtastic_LockdownAuth_valid_until_epoch_tag 3
|
||||
#define meshtastic_LockdownAuth_lock_now_tag 4
|
||||
#define meshtastic_HamParameters_call_sign_tag 1
|
||||
#define meshtastic_HamParameters_tx_power_tag 2
|
||||
#define meshtastic_HamParameters_frequency_tag 3
|
||||
@@ -560,6 +611,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_nodedb_reset_tag 100
|
||||
#define meshtastic_AdminMessage_ota_request_tag 102
|
||||
#define meshtastic_AdminMessage_sensor_config_tag 103
|
||||
#define meshtastic_AdminMessage_lockdown_auth_tag 104
|
||||
#define meshtastic_AdminMessage_session_passkey_tag 101
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
@@ -621,7 +673,8 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,nodedb_reset,nodedb_reset), 100) \
|
||||
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 102) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config), 103)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config), 103) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lockdown_auth,lockdown_auth), 104)
|
||||
#define meshtastic_AdminMessage_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_DEFAULT NULL
|
||||
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
|
||||
@@ -644,6 +697,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sensor_config,sensor_config)
|
||||
#define meshtastic_AdminMessage_payload_variant_key_verification_MSGTYPE meshtastic_KeyVerificationAdmin
|
||||
#define meshtastic_AdminMessage_payload_variant_ota_request_MSGTYPE meshtastic_AdminMessage_OTAEvent
|
||||
#define meshtastic_AdminMessage_payload_variant_sensor_config_MSGTYPE meshtastic_SensorConfig
|
||||
#define meshtastic_AdminMessage_payload_variant_lockdown_auth_MSGTYPE meshtastic_LockdownAuth
|
||||
|
||||
#define meshtastic_AdminMessage_InputEvent_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, event_code, 1) \
|
||||
@@ -659,6 +713,14 @@ X(a, STATIC, SINGULAR, BYTES, ota_hash, 2)
|
||||
#define meshtastic_AdminMessage_OTAEvent_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_OTAEvent_DEFAULT NULL
|
||||
|
||||
#define meshtastic_LockdownAuth_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BYTES, passphrase, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, boots_remaining, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, valid_until_epoch, 3) \
|
||||
X(a, STATIC, SINGULAR, BOOL, lock_now, 4)
|
||||
#define meshtastic_LockdownAuth_CALLBACK NULL
|
||||
#define meshtastic_LockdownAuth_DEFAULT NULL
|
||||
|
||||
#define meshtastic_HamParameters_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, call_sign, 1) \
|
||||
X(a, STATIC, SINGULAR, INT32, tx_power, 2) \
|
||||
@@ -737,6 +799,7 @@ X(a, STATIC, OPTIONAL, UINT32, set_accuracy, 1)
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_msg;
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_InputEvent_msg;
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_OTAEvent_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LockdownAuth_msg;
|
||||
extern const pb_msgdesc_t meshtastic_HamParameters_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
|
||||
extern const pb_msgdesc_t meshtastic_SharedContact_msg;
|
||||
@@ -751,6 +814,7 @@ extern const pb_msgdesc_t meshtastic_SHTXX_config_msg;
|
||||
#define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg
|
||||
#define meshtastic_AdminMessage_InputEvent_fields &meshtastic_AdminMessage_InputEvent_msg
|
||||
#define meshtastic_AdminMessage_OTAEvent_fields &meshtastic_AdminMessage_OTAEvent_msg
|
||||
#define meshtastic_LockdownAuth_fields &meshtastic_LockdownAuth_msg
|
||||
#define meshtastic_HamParameters_fields &meshtastic_HamParameters_msg
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_fields &meshtastic_NodeRemoteHardwarePinsResponse_msg
|
||||
#define meshtastic_SharedContact_fields &meshtastic_SharedContact_msg
|
||||
@@ -768,6 +832,7 @@ extern const pb_msgdesc_t meshtastic_SHTXX_config_msg;
|
||||
#define meshtastic_AdminMessage_size 511
|
||||
#define meshtastic_HamParameters_size 31
|
||||
#define meshtastic_KeyVerificationAdmin_size 25
|
||||
#define meshtastic_LockdownAuth_size 48
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_size 496
|
||||
#define meshtastic_SCD30_config_size 27
|
||||
#define meshtastic_SCD4X_config_size 29
|
||||
|
||||
@@ -33,6 +33,8 @@ typedef struct _meshtastic_PositionLite {
|
||||
uint32_t time;
|
||||
/* TODO: REPLACE */
|
||||
meshtastic_Position_LocSource location_source;
|
||||
/* Indicates the bits of precision set by the sending node */
|
||||
uint32_t precision_bits;
|
||||
} meshtastic_PositionLite;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_UserLite_public_key_t;
|
||||
@@ -211,7 +213,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
|
||||
#define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, 0}
|
||||
#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_NodeInfoLite_init_default {0, 0, 0, 0, false, 0, 0, 0, "", "", _meshtastic_HardwareModel_MIN, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
|
||||
#define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
||||
@@ -222,7 +224,7 @@ extern "C" {
|
||||
#define meshtastic_NodeDatabase_init_default {0, {0}, {0}, {0}, {0}, {0}}
|
||||
#define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0}
|
||||
#define meshtastic_BackupPreferences_init_default {0, 0, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default, false, meshtastic_ChannelFile_init_default, false, meshtastic_User_init_default}
|
||||
#define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
|
||||
#define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, 0}
|
||||
#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_NodeInfoLite_init_zero {0, 0, 0, 0, false, 0, 0, 0, "", "", _meshtastic_HardwareModel_MIN, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
|
||||
#define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
||||
@@ -240,6 +242,7 @@ extern "C" {
|
||||
#define meshtastic_PositionLite_altitude_tag 3
|
||||
#define meshtastic_PositionLite_time_tag 4
|
||||
#define meshtastic_PositionLite_location_source_tag 5
|
||||
#define meshtastic_PositionLite_precision_bits_tag 6
|
||||
#define meshtastic_UserLite_macaddr_tag 1
|
||||
#define meshtastic_UserLite_long_name_tag 2
|
||||
#define meshtastic_UserLite_short_name_tag 3
|
||||
@@ -298,7 +301,8 @@ X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \
|
||||
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \
|
||||
X(a, STATIC, SINGULAR, INT32, altitude, 3) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, time, 4) \
|
||||
X(a, STATIC, SINGULAR, UENUM, location_source, 5)
|
||||
X(a, STATIC, SINGULAR, UENUM, location_source, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, precision_bits, 6)
|
||||
#define meshtastic_PositionLite_CALLBACK NULL
|
||||
#define meshtastic_PositionLite_DEFAULT NULL
|
||||
|
||||
@@ -444,13 +448,13 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
|
||||
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
|
||||
#define meshtastic_BackupPreferences_size 2432
|
||||
#define meshtastic_ChannelFile_size 718
|
||||
#define meshtastic_DeviceState_size 1737
|
||||
#define meshtastic_DeviceState_size 1944
|
||||
#define meshtastic_NodeEnvironmentEntry_size 170
|
||||
#define meshtastic_NodeInfoLite_size 105
|
||||
#define meshtastic_NodePositionEntry_size 36
|
||||
#define meshtastic_NodePositionEntry_size 42
|
||||
#define meshtastic_NodeStatusEntry_size 89
|
||||
#define meshtastic_NodeTelemetryEntry_size 35
|
||||
#define meshtastic_PositionLite_size 28
|
||||
#define meshtastic_PositionLite_size 34
|
||||
#define meshtastic_UserLite_size 98
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -115,7 +115,7 @@ extern const pb_msgdesc_t meshtastic_NodeDatabase_Legacy_msg;
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* meshtastic_NodeDatabase_Legacy_size depends on runtime parameters */
|
||||
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_LEGACY_PB_H_MAX_SIZE meshtastic_NodeInfoLite_Legacy_size
|
||||
#define meshtastic_NodeInfoLite_Legacy_size 196
|
||||
#define meshtastic_NodeInfoLite_Legacy_size 202
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -57,6 +57,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
|
||||
PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_LockdownStatus, meshtastic_LockdownStatus, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_ClientNotification, meshtastic_ClientNotification, 2)
|
||||
|
||||
|
||||
@@ -134,6 +137,8 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -319,6 +319,8 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_HELTEC_V4_R8 = 132,
|
||||
/* The HELTEC_MESH_NODE_T1 uses an NRF52840 chip, plus an SX1262. */
|
||||
meshtastic_HardwareModel_HELTEC_MESH_NODE_T1 = 133,
|
||||
/* B&Q Consulting Station G3: TBD */
|
||||
meshtastic_HardwareModel_STATION_G3 = 134,
|
||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||
@@ -636,6 +638,25 @@ typedef enum _meshtastic_LogRecord_Level {
|
||||
meshtastic_LogRecord_Level_TRACE = 5
|
||||
} meshtastic_LogRecord_Level;
|
||||
|
||||
typedef enum _meshtastic_LockdownStatus_State {
|
||||
/* Default; should not be sent. */
|
||||
meshtastic_LockdownStatus_State_STATE_UNSPECIFIED = 0,
|
||||
/* No passphrase has ever been provisioned on this device.
|
||||
Client should prompt the operator to set one. */
|
||||
meshtastic_LockdownStatus_State_NEEDS_PROVISION = 1,
|
||||
/* Storage is locked or this client has not authenticated yet.
|
||||
lock_reason carries a machine-readable detail string.
|
||||
Client should present (or auto-replay) a passphrase via
|
||||
AdminMessage.lockdown_auth. */
|
||||
meshtastic_LockdownStatus_State_LOCKED = 2,
|
||||
/* Passphrase accepted; client is now authorized for this connection.
|
||||
boots_remaining and valid_until_epoch describe the active session
|
||||
token's TTL. */
|
||||
meshtastic_LockdownStatus_State_UNLOCKED = 3,
|
||||
/* Passphrase rejected. backoff_seconds is non-zero when rate-limited. */
|
||||
meshtastic_LockdownStatus_State_UNLOCK_FAILED = 4
|
||||
} meshtastic_LockdownStatus_State;
|
||||
|
||||
/* Struct definitions */
|
||||
/* A GPS Position */
|
||||
typedef struct _meshtastic_Position {
|
||||
@@ -799,6 +820,7 @@ typedef struct _meshtastic_Routing {
|
||||
} meshtastic_Routing;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(233) meshtastic_Data_payload_t;
|
||||
typedef PB_BYTES_ARRAY_T(64) meshtastic_Data_xeddsa_signature_t;
|
||||
/* (Formerly called SubPacket)
|
||||
The payload portion fo a packet, this is the actual bytes that are sent
|
||||
inside a radio packet (because from/to are broken out by the comms library) */
|
||||
@@ -832,6 +854,8 @@ typedef struct _meshtastic_Data {
|
||||
/* Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT. */
|
||||
bool has_bitfield;
|
||||
uint8_t bitfield;
|
||||
/* XEdDSA signature for the payload */
|
||||
meshtastic_Data_xeddsa_signature_t xeddsa_signature;
|
||||
} meshtastic_Data;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_KeyVerification_hash1_t;
|
||||
@@ -1036,6 +1060,8 @@ typedef struct _meshtastic_MeshPacket {
|
||||
uint32_t tx_after;
|
||||
/* Indicates which transport mechanism this packet arrived over */
|
||||
meshtastic_MeshPacket_TransportMechanism transport_mechanism;
|
||||
/* Indicates whether the packet has a valid signature */
|
||||
bool xeddsa_signed;
|
||||
} meshtastic_MeshPacket;
|
||||
|
||||
/* The bluetooth to device link:
|
||||
@@ -1092,6 +1118,10 @@ typedef struct _meshtastic_NodeInfo {
|
||||
/* True if node has been muted
|
||||
Persistes between NodeDB internal clean ups */
|
||||
bool is_muted;
|
||||
/* True if node is signing its packets via XEdDSA
|
||||
Persists between NodeDB internal clean ups
|
||||
LSB 1 of the bitfield */
|
||||
bool has_xeddsa_signed;
|
||||
} meshtastic_NodeInfo;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(16) meshtastic_MyNodeInfo_device_id_t;
|
||||
@@ -1146,6 +1176,38 @@ typedef struct _meshtastic_QueueStatus {
|
||||
uint32_t mesh_packet_id;
|
||||
} meshtastic_QueueStatus;
|
||||
|
||||
/* Lockdown state report from firmware to client (for hardened builds
|
||||
with MESHTASTIC_LOCKDOWN). Sent immediately after config_complete_id
|
||||
to inform a freshly-connected unauthorized client what it must do,
|
||||
and again in response to each LockdownAuth admin command. */
|
||||
typedef struct _meshtastic_LockdownStatus {
|
||||
/* Current lockdown state being reported. */
|
||||
meshtastic_LockdownStatus_State state;
|
||||
/* For LOCKED: machine-readable reason. Known values:
|
||||
"needs_auth" — storage already unlocked, client must auth
|
||||
"token_missing" — no boot token on flash
|
||||
"token_expired" — boot token wall-clock TTL elapsed
|
||||
"token_boots_zero" — boot token boot-count TTL exhausted
|
||||
"token_hmac_fail" — token tampered or wrong device
|
||||
"token_dek_fail" — token DEK decrypt failed
|
||||
"token_wrong_size" — token file corrupted
|
||||
"token_bad_magic" — token file corrupted
|
||||
"not_provisioned" — should generally use NEEDS_PROVISION state instead
|
||||
Other values may be added; clients should treat unknown values as
|
||||
"locked, ask for passphrase". */
|
||||
char lock_reason[32];
|
||||
/* For UNLOCKED: remaining boots on the issued session token.
|
||||
Decrements by 1 on each subsequent boot. */
|
||||
uint32_t boots_remaining;
|
||||
/* For UNLOCKED: wall-clock expiry of the issued session token,
|
||||
absolute Unix-epoch seconds. 0 = no time limit. */
|
||||
uint32_t valid_until_epoch;
|
||||
/* For UNLOCK_FAILED: seconds the client must wait before another
|
||||
passphrase attempt will be accepted. 0 = wrong passphrase, no
|
||||
backoff (immediate retry allowed but advisable to prompt user). */
|
||||
uint32_t backoff_seconds;
|
||||
} meshtastic_LockdownStatus;
|
||||
|
||||
typedef struct _meshtastic_KeyVerificationNumberInform {
|
||||
uint64_t nonce;
|
||||
char remote_longname[40];
|
||||
@@ -1319,6 +1381,12 @@ typedef struct _meshtastic_FromRadio {
|
||||
meshtastic_ClientNotification clientNotification;
|
||||
/* Persistent data for device-ui */
|
||||
meshtastic_DeviceUIConfig deviceuiConfig;
|
||||
/* Lockdown state notification for hardened firmware builds.
|
||||
Sent post-config (so unauthorized clients learn they must
|
||||
provision/unlock) and after each LockdownAuth admin command
|
||||
to report success or failure. Replaces the earlier scheme of
|
||||
encoding state as magic-string prefixes inside ClientNotification. */
|
||||
meshtastic_LockdownStatus lockdown_status;
|
||||
};
|
||||
} meshtastic_FromRadio;
|
||||
|
||||
@@ -1460,6 +1528,10 @@ extern "C" {
|
||||
#define _meshtastic_LogRecord_Level_MAX meshtastic_LogRecord_Level_CRITICAL
|
||||
#define _meshtastic_LogRecord_Level_ARRAYSIZE ((meshtastic_LogRecord_Level)(meshtastic_LogRecord_Level_CRITICAL+1))
|
||||
|
||||
#define _meshtastic_LockdownStatus_State_MIN meshtastic_LockdownStatus_State_STATE_UNSPECIFIED
|
||||
#define _meshtastic_LockdownStatus_State_MAX meshtastic_LockdownStatus_State_UNLOCK_FAILED
|
||||
#define _meshtastic_LockdownStatus_State_ARRAYSIZE ((meshtastic_LockdownStatus_State)(meshtastic_LockdownStatus_State_UNLOCK_FAILED+1))
|
||||
|
||||
#define meshtastic_Position_location_source_ENUMTYPE meshtastic_Position_LocSource
|
||||
#define meshtastic_Position_altitude_source_ENUMTYPE meshtastic_Position_AltSource
|
||||
|
||||
@@ -1490,6 +1562,8 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
#define meshtastic_LockdownStatus_state_ENUMTYPE meshtastic_LockdownStatus_State
|
||||
|
||||
#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level
|
||||
|
||||
|
||||
@@ -1517,19 +1591,20 @@ extern "C" {
|
||||
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
|
||||
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
||||
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0, {0, {0}}}
|
||||
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
|
||||
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_RemoteShell_init_default {_meshtastic_RemoteShell_OpCode_MIN, 0, 0, 0, {0, {0}}, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
|
||||
#define meshtastic_StatusMessage_init_default {""}
|
||||
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
|
||||
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
||||
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN, 0}
|
||||
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_MyNodeInfo_init_default {0, 0, 0, {0, {0}}, "", _meshtastic_FirmwareEdition_MIN, 0}
|
||||
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
|
||||
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
|
||||
#define meshtastic_LockdownStatus_init_default {_meshtastic_LockdownStatus_State_MIN, "", 0, 0, 0}
|
||||
#define meshtastic_ClientNotification_init_default {false, 0, 0, _meshtastic_LogRecord_Level_MIN, "", 0, {meshtastic_KeyVerificationNumberInform_init_default}}
|
||||
#define meshtastic_KeyVerificationNumberInform_init_default {0, "", 0}
|
||||
#define meshtastic_KeyVerificationNumberRequest_init_default {0, ""}
|
||||
@@ -1551,19 +1626,20 @@ extern "C" {
|
||||
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
|
||||
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
||||
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0, {0, {0}}}
|
||||
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
|
||||
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_RemoteShell_init_zero {_meshtastic_RemoteShell_OpCode_MIN, 0, 0, 0, {0, {0}}, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
|
||||
#define meshtastic_StatusMessage_init_zero {""}
|
||||
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
|
||||
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
||||
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN, 0}
|
||||
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0, {0, {0}}, "", _meshtastic_FirmwareEdition_MIN, 0}
|
||||
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
|
||||
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
|
||||
#define meshtastic_LockdownStatus_init_zero {_meshtastic_LockdownStatus_State_MIN, "", 0, 0, 0}
|
||||
#define meshtastic_ClientNotification_init_zero {false, 0, 0, _meshtastic_LogRecord_Level_MIN, "", 0, {meshtastic_KeyVerificationNumberInform_init_zero}}
|
||||
#define meshtastic_KeyVerificationNumberInform_init_zero {0, "", 0}
|
||||
#define meshtastic_KeyVerificationNumberRequest_init_zero {0, ""}
|
||||
@@ -1631,6 +1707,7 @@ extern "C" {
|
||||
#define meshtastic_Data_reply_id_tag 7
|
||||
#define meshtastic_Data_emoji_tag 8
|
||||
#define meshtastic_Data_bitfield_tag 9
|
||||
#define meshtastic_Data_xeddsa_signature_tag 10
|
||||
#define meshtastic_KeyVerification_nonce_tag 1
|
||||
#define meshtastic_KeyVerification_hash1_tag 2
|
||||
#define meshtastic_KeyVerification_hash2_tag 3
|
||||
@@ -1688,6 +1765,7 @@ extern "C" {
|
||||
#define meshtastic_MeshPacket_relay_node_tag 19
|
||||
#define meshtastic_MeshPacket_tx_after_tag 20
|
||||
#define meshtastic_MeshPacket_transport_mechanism_tag 21
|
||||
#define meshtastic_MeshPacket_xeddsa_signed_tag 22
|
||||
#define meshtastic_NodeInfo_num_tag 1
|
||||
#define meshtastic_NodeInfo_user_tag 2
|
||||
#define meshtastic_NodeInfo_position_tag 3
|
||||
@@ -1701,6 +1779,7 @@ extern "C" {
|
||||
#define meshtastic_NodeInfo_is_ignored_tag 11
|
||||
#define meshtastic_NodeInfo_is_key_manually_verified_tag 12
|
||||
#define meshtastic_NodeInfo_is_muted_tag 13
|
||||
#define meshtastic_NodeInfo_has_xeddsa_signed_tag 14
|
||||
#define meshtastic_MyNodeInfo_my_node_num_tag 1
|
||||
#define meshtastic_MyNodeInfo_reboot_count_tag 8
|
||||
#define meshtastic_MyNodeInfo_min_app_version_tag 11
|
||||
@@ -1716,6 +1795,11 @@ extern "C" {
|
||||
#define meshtastic_QueueStatus_free_tag 2
|
||||
#define meshtastic_QueueStatus_maxlen_tag 3
|
||||
#define meshtastic_QueueStatus_mesh_packet_id_tag 4
|
||||
#define meshtastic_LockdownStatus_state_tag 1
|
||||
#define meshtastic_LockdownStatus_lock_reason_tag 2
|
||||
#define meshtastic_LockdownStatus_boots_remaining_tag 3
|
||||
#define meshtastic_LockdownStatus_valid_until_epoch_tag 4
|
||||
#define meshtastic_LockdownStatus_backoff_seconds_tag 5
|
||||
#define meshtastic_KeyVerificationNumberInform_nonce_tag 1
|
||||
#define meshtastic_KeyVerificationNumberInform_remote_longname_tag 2
|
||||
#define meshtastic_KeyVerificationNumberInform_security_number_tag 3
|
||||
@@ -1775,6 +1859,7 @@ extern "C" {
|
||||
#define meshtastic_FromRadio_fileInfo_tag 15
|
||||
#define meshtastic_FromRadio_clientNotification_tag 16
|
||||
#define meshtastic_FromRadio_deviceuiConfig_tag 17
|
||||
#define meshtastic_FromRadio_lockdown_status_tag 18
|
||||
#define meshtastic_Heartbeat_nonce_tag 1
|
||||
#define meshtastic_ToRadio_packet_tag 1
|
||||
#define meshtastic_ToRadio_want_config_id_tag 3
|
||||
@@ -1861,7 +1946,8 @@ X(a, STATIC, SINGULAR, FIXED32, source, 5) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \
|
||||
X(a, STATIC, OPTIONAL, UINT32, bitfield, 9)
|
||||
X(a, STATIC, OPTIONAL, UINT32, bitfield, 9) \
|
||||
X(a, STATIC, SINGULAR, BYTES, xeddsa_signature, 10)
|
||||
#define meshtastic_Data_CALLBACK NULL
|
||||
#define meshtastic_Data_DEFAULT NULL
|
||||
|
||||
@@ -1946,7 +2032,8 @@ X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) \
|
||||
X(a, STATIC, SINGULAR, UINT32, next_hop, 18) \
|
||||
X(a, STATIC, SINGULAR, UINT32, relay_node, 19) \
|
||||
X(a, STATIC, SINGULAR, UINT32, tx_after, 20) \
|
||||
X(a, STATIC, SINGULAR, UENUM, transport_mechanism, 21)
|
||||
X(a, STATIC, SINGULAR, UENUM, transport_mechanism, 21) \
|
||||
X(a, STATIC, SINGULAR, BOOL, xeddsa_signed, 22)
|
||||
#define meshtastic_MeshPacket_CALLBACK NULL
|
||||
#define meshtastic_MeshPacket_DEFAULT NULL
|
||||
#define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data
|
||||
@@ -1964,7 +2051,8 @@ X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_ignored, 11) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_key_manually_verified, 12) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_muted, 13)
|
||||
X(a, STATIC, SINGULAR, BOOL, is_muted, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, has_xeddsa_signed, 14)
|
||||
#define meshtastic_NodeInfo_CALLBACK NULL
|
||||
#define meshtastic_NodeInfo_DEFAULT NULL
|
||||
#define meshtastic_NodeInfo_user_MSGTYPE meshtastic_User
|
||||
@@ -2015,7 +2103,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,clientNotification,clientNotification), 16) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,deviceuiConfig,deviceuiConfig), 17)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,deviceuiConfig,deviceuiConfig), 17) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lockdown_status,lockdown_status), 18)
|
||||
#define meshtastic_FromRadio_CALLBACK NULL
|
||||
#define meshtastic_FromRadio_DEFAULT NULL
|
||||
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
|
||||
@@ -2032,6 +2121,16 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,deviceuiConfig,deviceuiConfi
|
||||
#define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo
|
||||
#define meshtastic_FromRadio_payload_variant_clientNotification_MSGTYPE meshtastic_ClientNotification
|
||||
#define meshtastic_FromRadio_payload_variant_deviceuiConfig_MSGTYPE meshtastic_DeviceUIConfig
|
||||
#define meshtastic_FromRadio_payload_variant_lockdown_status_MSGTYPE meshtastic_LockdownStatus
|
||||
|
||||
#define meshtastic_LockdownStatus_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, state, 1) \
|
||||
X(a, STATIC, SINGULAR, STRING, lock_reason, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, boots_remaining, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, valid_until_epoch, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, backoff_seconds, 5)
|
||||
#define meshtastic_LockdownStatus_CALLBACK NULL
|
||||
#define meshtastic_LockdownStatus_DEFAULT NULL
|
||||
|
||||
#define meshtastic_ClientNotification_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, UINT32, reply_id, 1) \
|
||||
@@ -2192,6 +2291,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LogRecord_msg;
|
||||
extern const pb_msgdesc_t meshtastic_QueueStatus_msg;
|
||||
extern const pb_msgdesc_t meshtastic_FromRadio_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LockdownStatus_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ClientNotification_msg;
|
||||
extern const pb_msgdesc_t meshtastic_KeyVerificationNumberInform_msg;
|
||||
extern const pb_msgdesc_t meshtastic_KeyVerificationNumberRequest_msg;
|
||||
@@ -2228,6 +2328,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg
|
||||
#define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg
|
||||
#define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg
|
||||
#define meshtastic_LockdownStatus_fields &meshtastic_LockdownStatus_msg
|
||||
#define meshtastic_ClientNotification_fields &meshtastic_ClientNotification_msg
|
||||
#define meshtastic_KeyVerificationNumberInform_fields &meshtastic_KeyVerificationNumberInform_msg
|
||||
#define meshtastic_KeyVerificationNumberRequest_fields &meshtastic_KeyVerificationNumberRequest_msg
|
||||
@@ -2253,7 +2354,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_ChunkedPayload_size 245
|
||||
#define meshtastic_ClientNotification_size 482
|
||||
#define meshtastic_Compressed_size 239
|
||||
#define meshtastic_Data_size 269
|
||||
#define meshtastic_Data_size 335
|
||||
#define meshtastic_DeviceMetadata_size 54
|
||||
#define meshtastic_DuplicatedPublicKey_size 0
|
||||
#define meshtastic_FileInfo_size 236
|
||||
@@ -2263,14 +2364,15 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_KeyVerificationNumberInform_size 58
|
||||
#define meshtastic_KeyVerificationNumberRequest_size 52
|
||||
#define meshtastic_KeyVerification_size 79
|
||||
#define meshtastic_LockdownStatus_size 53
|
||||
#define meshtastic_LogRecord_size 426
|
||||
#define meshtastic_LowEntropyKey_size 0
|
||||
#define meshtastic_MeshPacket_size 381
|
||||
#define meshtastic_MeshPacket_size 450
|
||||
#define meshtastic_MqttClientProxyMessage_size 501
|
||||
#define meshtastic_MyNodeInfo_size 83
|
||||
#define meshtastic_NeighborInfo_size 258
|
||||
#define meshtastic_Neighbor_size 22
|
||||
#define meshtastic_NodeInfo_size 325
|
||||
#define meshtastic_NodeInfo_size 327
|
||||
#define meshtastic_NodeRemoteHardwarePin_size 29
|
||||
#define meshtastic_Position_size 144
|
||||
#define meshtastic_QueueStatus_size 23
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
@@ -80,7 +80,7 @@ static_assert(sizeof(meshtastic_NodeInfoLite) <= 130, "NodeInfoLite size increas
|
||||
#if defined(ARCH_STM32WL)
|
||||
#define MAX_NUM_NODES 10
|
||||
#elif defined(ARCH_NRF52)
|
||||
#define MAX_NUM_NODES 80
|
||||
#define MAX_NUM_NODES 150
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#include "Esp.h"
|
||||
static inline int get_max_num_nodes()
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
#include <AsyncUDP.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#define UDP_MULTICAST_DEFAUL_PORT 4403 // Default port for UDP multicast is same as TCP api server
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#include "target_specific.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_CH390D)
|
||||
@@ -291,10 +290,8 @@ static int32_t reconnectWiFi()
|
||||
#endif
|
||||
return 1000; // check once per second
|
||||
} else {
|
||||
#ifdef ARCH_RP2040
|
||||
onNetworkConnected(); // will only do anything once
|
||||
#endif
|
||||
return 300000; // every 5 minutes
|
||||
onNetworkConnected(); // will only do anything once (guarded by APStartupComplete)
|
||||
return 300000; // every 5 minutes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,9 +340,6 @@ bool initWifi()
|
||||
const char *wifiPsw = config.network.wifi_psk;
|
||||
|
||||
#ifndef ARCH_RP2040
|
||||
#if !MESHTASTIC_EXCLUDE_WEBSERVER
|
||||
createSSLCert(); // For WebServer
|
||||
#endif
|
||||
WiFi.persistent(false); // Disable flash storage for WiFi credentials
|
||||
#endif
|
||||
if (!*wifiPsw) // Treat empty password as no password
|
||||
@@ -370,6 +364,9 @@ bool initWifi()
|
||||
#endif
|
||||
}
|
||||
#ifdef ARCH_ESP32
|
||||
// Register WiFi event handler BEFORE createSSLCert() to prevent race condition:
|
||||
// Without this, WiFi can auto-reconnect during cert generation and fire GOT_IP
|
||||
// before the handler is registered, causing onNetworkConnected() to never run.
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.setSleep(false);
|
||||
@@ -391,6 +388,12 @@ bool initWifi()
|
||||
wifiDisconnectReason = info.wifi_sta_disconnected.reason;
|
||||
},
|
||||
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
||||
#endif
|
||||
|
||||
#ifndef ARCH_RP2040
|
||||
#if !MESHTASTIC_EXCLUDE_WEBSERVER
|
||||
createSSLCert(); // For WebServer - called after WiFi.onEvent() to avoid race condition
|
||||
#endif
|
||||
#endif
|
||||
LOG_DEBUG("JOINING WIFI soon: ssid=%s", wifiName);
|
||||
wifiReconnect = new Periodic("WifiConnect", reconnectWiFi);
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
extern bool needReconnect;
|
||||
|
||||
@@ -262,7 +262,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_ota_request_tag: {
|
||||
#if defined(ARCH_ESP32)
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
|
||||
LOG_INFO("OTA Requested");
|
||||
|
||||
if (r->ota_request.ota_hash.size != 32) {
|
||||
|
||||
@@ -21,9 +21,6 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
|
||||
textPacketList[textPacketListIndex] = mp.id;
|
||||
textPacketListIndex = (textPacketListIndex + 1) % TEXT_PACKET_LIST_SIZE;
|
||||
|
||||
// We only store/display messages destined for us.
|
||||
devicestate.rx_text_message = mp;
|
||||
devicestate.has_rx_text_message = true;
|
||||
IF_SCREEN(
|
||||
// Guard against running in MeshtasticUI or with no screen
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
@@ -59,4 +56,4 @@ bool TextMessageModule::recentlySeen(uint32_t id)
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* Text message handling for Meshtastic.
|
||||
*
|
||||
* This module is responsible for receiving and storing incoming text messages
|
||||
* from the mesh. It updates device state and notifies observers so that other
|
||||
* components (such as the MessageRenderer) can later display or process them.
|
||||
* from the mesh. It notifies observers so that other components (such as the
|
||||
* MessageRenderer) can later display or process them.
|
||||
*
|
||||
* Rendering of messages on screen is no longer done here.
|
||||
*/
|
||||
@@ -36,4 +36,4 @@ class TextMessageModule : public SinglePortModule, public Observable<const mesht
|
||||
size_t textPacketListIndex = 0;
|
||||
};
|
||||
|
||||
extern TextMessageModule *textMessageModule;
|
||||
extern TextMessageModule *textMessageModule;
|
||||
|
||||
@@ -90,8 +90,8 @@ int32_t PaxcounterModule::runOnce()
|
||||
configuration.blecounter = 1;
|
||||
configuration.blescantime = 0; // infinite
|
||||
configuration.wificounter = 1;
|
||||
configuration.wifi_channel_map = WIFI_CHANNEL_ALL;
|
||||
configuration.wifi_channel_switch_interval = 50;
|
||||
// configuration.wifi_channel_map = WIFI_CHANNEL_ALL;
|
||||
// configuration.wifi_channel_switch_interval = 50;
|
||||
configuration.wifi_rssi_threshold = Default::getConfiguredOrDefault(moduleConfig.paxcounter.wifi_threshold, -80);
|
||||
configuration.ble_rssi_threshold = Default::getConfiguredOrDefault(moduleConfig.paxcounter.ble_threshold, -80);
|
||||
libpax_update_config(&configuration);
|
||||
|
||||
+2
-6
@@ -19,12 +19,8 @@
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#elif HAS_ETHERNET && defined(USE_CH390D)
|
||||
#include "ESP32_CH390.h"
|
||||
#define ETH CH390
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
#include "Default.h"
|
||||
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
|
||||
|
||||
+164
-244
@@ -10,23 +10,18 @@
|
||||
#include "mesh/PhoneAPI.h"
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include "sleep.h"
|
||||
#include <NimBLEDevice.h>
|
||||
#include <BLE2904.h>
|
||||
#include <BLEAdvertising.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLESecurity.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
#include "NimBLEAdvertising.h"
|
||||
#include "NimBLEExtAdvertising.h"
|
||||
#include "PowerStatus.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
#include "host/ble_gap.h"
|
||||
#else
|
||||
#include "nimble/nimble/host/include/host/ble_gap.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
#include "host/ble_store.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -34,7 +29,6 @@ constexpr uint16_t kPreferredBleMtu = 517;
|
||||
constexpr uint16_t kPreferredBleTxOctets = 251;
|
||||
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
// Debugging options: careful, they slow things down quite a bit!
|
||||
// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration
|
||||
@@ -44,10 +38,10 @@ constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
#define NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE 3
|
||||
#define NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE 3
|
||||
|
||||
NimBLECharacteristic *fromNumCharacteristic;
|
||||
NimBLECharacteristic *BatteryCharacteristic;
|
||||
NimBLECharacteristic *logRadioCharacteristic;
|
||||
NimBLEServer *bleServer;
|
||||
BLECharacteristic *fromNumCharacteristic;
|
||||
BLECharacteristic *BatteryCharacteristic;
|
||||
BLECharacteristic *logRadioCharacteristic;
|
||||
BLEServer *bleServer;
|
||||
|
||||
static bool passkeyShowing;
|
||||
static std::atomic<uint16_t> nimbleBluetoothConnHandle{BLE_HS_CONN_HANDLE_NONE}; // BLE_HS_CONN_HANDLE_NONE means "no connection"
|
||||
@@ -118,7 +112,8 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
- Yes, we have to do some copy operations on pop because of this, but it's worth it to avoid cross-task memory management.
|
||||
|
||||
NOTIFY IS BROKEN:
|
||||
- Adding NIMBLE_PROPERTY::NOTIFY to FromRadioCharacteristic appears to break things. It is NOT backwards compatible.
|
||||
- Adding BLECharacteristic::PROPERTY_NOTIFY to FromRadioCharacteristic appears to break things. It is NOT backwards
|
||||
compatible.
|
||||
|
||||
ZERO-SIZE READS:
|
||||
- Returning a zero-size read from onRead breaks some clients during the config phase. So we have to block onRead until we
|
||||
@@ -139,7 +134,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
std::mutex fromPhoneMutex;
|
||||
std::atomic<size_t> fromPhoneQueueSize{0};
|
||||
// We use array here (and pay the cost of memcpy) to avoid dynamic memory allocations and frees across FreeRTOS tasks.
|
||||
std::array<NimBLEAttValue, NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE> fromPhoneQueue{};
|
||||
std::array<BLEValue, NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE> fromPhoneQueue{};
|
||||
|
||||
/* Packets to phone (BLE onRead callback) */
|
||||
std::mutex toPhoneMutex;
|
||||
@@ -301,7 +296,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
LOG_DEBUG("NimbleBluetooth: handling ToRadio packet, fromPhoneQueueSize=%u", fromPhoneQueueSize.load());
|
||||
|
||||
// Pop the front of fromPhoneQueue, holding the mutex only briefly while we pop.
|
||||
NimBLEAttValue val;
|
||||
BLEValue val;
|
||||
{ // scope for fromPhoneMutex mutex
|
||||
std::lock_guard<std::mutex> guard(fromPhoneMutex);
|
||||
val = fromPhoneQueue[0];
|
||||
@@ -316,7 +311,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
fromPhoneQueueSize--;
|
||||
}
|
||||
|
||||
handleToRadio(val.data(), val.length());
|
||||
handleToRadio(val.getData(), val.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,9 +323,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
#ifdef DEBUG_NIMBLE_NOTIFY
|
||||
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
// This logging slows things down when there are lots of packets going to the phone, like initial connection:
|
||||
LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc);
|
||||
@@ -340,13 +333,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
put_le32(val, fromRadioNum);
|
||||
|
||||
fromNumCharacteristic->setValue(val, sizeof(val));
|
||||
#ifdef NIMBLE_TWO
|
||||
// NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be
|
||||
// notify().
|
||||
fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
fromNumCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
@@ -409,14 +396,9 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
// Last ToRadio value received from the phone
|
||||
static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
|
||||
|
||||
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
class NimbleBluetoothToRadioCallback : public BLECharacteristicCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override
|
||||
#else
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic) override
|
||||
|
||||
#endif
|
||||
void onWrite(BLECharacteristic *pCharacteristic) override
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
// Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls.
|
||||
@@ -428,15 +410,17 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
LOG_DEBUG("BLE onWrite(%d): start millis=%d", currentWriteCount, startMillis);
|
||||
#endif
|
||||
|
||||
auto val = pCharacteristic->getValue();
|
||||
// Create a BLEValue and populate it with the received data
|
||||
BLEValue val;
|
||||
val.setValue(pCharacteristic->getData(), pCharacteristic->getLength());
|
||||
|
||||
if (memcmp(lastToRadio, val.data(), val.length()) != 0) {
|
||||
if (memcmp(lastToRadio, val.getData(), val.getLength()) != 0) {
|
||||
if (bluetoothPhoneAPI->fromPhoneQueueSize < NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE) {
|
||||
// Note: the comparison above is safe without a mutex because we are the only method that *increases*
|
||||
// fromPhoneQueueSize. (It's okay if fromPhoneQueueSize *decreases* in the main task meanwhile.)
|
||||
memcpy(lastToRadio, val.data(), val.length());
|
||||
memcpy(lastToRadio, val.getData(), val.getLength());
|
||||
|
||||
{ // scope for fromPhoneMutex mutex
|
||||
{ // scope for fromPhoneMutex mutexv, pCharacteristic->getLen
|
||||
// Append to fromPhoneQueue, protected by fromPhoneMutex. Hold the mutex as briefly as possible.
|
||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->fromPhoneMutex);
|
||||
bluetoothPhoneAPI->fromPhoneQueue.at(bluetoothPhoneAPI->fromPhoneQueueSize) = val;
|
||||
@@ -450,24 +434,21 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
#ifdef DEBUG_NIMBLE_ON_WRITE_TIMING
|
||||
int finishMillis = millis();
|
||||
LOG_DEBUG("BLE onWrite(%d): append to fromPhoneQueue took %u ms. numBytes=%d", currentWriteCount,
|
||||
finishMillis - startMillis, val.length());
|
||||
finishMillis - startMillis, val.getLength());
|
||||
#endif
|
||||
} else {
|
||||
LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount, val.length());
|
||||
LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount,
|
||||
val.getLength());
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.length());
|
||||
LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.getLength());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
class NimbleBluetoothFromRadioCallback : public BLECharacteristicCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override
|
||||
#else
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic) override
|
||||
#endif
|
||||
void onRead(BLECharacteristic *pCharacteristic) override
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
|
||||
@@ -573,34 +554,15 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
}
|
||||
};
|
||||
|
||||
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
public:
|
||||
NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; }
|
||||
|
||||
private:
|
||||
NimbleBluetooth *ble;
|
||||
|
||||
virtual uint32_t onPassKeyDisplay() override
|
||||
#else
|
||||
virtual uint32_t onPassKeyRequest() override
|
||||
#endif
|
||||
void onPassKeyNotify(uint32_t passkey) override
|
||||
{
|
||||
uint32_t passkey = config.bluetooth.fixed_pin;
|
||||
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Use random passkey");
|
||||
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
passkey = random(100000, 999999);
|
||||
}
|
||||
LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
|
||||
|
||||
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
#if HAS_SCREEN
|
||||
if (screen) {
|
||||
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
@@ -630,15 +592,8 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
}
|
||||
#endif
|
||||
passkeyShowing = true;
|
||||
|
||||
return passkey;
|
||||
}
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo) override
|
||||
#else
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc) override
|
||||
#endif
|
||||
void onAuthenticationComplete(ble_gap_conn_desc *desc) override
|
||||
{
|
||||
LOG_INFO("BLE authentication complete");
|
||||
|
||||
@@ -646,58 +601,47 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
clearPairingDisplay();
|
||||
|
||||
// Store the connection handle for future use
|
||||
#ifdef NIMBLE_TWO
|
||||
nimbleBluetoothConnHandle = connInfo.getConnHandle();
|
||||
#else
|
||||
nimbleBluetoothConnHandle = desc->conn_handle;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override
|
||||
class NimbleBluetoothServerCallback : public BLEServerCallbacks
|
||||
{
|
||||
public:
|
||||
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {}
|
||||
|
||||
private:
|
||||
NimbleBluetooth *ble;
|
||||
|
||||
void onConnect(BLEServer *pServer, struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
|
||||
BLEAddress peer_addr(desc->peer_id_addr);
|
||||
LOG_INFO("BLE incoming connection %s", peer_addr.toString().c_str());
|
||||
|
||||
const uint16_t connHandle = connInfo.getConnHandle();
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int phyResult =
|
||||
ble_gap_set_prefered_le_phy(connHandle, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_CODED_ANY);
|
||||
if (phyResult == 0) {
|
||||
LOG_INFO("BLE conn %u requested 2M PHY", connHandle);
|
||||
} else {
|
||||
LOG_WARN("Failed to prefer 2M PHY for conn %u, rc=%d", connHandle, phyResult);
|
||||
}
|
||||
#endif
|
||||
const uint16_t connHandle = desc->conn_handle;
|
||||
|
||||
// With Google Pixel 8 Android devices, this causes ESP32 device crash
|
||||
// when phone reconnects. Disable this to make progress on the
|
||||
// Arduino v3 migration while we investigate the Android compatibility
|
||||
// issue.
|
||||
#if 0
|
||||
int dataLenResult = ble_gap_set_data_len(connHandle, kPreferredBleTxOctets, kPreferredBleTxTimeUs);
|
||||
if (dataLenResult == 0) {
|
||||
LOG_INFO("BLE conn %u requested data length %u bytes", connHandle, kPreferredBleTxOctets);
|
||||
} else {
|
||||
LOG_WARN("Failed to raise data length for conn %u, rc=%d", connHandle, dataLenResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
|
||||
LOG_INFO("BLE conn %u peer MTU %u (target %u)", connHandle, pServer->getPeerMTU(connHandle), kPreferredBleMtu);
|
||||
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override
|
||||
void onDisconnect(BLEServer *pServer, struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
LOG_INFO("BLE disconnect reason: %d", reason);
|
||||
#else
|
||||
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) override
|
||||
{
|
||||
LOG_INFO("BLE disconnect");
|
||||
#endif
|
||||
#ifdef NIMBLE_TWO
|
||||
LOG_INFO("BLE disconnected");
|
||||
if (ble->isDeInit)
|
||||
return;
|
||||
#else
|
||||
if (nimbleBluetooth && nimbleBluetooth->isDeInit)
|
||||
return;
|
||||
#endif
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
@@ -722,43 +666,51 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
bluetoothPhoneAPI->writeCount = 0;
|
||||
}
|
||||
|
||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection"
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
// Restart Advertising
|
||||
ble->startAdvertising();
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (!pAdvertising->start(0)) {
|
||||
if (pAdvertising->isAdvertising()) {
|
||||
LOG_DEBUG("BLE advertising already running");
|
||||
} else {
|
||||
LOG_ERROR("BLE failed to restart advertising");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static NimbleBluetoothToRadioCallback *toRadioCallbacks;
|
||||
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->stop();
|
||||
pAdvertising->reset();
|
||||
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
// if (powerStatus->getHasBattery() == 1) {
|
||||
// pAdvertising->addServiceUUID(BLEUUID((uint16_t)0x180f));
|
||||
// }
|
||||
|
||||
BLEAdvertisementData scan = BLEAdvertisementData();
|
||||
scan.setName(getDeviceName());
|
||||
pAdvertising->setScanResponseData(scan);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
|
||||
if (!pAdvertising->start(0)) {
|
||||
LOG_ERROR("BLE failed to start advertising");
|
||||
} else {
|
||||
LOG_DEBUG("BLE Advertising started");
|
||||
}
|
||||
}
|
||||
|
||||
void NimbleBluetooth::shutdown()
|
||||
{
|
||||
// No measurable power saving for ESP32 during light-sleep(?)
|
||||
#ifndef ARCH_ESP32
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable bluetooth");
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Proper shutdown for ESP32. Needs reboot to reverse.
|
||||
void NimbleBluetooth::deinit()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -768,21 +720,19 @@ void NimbleBluetooth::deinit()
|
||||
#ifdef BLE_LED
|
||||
digitalWrite(BLE_LED, LED_STATE_OFF);
|
||||
#endif
|
||||
#ifndef NIMBLE_TWO
|
||||
NimBLEDevice::deinit();
|
||||
#endif
|
||||
|
||||
BLEDevice::deinit(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Has initial setup been completed
|
||||
bool NimbleBluetooth::isActive()
|
||||
{
|
||||
return bleServer;
|
||||
return bleServer != nullptr;
|
||||
}
|
||||
|
||||
bool NimbleBluetooth::isConnected()
|
||||
{
|
||||
return bleServer->getConnectedCount() > 0;
|
||||
return bleServer && bleServer->getConnectedCount() > 0;
|
||||
}
|
||||
|
||||
int NimbleBluetooth::getRssi()
|
||||
@@ -795,9 +745,9 @@ int NimbleBluetooth::getRssi()
|
||||
uint16_t connHandle = nimbleBluetoothConnHandle.load();
|
||||
|
||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
const auto peers = bleServer->getPeerDevices();
|
||||
const auto peers = bleServer->getPeerDevices(true);
|
||||
if (!peers.empty()) {
|
||||
connHandle = peers.front();
|
||||
connHandle = peers.begin()->first;
|
||||
nimbleBluetoothConnHandle = connHandle;
|
||||
}
|
||||
}
|
||||
@@ -825,74 +775,84 @@ void NimbleBluetooth::setup()
|
||||
|
||||
LOG_INFO("Init the NimBLE bluetooth module");
|
||||
|
||||
NimBLEDevice::init(getDeviceName());
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
BLEDevice::init(getDeviceName());
|
||||
BLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
|
||||
int mtuResult = BLEDevice::setMTU(kPreferredBleMtu);
|
||||
if (mtuResult == 0) {
|
||||
LOG_INFO("BLE MTU request set to %u", kPreferredBleMtu);
|
||||
} else {
|
||||
LOG_WARN("Unable to request MTU %u, rc=%d", kPreferredBleMtu, mtuResult);
|
||||
}
|
||||
|
||||
int phyResult = ble_gap_set_prefered_default_le_phy(BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK);
|
||||
if (phyResult == 0) {
|
||||
LOG_INFO("BLE default PHY preference set to 2M");
|
||||
} else {
|
||||
LOG_WARN("Failed to prefer 2M PHY by default, rc=%d", phyResult);
|
||||
}
|
||||
|
||||
int dataLenResult = ble_gap_write_sugg_def_data_len(kPreferredBleTxOctets, kPreferredBleTxTimeUs);
|
||||
if (dataLenResult == 0) {
|
||||
LOG_INFO("BLE suggested data length set to %u bytes", kPreferredBleTxOctets);
|
||||
} else {
|
||||
LOG_WARN("Failed to raise suggested data length (%u/%u), rc=%d", kPreferredBleTxOctets, kPreferredBleTxTimeUs,
|
||||
dataLenResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
|
||||
NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
||||
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
// Set IO capability to DisplayOnly for MITM authentication
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
// Set the passkey
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Use random passkey");
|
||||
pSecurity->setPassKey(false); // generate a random passkey
|
||||
} else {
|
||||
LOG_INFO("Use fixed passkey");
|
||||
pSecurity->setPassKey(true, config.bluetooth.fixed_pin);
|
||||
}
|
||||
// Enable authorization requirements:
|
||||
// - bonding: true (for persistent storage of the keys)
|
||||
// - MITM: true (enables Man-In-The-Middle protection for password prompts)
|
||||
// - secure connection: true (enables secure connection for encryption)
|
||||
pSecurity->setAuthenticationMode(true, true, true);
|
||||
} else {
|
||||
// No IO capability for no PIN mode
|
||||
pSecurity->setCapability(ESP_IO_CAP_NONE);
|
||||
// No PIN mode: no MITM protection
|
||||
pSecurity->setAuthenticationMode(true, false, false);
|
||||
}
|
||||
bleServer = NimBLEDevice::createServer();
|
||||
#ifdef NIMBLE_TWO
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
|
||||
#else
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
|
||||
#endif
|
||||
bleServer->setCallbacks(serverCallbacks, true);
|
||||
// Set the security callbacks
|
||||
BLEDevice::setSecurityCallbacks(new NimbleBluetoothSecurityCallback());
|
||||
bleServer = BLEDevice::createServer();
|
||||
|
||||
// BLEDevice::createServer calls ble_svc_gap_init, which resets the device
|
||||
// name to default, so set it again.
|
||||
int nameRc = ble_svc_gap_device_name_set(BLEDevice::getDeviceName().c_str());
|
||||
if (nameRc != 0) {
|
||||
LOG_ERROR("ble_svc_gap_device_name_set: rc=%d %s", nameRc, BLEUtils::returnCodeToString(nameRc));
|
||||
}
|
||||
|
||||
bleServer->setCallbacks(new NimbleBluetoothServerCallback(this));
|
||||
setupService();
|
||||
startAdvertising();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::setupService()
|
||||
{
|
||||
NimBLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
|
||||
NimBLECharacteristic *ToRadioCharacteristic;
|
||||
NimBLECharacteristic *FromRadioCharacteristic;
|
||||
BLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
|
||||
BLECharacteristic *ToRadioCharacteristic;
|
||||
BLECharacteristic *FromRadioCharacteristic;
|
||||
// Define the characteristics that the app is looking for
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE);
|
||||
// Allow notifications so phones can stream FromRadio without polling.
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
||||
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
||||
logRadioCharacteristic =
|
||||
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
||||
} else {
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(
|
||||
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(
|
||||
FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ);
|
||||
fromNumCharacteristic =
|
||||
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
bleService->createCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ);
|
||||
logRadioCharacteristic = bleService->createCharacteristic(LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
} else {
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_WRITE_AUTHEN |
|
||||
BLECharacteristic::PROPERTY_WRITE_ENC);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN |
|
||||
BLECharacteristic::PROPERTY_READ_ENC);
|
||||
fromNumCharacteristic = bleService->createCharacteristic(
|
||||
FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
|
||||
logRadioCharacteristic = bleService->createCharacteristic(
|
||||
LOGRADIO_UUID,
|
||||
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
|
||||
LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
|
||||
}
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
@@ -905,76 +865,31 @@ void NimbleBluetooth::setupService()
|
||||
bleService->start();
|
||||
|
||||
// Setup the battery service
|
||||
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
||||
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904();
|
||||
#else
|
||||
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
|
||||
#endif
|
||||
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
||||
BLEService *batteryService = bleServer->createService(BLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
BLE2904 *batteryLevelDescriptor = new BLE2904();
|
||||
batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
|
||||
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
||||
(uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
BatteryCharacteristic->addDescriptor(batteryLevelDescriptor);
|
||||
batteryService->start();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
NimBLEExtAdvertisement legacyAdvertising;
|
||||
|
||||
legacyAdvertising.setLegacyAdvertising(true);
|
||||
legacyAdvertising.setScannable(true);
|
||||
legacyAdvertising.setConnectable(true);
|
||||
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
|
||||
legacyAdvertising.setMinInterval(500);
|
||||
legacyAdvertising.setMaxInterval(1000);
|
||||
|
||||
NimBLEExtAdvertisement legacyScanResponse;
|
||||
legacyScanResponse.setLegacyAdvertising(true);
|
||||
legacyScanResponse.setConnectable(true);
|
||||
legacyScanResponse.setName(getDeviceName());
|
||||
|
||||
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
|
||||
LOG_ERROR("BLE failed to set legacyAdvertising");
|
||||
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
|
||||
LOG_ERROR("BLE failed to set legacyScanResponse");
|
||||
} else if (!pAdvertising->start(0, 0, 0)) {
|
||||
LOG_ERROR("BLE failed to start legacyAdvertising");
|
||||
}
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
pAdvertising->start(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
|
||||
BatteryCharacteristic->setValue(&level, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
BatteryCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void NimbleBluetooth::clearBonds()
|
||||
{
|
||||
LOG_INFO("Clearing bluetooth bonds!");
|
||||
NimBLEDevice::deleteAllBonds();
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
|
||||
}
|
||||
|
||||
void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
@@ -982,11 +897,16 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
if (!bleServer || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
#ifdef NIMBLE_TWO
|
||||
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
logRadioCharacteristic->notify(logMessage, length, true);
|
||||
#endif
|
||||
logRadioCharacteristic->setValue(logMessage, length);
|
||||
logRadioCharacteristic->notify();
|
||||
}
|
||||
|
||||
void clearNVS()
|
||||
{
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
|
||||
#ifdef ARCH_ESP32
|
||||
ESP.restart();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,16 +12,11 @@ class NimbleBluetooth : BluetoothApi
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
#if defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
bool isDeInit = false;
|
||||
|
||||
private:
|
||||
void setupService();
|
||||
#if !defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
};
|
||||
|
||||
void setBluetoothEnable(bool enable);
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef ESP32_FORCE_IRAM_MEMSET
|
||||
|
||||
/*
|
||||
* T-Beam/classic ESP32 boot workaround
|
||||
* ------------------------------------
|
||||
* During early flash operations the ESP32 disables cache, but some IRAM flash
|
||||
* code paths still reach libc memcpy/memset. If those resolve to flash-resident
|
||||
* implementations, startup can panic with cache-disabled access errors.
|
||||
*
|
||||
* We wrap memcpy/memset for the T-Beam environment. Fast path uses the
|
||||
* normal libc routines when cache is enabled; slow path uses IRAM-safe byte
|
||||
* loops when cache is disabled.
|
||||
*/
|
||||
|
||||
extern void *__real_memcpy(void *dst, const void *src, size_t n);
|
||||
|
||||
static inline bool IRAM_ATTR cache_is_enabled(void)
|
||||
{
|
||||
return (*(volatile uint32_t *)0x3FF00040u) & (1u << 3);
|
||||
}
|
||||
|
||||
extern void *IRAM_ATTR __wrap_memcpy(void *dst, const void *src, size_t n)
|
||||
{
|
||||
if (cache_is_enabled()) {
|
||||
return __real_memcpy(dst, src, n);
|
||||
}
|
||||
|
||||
uint8_t *d = (uint8_t *)dst;
|
||||
const uint8_t *s = (const uint8_t *)src;
|
||||
while (n--) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef ESP32_FORCE_IRAM_MEMSET
|
||||
|
||||
/*
|
||||
* T-Beam/classic ESP32 boot workaround
|
||||
* ------------------------------------
|
||||
* During early flash operations the ESP32 disables cache, but some IRAM flash
|
||||
* code paths still reach libc memcpy/memset. If those resolve to flash-resident
|
||||
* implementations, startup can panic with cache-disabled access errors.
|
||||
*
|
||||
* We wrap memcpy/memset for the T-Beam environment. Fast path uses the
|
||||
* normal libc routines when cache is enabled; slow path uses IRAM-safe byte
|
||||
* loops when cache is disabled.
|
||||
*/
|
||||
|
||||
extern void *__real_memset(void *dst, int c, size_t n);
|
||||
|
||||
static inline bool IRAM_ATTR cache_is_enabled(void)
|
||||
{
|
||||
return (*(volatile uint32_t *)0x3FF00040u) & (1u << 3);
|
||||
}
|
||||
|
||||
extern void *IRAM_ATTR __wrap_memset(void *dst, int c, size_t n)
|
||||
{
|
||||
if (cache_is_enabled()) {
|
||||
return __real_memset(dst, c, n);
|
||||
}
|
||||
|
||||
uint8_t *ptr = (uint8_t *)dst;
|
||||
const uint8_t fill = (uint8_t)c;
|
||||
while (n--) {
|
||||
*ptr++ = fill;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
/* Arduino fix: catch esp_event's orphaned .text.handler_execute section and align to 4 bytes */
|
||||
SECTIONS
|
||||
{
|
||||
.text.handler_execute ALIGN(4) :
|
||||
{
|
||||
KEEP(*(.text.handler_execute))
|
||||
KEEP(*(.text.handler_execute.*))
|
||||
. = ALIGN(4);
|
||||
}
|
||||
}
|
||||
INSERT AFTER .flash.text;
|
||||
@@ -158,6 +158,8 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_CHATTER_2
|
||||
#elif defined(STATION_G2)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_STATION_G2
|
||||
#elif defined(STATION_G3)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_STATION_G3
|
||||
#elif defined(UNPHONE)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_UNPHONE
|
||||
#elif defined(WIPHONE)
|
||||
|
||||
@@ -165,17 +165,30 @@ void esp32Setup()
|
||||
// #define APP_WATCHDOG_SECS 45
|
||||
#define APP_WATCHDOG_SECS 90
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t));
|
||||
wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000;
|
||||
wdt_config->trigger_panic = true;
|
||||
res = esp_task_wdt_init(wdt_config);
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
const esp_task_wdt_config_t wdt_config = {
|
||||
.timeout_ms = APP_WATCHDOG_SECS * 1000,
|
||||
.idle_core_mask = (1U << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1U,
|
||||
.trigger_panic = true,
|
||||
};
|
||||
res = esp_task_wdt_init(&wdt_config);
|
||||
if (res == ESP_ERR_INVALID_STATE) {
|
||||
LOG_WARN("Task watchdog already initialized, reconfiguring existing instance");
|
||||
res = esp_task_wdt_reconfigure(&wdt_config);
|
||||
}
|
||||
assert(res == ESP_OK);
|
||||
#else
|
||||
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||
if (res == ESP_ERR_INVALID_STATE) {
|
||||
LOG_WARN("Task watchdog already initialized, reusing existing instance");
|
||||
res = ESP_OK;
|
||||
}
|
||||
assert(res == ESP_OK);
|
||||
#endif
|
||||
res = esp_task_wdt_add(NULL);
|
||||
res = esp_task_wdt_status(NULL);
|
||||
if (res == ESP_ERR_NOT_FOUND) {
|
||||
res = esp_task_wdt_add(NULL);
|
||||
}
|
||||
assert(res == ESP_OK);
|
||||
|
||||
#if HAS_32768HZ
|
||||
@@ -258,8 +271,10 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
#endif
|
||||
variant_shutdown();
|
||||
|
||||
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
#endif
|
||||
|
||||
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
|
||||
esp_deep_sleep_start(); // TBD mA sleep current (battery)
|
||||
|
||||
@@ -386,34 +386,12 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
|
||||
meshtastic::BluetoothStatus newStatus(textkey);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
#if HAS_SCREEN && \
|
||||
!defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
#if HAS_SCREEN && !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
||||
if (screen) {
|
||||
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 12;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, "Enter this code");
|
||||
|
||||
display->setFont(FONT_LARGE);
|
||||
String displayPin(btPIN);
|
||||
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, pin);
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String deviceName = "Name: ";
|
||||
deviceName.concat(getDeviceName());
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, deviceName);
|
||||
});
|
||||
std::string configuredPasskeyText = std::to_string(configuredPasskey);
|
||||
std::string ble_message =
|
||||
"Bluetooth\nPIN\n[M]" + configuredPasskeyText.substr(0, 3) + " " + configuredPasskeyText.substr(3, 6);
|
||||
screen->showSimpleBanner(ble_message.c_str(), 30000);
|
||||
}
|
||||
#endif
|
||||
passkeyShowing = true;
|
||||
|
||||
+4
-7
@@ -4,8 +4,10 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h
|
||||
#include <esp_adc_cal.h>
|
||||
// #include <driver/adc.h>
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_cali_scheme.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include <soc/adc_channel.h>
|
||||
#endif
|
||||
|
||||
@@ -28,11 +30,6 @@
|
||||
#define NUM_CELLS 1
|
||||
#endif
|
||||
|
||||
#ifdef BAT_MEASURE_ADC_UNIT
|
||||
extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
|
||||
#include "soc/sens_reg.h" // needed for adc pin reset
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
#include "modules/Telemetry/Sensor/nullSensor.h"
|
||||
#if __has_include(<Adafruit_INA219.h>)
|
||||
|
||||
@@ -291,7 +291,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
|
||||
auto addToRoute = [](JsonArray *route, NodeNum num) {
|
||||
char long_name[40] = "Unknown";
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = nodeInfoLiteHasUser(node);
|
||||
if (name_known) {
|
||||
const size_t copy_len =
|
||||
|
||||
+30
-12
@@ -18,8 +18,6 @@
|
||||
#include "target_specific.h"
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// "esp_pm_config_esp32_t is deprecated, please include esp_pm.h and use esp_pm_config_t instead"
|
||||
#include "esp32/pm.h"
|
||||
#include "esp_pm.h"
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
@@ -146,15 +144,31 @@ void initDeepSleep()
|
||||
// If we booted because our timer ran out or the user pressed reset, send those as fake events
|
||||
RESET_REASON hwReason = rtc_get_reset_reason(0);
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
if (hwReason == BROWN_OUT_RESET)
|
||||
reason = "brownout";
|
||||
else if (hwReason == HP_CORE_HP_WDT_RESET)
|
||||
reason = "taskWatchdog";
|
||||
else if (hwReason == HP_CORE_LP_WDT_RESET)
|
||||
reason = "intWatchdog";
|
||||
else if (hwReason == CHIP_LP_WDT_RESET)
|
||||
reason = "chipWatchdog";
|
||||
else if (hwReason == SUPER_WDT_RESET)
|
||||
reason = "superWatchdog";
|
||||
else if (hwReason == HP_SYS_HP_WDT_RESET)
|
||||
reason = "systemWatchdog";
|
||||
else if (hwReason == HP_SYS_LP_WDT_RESET)
|
||||
reason = "systemLowPowerWatchdog";
|
||||
#else
|
||||
if (hwReason == RTCWDT_BROWN_OUT_RESET)
|
||||
reason = "brownout";
|
||||
|
||||
if (hwReason == TG0WDT_SYS_RESET)
|
||||
else if (hwReason == RTCWDT_RTC_RESET)
|
||||
reason = "rtcWatchdog";
|
||||
else if (hwReason == TG0WDT_SYS_RESET)
|
||||
reason = "taskWatchdog";
|
||||
|
||||
if (hwReason == TG1WDT_SYS_RESET)
|
||||
else if (hwReason == TG1WDT_SYS_RESET)
|
||||
reason = "intWatchdog";
|
||||
|
||||
#endif
|
||||
LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s", wakeCause, bootCount, reason);
|
||||
#endif
|
||||
|
||||
@@ -397,8 +411,10 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
|
||||
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
|
||||
|
||||
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
#endif
|
||||
|
||||
#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP)
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
@@ -532,11 +548,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
*/
|
||||
void enableModemSleep()
|
||||
{
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
static esp_pm_config_t esp32_config; // filled with zeros because bss
|
||||
#else
|
||||
static esp_pm_config_esp32_t esp32_config; // filled with zeros because bss
|
||||
#endif
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
@@ -545,6 +557,12 @@ void enableModemSleep()
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#if CONFIG_ESP32P4_REV_MIN_FULL < 300
|
||||
esp32_config.max_freq_mhz = 360;
|
||||
#else
|
||||
esp32_config.max_freq_mhz = 400;
|
||||
#endif
|
||||
#else
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
|
||||
#endif
|
||||
@@ -562,8 +580,8 @@ bool shouldLoraWake(uint32_t msecToWake)
|
||||
|
||||
void enableLoraInterrupt()
|
||||
{
|
||||
esp_err_t res;
|
||||
#if SOC_PM_SUPPORT_EXT_WAKEUP && defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
|
||||
esp_err_t res;
|
||||
res = gpio_pulldown_en((gpio_num_t)LORA_DIO1);
|
||||
if (res != ESP_OK) {
|
||||
LOG_ERROR("gpio_pulldown_en(LORA_DIO1) result %d", res);
|
||||
|
||||
+12
-2
@@ -119,18 +119,24 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
|
||||
case meshtastic_XModem_Control_STX:
|
||||
if ((xmodemPacket.seq == 0) && !isReceiving && !isTransmitting) {
|
||||
// NULL packet has the destination filename
|
||||
memcpy(filename, &xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
|
||||
strncpy(filename, (const char *)xmodemPacket.buffer.bytes, sizeof(filename) - 1);
|
||||
filename[sizeof(filename) - 1] = '\0';
|
||||
|
||||
if (xmodemPacket.control == meshtastic_XModem_Control_SOH) { // Receive this file and put to Flash
|
||||
// FILE_O_WRITE on Adafruit_LittleFS is append, not truncate — remove first.
|
||||
spiLock->lock();
|
||||
if (FSCom.exists(filename))
|
||||
FSCom.remove(filename);
|
||||
file = FSCom.open(filename, FILE_O_WRITE);
|
||||
spiLock->unlock();
|
||||
if (file) {
|
||||
LOG_INFO("XModem: receiving %s", filename);
|
||||
sendControl(meshtastic_XModem_Control_ACK);
|
||||
isReceiving = true;
|
||||
packetno = 1;
|
||||
break;
|
||||
}
|
||||
LOG_WARN("XModem: open(%s, WRITE) failed", filename);
|
||||
sendControl(meshtastic_XModem_Control_NAK);
|
||||
isReceiving = false;
|
||||
break;
|
||||
@@ -168,8 +174,12 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
|
||||
check(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size, xmodemPacket.crc16)) {
|
||||
// valid packet
|
||||
spiLock->lock();
|
||||
file.write(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
|
||||
size_t written = file.write(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
|
||||
spiLock->unlock();
|
||||
if (written != xmodemPacket.buffer.size) {
|
||||
LOG_WARN("XModem: short write seq=%d expected=%d wrote=%d (LittleFS partition full?)",
|
||||
(int)xmodemPacket.seq, (int)xmodemPacket.buffer.size, (int)written);
|
||||
}
|
||||
sendControl(meshtastic_XModem_Control_ACK);
|
||||
packetno++;
|
||||
break;
|
||||
|
||||
@@ -52,6 +52,9 @@ class XModemAdapter
|
||||
meshtastic_XModem getForPhone();
|
||||
void resetForPhone();
|
||||
|
||||
// True while a file transfer is in flight; lets callers avoid racing our `file` handle.
|
||||
bool isBusy() const { return isReceiving || isTransmitting; }
|
||||
|
||||
private:
|
||||
bool isReceiving = false;
|
||||
bool isTransmitting = false;
|
||||
|
||||
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
# Fake NodeDB Fixtures
|
||||
|
||||
Deterministic JSONL seed files for the v25 `meshtastic_NodeDatabase` format
|
||||
plus tooling that compiles them to binary `.proto` files and pushes them
|
||||
onto a device for testing.
|
||||
|
||||
Centered on Truth or Consequences, NM (33.1284°N, 107.2528°W) with a 60 km
|
||||
spread — change via `--centroid` and `--spread-km` if you want a different
|
||||
geography.
|
||||
|
||||
## Pipeline
|
||||
|
||||
```text
|
||||
bin/gen-fake-nodedb-seed.py
|
||||
↓ (single Random(seed); no wall-clock dependence)
|
||||
test/fixtures/nodedb/seed_v25_<N>.jsonl ← committed, hand-editable
|
||||
↓
|
||||
bin/seed-json-to-proto.py
|
||||
↓ (resolves *_offset_sec → now-relative epochs at compile time)
|
||||
build/fixtures/nodedb/nodes_v25_<N>.proto ← .gitignored, fresh timestamps
|
||||
↓
|
||||
- Portduino: cp to ~/.portduino/<config>/prefs/nodes.proto
|
||||
- Hardware: XModem upload via mcp-server's push_fake_nodedb tool
|
||||
```
|
||||
|
||||
## What's committed
|
||||
|
||||
| File | Size | Purpose |
|
||||
| --------------------- | ------- | ------------------------------------------------------- |
|
||||
| `seed_v25_0250.jsonl` | ~200 KB | Matches ESP32-S3 high-flash MAX_NUM_NODES cap |
|
||||
| `seed_v25_0500.jsonl` | ~400 KB | Stress between caps |
|
||||
| `seed_v25_1000.jsonl` | ~800 KB | Large mesh stress |
|
||||
| `seed_v25_2000.jsonl` | ~1.6 MB | Truncation/eviction stress (exceeds every platform cap) |
|
||||
|
||||
## Determinism contract
|
||||
|
||||
**Structural fields are deterministic** given a fixed `--seed`: NodeNum,
|
||||
long_name, short_name, hw_model, role, public_key, snr, channel, hops_away,
|
||||
next_hop, bitfield flags, latitude/longitude/altitude, all
|
||||
DeviceMetrics/EnvironmentMetrics/StatusMessage values.
|
||||
|
||||
**Timestamps are intentionally non-deterministic** at compile time. The JSONL
|
||||
stores `*_offset_sec` (seconds before "now"); the compile step subtracts these
|
||||
from current wall clock so the loaded NodeDB shows fresh "recently heard"
|
||||
peers regardless of when the fixture was generated. Pass `--now-epoch T` to
|
||||
the compile step to pin it for byte-identical CI artifacts.
|
||||
|
||||
## Active-board allow-list
|
||||
|
||||
`hw_model` values are restricted to the intersection of:
|
||||
|
||||
1. Variants with `custom_meshtastic_support_level = 1` in `variants/*/*/platformio.ini`
|
||||
2. Values present in the `HardwareModel` enum in `mesh.proto`
|
||||
|
||||
This excludes legacy/deprecated boards (Heltec V1–V2, TLORA V1–V2, classic
|
||||
TBEAM (4) and TBEAM_V0P7 (6), Nano G1, Station G1/G2, etc.) and fuzzer-only
|
||||
sentinels (PORTDUINO, ANDROID_SIM, DIY_V1, LORA_RELAY_V1, etc.).
|
||||
|
||||
Refresh the allow-list in `bin/gen-fake-nodedb-seed.py:HW_MODEL_WEIGHTS` when
|
||||
boards graduate to tier-1 (or retire). One-liner to print the current
|
||||
intersection:
|
||||
|
||||
```bash
|
||||
for f in $(find variants -name 'platformio.ini' | xargs grep -lE 'custom_meshtastic_support_level = 1'); do
|
||||
grep custom_meshtastic_hw_model_slug "$f" | awk -F= '{print $2}' | tr -d ' '
|
||||
done | sort -u | comm -12 - <(
|
||||
bin/_generated/meshtastic_v25/__init__.py >/dev/null 2>&1 || ./bin/regen-py-protos.sh >&2
|
||||
python3 -c "import sys; sys.path.insert(0,'bin/_generated'); \
|
||||
from meshtastic_v25.mesh_pb2 import HardwareModel; \
|
||||
print('\n'.join(HardwareModel.keys()))" | sort
|
||||
)
|
||||
```
|
||||
|
||||
## Role allow-list
|
||||
|
||||
`role` is drawn from non-deprecated `Config.DeviceConfig.Role` values:
|
||||
|
||||
- Excluded: `ROUTER_CLIENT` (deprecated v2.3.15), `REPEATER` (deprecated v2.7.11)
|
||||
- Active: CLIENT, CLIENT_MUTE, ROUTER, TRACKER, SENSOR, TAK, CLIENT_HIDDEN,
|
||||
LOST_AND_FOUND, TAK_TRACKER, ROUTER_LATE, CLIENT_BASE
|
||||
|
||||
## Quickstart
|
||||
|
||||
### Regenerate fixtures with fresh timestamps
|
||||
|
||||
```bash
|
||||
./bin/regen-fake-nodedbs.sh
|
||||
```
|
||||
|
||||
This recompiles all four `.proto` outputs into `build/fixtures/nodedb/` from
|
||||
the committed JSONL seeds, using current wall clock for timestamps. Re-run
|
||||
whenever you want "recent-looking" cached state on a freshly-booted device.
|
||||
|
||||
### Bump the seed (regenerate JSONL structure)
|
||||
|
||||
```bash
|
||||
REGEN_SEEDS=yes ./bin/regen-fake-nodedbs.sh
|
||||
```
|
||||
|
||||
This overwrites the committed JSONL files. Commit the result.
|
||||
|
||||
### Hand-edit a specific scenario
|
||||
|
||||
```bash
|
||||
# Find the node you want to tweak, edit the line in place.
|
||||
$EDITOR test/fixtures/nodedb/seed_v25_0250.jsonl
|
||||
|
||||
# Recompile and push.
|
||||
./bin/regen-fake-nodedbs.sh
|
||||
```
|
||||
|
||||
Each line of the JSONL is one node + metadata as the first line. Field schema
|
||||
documented inline in `bin/gen-fake-nodedb-seed.py`. To override a specific
|
||||
timestamp, replace the `last_heard_offset_sec` field with `last_heard` (an
|
||||
absolute epoch); the compile step honors absolute values.
|
||||
|
||||
### Load onto Portduino (native macOS / linux)
|
||||
|
||||
```bash
|
||||
cp build/fixtures/nodedb/nodes_v25_1000.proto ~/.portduino/default/prefs/nodes.proto
|
||||
# Run the native binary; loadFromDisk picks it up at boot.
|
||||
```
|
||||
|
||||
### Push to USB-attached hardware via mcp-server
|
||||
|
||||
```python
|
||||
# From within the mcp-server tool surface:
|
||||
push_fake_nodedb(
|
||||
size=500,
|
||||
target="hardware",
|
||||
port="/dev/cu.usbmodem21301", # discover via list_devices
|
||||
confirm=True, # gates the destructive write + reboot
|
||||
)
|
||||
```
|
||||
|
||||
Streams the proto over XModem to `/prefs/nodes.proto`, then issues a 1-second
|
||||
reboot so `loadFromDisk` picks it up on next boot. CRC16-CCITT-validated
|
||||
chunks; retries each chunk up to 5× on NAK before aborting with `CAN`.
|
||||
|
||||
## Schema reference
|
||||
|
||||
See `bin/gen-fake-nodedb-seed.py` for the JSONL field reference. Key points:
|
||||
|
||||
- `num` is a hex string (`"0xa1b2c3d4"`)
|
||||
- `public_key_hex` is 64 hex chars (32 bytes), empty for keyless nodes
|
||||
- `hw_model` and `role` are enum **names**; the compile step resolves them
|
||||
via `HardwareModel.Value(name)` / `Config.DeviceConfig.Role.Value(name)`
|
||||
- `bitfield` is a struct of named booleans; the compile step packs them
|
||||
per the bit positions in `src/mesh/NodeDB.h:467-484`
|
||||
- `position` / `telemetry` / `environment` / `status` are nullable;
|
||||
coverage ratios at seed time decide which nodes get which
|
||||
- `latitude` / `longitude` are floats in degrees (compiled to `latitude_i =
|
||||
int(lat * 1e7)` matching `meshtastic_PositionLite`)
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
{"_meta": {"centroid": [33.1284, -107.2528], "count": 250, "coverage": {"environment": 0.25, "position": 0.85, "status": 0.4, "telemetry": 0.7}, "generated_at_iso": "1970-08-23T11:55:11Z", "last_heard_max_sec": 604800, "last_heard_mean_sec": 3600, "my_node_num_excluded": null, "seed": 20260511, "spread_km": 60.0, "version": 25}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1009.74, "iaq": 31, "relative_humidity": 46.72, "temperature": 24.16}, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 4809, "long_name": "Drifting Phoenix", "next_hop": 253, "num": "0x0005e869", "position": {"altitude": 1338, "latitude": 33.690292, "location_source": "LOC_INTERNAL", "longitude": -106.436201, "time_offset_sec": 4996}, "public_key_hex": "056060d6ceae374c7ee39ffb5fb6c2503d238610f2277c47e7cc008a9a096dc5", "role": "CLIENT", "short_name": "DB5I", "snr": 6.64, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1023.84, "iaq": 38, "relative_humidity": 21.78, "temperature": 30.58}, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 1304, "long_name": "Hidden Aspen", "next_hop": 0, "num": "0x00511844", "position": {"altitude": 1535, "latitude": 32.558935, "location_source": "LOC_INTERNAL", "longitude": -107.91722, "time_offset_sec": 1442}, "public_key_hex": "3cae2af9a3e9e2d04829c53ad6cac0f87d8cc81ef4b3ac26a20548113a0d8280", "role": "CLIENT", "short_name": "H4WA", "snr": -0.68, "status": null, "telemetry": {"air_util_tx": 0.075, "battery_level": 75, "channel_utilization": 11.57, "uptime_seconds": 162386, "voltage": 3.975}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1836, "long_name": "Iron Otter", "next_hop": 0, "num": "0x02396342", "position": {"altitude": 1379, "latitude": 32.240662, "location_source": "LOC_INTERNAL", "longitude": -107.329117, "time_offset_sec": 2132}, "public_key_hex": "12e9fbc814fd38f3cb2f87dff7de3e9d16d456ee4c68c35cbba37c34e69182f4", "role": "CLIENT", "short_name": "IAJQ", "snr": 10.63, "status": null, "telemetry": {"air_util_tx": 0.304, "battery_level": 56, "channel_utilization": 36.83, "uptime_seconds": 103925, "voltage": 3.804}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.88, "iaq": 72, "relative_humidity": 36.6, "temperature": 17.87}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 1507, "long_name": "Tall Cougar", "next_hop": 0, "num": "0x02b15d65", "position": {"altitude": 1087, "latitude": 32.881111, "location_source": "LOC_INTERNAL", "longitude": -106.606342, "time_offset_sec": 1695}, "public_key_hex": "78f0eb2208a2d578e455169e63f635a2d7d5229e6482453317a1971256a93a44", "role": "CLIENT", "short_name": "TYD8", "snr": 6.84, "status": null, "telemetry": {"air_util_tx": 0.978, "battery_level": 43, "channel_utilization": 10.6, "uptime_seconds": 133906, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1009.27, "iaq": 67, "relative_humidity": 60.2, "temperature": 23.87}, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 4638, "long_name": "Quick Yucca", "next_hop": 0, "num": "0x03829eb5", "position": {"altitude": 1526, "latitude": 32.816327, "location_source": "LOC_INTERNAL", "longitude": -107.795843, "time_offset_sec": 4912}, "public_key_hex": "502fc9984eefe0f5022e7c3b1baac8c2701d24e5e9a64a08791026f85ca17d3b", "role": "CLIENT", "short_name": "Q9YW", "snr": 8.1, "status": null, "telemetry": {"air_util_tx": 0.102, "battery_level": 100, "channel_utilization": 31.04, "uptime_seconds": 739545, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 4946, "long_name": "Wandering Doe", "next_hop": 0, "num": "0x03f1d514", "position": {"altitude": 1509, "latitude": 33.566253, "location_source": "LOC_INTERNAL", "longitude": -107.554414, "time_offset_sec": 4982}, "public_key_hex": "6bbae52865c1305682f3ac1ef3de4e3d39ec219cab928731137f2131883ef397", "role": "TRACKER", "short_name": "WR0J", "snr": 10.58, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 4827, "long_name": "Lone Hare", "next_hop": 0, "num": "0x04ca3beb", "position": {"altitude": 1092, "latitude": 33.004522, "location_source": "LOC_INTERNAL", "longitude": -107.283169, "time_offset_sec": 4829}, "public_key_hex": "", "role": "SENSOR", "short_name": "LO87", "snr": 12.0, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.037, "battery_level": 27, "channel_utilization": 2.08, "uptime_seconds": 49520, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 2448, "long_name": "Dusk Cougar", "next_hop": 0, "num": "0x0535c6ea", "position": {"altitude": 856, "latitude": 31.937803, "location_source": "LOC_INTERNAL", "longitude": -106.516028, "time_offset_sec": 2539}, "public_key_hex": "461adb831f736b11725a4c1a4574c6a74c88753d8046435f641a0fea108e9962", "role": "CLIENT_BASE", "short_name": "DFI5", "snr": 9.58, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 160, "long_name": "Sneaky Hawk", "next_hop": 0, "num": "0x061c8543", "position": {"altitude": 1543, "latitude": 33.531176, "location_source": "LOC_INTERNAL", "longitude": -107.114202, "time_offset_sec": 177}, "public_key_hex": "e70785c21cb865614a71a43f5bfb6d1139fc672e80fd598e3c1a15bfefe9a2af", "role": "CLIENT", "short_name": "SWXS", "snr": 8.12, "status": null, "telemetry": {"air_util_tx": 1.227, "battery_level": 101, "channel_utilization": 14.58, "uptime_seconds": 3338, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1717, "long_name": "Gold Adder", "next_hop": 0, "num": "0x0672aa7b", "position": {"altitude": 926, "latitude": 32.997975, "location_source": "LOC_INTERNAL", "longitude": -108.310088, "time_offset_sec": 1985}, "public_key_hex": "", "role": "CLIENT", "short_name": "GKVX", "snr": 1.62, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": {"barometric_pressure": 1015.21, "iaq": 55, "relative_humidity": 34.4, "temperature": 38.58}, "hops_away": 0, "hw_model": "THINKNODE_M1", "last_heard_offset_sec": 1257, "long_name": "Silent Dolphin", "next_hop": 0, "num": "0x070e1b66", "position": {"altitude": 1591, "latitude": 33.299113, "location_source": "LOC_INTERNAL", "longitude": -107.818463, "time_offset_sec": 1434}, "public_key_hex": "bc40ef1f82acda597f9259c27880e996eaf5d31db18a08daf4b92c333364f823", "role": "CLIENT", "short_name": "🗻", "snr": 0.08, "status": null, "telemetry": {"air_util_tx": 1.092, "battery_level": 57, "channel_utilization": 3.38, "uptime_seconds": 68787, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 948, "long_name": "Rough Doe", "next_hop": 0, "num": "0x07adbb88", "position": {"altitude": 1658, "latitude": 33.262723, "location_source": "LOC_INTERNAL", "longitude": -108.220474, "time_offset_sec": 1019}, "public_key_hex": "4a8bd22cc5910d64d9d1cbb9cfac6ba12e15d5a3eaf432a25914e282afdf6ad8", "role": "CLIENT", "short_name": "RP6F", "snr": 7.14, "status": null, "telemetry": {"air_util_tx": 0.718, "battery_level": 38, "channel_utilization": 5.75, "uptime_seconds": 52963, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 4100, "long_name": "Wandering Crow", "next_hop": 0, "num": "0x07bc9340", "position": {"altitude": 1555, "latitude": 33.465538, "location_source": "LOC_INTERNAL", "longitude": -108.0528, "time_offset_sec": 4267}, "public_key_hex": "8c38b26d1f0494c5e8ae1e8f259b2127f7a4ed7193820e2b5142196b4a5ca72e", "role": "CLIENT", "short_name": "🌵", "snr": 7.37, "status": null, "telemetry": {"air_util_tx": 0.128, "battery_level": 43, "channel_utilization": 13.24, "uptime_seconds": 3455, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 135, "long_name": "Lone Hawk", "next_hop": 0, "num": "0x083ddc7d", "position": {"altitude": 1727, "latitude": 33.142932, "location_source": "LOC_INTERNAL", "longitude": -107.69923, "time_offset_sec": 190}, "public_key_hex": "633bbaccd26d91e50d7482dfb72d7caa0c3d3ea2ac4537035af5dec53d703135", "role": "CLIENT", "short_name": "LPH4", "snr": 8.17, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1028.3, "iaq": 19, "relative_humidity": 65.94, "temperature": 2.42}, "hops_away": 3, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 6576, "long_name": "Sleepy Doe", "next_hop": 171, "num": "0x08a9fb83", "position": null, "public_key_hex": "5e98de33af5bf7584fa18bc37877e763892d8564bd3878a360631810d5b347ea", "role": "CLIENT", "short_name": "🐺", "snr": 9.09, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1011.54, "iaq": 58, "relative_humidity": 42.81, "temperature": 3.35}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 2645, "long_name": "Howling Elk", "next_hop": 0, "num": "0x08d5357a", "position": {"altitude": 1113, "latitude": 33.563203, "location_source": "LOC_INTERNAL", "longitude": -107.077155, "time_offset_sec": 2657}, "public_key_hex": "04c61243842c533fad9ecf8d25f76123d1626f074b5e0f27251eb78a7b8e7e2c", "role": "CLIENT", "short_name": "H8L8", "snr": 8.67, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.756, "battery_level": 84, "channel_utilization": 18.15, "uptime_seconds": 172126, "voltage": 4.056}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 587, "long_name": "Drifting Mamba", "next_hop": 64, "num": "0x08d602a7", "position": {"altitude": 1280, "latitude": 32.981947, "location_source": "LOC_INTERNAL", "longitude": -107.511111, "time_offset_sec": 885}, "public_key_hex": "6394f02f41a0e243c6ca8a8caf75c1e0b417e213446bf2713d9938fd759cb410", "role": "CLIENT", "short_name": "DLUF", "snr": 9.46, "status": null, "telemetry": {"air_util_tx": 0.411, "battery_level": 101, "channel_utilization": 16.4, "uptime_seconds": 75514, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 2735, "long_name": "Smooth Badger", "next_hop": 0, "num": "0x0923d80c", "position": {"altitude": 1401, "latitude": 33.18486, "location_source": "LOC_INTERNAL", "longitude": -107.615573, "time_offset_sec": 2892}, "public_key_hex": "c09db88982c59eac6204dd0dddb9adbe05e778349f78c2f62fce90dadd433f05", "role": "CLIENT", "short_name": "SANX", "snr": 7.62, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.17, "iaq": 20, "relative_humidity": 68.57, "temperature": 12.23}, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 10427, "long_name": "Silver Wolf", "next_hop": 23, "num": "0x0a277969", "position": {"altitude": 1432, "latitude": 33.347268, "location_source": "LOC_INTERNAL", "longitude": -107.752326, "time_offset_sec": 10447}, "public_key_hex": "", "role": "CLIENT", "short_name": "SB1N", "snr": 2.51, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1485, "long_name": "Silent Falcon", "next_hop": 100, "num": "0x0a526ec8", "position": {"altitude": 1404, "latitude": 32.852863, "location_source": "LOC_INTERNAL", "longitude": -105.833072, "time_offset_sec": 1677}, "public_key_hex": "1b3b314eca34b5eda9bfee5edef66f954a24378efc0ada41cb6865818a18627d", "role": "CLIENT_MUTE", "short_name": "SCXD", "snr": 6.59, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.855, "battery_level": 28, "channel_utilization": 8.74, "uptime_seconds": 162069, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 6351, "long_name": "Sneaky Viper", "next_hop": 0, "num": "0x0a87420a", "position": {"altitude": 2097, "latitude": 33.46946, "location_source": "LOC_INTERNAL", "longitude": -107.854801, "time_offset_sec": 6351}, "public_key_hex": "", "role": "CLIENT", "short_name": "SXGT", "snr": 9.66, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 2668, "long_name": "Slow Raven", "next_hop": 0, "num": "0x0a9785bb", "position": {"altitude": 1168, "latitude": 33.399476, "location_source": "LOC_INTERNAL", "longitude": -107.002506, "time_offset_sec": 2827}, "public_key_hex": "c8f36e0258e1c3c3223010df3176b2ea4e4c4479c49ca206aa7e9e4a7acf6d95", "role": "CLIENT_MUTE", "short_name": "S53U", "snr": 11.62, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.144, "battery_level": 61, "channel_utilization": 2.7, "uptime_seconds": 31838, "voltage": 3.849}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 74, "long_name": "Wild Phoenix", "next_hop": 0, "num": "0x0b377d65", "position": {"altitude": 1535, "latitude": 32.619246, "location_source": "LOC_INTERNAL", "longitude": -107.303937, "time_offset_sec": 216}, "public_key_hex": "8ed086ac62317421d7a9a962c1f79458c7b1ce9c00da0c45037b26dfeb1cd070", "role": "CLIENT", "short_name": "🗻", "snr": 8.49, "status": null, "telemetry": {"air_util_tx": 0.959, "battery_level": 40, "channel_utilization": 16.45, "uptime_seconds": 46826, "voltage": 3.66}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 639, "long_name": "Lone Yucca", "next_hop": 0, "num": "0x0bbac3e3", "position": {"altitude": 1418, "latitude": 32.838025, "location_source": "LOC_INTERNAL", "longitude": -106.952382, "time_offset_sec": 755}, "public_key_hex": "565e3acd1523d5efd9db6717454eb11ad7832e6306d8c769fb6df4f1914ecb11", "role": "TAK_TRACKER", "short_name": "🌵", "snr": 9.57, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.085, "battery_level": 38, "channel_utilization": 13.02, "uptime_seconds": 46809, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 4396, "long_name": "Solar Raven", "next_hop": 0, "num": "0x0cad1877", "position": {"altitude": 1442, "latitude": 33.838306, "location_source": "LOC_INTERNAL", "longitude": -107.31537, "time_offset_sec": 4451}, "public_key_hex": "", "role": "ROUTER_LATE", "short_name": "ST9H", "snr": 10.64, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 6007, "long_name": "Smooth Bison", "next_hop": 225, "num": "0x0cb3920e", "position": {"altitude": 1473, "latitude": 32.799514, "location_source": "LOC_INTERNAL", "longitude": -107.318775, "time_offset_sec": 6032}, "public_key_hex": "135c23e5ef3009668b333313a4ec57f58b74148d90135fddc983d31774ad4c00", "role": "CLIENT", "short_name": "SDEG", "snr": 6.49, "status": null, "telemetry": {"air_util_tx": 0.252, "battery_level": 76, "channel_utilization": 12.26, "uptime_seconds": 1707, "voltage": 3.984}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 3075, "long_name": "Red Adder", "next_hop": 0, "num": "0x0cd3c9b8", "position": {"altitude": 1746, "latitude": 32.398996, "location_source": "LOC_INTERNAL", "longitude": -107.079664, "time_offset_sec": 3102}, "public_key_hex": "d6e909ad875b1afe4e2acca344f4b77a75eb0ac6eab0231ba27cf97a41a6a2e6", "role": "ROUTER_LATE", "short_name": "RKHV", "snr": 9.3, "status": null, "telemetry": {"air_util_tx": 0.092, "battery_level": 54, "channel_utilization": 6.94, "uptime_seconds": 46741, "voltage": 3.786}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 11846, "long_name": "Blue Turtle", "next_hop": 42, "num": "0x0cf6f075", "position": {"altitude": 1599, "latitude": 33.292365, "location_source": "LOC_INTERNAL", "longitude": -106.642067, "time_offset_sec": 11851}, "public_key_hex": "29f0d88c9858804290119564279b259728e09815ba7f953bfbbc0235a3b7ca59", "role": "ROUTER", "short_name": "BE2J", "snr": 3.22, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.103, "battery_level": 30, "channel_utilization": 9.29, "uptime_seconds": 119323, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 12334, "long_name": "Sleepy Badger", "next_hop": 0, "num": "0x0d3ce504", "position": {"altitude": 1333, "latitude": 33.571284, "location_source": "LOC_INTERNAL", "longitude": -107.427147, "time_offset_sec": 12617}, "public_key_hex": "ef30355e2fd16b456d44a21f9b96bc848eed29615dc26abc43e4968a50aae461", "role": "CLIENT", "short_name": "SPED", "snr": 5.13, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 2432, "long_name": "Burning Coyote", "next_hop": 0, "num": "0x0d8ca8ba", "position": null, "public_key_hex": "aa735974126186912e6affeb580aeb2cdd7eaefdbae2844c4df102f11b316e71", "role": "CLIENT", "short_name": "BG4J", "snr": 9.6, "status": null, "telemetry": {"air_util_tx": 0.585, "battery_level": 89, "channel_utilization": 33.07, "uptime_seconds": 48, "voltage": 4.101}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 697, "long_name": "Gold Pine", "next_hop": 0, "num": "0x0dc79902", "position": {"altitude": 1547, "latitude": 33.325688, "location_source": "LOC_INTERNAL", "longitude": -106.36506, "time_offset_sec": 948}, "public_key_hex": "2a23f899fd519cfc08c957b9e8ba17540d56edeba480530a48947799fef05839", "role": "CLIENT", "short_name": "🌵", "snr": 5.6, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 5174, "long_name": "Black Salmon", "next_hop": 0, "num": "0x0df39b49", "position": {"altitude": 1507, "latitude": 33.799625, "location_source": "LOC_INTERNAL", "longitude": -107.081248, "time_offset_sec": 5294}, "public_key_hex": "67046c933ed949ce758a1b4dbe62e1465acdbd08f6610b21ab98606c2ebea11b", "role": "CLIENT", "short_name": "BC24", "snr": 4.36, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.75, "iaq": 52, "relative_humidity": 53.83, "temperature": 22.67}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2507, "long_name": "Silver Ridge NM2PJ", "next_hop": 0, "num": "0x0e466af3", "position": {"altitude": 1161, "latitude": 32.844045, "location_source": "LOC_INTERNAL", "longitude": -106.934913, "time_offset_sec": 2662}, "public_key_hex": "3c7c399e35477b4b00daa1a6fbe53a9f867b0d2911654098069329b3651c3f01", "role": "ROUTER", "short_name": "SLVY", "snr": 4.23, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.005, "battery_level": 87, "channel_utilization": 15.46, "uptime_seconds": 63106, "voltage": 4.083}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 1105, "long_name": "Red Adder", "next_hop": 142, "num": "0x0e73347a", "position": {"altitude": 1713, "latitude": 32.128782, "location_source": "LOC_INTERNAL", "longitude": -107.197526, "time_offset_sec": 1241}, "public_key_hex": "fb59e2604d8f8506f13a00e620f3ac7213794c0ec66b9cca1ff18487eba8c3f5", "role": "CLIENT", "short_name": "RZM6", "snr": 11.91, "status": null, "telemetry": {"air_util_tx": 0.899, "battery_level": 34, "channel_utilization": 30.04, "uptime_seconds": 348824, "voltage": 3.606}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 5269, "long_name": "Smooth Squirrel", "next_hop": 0, "num": "0x0e74c170", "position": {"altitude": 1173, "latitude": 33.375622, "location_source": "LOC_INTERNAL", "longitude": -107.383683, "time_offset_sec": 5279}, "public_key_hex": "5d76465b574f6a8605a021ad5297582546fa352876f250cb0e6a43c4f3d99263", "role": "CLIENT", "short_name": "🦌", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 1.626, "battery_level": 101, "channel_utilization": 12.18, "uptime_seconds": 192410, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 7, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 195, "long_name": "Sharp Mustang", "next_hop": 189, "num": "0x0f969f92", "position": {"altitude": 1326, "latitude": 33.071155, "location_source": "LOC_INTERNAL", "longitude": -107.28847, "time_offset_sec": 275}, "public_key_hex": "203b997abe373c62563240a3f88aa604ec0b7f226eaec9d44687c05ee3e099ca", "role": "CLIENT", "short_name": "SCYS", "snr": 3.98, "status": null, "telemetry": {"air_util_tx": 0.509, "battery_level": 88, "channel_utilization": 22.37, "uptime_seconds": 94713, "voltage": 4.092}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 556, "long_name": "Lost Falcon", "next_hop": 10, "num": "0x1068b9c3", "position": {"altitude": 1639, "latitude": 32.08854, "location_source": "LOC_INTERNAL", "longitude": -107.420525, "time_offset_sec": 835}, "public_key_hex": "403a60ad42e3e05848cda956b3572c2b6d9716487ee0c8ea9f29b03516d095f0", "role": "CLIENT", "short_name": "L2J5", "snr": 12.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.19, "iaq": 94, "relative_humidity": 74.04, "temperature": 9.55}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2179, "long_name": "Brave Shark", "next_hop": 0, "num": "0x106e1aa9", "position": {"altitude": 1516, "latitude": 33.052381, "location_source": "LOC_INTERNAL", "longitude": -106.368485, "time_offset_sec": 2181}, "public_key_hex": "fe2b739b59a8e775f040b609fddd7c541e43f6e02b7b752e1dc6e4fb5481da32", "role": "CLIENT", "short_name": "BQFR", "snr": 8.56, "status": null, "telemetry": {"air_util_tx": 0.393, "battery_level": 55, "channel_utilization": 12.42, "uptime_seconds": 125558, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1770, "long_name": "Black Turtle", "next_hop": 0, "num": "0x10fdbb7c", "position": {"altitude": 1120, "latitude": 32.111339, "location_source": "LOC_INTERNAL", "longitude": -107.662349, "time_offset_sec": 2036}, "public_key_hex": "", "role": "CLIENT", "short_name": "🦂", "snr": 7.83, "status": null, "telemetry": {"air_util_tx": 0.72, "battery_level": 79, "channel_utilization": 19.07, "uptime_seconds": 53503, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.23, "iaq": 37, "relative_humidity": 78.84, "temperature": 19.85}, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 903, "long_name": "Howling Iguana", "next_hop": 26, "num": "0x114c8d0c", "position": {"altitude": 1474, "latitude": 33.869401, "location_source": "LOC_INTERNAL", "longitude": -108.620133, "time_offset_sec": 1079}, "public_key_hex": "953c0a6d8bf0ac6e8db0c8fd701f51841183ef8fb1ba16b109dc8290411d4549", "role": "CLIENT", "short_name": "HDW4", "snr": 10.98, "status": {"status": "running"}, "telemetry": {"air_util_tx": 2.014, "battery_level": 98, "channel_utilization": 17.04, "uptime_seconds": 38009, "voltage": 4.182}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": {"barometric_pressure": 1017.71, "iaq": 33, "relative_humidity": 62.97, "temperature": 21.51}, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 1820, "long_name": "Wild Eagle", "next_hop": 0, "num": "0x120b19a3", "position": {"altitude": 1391, "latitude": 33.611799, "location_source": "LOC_INTERNAL", "longitude": -107.006216, "time_offset_sec": 1904}, "public_key_hex": "4f7cac84044ada634f60e87e9a63e56ca1cb8a73f76306f0627c8078063c568e", "role": "ROUTER", "short_name": "WPE2", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.756, "battery_level": 97, "channel_utilization": 3.27, "uptime_seconds": 12783, "voltage": 4.173}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 1, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 3752, "long_name": "Red Mole K17PL", "next_hop": 126, "num": "0x12384425", "position": null, "public_key_hex": "a590327c39a8811f4b495c11c1303361da64452685d896d3b6ed9f1adfc060f8", "role": "CLIENT", "short_name": "🐢", "snr": 5.84, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.333, "battery_level": 12, "channel_utilization": 18.99, "uptime_seconds": 53482, "voltage": 3.408}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 6541, "long_name": "Tall Squirrel", "next_hop": 79, "num": "0x126ceb2c", "position": {"altitude": 1375, "latitude": 32.841802, "location_source": "LOC_INTERNAL", "longitude": -107.427944, "time_offset_sec": 6724}, "public_key_hex": "", "role": "CLIENT", "short_name": "TQM3", "snr": 2.55, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1002.98, "iaq": 111, "relative_humidity": 89.08, "temperature": 33.48}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 4162, "long_name": "Steel Turtle", "next_hop": 0, "num": "0x12f76d86", "position": {"altitude": 1208, "latitude": 32.892447, "location_source": "LOC_INTERNAL", "longitude": -107.118326, "time_offset_sec": 4430}, "public_key_hex": "", "role": "CLIENT", "short_name": "S5F4", "snr": 10.89, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.292, "battery_level": 53, "channel_utilization": 12.83, "uptime_seconds": 2678, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "SENSECAP_INDICATOR", "last_heard_offset_sec": 224, "long_name": "Silent Raven", "next_hop": 201, "num": "0x130f2fb7", "position": {"altitude": 1413, "latitude": 32.938113, "location_source": "LOC_INTERNAL", "longitude": -107.179085, "time_offset_sec": 225}, "public_key_hex": "a7efa3ea6ddaa0a40bcc0e12b957c55331369acee2899c0a0d2b8028d2f1e170", "role": "CLIENT", "short_name": "SZWZ", "snr": 6.34, "status": null, "telemetry": {"air_util_tx": 0.329, "battery_level": 79, "channel_utilization": 19.84, "uptime_seconds": 40527, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "WISMESH_TAG", "last_heard_offset_sec": 3447, "long_name": "Whispering Adder", "next_hop": 23, "num": "0x13293edd", "position": {"altitude": 1269, "latitude": 33.452132, "location_source": "LOC_INTERNAL", "longitude": -106.69832, "time_offset_sec": 3648}, "public_key_hex": "f52d28036520d2bcaa71bd4c3cf8705bb41aec203e95eb97240a7d90ff224378", "role": "CLIENT", "short_name": "W53C", "snr": 6.71, "status": {"status": "weak-signal"}, "telemetry": {"air_util_tx": 0.102, "battery_level": 25, "channel_utilization": 9.98, "uptime_seconds": 14375, "voltage": 3.525}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 5126, "long_name": "Shady Crane", "next_hop": 4, "num": "0x13b2ba9f", "position": {"altitude": 1406, "latitude": 32.818484, "location_source": "LOC_INTERNAL", "longitude": -107.401426, "time_offset_sec": 5424}, "public_key_hex": "b9881b3aa54bb216618fcd9667532add5f2de8020a7c9143e7a160145af4f103", "role": "CLIENT", "short_name": "SP0D", "snr": 6.85, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 2395, "long_name": "River Viper", "next_hop": 0, "num": "0x14758ac9", "position": {"altitude": 1254, "latitude": 32.825863, "location_source": "LOC_INTERNAL", "longitude": -107.703222, "time_offset_sec": 2430}, "public_key_hex": "430af60798f22e409d7c4f8fc662539c25cd09c16b5a3e3eb987fc46c0469167", "role": "CLIENT", "short_name": "R3ZD", "snr": 11.9, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 580, "long_name": "Blue Mesa", "next_hop": 155, "num": "0x14a5204d", "position": {"altitude": 1187, "latitude": 33.093378, "location_source": "LOC_INTERNAL", "longitude": -106.854458, "time_offset_sec": 613}, "public_key_hex": "6358368d5865bb7758b9834d3512457be607092ed2e299be69da713780342f33", "role": "CLIENT", "short_name": "BTTY", "snr": 6.51, "status": null, "telemetry": {"air_util_tx": 2.628, "battery_level": 40, "channel_utilization": 22.99, "uptime_seconds": 87527, "voltage": 3.66}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 1003, "long_name": "Old Mamba", "next_hop": 0, "num": "0x1564261a", "position": {"altitude": 1666, "latitude": 33.943138, "location_source": "LOC_INTERNAL", "longitude": -107.171092, "time_offset_sec": 1202}, "public_key_hex": "54240722fd0126e61a3882ec9c403788f496ef6ba21ddabd23d8252013b10059", "role": "CLIENT", "short_name": "OZKL", "snr": 8.48, "status": {"status": "low-batt"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 12975, "long_name": "Whispering Bear", "next_hop": 205, "num": "0x1615bf36", "position": {"altitude": 738, "latitude": 33.554574, "location_source": "LOC_INTERNAL", "longitude": -106.641352, "time_offset_sec": 13150}, "public_key_hex": "7a01f1d9fc0dc8be20d7f267aa9c9abe5d2ed23c5173372b9536c2b8b2397827", "role": "CLIENT", "short_name": "WBL0", "snr": 9.7, "status": null, "telemetry": {"air_util_tx": 0.222, "battery_level": 50, "channel_utilization": 7.5, "uptime_seconds": 23756, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.22, "iaq": 28, "relative_humidity": 73.84, "temperature": 37.27}, "hops_away": 3, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 2565, "long_name": "Gold Yucca", "next_hop": 248, "num": "0x1644d8bc", "position": {"altitude": 1764, "latitude": 32.887885, "location_source": "LOC_INTERNAL", "longitude": -106.298752, "time_offset_sec": 2844}, "public_key_hex": "247e5e5b436da4f5db45db12f0c373deb057d05621ca70d77233b4a279d3b89a", "role": "CLIENT", "short_name": "GL20", "snr": 0.4, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 2941, "long_name": "Tiny Gecko", "next_hop": 0, "num": "0x16b3cb8d", "position": {"altitude": 1348, "latitude": 33.724181, "location_source": "LOC_INTERNAL", "longitude": -107.174284, "time_offset_sec": 2941}, "public_key_hex": "941fd5f96cf769a08fbff7f3215ca75ad0a9b18fdf8cb78dc29cd40bb82d2150", "role": "CLIENT", "short_name": "TO7I", "snr": 7.42, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 871, "long_name": "Steel Trout", "next_hop": 175, "num": "0x1711f35f", "position": null, "public_key_hex": "139c29b1aa2cb64879370ed42b478b94a9af417661a7cc02f8cdec0ad17845fc", "role": "TRACKER", "short_name": "S7DX", "snr": 4.27, "status": null, "telemetry": {"air_util_tx": 0.645, "battery_level": 44, "channel_utilization": 3.4, "uptime_seconds": 160815, "voltage": 3.696}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3326, "long_name": "Red Wolf", "next_hop": 233, "num": "0x177be91b", "position": null, "public_key_hex": "55adf85e42585ebe03f0e32cb630e12472993b9fd0e7052572d843e0eba6d52f", "role": "CLIENT", "short_name": "RHM4", "snr": 0.38, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 1462, "long_name": "New Aspen", "next_hop": 0, "num": "0x17d34d2f", "position": null, "public_key_hex": "bbf436b42ee2abed84219c04516f33a62e5393f50b6b849495bc8c0bbb053cf3", "role": "CLIENT", "short_name": "NHXL", "snr": 4.01, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 4776, "long_name": "River Seal", "next_hop": 33, "num": "0x17f3a94a", "position": {"altitude": 1664, "latitude": 32.860698, "location_source": "LOC_INTERNAL", "longitude": -106.353502, "time_offset_sec": 5072}, "public_key_hex": "316b919aac604507432d927ff4bc7458e3852b85105a918fd3ddfc41549f488f", "role": "CLIENT", "short_name": "R7R3", "snr": 5.17, "status": null, "telemetry": {"air_util_tx": 1.346, "battery_level": 17, "channel_utilization": 4.74, "uptime_seconds": 30218, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": {"barometric_pressure": 1021.6, "iaq": 17, "relative_humidity": 65.68, "temperature": 24.24}, "hops_away": 3, "hw_model": "RAK3401", "last_heard_offset_sec": 350, "long_name": "Hidden Pine", "next_hop": 251, "num": "0x180443f5", "position": {"altitude": 1319, "latitude": 32.404289, "location_source": "LOC_INTERNAL", "longitude": -106.934617, "time_offset_sec": 508}, "public_key_hex": "9fadbb08623c9913b28bd7a458638bdbc6f8f7e31181b9a121ffcde8b20bbc1e", "role": "CLIENT", "short_name": "🦅", "snr": 0.66, "status": null, "telemetry": {"air_util_tx": 0.806, "battery_level": 33, "channel_utilization": 12.19, "uptime_seconds": 95281, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 142, "long_name": "Sleepy Bison", "next_hop": 0, "num": "0x18dd2de3", "position": {"altitude": 1129, "latitude": 33.62816, "location_source": "LOC_INTERNAL", "longitude": -107.024573, "time_offset_sec": 313}, "public_key_hex": "", "role": "ROUTER", "short_name": "SS7V", "snr": 6.53, "status": null, "telemetry": {"air_util_tx": 0.689, "battery_level": 44, "channel_utilization": 2.6, "uptime_seconds": 2877, "voltage": 3.696}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1014.99, "iaq": 73, "relative_humidity": 78.79, "temperature": 28.95}, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 567, "long_name": "Fast Turtle", "next_hop": 0, "num": "0x18ef7a96", "position": null, "public_key_hex": "0ec2598567e0e8cd092b276487781df7399307ff10d12964f08b6f99ee76e68a", "role": "CLIENT", "short_name": "FUHT", "snr": 1.18, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1075, "long_name": "Canyon Coyote", "next_hop": 0, "num": "0x1939a174", "position": {"altitude": 1239, "latitude": 32.965874, "location_source": "LOC_INTERNAL", "longitude": -106.002274, "time_offset_sec": 1084}, "public_key_hex": "bea2d386101b64e5fe16904ee43db809e962b2edc92d583d3bce4cf469c9c770", "role": "CLIENT", "short_name": "C42Q", "snr": 4.07, "status": null, "telemetry": {"air_util_tx": 0.587, "battery_level": 36, "channel_utilization": 29.37, "uptime_seconds": 161621, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 7533, "long_name": "Brave Phoenix", "next_hop": 161, "num": "0x19606048", "position": {"altitude": 1207, "latitude": 32.520481, "location_source": "LOC_INTERNAL", "longitude": -107.707377, "time_offset_sec": 7611}, "public_key_hex": "", "role": "CLIENT", "short_name": "B2EE", "snr": 5.18, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.51, "iaq": 81, "relative_humidity": 41.63, "temperature": 18.29}, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 5429, "long_name": "Found Ridge", "next_hop": 95, "num": "0x1967fd5f", "position": {"altitude": 1367, "latitude": 32.374835, "location_source": "LOC_INTERNAL", "longitude": -106.844832, "time_offset_sec": 5572}, "public_key_hex": "1a0dbcfd50865ed35a464d947bebb1364c0ede8ae6fabca77ee2ee5bc7a22db2", "role": "CLIENT", "short_name": "FMOK", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 1.306, "battery_level": 71, "channel_utilization": 22.2, "uptime_seconds": 125119, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1001.64, "iaq": 86, "relative_humidity": 7.71, "temperature": 31.37}, "hops_away": 2, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 1873, "long_name": "Loud Marmot KX8YO", "next_hop": 148, "num": "0x19c898a0", "position": {"altitude": 1783, "latitude": 32.405528, "location_source": "LOC_INTERNAL", "longitude": -107.106416, "time_offset_sec": 2160}, "public_key_hex": "954f20cebbcc2abe846c90d8fd01106873d22cd7aa47dc862c5cc75981a81729", "role": "CLIENT", "short_name": "L7SZ", "snr": 6.87, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.397, "battery_level": 90, "channel_utilization": 14.17, "uptime_seconds": 6055, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 7996, "long_name": "Hidden Mesa", "next_hop": 0, "num": "0x1a39a908", "position": {"altitude": 1371, "latitude": 32.42213, "location_source": "LOC_INTERNAL", "longitude": -107.093866, "time_offset_sec": 8025}, "public_key_hex": "443b5cca642cee1c46ca97cbcbe87b55a133c2d547d62c5487bc7c3af01bb1a1", "role": "CLIENT", "short_name": "HSZR", "snr": 8.5, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 5421, "long_name": "Brave Shark", "next_hop": 90, "num": "0x1b56b90e", "position": {"altitude": 1441, "latitude": 32.889426, "location_source": "LOC_INTERNAL", "longitude": -106.537159, "time_offset_sec": 5704}, "public_key_hex": "27480efc8066b57abc0b7b949808894010ef10c105a2e88d289f5d040df21976", "role": "CLIENT", "short_name": "🌊", "snr": 9.72, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.202, "battery_level": 59, "channel_utilization": 23.59, "uptime_seconds": 101999, "voltage": 3.831}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2302, "long_name": "Happy Gecko", "next_hop": 0, "num": "0x1b6bf44c", "position": null, "public_key_hex": "7206286d9afd3155730837948ec0038876c333330cbd131ef96209e7b277d647", "role": "CLIENT", "short_name": "H15N", "snr": 0.62, "status": null, "telemetry": {"air_util_tx": 2.207, "battery_level": 93, "channel_utilization": 1.79, "uptime_seconds": 122358, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 7, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3119, "long_name": "Floating Lion", "next_hop": 202, "num": "0x1b713583", "position": null, "public_key_hex": "1d6bd24a1414da9f59d73703954239bb4b8cc827bae76dbaa09d01ba52452f85", "role": "CLIENT", "short_name": "FTPZ", "snr": 5.03, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.397, "battery_level": 68, "channel_utilization": 8.09, "uptime_seconds": 241505, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 2382, "long_name": "Short Raven", "next_hop": 0, "num": "0x1c0560ca", "position": {"altitude": 1464, "latitude": 34.21151, "location_source": "LOC_INTERNAL", "longitude": -107.210931, "time_offset_sec": 2445}, "public_key_hex": "dc645a4e861a9fc93aa0bf8005e0684ab7cf2f24058726eb2a214c97d68ca2fb", "role": "CLIENT", "short_name": "🐺", "snr": 11.22, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.24, "battery_level": 38, "channel_utilization": 15.58, "uptime_seconds": 50239, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 5657, "long_name": "Wandering Wolf", "next_hop": 0, "num": "0x1c36dc90", "position": {"altitude": 1147, "latitude": 32.250805, "location_source": "LOC_INTERNAL", "longitude": -106.884952, "time_offset_sec": 5947}, "public_key_hex": "f1d21755ecb8465c3c00f424d40aace7520544dc1102bf2f6ced1eae30989314", "role": "CLIENT", "short_name": "W2SJ", "snr": 8.52, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.01, "battery_level": 45, "channel_utilization": 20.36, "uptime_seconds": 18959, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.6, "iaq": 83, "relative_humidity": 52.43, "temperature": 23.21}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 133, "long_name": "Desert Mesa", "next_hop": 0, "num": "0x1cefe3cb", "position": {"altitude": 1311, "latitude": 33.656763, "location_source": "LOC_INTERNAL", "longitude": -107.626614, "time_offset_sec": 301}, "public_key_hex": "69dc1000761b943914f461621060293f761303807ce4a9178729eda65e9cdcff", "role": "CLIENT", "short_name": "DTI2", "snr": 0.02, "status": {"status": "online"}, "telemetry": {"air_util_tx": 1.621, "battery_level": 16, "channel_utilization": 9.62, "uptime_seconds": 45293, "voltage": 3.444}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 1807, "long_name": "Sunny Hawk", "next_hop": 0, "num": "0x1d722068", "position": {"altitude": 812, "latitude": 32.577717, "location_source": "LOC_INTERNAL", "longitude": -107.408054, "time_offset_sec": 2034}, "public_key_hex": "beabbf8bc9b06cea1c5c4a08c7888084eb3929c37760a670764fd6cadd7295a6", "role": "CLIENT", "short_name": "🦉", "snr": 9.23, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1012.02, "iaq": 63, "relative_humidity": 96.05, "temperature": 20.13}, "hops_away": 1, "hw_model": "TBEAM_1_WATT", "last_heard_offset_sec": 161, "long_name": "Frozen Cedar", "next_hop": 195, "num": "0x1d8163c2", "position": {"altitude": 1238, "latitude": 32.869186, "location_source": "LOC_INTERNAL", "longitude": -107.651904, "time_offset_sec": 416}, "public_key_hex": "91f0e28d288761bdb1f304ebe33f2d5fd7d9df9d44cb6a2f065b8e47e5fc7ac3", "role": "CLIENT", "short_name": "FKIT", "snr": 6.32, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.164, "battery_level": 41, "channel_utilization": 13.01, "uptime_seconds": 43618, "voltage": 3.669}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 1639, "long_name": "Canyon Owl", "next_hop": 27, "num": "0x1d82b94f", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "C7ZK", "snr": 9.18, "status": null, "telemetry": {"air_util_tx": 1.023, "battery_level": 48, "channel_utilization": 14.43, "uptime_seconds": 38354, "voltage": 3.732}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 4184, "long_name": "Smooth Mole", "next_hop": 0, "num": "0x1daca15d", "position": {"altitude": 1185, "latitude": 33.384658, "location_source": "LOC_INTERNAL", "longitude": -107.289712, "time_offset_sec": 4264}, "public_key_hex": "46921f68b88565974b589cac24f86cfe93ea100dbe7022089f71df082bb333cd", "role": "CLIENT_MUTE", "short_name": "🌙", "snr": 2.51, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1016.77, "iaq": 7, "relative_humidity": 69.95, "temperature": 30.02}, "hops_away": 2, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 9674, "long_name": "Storm Mesa", "next_hop": 225, "num": "0x1de48b4f", "position": {"altitude": 1496, "latitude": 33.354591, "location_source": "LOC_INTERNAL", "longitude": -106.570413, "time_offset_sec": 9772}, "public_key_hex": "8a3610408b8070eb103d779cf5e9d2900d4cb30d84598400cd21a9943b9d7df9", "role": "CLIENT", "short_name": "SY02", "snr": 3.07, "status": {"status": "weak-signal"}, "telemetry": {"air_util_tx": 1.042, "battery_level": 35, "channel_utilization": 43.64, "uptime_seconds": 199707, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 480, "long_name": "Brave Pike", "next_hop": 0, "num": "0x1e7ed5d4", "position": {"altitude": 798, "latitude": 33.470996, "location_source": "LOC_INTERNAL", "longitude": -107.416067, "time_offset_sec": 550}, "public_key_hex": "bf0171d6339e8eb928b8ba93385a22027a661d1a8b29f7e5984f28e309fd6d34", "role": "CLIENT", "short_name": "B2ZX", "snr": 6.14, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.361, "battery_level": 92, "channel_utilization": 8.47, "uptime_seconds": 190763, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 2556, "long_name": "Frosty Pine", "next_hop": 0, "num": "0x1ea77801", "position": {"altitude": 1528, "latitude": 32.418991, "location_source": "LOC_INTERNAL", "longitude": -106.093586, "time_offset_sec": 2772}, "public_key_hex": "0dea045571181b0214c5f3e0b31d33f7052cd424ecd71ffe705af811d3b25bcb", "role": "ROUTER", "short_name": "FOSC", "snr": 6.35, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "CROWPANEL", "last_heard_offset_sec": 2235, "long_name": "Lunar Whale", "next_hop": 0, "num": "0x1f6cc1bc", "position": {"altitude": 899, "latitude": 33.411689, "location_source": "LOC_INTERNAL", "longitude": -107.405901, "time_offset_sec": 2430}, "public_key_hex": "2736e197f4f6da0a2ee69c1e3a51ee35629d2867e1d3c34b4227d235b686a81f", "role": "CLIENT", "short_name": "LDSY", "snr": 10.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 8864, "long_name": "Loud Marmot", "next_hop": 0, "num": "0x1fdf7664", "position": {"altitude": 1536, "latitude": 33.654204, "location_source": "LOC_INTERNAL", "longitude": -107.187343, "time_offset_sec": 8957}, "public_key_hex": "f010cbc4e81263a439bc4f2716cb167d1714c92abb0a23c491b3602c772d383a", "role": "TAK", "short_name": "🌲", "snr": 6.38, "status": {"status": "low-batt"}, "telemetry": {"air_util_tx": 0.972, "battery_level": 58, "channel_utilization": 19.6, "uptime_seconds": 52550, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 9543, "long_name": "Smooth Pike", "next_hop": 0, "num": "0x201cbee0", "position": {"altitude": 1348, "latitude": 33.759616, "location_source": "LOC_INTERNAL", "longitude": -106.64923, "time_offset_sec": 9837}, "public_key_hex": "c32890238040eeb56215ef4b8e9e5c101015db7ca36b004f121a43c7ff899822", "role": "CLIENT_HIDDEN", "short_name": "SQK3", "snr": 5.33, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 4229, "long_name": "Shady Colt", "next_hop": 168, "num": "0x206963c3", "position": {"altitude": 1118, "latitude": 33.832162, "location_source": "LOC_INTERNAL", "longitude": -107.073608, "time_offset_sec": 4391}, "public_key_hex": "e1315e6691a0d99979d84e54f6500c2317aaae905fb491f4dc0222f8703aca42", "role": "CLIENT", "short_name": "S3EW", "snr": 8.78, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 12150, "long_name": "Forest Phoenix", "next_hop": 0, "num": "0x218f5088", "position": {"altitude": 1106, "latitude": 32.897549, "location_source": "LOC_INTERNAL", "longitude": -107.531896, "time_offset_sec": 12292}, "public_key_hex": "2d66dd4145234b2b57ea024df39279c239e97431db054756de54678b20f5da93", "role": "SENSOR", "short_name": "FW9Z", "snr": 9.15, "status": null, "telemetry": {"air_util_tx": 0.139, "battery_level": 79, "channel_utilization": 17.85, "uptime_seconds": 124412, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1604, "long_name": "Sunny Mustang", "next_hop": 0, "num": "0x237435df", "position": {"altitude": 1720, "latitude": 33.689395, "location_source": "LOC_INTERNAL", "longitude": -106.620763, "time_offset_sec": 1795}, "public_key_hex": "de8c3a94d4068daa81f917c544b401537ede4f41ee908cfcfab3b8a7cbb077cf", "role": "CLIENT", "short_name": "SXDZ", "snr": 3.47, "status": null, "telemetry": {"air_util_tx": 0.434, "battery_level": 26, "channel_utilization": 7.32, "uptime_seconds": 78190, "voltage": 3.534}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1284, "long_name": "Silver Dolphin", "next_hop": 113, "num": "0x23f54207", "position": {"altitude": 866, "latitude": 33.871469, "location_source": "LOC_INTERNAL", "longitude": -107.534801, "time_offset_sec": 1437}, "public_key_hex": "306a6a50b4b716250b255ea086314fc4ce70eb71e959107baa0284f4920f6822", "role": "ROUTER", "short_name": "S2F3", "snr": 8.89, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 759, "long_name": "Mountain Cactus", "next_hop": 0, "num": "0x23f8dd43", "position": {"altitude": 1548, "latitude": 33.293569, "location_source": "LOC_INTERNAL", "longitude": -107.692603, "time_offset_sec": 954}, "public_key_hex": "ad1317a5f42db4e6d4adc9505d7116df9f9a2f3063134627c22e8c42d8d0b861", "role": "CLIENT", "short_name": "MWI3", "snr": 0.4, "status": null, "telemetry": {"air_util_tx": 0.832, "battery_level": 85, "channel_utilization": 3.39, "uptime_seconds": 19402, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": null, "hops_away": 2, "hw_model": "T_DECK", "last_heard_offset_sec": 3147, "long_name": "Floating Cougar", "next_hop": 42, "num": "0x2483da22", "position": {"altitude": 1574, "latitude": 34.618557, "location_source": "LOC_INTERNAL", "longitude": -106.795298, "time_offset_sec": 3366}, "public_key_hex": "70743e0a888a095156f73537ad455490bbd8c3f542ed955a9cb39333730d0a7d", "role": "CLIENT", "short_name": "FXM3", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.507, "battery_level": 63, "channel_utilization": 5.56, "uptime_seconds": 102994, "voltage": 3.867}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 1063, "long_name": "Silent Seal", "next_hop": 0, "num": "0x2543fee0", "position": null, "public_key_hex": "437a8bf578d601494b638780ceb552c36172d1286d5a73133983be72837575f7", "role": "CLIENT", "short_name": "SO11", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.017, "battery_level": 28, "channel_utilization": 6.46, "uptime_seconds": 17298, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 796, "long_name": "Sunny Crow", "next_hop": 0, "num": "0x2563d10d", "position": {"altitude": 1406, "latitude": 32.882114, "location_source": "LOC_INTERNAL", "longitude": -107.561773, "time_offset_sec": 860}, "public_key_hex": "9239b3f0c406543fd79d7fc60add1f54c6b1dcf6f439976148be084955f2e6cf", "role": "ROUTER", "short_name": "S4BI", "snr": 9.54, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 5393, "long_name": "Sky Cobra", "next_hop": 38, "num": "0x256cfdf9", "position": {"altitude": 1230, "latitude": 33.35275, "location_source": "LOC_INTERNAL", "longitude": -106.720733, "time_offset_sec": 5559}, "public_key_hex": "66abd534d8050bd3a9b5e673958b42dd8be93dcc7b7bb3df3cddd55c54e6ddff", "role": "CLIENT", "short_name": "🦉", "snr": 1.45, "status": null, "telemetry": {"air_util_tx": 0.593, "battery_level": 79, "channel_utilization": 14.33, "uptime_seconds": 30967, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.13, "iaq": 0, "relative_humidity": 66.62, "temperature": 28.28}, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 7681, "long_name": "Found Sage", "next_hop": 8, "num": "0x2947046c", "position": {"altitude": 995, "latitude": 32.620757, "location_source": "LOC_INTERNAL", "longitude": -107.727416, "time_offset_sec": 7747}, "public_key_hex": "", "role": "CLIENT", "short_name": "FX3S", "snr": 9.15, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.41, "iaq": 37, "relative_humidity": 57.37, "temperature": 9.65}, "hops_away": 0, "hw_model": "SENSECAP_INDICATOR", "last_heard_offset_sec": 2786, "long_name": "Stone Cobra", "next_hop": 0, "num": "0x294c2dba", "position": {"altitude": 1400, "latitude": 32.83756, "location_source": "LOC_INTERNAL", "longitude": -107.65565, "time_offset_sec": 2822}, "public_key_hex": "7c0192e6eb7bad02522f3afaeec7e41451816fb5d3c3c96eabc35c66dba69015", "role": "CLIENT_MUTE", "short_name": "SM0Q", "snr": 5.47, "status": null, "telemetry": {"air_util_tx": 0.835, "battery_level": 33, "channel_utilization": 8.37, "uptime_seconds": 48228, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 3855, "long_name": "Wandering Bronco", "next_hop": 0, "num": "0x29ca8824", "position": {"altitude": 1308, "latitude": 32.768325, "location_source": "LOC_INTERNAL", "longitude": -107.304989, "time_offset_sec": 4107}, "public_key_hex": "0e293c8e21f1632490b3bf622b3034f74bb3f48300640d0f085c41e49cdee87f", "role": "CLIENT", "short_name": "WFFI", "snr": 4.79, "status": null, "telemetry": {"air_util_tx": 0.462, "battery_level": 39, "channel_utilization": 2.79, "uptime_seconds": 143329, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 7594, "long_name": "Happy Juniper", "next_hop": 0, "num": "0x2a38022d", "position": {"altitude": 1602, "latitude": 33.250885, "location_source": "LOC_INTERNAL", "longitude": -107.23156, "time_offset_sec": 7779}, "public_key_hex": "3e1c289337c793db000bbde022cf406494f9d412b35394570ac89f57652fd58d", "role": "CLIENT", "short_name": "HXCF", "snr": 6.13, "status": null, "telemetry": {"air_util_tx": 0.093, "battery_level": 101, "channel_utilization": 11.18, "uptime_seconds": 158807, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 370, "long_name": "Steel Hawk", "next_hop": 83, "num": "0x2a45d990", "position": {"altitude": 1416, "latitude": 32.684826, "location_source": "LOC_INTERNAL", "longitude": -107.454352, "time_offset_sec": 538}, "public_key_hex": "41e079053e96130355138dce101fb501f67a2371b6392ccd446936d4982687cb", "role": "CLIENT", "short_name": "S9DH", "snr": 3.49, "status": null, "telemetry": {"air_util_tx": 1.05, "battery_level": 60, "channel_utilization": 11.14, "uptime_seconds": 103663, "voltage": 3.84}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 5107, "long_name": "Blue Bison", "next_hop": 0, "num": "0x2af49b14", "position": {"altitude": 1731, "latitude": 32.352734, "location_source": "LOC_INTERNAL", "longitude": -107.333148, "time_offset_sec": 5235}, "public_key_hex": "4c6ce116048de8bce3727736799275a78ae798d97a2ac3f8af06c121c05a1681", "role": "TRACKER", "short_name": "B3XZ", "snr": 9.17, "status": null, "telemetry": {"air_util_tx": 0.319, "battery_level": 66, "channel_utilization": 11.87, "uptime_seconds": 25973, "voltage": 3.894}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 8236, "long_name": "Happy Hawk", "next_hop": 0, "num": "0x2b03792b", "position": null, "public_key_hex": "3868199e2f9abe0163cb67380ce20d0c601cbe73763ad6f223a4bfff063cab38", "role": "CLIENT", "short_name": "🌙", "snr": 5.17, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.609, "battery_level": 95, "channel_utilization": 20.61, "uptime_seconds": 72195, "voltage": 4.155}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2541, "long_name": "Smooth Adder", "next_hop": 185, "num": "0x2c7d0593", "position": {"altitude": 1178, "latitude": 32.166593, "location_source": "LOC_INTERNAL", "longitude": -106.773143, "time_offset_sec": 2622}, "public_key_hex": "9a0cb934580a5f33cbcd3de39a9f6ab5012c1889e33fc226dc9f91326d26fd14", "role": "CLIENT", "short_name": "🌲", "snr": 9.33, "status": null, "telemetry": {"air_util_tx": 1.019, "battery_level": 93, "channel_utilization": 6.5, "uptime_seconds": 63099, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 961, "long_name": "Drifting Pike", "next_hop": 0, "num": "0x2d0e8c42", "position": {"altitude": 1532, "latitude": 34.734724, "location_source": "LOC_INTERNAL", "longitude": -107.084259, "time_offset_sec": 1026}, "public_key_hex": "ad3f772db064508f43eb82bb8e958d4d8d4fab3308c5a5d194a696ba2fc12052", "role": "CLIENT", "short_name": "DI45", "snr": 5.8, "status": null, "telemetry": {"air_util_tx": 0.122, "battery_level": 58, "channel_utilization": 14.53, "uptime_seconds": 227656, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1014.68, "iaq": 14, "relative_humidity": 85.82, "temperature": 22.23}, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 1964, "long_name": "Loud Shark", "next_hop": 0, "num": "0x2e4cc1e6", "position": {"altitude": 1213, "latitude": 34.237167, "location_source": "LOC_INTERNAL", "longitude": -107.199683, "time_offset_sec": 2091}, "public_key_hex": "5c833a6036236e58f63965ce3ab3256b3bc170f31928512f7e1cfabca03a4cbd", "role": "CLIENT", "short_name": "LDFT", "snr": 10.85, "status": null, "telemetry": {"air_util_tx": 0.153, "battery_level": 73, "channel_utilization": 1.07, "uptime_seconds": 132926, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 126, "long_name": "Red Iguana", "next_hop": 116, "num": "0x2e68f619", "position": {"altitude": 1832, "latitude": 32.923994, "location_source": "LOC_INTERNAL", "longitude": -106.657373, "time_offset_sec": 329}, "public_key_hex": "", "role": "CLIENT", "short_name": "R7ZB", "snr": 4.76, "status": null, "telemetry": {"air_util_tx": 0.338, "battery_level": 24, "channel_utilization": 3.24, "uptime_seconds": 12781, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1006.38, "iaq": 30, "relative_humidity": 39.21, "temperature": 27.25}, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 12488, "long_name": "Misty Stag", "next_hop": 113, "num": "0x2ed4a8e4", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "MS2B", "snr": 0.46, "status": null, "telemetry": {"air_util_tx": 0.209, "battery_level": 73, "channel_utilization": 4.95, "uptime_seconds": 21300, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 706, "long_name": "Happy Crane", "next_hop": 0, "num": "0x2f190630", "position": {"altitude": 1326, "latitude": 32.193645, "location_source": "LOC_INTERNAL", "longitude": -108.049345, "time_offset_sec": 819}, "public_key_hex": "a3fbfff1a8985ff8dfc323d60115bfa7cf7b900797769c2108ee2aedafaa1280", "role": "CLIENT", "short_name": "H726", "snr": 0.66, "status": null, "telemetry": {"air_util_tx": 0.209, "battery_level": 80, "channel_utilization": 20.01, "uptime_seconds": 393994, "voltage": 4.02}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 358, "long_name": "Silent Mustang K14IJ", "next_hop": 0, "num": "0x2f57c805", "position": {"altitude": 1410, "latitude": 33.695552, "location_source": "LOC_INTERNAL", "longitude": -107.067123, "time_offset_sec": 485}, "public_key_hex": "16d1f52621f47df16b03a20e6523299f5efe1f4f95b7aeb10457858966ed2a82", "role": "CLIENT", "short_name": "SONZ", "snr": 2.78, "status": null, "telemetry": {"air_util_tx": 0.535, "battery_level": 10, "channel_utilization": 16.78, "uptime_seconds": 11456, "voltage": 3.39}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.38, "iaq": 29, "relative_humidity": 35.93, "temperature": 35.08}, "hops_away": 1, "hw_model": "XIAO_NRF52_KIT", "last_heard_offset_sec": 228, "long_name": "Wild Bear", "next_hop": 190, "num": "0x2f8120b8", "position": {"altitude": 1569, "latitude": 32.915073, "location_source": "LOC_INTERNAL", "longitude": -107.459333, "time_offset_sec": 339}, "public_key_hex": "1501e8e5e651653894574d21e3af92c8cc152d5f5f2b361a57f1566a27743f4a", "role": "CLIENT", "short_name": "🐢", "snr": 8.88, "status": null, "telemetry": {"air_util_tx": 0.378, "battery_level": 40, "channel_utilization": 19.12, "uptime_seconds": 32255, "voltage": 3.66}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 1841, "long_name": "Sneaky Owl", "next_hop": 155, "num": "0x2fb30c88", "position": null, "public_key_hex": "ed99fa218967e31fed4ff0766c016c3449d50055d1106ad503e8b9e2cdfc2b41", "role": "CLIENT", "short_name": "SGO4", "snr": 0.44, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.131, "battery_level": 96, "channel_utilization": 16.88, "uptime_seconds": 13908, "voltage": 4.164}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4212, "long_name": "Roving Squirrel", "next_hop": 62, "num": "0x2fe8f471", "position": {"altitude": 1083, "latitude": 33.046297, "location_source": "LOC_INTERNAL", "longitude": -107.497098, "time_offset_sec": 4479}, "public_key_hex": "fc5ac53f01bbc0951caf461a2cbf92eee367800881041f132b0d9f5623877728", "role": "CLIENT", "short_name": "RK7N", "snr": 3.45, "status": null, "telemetry": {"air_util_tx": 0.622, "battery_level": 89, "channel_utilization": 5.45, "uptime_seconds": 150733, "voltage": 4.101}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 4401, "long_name": "Sharp Cougar", "next_hop": 83, "num": "0x3117ad36", "position": null, "public_key_hex": "bcfa30fdae164084e2b1cfc1d6e17125c37a50480e57a3679c892fcce5a6168e", "role": "CLIENT", "short_name": "SNGJ", "snr": 9.12, "status": {"status": "online"}, "telemetry": {"air_util_tx": 1.231, "battery_level": 52, "channel_utilization": 21.0, "uptime_seconds": 23937, "voltage": 3.768}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 415, "long_name": "Gold Viper", "next_hop": 127, "num": "0x31bef7f0", "position": {"altitude": 1377, "latitude": 32.021334, "location_source": "LOC_INTERNAL", "longitude": -107.433857, "time_offset_sec": 697}, "public_key_hex": "5823cdb30133a932c4a81c9b454e608d811974d97cb709c70fb10fe5ab8345b9", "role": "CLIENT", "short_name": "G2ZX", "snr": 8.05, "status": null, "telemetry": {"air_util_tx": 0.521, "battery_level": 79, "channel_utilization": 3.1, "uptime_seconds": 216248, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 3129, "long_name": "Iron Lynx", "next_hop": 100, "num": "0x3208623d", "position": {"altitude": 1701, "latitude": 33.189569, "location_source": "LOC_INTERNAL", "longitude": -107.539966, "time_offset_sec": 3383}, "public_key_hex": "892dea76c661963c99ed1f1195d4bea9c6fa5163475b5b35ce9b3a5fda420ec0", "role": "CLIENT_HIDDEN", "short_name": "IWY3", "snr": 9.49, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1096, "long_name": "Steel Doe", "next_hop": 0, "num": "0x322f653a", "position": {"altitude": 1245, "latitude": 32.930805, "location_source": "LOC_INTERNAL", "longitude": -105.812392, "time_offset_sec": 1218}, "public_key_hex": "d75ceb23a3e896da98903cdeb81a6c3b946cfe56199eee9556f836f52c857815", "role": "CLIENT", "short_name": "S78M", "snr": 4.93, "status": null, "telemetry": {"air_util_tx": 0.211, "battery_level": 33, "channel_utilization": 39.3, "uptime_seconds": 98053, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 2228, "long_name": "Brave Salmon", "next_hop": 0, "num": "0x3245221d", "position": {"altitude": 938, "latitude": 32.657217, "location_source": "LOC_INTERNAL", "longitude": -107.920171, "time_offset_sec": 2525}, "public_key_hex": "54d85c0b0ddf309d95b3acdf2d203c2fa39f500256cd6362250d4061d48c3b07", "role": "SENSOR", "short_name": "B4DI", "snr": 1.54, "status": null, "telemetry": {"air_util_tx": 0.182, "battery_level": 19, "channel_utilization": 8.93, "uptime_seconds": 87158, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 2151, "long_name": "Storm Raven", "next_hop": 0, "num": "0x341f4692", "position": {"altitude": 1618, "latitude": 33.195702, "location_source": "LOC_INTERNAL", "longitude": -108.073506, "time_offset_sec": 2318}, "public_key_hex": "2bbc851a951cefd1b2b48a8b5b55e3263efd3ee5e6a33d131a19740950d46e16", "role": "CLIENT", "short_name": "SOEX", "snr": 12.0, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 2915, "long_name": "Frosty Juniper", "next_hop": 0, "num": "0x345796f1", "position": {"altitude": 1374, "latitude": 33.249679, "location_source": "LOC_INTERNAL", "longitude": -107.230585, "time_offset_sec": 2978}, "public_key_hex": "97c9b6de4acbacfc11b88b0f29c6a37e85bbf4e5c1c0e225b0865d83a2992c16", "role": "CLIENT", "short_name": "🌙", "snr": 6.38, "status": null, "telemetry": {"air_util_tx": 0.502, "battery_level": 71, "channel_utilization": 19.85, "uptime_seconds": 117857, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 9084, "long_name": "Storm Hawk", "next_hop": 0, "num": "0x34eb1977", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "S0JA", "snr": 1.68, "status": null, "telemetry": {"air_util_tx": 1.081, "battery_level": 50, "channel_utilization": 7.61, "uptime_seconds": 38562, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 1346, "long_name": "Forest Trout", "next_hop": 0, "num": "0x34f751c6", "position": {"altitude": 1326, "latitude": 32.338295, "location_source": "LOC_INTERNAL", "longitude": -107.444579, "time_offset_sec": 1349}, "public_key_hex": "3b2b981eeb23c8feb07890fc4a130978cc01042f49838735eeda90536da89afc", "role": "CLIENT", "short_name": "FTSG", "snr": 5.76, "status": null, "telemetry": {"air_util_tx": 0.426, "battery_level": 27, "channel_utilization": 11.69, "uptime_seconds": 8654, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 3630, "long_name": "Tiny Beaver", "next_hop": 0, "num": "0x350928d2", "position": {"altitude": 1394, "latitude": 33.767341, "location_source": "LOC_INTERNAL", "longitude": -107.072889, "time_offset_sec": 3829}, "public_key_hex": "38ebb2ccc72aa736e8dc5f4d9775bffc50dcb99766b48a972525e0ae84985b1d", "role": "ROUTER", "short_name": "TA4T", "snr": 6.23, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 3, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1205, "long_name": "Storm Crane", "next_hop": 124, "num": "0x35280f4d", "position": {"altitude": 1226, "latitude": 32.43356, "location_source": "LOC_INTERNAL", "longitude": -106.613189, "time_offset_sec": 1366}, "public_key_hex": "587cd2b9f47a6d59abf22ca05d92d521900ce70b27f1054f913d4498b434222e", "role": "CLIENT", "short_name": "SMH0", "snr": 3.07, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "THINKNODE_M6", "last_heard_offset_sec": 822, "long_name": "Roving Otter", "next_hop": 208, "num": "0x35281377", "position": {"altitude": 1321, "latitude": 32.536876, "location_source": "LOC_INTERNAL", "longitude": -108.039779, "time_offset_sec": 1027}, "public_key_hex": "ba0d4fb69c240d9a29dc357e3f5fd9479bd61da30e785c66722de8ef2fcc8c2d", "role": "CLIENT", "short_name": "RS7Q", "snr": -5.92, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.742, "battery_level": 101, "channel_utilization": 7.82, "uptime_seconds": 179764, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1449, "long_name": "Howling Falcon", "next_hop": 198, "num": "0x35eaa098", "position": {"altitude": 1552, "latitude": 33.404432, "location_source": "LOC_INTERNAL", "longitude": -107.924439, "time_offset_sec": 1581}, "public_key_hex": "a1fc3b0d9893f1242bea61d384ac9165cc0766cbd3a48f3ccd6a18ee6f812319", "role": "CLIENT_HIDDEN", "short_name": "HJ3F", "snr": 5.04, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 372, "long_name": "Tiny Elk", "next_hop": 0, "num": "0x35f0cc39", "position": {"altitude": 1427, "latitude": 33.290895, "location_source": "LOC_INTERNAL", "longitude": -107.201106, "time_offset_sec": 499}, "public_key_hex": "330a4052ea490328a3956becda9a08acdcfbc16bc0478a07cef222b5a30daafd", "role": "CLIENT", "short_name": "TE54", "snr": 9.56, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.597, "battery_level": 28, "channel_utilization": 4.76, "uptime_seconds": 31339, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 1101, "long_name": "Sunny Gecko", "next_hop": 116, "num": "0x3619ed0e", "position": {"altitude": 1985, "latitude": 33.257677, "location_source": "LOC_INTERNAL", "longitude": -107.385914, "time_offset_sec": 1362}, "public_key_hex": "757f839679e19f0949429e4df3ac104d6868cb752fe529ea4723c49d2e3f0845", "role": "CLIENT", "short_name": "🐢", "snr": 7.04, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 1366, "long_name": "Sleepy Colt", "next_hop": 0, "num": "0x379a7f4b", "position": {"altitude": 1482, "latitude": 33.010008, "location_source": "LOC_INTERNAL", "longitude": -108.104341, "time_offset_sec": 1645}, "public_key_hex": "d7e6c8d58e00e128529228bca8331472cefe3b6ade499a4229579e44a06a9909", "role": "CLIENT", "short_name": "SBJW", "snr": 6.5, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.41, "iaq": 52, "relative_humidity": 47.13, "temperature": 29.47}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4284, "long_name": "Sky Falcon", "next_hop": 0, "num": "0x37b90b2e", "position": {"altitude": 1498, "latitude": 32.135808, "location_source": "LOC_INTERNAL", "longitude": -106.657873, "time_offset_sec": 4402}, "public_key_hex": "9ee39d5af7fec89de02a3ce7d48b9ad82bb218d75d9324ff74eb317cab8aaf60", "role": "CLIENT", "short_name": "S9D7", "snr": 7.46, "status": null, "telemetry": {"air_util_tx": 0.463, "battery_level": 33, "channel_utilization": 16.68, "uptime_seconds": 196876, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.65, "iaq": 36, "relative_humidity": 46.5, "temperature": 23.13}, "hops_away": 3, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1643, "long_name": "Brave Owl", "next_hop": 85, "num": "0x37dccaab", "position": null, "public_key_hex": "ce6a097bf64b3b2f149276d389a0aaf54c3c62b3822b4dd57cad98049d85f645", "role": "CLIENT", "short_name": "B8MT", "snr": 5.19, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.454, "battery_level": 85, "channel_utilization": 22.66, "uptime_seconds": 261777, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1240, "long_name": "Silver Badger", "next_hop": 0, "num": "0x38ba7d6c", "position": null, "public_key_hex": "b735a5154885ba6bef03bd47d559b3d20e3c3a0ae4e3fa77fd70ffd623bd7a4b", "role": "CLIENT", "short_name": "SZTG", "snr": 1.95, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 4, "hw_model": "RAK4631", "last_heard_offset_sec": 1066, "long_name": "Wandering Bronco", "next_hop": 145, "num": "0x38e31b4f", "position": {"altitude": 1172, "latitude": 32.504572, "location_source": "LOC_INTERNAL", "longitude": -106.301649, "time_offset_sec": 1177}, "public_key_hex": "70d05e317b534b7d3818add161f43ca366cdee3abee2f7587393217af8e3e7da", "role": "CLIENT", "short_name": "WICA", "snr": 6.52, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "RAK4631", "last_heard_offset_sec": 1995, "long_name": "Lunar Bronco", "next_hop": 130, "num": "0x3a48853f", "position": {"altitude": 1400, "latitude": 33.976144, "location_source": "LOC_INTERNAL", "longitude": -107.015442, "time_offset_sec": 1996}, "public_key_hex": "e7b6c54f0d2b8d129baad7ad2370629dd0a8a969799b4edad53b28c5d70a137a", "role": "CLIENT", "short_name": "LW2P", "snr": 2.96, "status": null, "telemetry": {"air_util_tx": 1.042, "battery_level": 101, "channel_utilization": 8.36, "uptime_seconds": 170554, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3738, "long_name": "Drifting Mamba", "next_hop": 0, "num": "0x3a7a9d31", "position": {"altitude": 1324, "latitude": 32.805193, "location_source": "LOC_INTERNAL", "longitude": -107.892675, "time_offset_sec": 3975}, "public_key_hex": "8792509c722d7ce22641ad6eeaf4d49c0990d572a660c8a9b4866ad818e99518", "role": "CLIENT_MUTE", "short_name": "DXZL", "snr": 5.37, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.616, "battery_level": 52, "channel_utilization": 13.15, "uptime_seconds": 19131, "voltage": 3.768}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 694, "long_name": "Bright Pony", "next_hop": 0, "num": "0x3a9ebc7a", "position": {"altitude": 1679, "latitude": 33.197556, "location_source": "LOC_INTERNAL", "longitude": -107.739604, "time_offset_sec": 705}, "public_key_hex": "bcf5471fc71cf14cda41b23fb2bdbe2a0ad0d9545130faabcb8868094d58ed6d", "role": "TAK_TRACKER", "short_name": "BPWE", "snr": 10.59, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.494, "battery_level": 86, "channel_utilization": 15.79, "uptime_seconds": 5144, "voltage": 4.074}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 6627, "long_name": "Roving Shark", "next_hop": 0, "num": "0x3c626203", "position": {"altitude": 1389, "latitude": 32.858857, "location_source": "LOC_INTERNAL", "longitude": -107.351624, "time_offset_sec": 6770}, "public_key_hex": "425316a371780a5d7cd4c0f286e8c2ca392ed59eb2ee7164282b4f000fd8c98d", "role": "CLIENT", "short_name": "RBC5", "snr": 7.03, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 3404, "long_name": "Quick Falcon", "next_hop": 0, "num": "0x3cd1fc8b", "position": {"altitude": 1465, "latitude": 32.944934, "location_source": "LOC_INTERNAL", "longitude": -107.49285, "time_offset_sec": 3444}, "public_key_hex": "", "role": "CLIENT", "short_name": "QLVF", "snr": 12.0, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.122, "battery_level": 33, "channel_utilization": 5.63, "uptime_seconds": 104974, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 3961, "long_name": "Frozen Bronco", "next_hop": 0, "num": "0x3d841286", "position": null, "public_key_hex": "4808b539f3d118bcf2ed4b9b90d28b11ef7b8840e0e9edd5cc7706b51c906125", "role": "CLIENT", "short_name": "FFER", "snr": 6.24, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.634, "battery_level": 20, "channel_utilization": 17.12, "uptime_seconds": 7949, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4991, "long_name": "Lone Coyote", "next_hop": 0, "num": "0x3f08ef02", "position": {"altitude": 1232, "latitude": 33.103679, "location_source": "LOC_INTERNAL", "longitude": -106.900331, "time_offset_sec": 5175}, "public_key_hex": "6525d25dc21d4b76bd54da497bad80a83f0e5491756a1a0344a479f39eb0ba71", "role": "CLIENT", "short_name": "LLVT", "snr": 7.79, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 3286, "long_name": "Iron Viper", "next_hop": 0, "num": "0x3f677662", "position": {"altitude": 1848, "latitude": 33.278539, "location_source": "LOC_INTERNAL", "longitude": -108.310349, "time_offset_sec": 3409}, "public_key_hex": "c413d2c51a6764b0589503e1bda72c9840f3fd0d5687e0b5df6dc04640b662c7", "role": "CLIENT", "short_name": "I0D3", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.179, "battery_level": 22, "channel_utilization": 8.54, "uptime_seconds": 220137, "voltage": 3.498}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAG", "last_heard_offset_sec": 5466, "long_name": "Black Lynx AB8ET", "next_hop": 0, "num": "0x3f7ea7eb", "position": {"altitude": 1676, "latitude": 32.76055, "location_source": "LOC_INTERNAL", "longitude": -106.901877, "time_offset_sec": 5692}, "public_key_hex": "f7e96bc6a9e0214c7e3fda88b27f4969e291adce95396869c42ec77126587cd9", "role": "CLIENT", "short_name": "BBUC", "snr": 9.53, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.233, "battery_level": 101, "channel_utilization": 6.48, "uptime_seconds": 36311, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.36, "iaq": 44, "relative_humidity": 72.23, "temperature": 20.48}, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 2354, "long_name": "Dawn Tortoise", "next_hop": 153, "num": "0x3fc5bdc2", "position": null, "public_key_hex": "fda1f9f1e8ed7a0c5b37bff3d18c5d15aaf72b17e03e2248c7565785671247eb", "role": "CLIENT", "short_name": "DETB", "snr": 6.68, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 231, "long_name": "Silver Bass", "next_hop": 7, "num": "0x3ff95e93", "position": {"altitude": 1203, "latitude": 32.849007, "location_source": "LOC_INTERNAL", "longitude": -108.197204, "time_offset_sec": 521}, "public_key_hex": "849a63f901ce989581c4939d4fc3ad466203b6a02c1638a8fa8a5e504b21f96e", "role": "CLIENT", "short_name": "SENN", "snr": 4.24, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.876, "battery_level": 20, "channel_utilization": 2.39, "uptime_seconds": 179024, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.48, "iaq": 0, "relative_humidity": 16.24, "temperature": 34.19}, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 8035, "long_name": "Mountain Trout", "next_hop": 184, "num": "0x417a5306", "position": {"altitude": 1503, "latitude": 33.624464, "location_source": "LOC_INTERNAL", "longitude": -106.72661, "time_offset_sec": 8119}, "public_key_hex": "35de117ffe9f5589301f410d8b721a21e7fc4e3a2893eaac77fdf74d3afd98c9", "role": "CLIENT", "short_name": "M97I", "snr": 9.69, "status": {"status": "no-gps"}, "telemetry": {"air_util_tx": 0.781, "battery_level": 85, "channel_utilization": 7.93, "uptime_seconds": 84523, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.45, "iaq": 97, "relative_humidity": 43.82, "temperature": 7.08}, "hops_away": 0, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 1671, "long_name": "Howling Falcon", "next_hop": 0, "num": "0x41840415", "position": {"altitude": 1497, "latitude": 32.148268, "location_source": "LOC_INTERNAL", "longitude": -107.216316, "time_offset_sec": 1867}, "public_key_hex": "c6dd6006e0a5c8161af6a399b638f234a6a9a1aa4cc49f88a86a05598d6f0b82", "role": "CLIENT", "short_name": "HKBX", "snr": 12.0, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "T_DECK", "last_heard_offset_sec": 2561, "long_name": "Happy Iguana", "next_hop": 112, "num": "0x41e5b63a", "position": {"altitude": 1296, "latitude": 32.452598, "location_source": "LOC_INTERNAL", "longitude": -106.65747, "time_offset_sec": 2656}, "public_key_hex": "e40bef49256a922269b637c229b4cbfec4bcb93111fc93df36d58c28235c60d0", "role": "CLIENT", "short_name": "HCQ1", "snr": 6.42, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 378, "long_name": "Sleepy Pony", "next_hop": 110, "num": "0x42827046", "position": {"altitude": 1571, "latitude": 33.761147, "location_source": "LOC_INTERNAL", "longitude": -108.038104, "time_offset_sec": 493}, "public_key_hex": "8675ad01e7bef086d31a9de38cabe2990be1c1fbe6c9070b1114f22fc1c3ab9b", "role": "CLIENT", "short_name": "SZQ4", "snr": 8.86, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.245, "battery_level": 30, "channel_utilization": 2.76, "uptime_seconds": 32001, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.27, "iaq": 64, "relative_humidity": 67.28, "temperature": 23.25}, "hops_away": 1, "hw_model": "SENSECAP_INDICATOR", "last_heard_offset_sec": 7780, "long_name": "Smooth Heron", "next_hop": 149, "num": "0x43098402", "position": {"altitude": 1709, "latitude": 33.548948, "location_source": "LOC_INTERNAL", "longitude": -107.227654, "time_offset_sec": 7782}, "public_key_hex": "30bbdb755f48cb79b58ab77c854d4ee8ba5d74bb8be93c3abfe8f2a0cb765ec6", "role": "CLIENT", "short_name": "SJ03", "snr": 7.38, "status": null, "telemetry": {"air_util_tx": 0.942, "battery_level": 66, "channel_utilization": 9.03, "uptime_seconds": 51036, "voltage": 3.894}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 396, "long_name": "Steel Bronco", "next_hop": 0, "num": "0x435d2911", "position": {"altitude": 1673, "latitude": 32.580277, "location_source": "LOC_INTERNAL", "longitude": -106.975968, "time_offset_sec": 564}, "public_key_hex": "72d1139e64da66ab88fe4509ef2fafd974b1ffaca6011c2e3bd45001adf39435", "role": "CLIENT", "short_name": "S1YB", "snr": 2.23, "status": null, "telemetry": {"air_util_tx": 2.157, "battery_level": 74, "channel_utilization": 5.14, "uptime_seconds": 164568, "voltage": 3.966}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 835, "long_name": "Howling Dolphin", "next_hop": 0, "num": "0x43eb37a7", "position": {"altitude": 1020, "latitude": 32.758911, "location_source": "LOC_INTERNAL", "longitude": -107.320715, "time_offset_sec": 1086}, "public_key_hex": "095341f06bac766786c813771f272925637b520d163e44d0683a156ef98722b2", "role": "CLIENT", "short_name": "HT3M", "snr": 2.25, "status": null, "telemetry": {"air_util_tx": 1.207, "battery_level": 30, "channel_utilization": 23.12, "uptime_seconds": 49884, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 362, "long_name": "Copper Falcon", "next_hop": 0, "num": "0x447c1421", "position": {"altitude": 1398, "latitude": 33.482628, "location_source": "LOC_INTERNAL", "longitude": -106.782732, "time_offset_sec": 501}, "public_key_hex": "b51c82595a0ba524f559479ce0ca39f6a7b5dbcd9f1c8342021a6b666d4bb598", "role": "CLIENT", "short_name": "🌙", "snr": 9.3, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.389, "battery_level": 100, "channel_utilization": 13.31, "uptime_seconds": 164327, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 2034, "long_name": "Storm Hare", "next_hop": 212, "num": "0x455852df", "position": {"altitude": 1411, "latitude": 33.160165, "location_source": "LOC_INTERNAL", "longitude": -106.978518, "time_offset_sec": 2200}, "public_key_hex": "663033cb89273d29f0fd905dc9133b81196d6ae87bac26f5753bfb131854db42", "role": "CLIENT", "short_name": "S7JT", "snr": 5.06, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1006.1, "iaq": 59, "relative_humidity": 46.21, "temperature": 20.36}, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 1003, "long_name": "Green Dolphin", "next_hop": 0, "num": "0x465af007", "position": null, "public_key_hex": "e78e261f75aafb55d6bbba0e9edd7cc11badfae3eb4186beb779c5dcb3255cd8", "role": "CLIENT", "short_name": "GI2G", "snr": 1.93, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 418, "long_name": "Brave Heron", "next_hop": 0, "num": "0x48134994", "position": {"altitude": 1581, "latitude": 33.071357, "location_source": "LOC_INTERNAL", "longitude": -108.099084, "time_offset_sec": 565}, "public_key_hex": "76a6ad472a5cff57ed41eb49c1816811c240fad47b9af833300228d122aa18f5", "role": "CLIENT_HIDDEN", "short_name": "BWKH", "snr": 6.59, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.187, "battery_level": 23, "channel_utilization": 2.78, "uptime_seconds": 34162, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1008.42, "iaq": 61, "relative_humidity": 55.86, "temperature": 1.38}, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 4712, "long_name": "Tall Bear", "next_hop": 0, "num": "0x48bda4f9", "position": {"altitude": 1327, "latitude": 31.889102, "location_source": "LOC_INTERNAL", "longitude": -107.933255, "time_offset_sec": 4967}, "public_key_hex": "3aa01e4ffd3b8b7e47642303a170636cb7de191e004e122ad7e7675eef2fda6f", "role": "CLIENT", "short_name": "TFGL", "snr": -1.71, "status": null, "telemetry": {"air_util_tx": 0.461, "battery_level": 83, "channel_utilization": 18.67, "uptime_seconds": 100258, "voltage": 4.047}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 258, "long_name": "White Bison", "next_hop": 0, "num": "0x48d0b194", "position": {"altitude": 1459, "latitude": 33.782262, "location_source": "LOC_INTERNAL", "longitude": -107.509689, "time_offset_sec": 295}, "public_key_hex": "49e0f1b4d280f33198462cc127e555a1b22c6114145d18e9a14299511b230d9c", "role": "CLIENT_MUTE", "short_name": "WG10", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 1.751, "battery_level": 100, "channel_utilization": 26.99, "uptime_seconds": 7542, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1003.81, "iaq": 124, "relative_humidity": 93.64, "temperature": 27.01}, "hops_away": 0, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 868, "long_name": "Found Bluff", "next_hop": 0, "num": "0x49e59f8b", "position": {"altitude": 1285, "latitude": 32.324249, "location_source": "LOC_INTERNAL", "longitude": -106.922861, "time_offset_sec": 1022}, "public_key_hex": "7d26af47afa14a475a43c0d3729ae78c06b25eb4c510e7a9b2a20a0480531dc5", "role": "CLIENT", "short_name": "F62Q", "snr": 2.03, "status": null, "telemetry": {"air_util_tx": 0.093, "battery_level": 36, "channel_utilization": 11.36, "uptime_seconds": 18990, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.75, "iaq": 34, "relative_humidity": 36.1, "temperature": 27.18}, "hops_away": 1, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 5593, "long_name": "Blue Bronco", "next_hop": 203, "num": "0x4a07175b", "position": null, "public_key_hex": "ffcf68b0815970081d634563ff83bde989bcb1f9cd5d61f6d6cc3d2ff1fad152", "role": "CLIENT", "short_name": "🌙", "snr": 10.77, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.56, "battery_level": 66, "channel_utilization": 11.41, "uptime_seconds": 19830, "voltage": 3.894}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 1260, "long_name": "Frozen Cobra", "next_hop": 23, "num": "0x4a440efd", "position": {"altitude": 1458, "latitude": 33.287147, "location_source": "LOC_INTERNAL", "longitude": -107.423088, "time_offset_sec": 1271}, "public_key_hex": "d2b9da3fa597cad62300e868e5f3f9f5bcbf470d4de800b67d21d752cca5b033", "role": "CLIENT", "short_name": "FJJK", "snr": 4.15, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 3201, "long_name": "Frosty Whale", "next_hop": 30, "num": "0x4a65ed60", "position": {"altitude": 1440, "latitude": 33.617218, "location_source": "LOC_INTERNAL", "longitude": -107.490683, "time_offset_sec": 3323}, "public_key_hex": "2a54ca7e0ca678ad6c80cf14e49ea9d04fe0d7197e281125800c6d8a78dcfb15", "role": "CLIENT", "short_name": "🌙", "snr": 12.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 4077, "long_name": "White Oak KQ6JZ", "next_hop": 0, "num": "0x4ab70ba8", "position": {"altitude": 1469, "latitude": 33.384405, "location_source": "LOC_INTERNAL", "longitude": -107.365826, "time_offset_sec": 4124}, "public_key_hex": "95d3ff7660fc0f84136aa457178490c7a80c8a442696b77490b14ce708bf6343", "role": "ROUTER", "short_name": "WCCR", "snr": 2.33, "status": null, "telemetry": {"air_util_tx": 0.23, "battery_level": 27, "channel_utilization": 13.09, "uptime_seconds": 58027, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": {"barometric_pressure": 1016.03, "iaq": 54, "relative_humidity": 67.96, "temperature": 16.03}, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 2928, "long_name": "Tiny Mamba", "next_hop": 235, "num": "0x4aedcfb7", "position": {"altitude": 1754, "latitude": 34.012849, "location_source": "LOC_INTERNAL", "longitude": -106.947321, "time_offset_sec": 3160}, "public_key_hex": "12890fb733850d94ded99326d5675cfddc5d9a133082a749fc56db0de5ec6fac", "role": "SENSOR", "short_name": "TQNJ", "snr": 4.78, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.222, "battery_level": 97, "channel_utilization": 13.6, "uptime_seconds": 47867, "voltage": 4.173}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": {"barometric_pressure": 1012.81, "iaq": 28, "relative_humidity": 40.75, "temperature": 8.84}, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 1916, "long_name": "Dawn Trout", "next_hop": 52, "num": "0x4d187e0c", "position": {"altitude": 1222, "latitude": 33.05008, "location_source": "LOC_INTERNAL", "longitude": -108.01885, "time_offset_sec": 1924}, "public_key_hex": "813f81fd4523b85fc3cf532d515c7c26d17cec31ffac8c79ffb47f4acc1e4974", "role": "CLIENT", "short_name": "D9JM", "snr": 2.33, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.038, "battery_level": 73, "channel_utilization": 8.21, "uptime_seconds": 49948, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2519, "long_name": "Quick Viper", "next_hop": 53, "num": "0x4d58a77a", "position": {"altitude": 1692, "latitude": 33.217413, "location_source": "LOC_INTERNAL", "longitude": -105.91945, "time_offset_sec": 2662}, "public_key_hex": "", "role": "CLIENT", "short_name": "QVG4", "snr": 9.9, "status": null, "telemetry": {"air_util_tx": 1.169, "battery_level": 80, "channel_utilization": 7.86, "uptime_seconds": 8886, "voltage": 4.02}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 956, "long_name": "Forest Elk", "next_hop": 0, "num": "0x4de415a9", "position": {"altitude": 1312, "latitude": 33.522763, "location_source": "LOC_INTERNAL", "longitude": -107.09793, "time_offset_sec": 1044}, "public_key_hex": "13ec2f9813b533355170fb10f7b9edf6968cfa202f05c239991f510b95c8c407", "role": "CLIENT", "short_name": "FXPV", "snr": 7.91, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1005.22, "iaq": 75, "relative_humidity": 59.03, "temperature": 13.82}, "hops_away": 1, "hw_model": "RAK3401", "last_heard_offset_sec": 7379, "long_name": "Giant Iguana", "next_hop": 182, "num": "0x4dfe4aca", "position": {"altitude": 1564, "latitude": 33.87132, "location_source": "LOC_INTERNAL", "longitude": -107.840595, "time_offset_sec": 7664}, "public_key_hex": "", "role": "CLIENT", "short_name": "GZ9E", "snr": 4.22, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.223, "battery_level": 17, "channel_utilization": 11.34, "uptime_seconds": 59951, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1002.46, "iaq": 82, "relative_humidity": 100.0, "temperature": 13.55}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3541, "long_name": "Frosty Otter", "next_hop": 0, "num": "0x4eb45c1b", "position": {"altitude": 1558, "latitude": 33.765757, "location_source": "LOC_INTERNAL", "longitude": -107.524716, "time_offset_sec": 3795}, "public_key_hex": "51b4aa27f341a7fec39219763ac33a36632089e7791650baeaa6c3bd7667cb8b", "role": "CLIENT", "short_name": "FMHL", "snr": 2.7, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 2403, "long_name": "Canyon Pike", "next_hop": 0, "num": "0x4f25ffaf", "position": {"altitude": 978, "latitude": 33.295073, "location_source": "LOC_INTERNAL", "longitude": -107.935912, "time_offset_sec": 2565}, "public_key_hex": "21f12d839251e7f55a50977347c0af93868e95069eab8a009570663787d55725", "role": "CLIENT", "short_name": "CHTT", "snr": 5.71, "status": null, "telemetry": {"air_util_tx": 0.173, "battery_level": 32, "channel_utilization": 19.04, "uptime_seconds": 37771, "voltage": 3.588}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 1444, "long_name": "Howling Tortoise W52LP", "next_hop": 0, "num": "0x4fdbfa4e", "position": {"altitude": 1521, "latitude": 31.835226, "location_source": "LOC_INTERNAL", "longitude": -107.13257, "time_offset_sec": 1486}, "public_key_hex": "d473b075d25507367010facf4b34a5c144d239bc76506eee18143557850e5262", "role": "CLIENT", "short_name": "H19L", "snr": 0.59, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3414, "long_name": "Found Aspen", "next_hop": 0, "num": "0x52ed71ee", "position": {"altitude": 1309, "latitude": 33.404487, "location_source": "LOC_INTERNAL", "longitude": -107.502565, "time_offset_sec": 3426}, "public_key_hex": "", "role": "CLIENT", "short_name": "FJ4E", "snr": 12.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 716, "long_name": "River Crane", "next_hop": 0, "num": "0x54feae48", "position": {"altitude": 1529, "latitude": 32.829459, "location_source": "LOC_INTERNAL", "longitude": -107.885714, "time_offset_sec": 764}, "public_key_hex": "775ebe272d85d4cf4aedf637cffff0cc46855f0be5f3bde6955c223b122b9efa", "role": "CLIENT", "short_name": "ROMG", "snr": 5.32, "status": null, "telemetry": {"air_util_tx": 1.047, "battery_level": 63, "channel_utilization": 15.17, "uptime_seconds": 73126, "voltage": 3.867}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "NOMADSTAR_METEOR_PRO", "last_heard_offset_sec": 4286, "long_name": "White Adder", "next_hop": 155, "num": "0x5542b82e", "position": {"altitude": 1199, "latitude": 33.325201, "location_source": "LOC_INTERNAL", "longitude": -107.359853, "time_offset_sec": 4325}, "public_key_hex": "23e55c5b94a22177bd6b7b480e5e9a2a781366259e03d169fd5af83b17572937", "role": "CLIENT", "short_name": "WFIS", "snr": 7.24, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 8886, "long_name": "Roving Shark", "next_hop": 0, "num": "0x556fe77a", "position": null, "public_key_hex": "200a37f6b73e3c525cd6cc8cad5af856428c4234d491ab404754739e93f4eca8", "role": "CLIENT", "short_name": "RJKM", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 1.168, "battery_level": 79, "channel_utilization": 7.74, "uptime_seconds": 64718, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 476, "long_name": "Lost Adder", "next_hop": 23, "num": "0x55daf3c7", "position": {"altitude": 1141, "latitude": 33.477638, "location_source": "LOC_INTERNAL", "longitude": -107.152887, "time_offset_sec": 670}, "public_key_hex": "63867a7544c551fc3e97436e9a2c9a4e0d508ee5d43367b9441108835c23c0de", "role": "CLIENT", "short_name": "LGUR", "snr": 3.8, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 2083, "long_name": "Sunny Aspen", "next_hop": 40, "num": "0x560289a0", "position": null, "public_key_hex": "758c818600d3dcf61e33702311a7e78d8d5ff3a5c2c0a0cc281537b0c32aa9d4", "role": "CLIENT", "short_name": "SC6I", "snr": 6.55, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.365, "battery_level": 90, "channel_utilization": 12.33, "uptime_seconds": 171767, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 1, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 241, "long_name": "Wandering Bear", "next_hop": 125, "num": "0x5614cdff", "position": {"altitude": 1380, "latitude": 33.963666, "location_source": "LOC_INTERNAL", "longitude": -108.530761, "time_offset_sec": 390}, "public_key_hex": "e7b26726240c6b8b457842831bcb1a82dd1e69215644d1e0adbae406bcc95010", "role": "ROUTER", "short_name": "WIMK", "snr": -1.84, "status": null, "telemetry": {"air_util_tx": 0.547, "battery_level": 46, "channel_utilization": 1.46, "uptime_seconds": 163671, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 7032, "long_name": "Sneaky Aspen", "next_hop": 94, "num": "0x561cfd98", "position": {"altitude": 1380, "latitude": 33.055831, "location_source": "LOC_INTERNAL", "longitude": -107.450441, "time_offset_sec": 7189}, "public_key_hex": "3a0f320e86669da351f6ba7b62df332050d30bc8b37332d1ef9c841a85d265e1", "role": "CLIENT", "short_name": "SSKN", "snr": 9.77, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 6234, "long_name": "Drowsy Whale", "next_hop": 245, "num": "0x56e1cb9d", "position": {"altitude": 1750, "latitude": 33.794131, "location_source": "LOC_INTERNAL", "longitude": -107.239442, "time_offset_sec": 6447}, "public_key_hex": "db63270d2c6cf45ef9f76f16bccbf17c1a07329fc5cceeec58924383e673b40b", "role": "CLIENT", "short_name": "DYTA", "snr": 9.73, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 8604, "long_name": "Tiny Doe", "next_hop": 0, "num": "0x575f0d95", "position": {"altitude": 1546, "latitude": 33.873224, "location_source": "LOC_INTERNAL", "longitude": -107.630514, "time_offset_sec": 8652}, "public_key_hex": "8f933aa44310ebcc8e988f2b180e3aad23fa7a8554b4762b9c645d3b833b6358", "role": "SENSOR", "short_name": "TIH4", "snr": 10.42, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.062, "battery_level": 21, "channel_utilization": 5.18, "uptime_seconds": 8818, "voltage": 3.489}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 349, "long_name": "Frosty Bear", "next_hop": 0, "num": "0x57c19004", "position": null, "public_key_hex": "", "role": "ROUTER", "short_name": "🌵", "snr": 6.04, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 601, "long_name": "Dusk Adder", "next_hop": 207, "num": "0x57e60523", "position": {"altitude": 1549, "latitude": 32.829103, "location_source": "LOC_INTERNAL", "longitude": -107.560567, "time_offset_sec": 830}, "public_key_hex": "56c6eb6548952819973855d9771675bd6b7d558d4a2266016e31d5737e792c07", "role": "CLIENT_MUTE", "short_name": "DDM3", "snr": 9.71, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.036, "battery_level": 28, "channel_utilization": 10.8, "uptime_seconds": 259221, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1013.6, "iaq": 84, "relative_humidity": 27.27, "temperature": 13.97}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1478, "long_name": "Dawn Turtle", "next_hop": 0, "num": "0x5896a943", "position": {"altitude": 1167, "latitude": 33.168007, "location_source": "LOC_INTERNAL", "longitude": -107.364102, "time_offset_sec": 1491}, "public_key_hex": "d0b326bc3c6cb2bf029fae9c194080c65224a09371d2c69be954d4198f039f89", "role": "CLIENT", "short_name": "DHR5", "snr": 6.29, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.44, "iaq": 14, "relative_humidity": 63.52, "temperature": 30.46}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1140, "long_name": "Solar Juniper", "next_hop": 0, "num": "0x5a4b7fad", "position": null, "public_key_hex": "03daa9821e8e2aa97c0f48637ebef53d057a4a5d48dd71e6350577c665ac6e58", "role": "CLIENT", "short_name": "SFNK", "snr": 8.01, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 2454, "long_name": "Canyon Mole", "next_hop": 204, "num": "0x5a7e7001", "position": {"altitude": 1350, "latitude": 33.054618, "location_source": "LOC_INTERNAL", "longitude": -107.149237, "time_offset_sec": 2705}, "public_key_hex": "984d1c5f9099ae076b3ea4729001af8ce4ef8fbd444a3a390d6de8dc245f2c78", "role": "CLIENT", "short_name": "C5DS", "snr": -2.29, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.709, "battery_level": 13, "channel_utilization": 18.33, "uptime_seconds": 338757, "voltage": 3.417}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.96, "iaq": 65, "relative_humidity": 45.12, "temperature": 17.17}, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 655, "long_name": "Slow Bear", "next_hop": 0, "num": "0x5ab0ef52", "position": {"altitude": 1458, "latitude": 33.247835, "location_source": "LOC_INTERNAL", "longitude": -106.650083, "time_offset_sec": 729}, "public_key_hex": "4333ec040b37aa1b83c3bc243af0fe1af308e9abf22241cbde4f15cbf389b8a7", "role": "CLIENT", "short_name": "🐢", "snr": 4.76, "status": null, "telemetry": {"air_util_tx": 1.372, "battery_level": 63, "channel_utilization": 13.22, "uptime_seconds": 39950, "voltage": 3.867}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 2380, "long_name": "Roving Trout", "next_hop": 0, "num": "0x5b53b025", "position": {"altitude": 1058, "latitude": 33.419824, "location_source": "LOC_INTERNAL", "longitude": -106.100673, "time_offset_sec": 2647}, "public_key_hex": "3b5f5e5a541107f3d05babc1b1e67b08d5bd603e1897fbb3f904a0020717b793", "role": "ROUTER", "short_name": "RDI5", "snr": 7.3, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.474, "battery_level": 46, "channel_utilization": 13.57, "uptime_seconds": 6168, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 1672, "long_name": "Red Heron KQ5BO", "next_hop": 43, "num": "0x5d303a81", "position": {"altitude": 1066, "latitude": 33.490828, "location_source": "LOC_INTERNAL", "longitude": -106.612674, "time_offset_sec": 1834}, "public_key_hex": "17e0b9248eaf07c217234ab685b331cceb5223fbabdd8448c630353803ef883b", "role": "CLIENT", "short_name": "R8YI", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 1.633, "battery_level": 75, "channel_utilization": 11.71, "uptime_seconds": 161845, "voltage": 3.975}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1019.33, "iaq": 23, "relative_humidity": 41.44, "temperature": 24.44}, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 2583, "long_name": "Whispering Crane", "next_hop": 93, "num": "0x5d419cc9", "position": {"altitude": 2030, "latitude": 33.687682, "location_source": "LOC_INTERNAL", "longitude": -106.988016, "time_offset_sec": 2854}, "public_key_hex": "205e7bccba09d6670edf6b8ac10cacb1814f5d090f8e6117301ebf5f52af6172", "role": "CLIENT", "short_name": "W2LP", "snr": 8.4, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.544, "battery_level": 71, "channel_utilization": 28.41, "uptime_seconds": 12805, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_SOLAR_NODE", "last_heard_offset_sec": 6978, "long_name": "Burning Mesa", "next_hop": 0, "num": "0x5d884220", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "🌙", "snr": 7.23, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 2532, "long_name": "Lone Juniper", "next_hop": 0, "num": "0x5df06225", "position": {"altitude": 1459, "latitude": 33.67153, "location_source": "LOC_INTERNAL", "longitude": -108.298245, "time_offset_sec": 2709}, "public_key_hex": "8e96a3a4c8c92d3a6bdca70311296555f9673de6589f53a7c1bbf5025203663f", "role": "CLIENT", "short_name": "L4J5", "snr": 7.22, "status": {"status": "low-batt"}, "telemetry": {"air_util_tx": 1.076, "battery_level": 74, "channel_utilization": 14.44, "uptime_seconds": 49677, "voltage": 3.966}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 533, "long_name": "Silver Beaver", "next_hop": 0, "num": "0x5e2a849f", "position": {"altitude": 1205, "latitude": 31.904079, "location_source": "LOC_INTERNAL", "longitude": -106.811678, "time_offset_sec": 629}, "public_key_hex": "2595fac54c897f642e2401c5bc531e8fda9f093303e314ca10c3d71a9765ba99", "role": "CLIENT", "short_name": "SMRO", "snr": 9.05, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 10743, "long_name": "Loud Ridge", "next_hop": 0, "num": "0x5ee7b9ab", "position": {"altitude": 1458, "latitude": 32.804323, "location_source": "LOC_INTERNAL", "longitude": -107.157219, "time_offset_sec": 10931}, "public_key_hex": "324fe432faf69f8542e27a4f842930cc5427c6506c43d223c1327b9b450ab218", "role": "CLIENT", "short_name": "L4S6", "snr": 3.08, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.57, "iaq": 0, "relative_humidity": 30.0, "temperature": 15.21}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 291, "long_name": "Stone Juniper", "next_hop": 0, "num": "0x5eed232e", "position": null, "public_key_hex": "25819aa7eb792ba9c7536908ce600978222847123ec836dd5efffb4a99042fe5", "role": "CLIENT", "short_name": "SDK1", "snr": 9.23, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.338, "battery_level": 29, "channel_utilization": 11.26, "uptime_seconds": 43857, "voltage": 3.561}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1005.29, "iaq": 0, "relative_humidity": 73.52, "temperature": 17.84}, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3582, "long_name": "Sunny Doe", "next_hop": 0, "num": "0x5f3a27b9", "position": null, "public_key_hex": "b650660e551a64ba46e1dc907598af9a63c830004ea1b58f4fa2f8d2981928c2", "role": "CLIENT", "short_name": "SY3A", "snr": 9.44, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1024.32, "iaq": 55, "relative_humidity": 54.13, "temperature": 27.45}, "hops_away": 0, "hw_model": "NOMADSTAR_METEOR_PRO", "last_heard_offset_sec": 4388, "long_name": "Bright Juniper", "next_hop": 0, "num": "0x60d24c47", "position": {"altitude": 1787, "latitude": 33.333417, "location_source": "LOC_INTERNAL", "longitude": -107.568488, "time_offset_sec": 4652}, "public_key_hex": "cb7db9874d97fb0ac506c3fd41b0c8c7bd2eb559826276c36e74e2e7555f5a81", "role": "CLIENT", "short_name": "🦉", "snr": 8.43, "status": null, "telemetry": {"air_util_tx": 1.427, "battery_level": 90, "channel_utilization": 14.19, "uptime_seconds": 76369, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1056, "long_name": "Wild Badger", "next_hop": 77, "num": "0x6200f9ce", "position": {"altitude": 1253, "latitude": 33.041987, "location_source": "LOC_INTERNAL", "longitude": -106.950457, "time_offset_sec": 1202}, "public_key_hex": "e23be3dd4813837634a58c9d1854da85f3dbd150ae89174b27682acc486193da", "role": "CLIENT", "short_name": "🔥", "snr": 4.7, "status": null, "telemetry": {"air_util_tx": 0.535, "battery_level": 82, "channel_utilization": 10.39, "uptime_seconds": 59624, "voltage": 4.038}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 6752, "long_name": "Forest Adder", "next_hop": 28, "num": "0x63013b03", "position": {"altitude": 1583, "latitude": 33.190369, "location_source": "LOC_INTERNAL", "longitude": -106.809159, "time_offset_sec": 6781}, "public_key_hex": "58aa10becbc7ede30777f73b3ed202f8450ad4e4437adf4cdd8e8f80e3712cd0", "role": "CLIENT", "short_name": "🌊", "snr": 9.73, "status": null, "telemetry": {"air_util_tx": 0.868, "battery_level": 17, "channel_utilization": 4.02, "uptime_seconds": 34171, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 66, "long_name": "Misty Crow", "next_hop": 0, "num": "0x634d6509", "position": null, "public_key_hex": "a1b2605f00d145be4708ced109705732bd361a0c8007de5a7b9ff0d6f51d8e18", "role": "CLIENT", "short_name": "MBRM", "snr": 7.44, "status": null, "telemetry": {"air_util_tx": 0.281, "battery_level": 63, "channel_utilization": 15.75, "uptime_seconds": 127038, "voltage": 3.867}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 7252, "long_name": "Found Gecko", "next_hop": 0, "num": "0x637a7112", "position": {"altitude": 1446, "latitude": 32.690082, "location_source": "LOC_INTERNAL", "longitude": -107.860767, "time_offset_sec": 7428}, "public_key_hex": "8846b0dbc05917aeb4705ad55a561bdce07fd33292ea833420fdad02af31e165", "role": "CLIENT", "short_name": "FMBH", "snr": 4.12, "status": null, "telemetry": {"air_util_tx": 0.813, "battery_level": 65, "channel_utilization": 10.12, "uptime_seconds": 3331, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 4011, "long_name": "Smooth Doe", "next_hop": 0, "num": "0x64527cb3", "position": {"altitude": 1352, "latitude": 33.789084, "location_source": "LOC_INTERNAL", "longitude": -107.13058, "time_offset_sec": 4044}, "public_key_hex": "5e2df0ded253ee2ef12d3a480bbc7d5224c32a1a10f031a5f4767929dd51c957", "role": "ROUTER", "short_name": "SSCW", "snr": 12.0, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.042, "battery_level": 98, "channel_utilization": 5.9, "uptime_seconds": 373425, "voltage": 4.182}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 3443, "long_name": "River Pike", "next_hop": 0, "num": "0x645fe930", "position": {"altitude": 1695, "latitude": 33.371402, "location_source": "LOC_INTERNAL", "longitude": -108.077662, "time_offset_sec": 3707}, "public_key_hex": "2b53f8e1f6d917da69b84232cf02ef52d63c3110d243a6365ca0d9f624a9676a", "role": "CLIENT_MUTE", "short_name": "RYR4", "snr": 9.34, "status": null, "telemetry": {"air_util_tx": 0.699, "battery_level": 55, "channel_utilization": 15.11, "uptime_seconds": 47524, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 7136, "long_name": "Blue Mamba", "next_hop": 75, "num": "0x64a95e31", "position": {"altitude": 953, "latitude": 33.832252, "location_source": "LOC_INTERNAL", "longitude": -108.054814, "time_offset_sec": 7336}, "public_key_hex": "6b53989af627f43df024b8cc2af77b8807e116d529a4f2a935f4841f115cd2f7", "role": "CLIENT", "short_name": "B41R", "snr": 2.61, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1273, "long_name": "River Coyote", "next_hop": 0, "num": "0x64cc9bac", "position": null, "public_key_hex": "93157fedd5c540dd2dba4723d1ac996e48a4a821b5a5c92e2c032f74f62f3cf2", "role": "CLIENT", "short_name": "RIS0", "snr": 3.55, "status": null, "telemetry": {"air_util_tx": 1.163, "battery_level": 89, "channel_utilization": 16.15, "uptime_seconds": 62238, "voltage": 4.101}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M5", "last_heard_offset_sec": 1921, "long_name": "Dusk Squirrel", "next_hop": 0, "num": "0x65bd58c3", "position": {"altitude": 1254, "latitude": 33.254787, "location_source": "LOC_INTERNAL", "longitude": -107.104805, "time_offset_sec": 2207}, "public_key_hex": "66c55dd5187047a62840c5dbc4609548c111433a75f9e871de15fe8fa8f618d8", "role": "CLIENT", "short_name": "DUYQ", "snr": 2.32, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1009.51, "iaq": 68, "relative_humidity": 47.61, "temperature": 32.73}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 8470, "long_name": "Sleepy Cobra", "next_hop": 0, "num": "0x6630f717", "position": {"altitude": 806, "latitude": 33.128898, "location_source": "LOC_INTERNAL", "longitude": -107.493292, "time_offset_sec": 8739}, "public_key_hex": "8f0b633549dc4dc8293407769a55bf0e85cf3c08b158d76a89ca5c0b10545431", "role": "CLIENT", "short_name": "SMQ1", "snr": 7.84, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.08, "battery_level": 83, "channel_utilization": 21.33, "uptime_seconds": 108301, "voltage": 4.047}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 7996, "long_name": "Silent Phoenix", "next_hop": 0, "num": "0x6644ef0a", "position": {"altitude": 1187, "latitude": 33.112567, "location_source": "LOC_INTERNAL", "longitude": -107.827152, "time_offset_sec": 8174}, "public_key_hex": "1b93a24c71ee7ea1b995d78094fdb96358abebd9217b88cc780752a933d89239", "role": "CLIENT", "short_name": "SBGO", "snr": 0.65, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2863, "long_name": "Dusk Hare", "next_hop": 0, "num": "0x6726f381", "position": {"altitude": 1010, "latitude": 32.705604, "location_source": "LOC_INTERNAL", "longitude": -106.046648, "time_offset_sec": 3132}, "public_key_hex": "181c2327d931a05576a5de436b0a31fb67542f0712c5cfde5c456a17b370f89e", "role": "CLIENT", "short_name": "DOM5", "snr": 4.13, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.76, "iaq": 39, "relative_humidity": 99.59, "temperature": 22.67}, "hops_away": 0, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 750, "long_name": "Drowsy Mamba", "next_hop": 0, "num": "0x675d77f2", "position": {"altitude": 1325, "latitude": 33.642278, "location_source": "LOC_INTERNAL", "longitude": -107.295189, "time_offset_sec": 837}, "public_key_hex": "cee4b8911b3920af5c95a121d34a201df99a6056909c028395064bdd7303907a", "role": "ROUTER", "short_name": "D1SL", "snr": 8.82, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.992, "battery_level": 11, "channel_utilization": 14.51, "uptime_seconds": 6720, "voltage": 3.399}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1315, "long_name": "Mountain Mole", "next_hop": 0, "num": "0x675e586d", "position": {"altitude": 1221, "latitude": 33.30228, "location_source": "LOC_INTERNAL", "longitude": -107.004475, "time_offset_sec": 1318}, "public_key_hex": "787716e0fe5232c05fd38d208b996e83d6d20b618ce8358ecc8c31725c6b579a", "role": "CLIENT", "short_name": "MGNO", "snr": 2.54, "status": null, "telemetry": {"air_util_tx": 0.117, "battery_level": 16, "channel_utilization": 8.72, "uptime_seconds": 198097, "voltage": 3.444}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "T_DECK", "last_heard_offset_sec": 3622, "long_name": "Lunar Adder", "next_hop": 28, "num": "0x679bda37", "position": {"altitude": 1045, "latitude": 33.072766, "location_source": "LOC_INTERNAL", "longitude": -107.309558, "time_offset_sec": 3683}, "public_key_hex": "e5972098bc4c8ca34697e9696ab413b34113f251d3e796b1fa530f18a5b4f1bd", "role": "TRACKER", "short_name": "LIHE", "snr": 2.11, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.537, "battery_level": 17, "channel_utilization": 8.68, "uptime_seconds": 135160, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 3914, "long_name": "Roving Lynx", "next_hop": 0, "num": "0x68725ce0", "position": {"altitude": 832, "latitude": 32.945027, "location_source": "LOC_INTERNAL", "longitude": -107.479834, "time_offset_sec": 4072}, "public_key_hex": "ecd43f13c918a4f7209806d2c559c3aed2b9e5d5a7c77c0efa288eefe27ca02c", "role": "CLIENT", "short_name": "RE96", "snr": 6.58, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 13132, "long_name": "Mountain Turtle", "next_hop": 0, "num": "0x68c6bb7d", "position": {"altitude": 1143, "latitude": 33.711301, "location_source": "LOC_INTERNAL", "longitude": -107.708401, "time_offset_sec": 13212}, "public_key_hex": "e8548136b1d7de28fd487fae5c42e5e260717df2bf12792f383026e426ad067e", "role": "CLIENT", "short_name": "M215", "snr": 9.88, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.45, "battery_level": 49, "channel_utilization": 14.85, "uptime_seconds": 145984, "voltage": 3.741}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 1235, "long_name": "Drifting Owl", "next_hop": 0, "num": "0x68e7d58a", "position": {"altitude": 1415, "latitude": 33.558474, "location_source": "LOC_INTERNAL", "longitude": -107.839134, "time_offset_sec": 1430}, "public_key_hex": "01333b1228fd5e6d5cf18b0c6aae8fd6607251652c7f1a43f8846f3ac477dd15", "role": "CLIENT", "short_name": "D0H7", "snr": 4.19, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.244, "battery_level": 13, "channel_utilization": 26.08, "uptime_seconds": 72799, "voltage": 3.417}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "RAK4631", "last_heard_offset_sec": 2887, "long_name": "Red Pine", "next_hop": 197, "num": "0x69348f05", "position": {"altitude": 1216, "latitude": 33.204311, "location_source": "LOC_INTERNAL", "longitude": -106.604498, "time_offset_sec": 3187}, "public_key_hex": "89e55fe503cb45232d898096b28af07ed4bef11e92c21c928d4650d2c71501d9", "role": "CLIENT_HIDDEN", "short_name": "RO7M", "snr": 10.35, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4817, "long_name": "Burning Seal", "next_hop": 0, "num": "0x69998187", "position": {"altitude": 1293, "latitude": 32.392812, "location_source": "LOC_INTERNAL", "longitude": -106.344819, "time_offset_sec": 4831}, "public_key_hex": "1a4931a427d05e1bdaefa99f70dd2b2a220bfa2e8dec553847d9712b0c70b8d5", "role": "CLIENT", "short_name": "BSS4", "snr": -0.02, "status": null, "telemetry": {"air_util_tx": 0.239, "battery_level": 35, "channel_utilization": 6.97, "uptime_seconds": 204896, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 10076, "long_name": "Blue Squirrel", "next_hop": 212, "num": "0x6a7fcd9f", "position": {"altitude": 1505, "latitude": 32.932357, "location_source": "LOC_INTERNAL", "longitude": -107.01105, "time_offset_sec": 10153}, "public_key_hex": "1bdafc49306dbfb207f4ee164db19b4cb0c1135bb6b63fa03eb007d0dbb2da9b", "role": "ROUTER_LATE", "short_name": "🗻", "snr": 6.42, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1006.82, "iaq": 102, "relative_humidity": 81.51, "temperature": 13.71}, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 61, "long_name": "Sky Mesa", "next_hop": 0, "num": "0x6a9a8728", "position": {"altitude": 1638, "latitude": 33.079767, "location_source": "LOC_INTERNAL", "longitude": -106.860994, "time_offset_sec": 262}, "public_key_hex": "e39040ae3ea3066dbf14b8cf387e1b5d9fdb09bd077b6469c1d2a66b0d0219dc", "role": "TAK_TRACKER", "short_name": "SE1H", "snr": 5.92, "status": null, "telemetry": {"air_util_tx": 0.459, "battery_level": 101, "channel_utilization": 4.93, "uptime_seconds": 56564, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1016.22, "iaq": 62, "relative_humidity": 26.67, "temperature": 11.62}, "hops_away": 2, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 170, "long_name": "River Oak", "next_hop": 103, "num": "0x6b3d3aa3", "position": {"altitude": 1571, "latitude": 33.14479, "location_source": "LOC_INTERNAL", "longitude": -107.376011, "time_offset_sec": 200}, "public_key_hex": "d41101385568b35c0c170c5e2098fa3e11b88bde31402a021e9cb481f8933f21", "role": "CLIENT", "short_name": "RYK6", "snr": 8.07, "status": null, "telemetry": {"air_util_tx": 1.217, "battery_level": 32, "channel_utilization": 6.0, "uptime_seconds": 84071, "voltage": 3.588}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 50, "long_name": "River Hawk", "next_hop": 194, "num": "0x6bb1ca2b", "position": {"altitude": 1110, "latitude": 32.829006, "location_source": "LOC_INTERNAL", "longitude": -106.27801, "time_offset_sec": 295}, "public_key_hex": "2bba507bbb6153f5a25d4cb9d186ce98c3e93e9dfc5eccaa98125dd437c1b196", "role": "CLIENT", "short_name": "RYWL", "snr": 0.44, "status": null, "telemetry": {"air_util_tx": 0.346, "battery_level": 87, "channel_utilization": 10.21, "uptime_seconds": 421, "voltage": 4.083}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4785, "long_name": "Black Whale", "next_hop": 127, "num": "0x6bb4d10a", "position": {"altitude": 1237, "latitude": 33.509274, "location_source": "LOC_INTERNAL", "longitude": -106.277284, "time_offset_sec": 4880}, "public_key_hex": "7de62156e43c349c6a67dcc5a3afb96c4f088dcf59854561594eebb0280759a8", "role": "CLIENT", "short_name": "BFHZ", "snr": 1.01, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.162, "battery_level": 82, "channel_utilization": 10.28, "uptime_seconds": 20090, "voltage": 4.038}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 3, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 4229, "long_name": "Iron Tortoise", "next_hop": 0, "num": "0x6c43002f", "position": {"altitude": 1582, "latitude": 33.864042, "location_source": "LOC_INTERNAL", "longitude": -107.226095, "time_offset_sec": 4305}, "public_key_hex": "e56dfc70a9f0b54b0a21d867b7b9efeb1859fade19a8bc178becee94549646f7", "role": "CLIENT", "short_name": "I3HH", "snr": 8.44, "status": null, "telemetry": {"air_util_tx": 0.307, "battery_level": 24, "channel_utilization": 13.72, "uptime_seconds": 23775, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 298, "long_name": "Brave Doe", "next_hop": 182, "num": "0x6cc6ba99", "position": {"altitude": 1194, "latitude": 32.579495, "location_source": "LOC_INTERNAL", "longitude": -106.163977, "time_offset_sec": 572}, "public_key_hex": "01a7d519845227b7f66960aa742159c8258c155ab55aa628eab78726d39d4cc8", "role": "ROUTER", "short_name": "BMOQ", "snr": 4.42, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.106, "battery_level": 46, "channel_utilization": 5.93, "uptime_seconds": 15221, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 1556, "long_name": "Wandering Phoenix", "next_hop": 0, "num": "0x6d0c22c9", "position": {"altitude": 1703, "latitude": 32.702117, "location_source": "LOC_INTERNAL", "longitude": -106.689231, "time_offset_sec": 1797}, "public_key_hex": "4c41a2ca4984fd5632ca82f398454fcb1702528255edeefdf2da4a237fb3239f", "role": "CLIENT", "short_name": "🦋", "snr": 3.31, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 6253, "long_name": "Drowsy Raven KE7YJ", "next_hop": 0, "num": "0x6d39e1de", "position": {"altitude": 1292, "latitude": 32.404734, "location_source": "LOC_INTERNAL", "longitude": -107.157604, "time_offset_sec": 6487}, "public_key_hex": "fc6ee6e0f7c7960932b081f1b00c18fcb8e423f20c6f0e4fca30062c419bcaa1", "role": "SENSOR", "short_name": "DA49", "snr": 4.41, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.475, "battery_level": 49, "channel_utilization": 8.17, "uptime_seconds": 25790, "voltage": 3.741}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 12232, "long_name": "Fast Lynx", "next_hop": 48, "num": "0x6e0bad26", "position": null, "public_key_hex": "66930b442ce2a258b51c21dc63651a4ecc466b0ec25378ed28d7220beb586c4a", "role": "CLIENT", "short_name": "FNQ9", "snr": 3.75, "status": null, "telemetry": {"air_util_tx": 0.872, "battery_level": 33, "channel_utilization": 31.18, "uptime_seconds": 165544, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 8933, "long_name": "Green Falcon", "next_hop": 153, "num": "0x6f95c8de", "position": {"altitude": 1507, "latitude": 33.058043, "location_source": "LOC_INTERNAL", "longitude": -107.753248, "time_offset_sec": 9029}, "public_key_hex": "4a0fd161559916272677d7be03a4385deee355bca63ede5f2e89ac17b4544cc6", "role": "CLIENT", "short_name": "GG11", "snr": 2.21, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 3961, "long_name": "Desert Arroyo", "next_hop": 104, "num": "0x70672210", "position": {"altitude": 2110, "latitude": 33.806679, "location_source": "LOC_INTERNAL", "longitude": -108.245578, "time_offset_sec": 4111}, "public_key_hex": "5f76a5eb313191ea039acdb18eb537840b25569c0605981c8edbb7e2c484b50d", "role": "CLIENT_MUTE", "short_name": "DTNH", "snr": 7.35, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.148, "battery_level": 31, "channel_utilization": 12.08, "uptime_seconds": 202950, "voltage": 3.579}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1016.01, "iaq": 19, "relative_humidity": 46.48, "temperature": 25.33}, "hops_away": 3, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1898, "long_name": "Sleepy Moose", "next_hop": 135, "num": "0x70b79b40", "position": {"altitude": 1072, "latitude": 33.477423, "location_source": "LOC_INTERNAL", "longitude": -107.485895, "time_offset_sec": 1982}, "public_key_hex": "23f70e1dd5dd6191ac0d07dc43186a8bb3cae86918212db4a90cd65e4bec27fc", "role": "CLIENT", "short_name": "SEJI", "snr": 8.49, "status": null, "telemetry": {"air_util_tx": 0.426, "battery_level": 92, "channel_utilization": 5.77, "uptime_seconds": 34594, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.78, "iaq": 88, "relative_humidity": 78.78, "temperature": 17.98}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 81, "long_name": "Iron Dolphin", "next_hop": 0, "num": "0x71f35893", "position": {"altitude": 1195, "latitude": 33.158653, "location_source": "LOC_INTERNAL", "longitude": -106.688519, "time_offset_sec": 179}, "public_key_hex": "6f5d9f31615955d38cc92366808bd822a5754c33fcf5b22f2474952711b333a0", "role": "CLIENT_MUTE", "short_name": "I8FE", "snr": 8.95, "status": null, "telemetry": {"air_util_tx": 0.104, "battery_level": 37, "channel_utilization": 11.73, "uptime_seconds": 200720, "voltage": 3.633}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 3827, "long_name": "Drowsy Pony", "next_hop": 0, "num": "0x726683a9", "position": {"altitude": 1471, "latitude": 34.724546, "location_source": "LOC_INTERNAL", "longitude": -107.151964, "time_offset_sec": 4047}, "public_key_hex": "ae5af96e0c00ec919f651fa6da80144a28b8ec69ac33f5b0de3282f19c1c1e7a", "role": "CLIENT", "short_name": "DDUV", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 2.128, "battery_level": 10, "channel_utilization": 3.03, "uptime_seconds": 145294, "voltage": 3.39}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 1763, "long_name": "Steel Yucca", "next_hop": 0, "num": "0x73341f37", "position": {"altitude": 1716, "latitude": 33.486852, "location_source": "LOC_INTERNAL", "longitude": -108.240058, "time_offset_sec": 1912}, "public_key_hex": "f6cea0dd788cba03ac16d70b0bd99ae41018ff0be20d67497abb12d7327a41ba", "role": "CLIENT", "short_name": "SHCI", "snr": 3.0, "status": null, "telemetry": {"air_util_tx": 0.36, "battery_level": 38, "channel_utilization": 15.8, "uptime_seconds": 134943, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 139, "long_name": "Tiny Mole", "next_hop": 0, "num": "0x7348832d", "position": {"altitude": 1543, "latitude": 33.096162, "location_source": "LOC_INTERNAL", "longitude": -107.278135, "time_offset_sec": 297}, "public_key_hex": "1b0d2d4d35da0ced6fa31182ef5423eb536b7e3e53bdf5f98e58c44f54dc1530", "role": "TRACKER", "short_name": "TKFB", "snr": 11.46, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1017.26, "iaq": 66, "relative_humidity": 64.85, "temperature": 19.22}, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 780, "long_name": "Tall Crane", "next_hop": 0, "num": "0x738e72e8", "position": {"altitude": 1695, "latitude": 32.448476, "location_source": "LOC_INTERNAL", "longitude": -106.519086, "time_offset_sec": 947}, "public_key_hex": "c3019685ef33305cfc329b54740d4ada92b4a40fb535425a089f1e3d0b4d0053", "role": "TAK", "short_name": "TV6K", "snr": 0.74, "status": null, "telemetry": {"air_util_tx": 1.299, "battery_level": 38, "channel_utilization": 12.46, "uptime_seconds": 51020, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1033.06, "iaq": 7, "relative_humidity": 65.48, "temperature": 25.7}, "hops_away": 0, "hw_model": "HELTEC_HT62", "last_heard_offset_sec": 4821, "long_name": "Hidden Trout", "next_hop": 0, "num": "0x7491806d", "position": {"altitude": 1434, "latitude": 33.056575, "location_source": "LOC_INTERNAL", "longitude": -107.720741, "time_offset_sec": 5075}, "public_key_hex": "7c8691bfc6658d31d67acd477f09750318317ce021dee1599bb108ed25cdf0e3", "role": "CLIENT", "short_name": "🦉", "snr": 11.07, "status": null, "telemetry": {"air_util_tx": 1.117, "battery_level": 49, "channel_utilization": 14.84, "uptime_seconds": 11493, "voltage": 3.741}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 5, "environment": null, "hops_away": 1, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 834, "long_name": "Hidden Bronco", "next_hop": 97, "num": "0x749590b2", "position": null, "public_key_hex": "522be6ad7b0c0737d1080a2dd82f8972aba976c16596884ce7e2a8f58e4e0dc1", "role": "CLIENT", "short_name": "H2R2", "snr": 7.6, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 3119, "long_name": "Bright Bluff", "next_hop": 0, "num": "0x7629ea15", "position": {"altitude": 1657, "latitude": 32.845151, "location_source": "LOC_INTERNAL", "longitude": -107.295757, "time_offset_sec": 3314}, "public_key_hex": "e3fcb20d29c60cffaac484bc143758079667bd954af5dfb57e2f125a216379d8", "role": "CLIENT_BASE", "short_name": "B1SA", "snr": 6.42, "status": null, "telemetry": {"air_util_tx": 0.154, "battery_level": 28, "channel_utilization": 10.1, "uptime_seconds": 102430, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 749, "long_name": "Drowsy Mustang", "next_hop": 0, "num": "0x768eeba7", "position": {"altitude": 1345, "latitude": 32.71891, "location_source": "LOC_INTERNAL", "longitude": -106.516389, "time_offset_sec": 804}, "public_key_hex": "18ad33e52e42b79a698639dc4cd32e651f0a5fb5bd672f76381b0193fd9db55f", "role": "CLIENT", "short_name": "DV2L", "snr": 7.02, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.39, "iaq": 35, "relative_humidity": 55.44, "temperature": 29.33}, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 554, "long_name": "Drowsy Crow", "next_hop": 65, "num": "0x76dc5c41", "position": {"altitude": 844, "latitude": 33.041994, "location_source": "LOC_INTERNAL", "longitude": -107.408948, "time_offset_sec": 822}, "public_key_hex": "5304dd65402a959318fa4c834d9e03128c6c0ced8ac983d3666d33e9762c41e1", "role": "CLIENT", "short_name": "DMAJ", "snr": 6.41, "status": null, "telemetry": {"air_util_tx": 0.826, "battery_level": 64, "channel_utilization": 7.58, "uptime_seconds": 90535, "voltage": 3.876}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 6010, "long_name": "Iron Fox", "next_hop": 172, "num": "0x7723d1a0", "position": {"altitude": 1262, "latitude": 33.212945, "location_source": "LOC_INTERNAL", "longitude": -107.64994, "time_offset_sec": 6239}, "public_key_hex": "04ba223a1cd802a0bf24cf805578318d66509cae17d34ae5397e5b2b5837bb14", "role": "CLIENT", "short_name": "ITHM", "snr": 4.81, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 393, "long_name": "Quick Heron", "next_hop": 126, "num": "0x77fbcdf6", "position": {"altitude": 1617, "latitude": 33.585965, "location_source": "LOC_INTERNAL", "longitude": -106.371643, "time_offset_sec": 397}, "public_key_hex": "d0d19a00abd92f2bcf1592962a1313138225a83a8e87064cdb8114ecfba39ce3", "role": "CLIENT", "short_name": "🔥", "snr": 5.24, "status": null, "telemetry": {"air_util_tx": 0.794, "battery_level": 43, "channel_utilization": 21.17, "uptime_seconds": 58781, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 7221, "long_name": "Sleepy Colt", "next_hop": 0, "num": "0x790493d5", "position": {"altitude": 1295, "latitude": 33.4132, "location_source": "LOC_INTERNAL", "longitude": -106.63144, "time_offset_sec": 7510}, "public_key_hex": "", "role": "CLIENT", "short_name": "SCHP", "snr": 5.7, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 2110, "long_name": "Drifting Mesa KE0ZH", "next_hop": 49, "num": "0x79d393b0", "position": {"altitude": 1682, "latitude": 32.968426, "location_source": "LOC_INTERNAL", "longitude": -107.457182, "time_offset_sec": 2339}, "public_key_hex": "6e0ae215f2384f9122ece761c69ba9e1a42073a3470047274fcdd72a0248cad4", "role": "CLIENT", "short_name": "DA8J", "snr": -0.97, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1759, "long_name": "Red Hare KX2EP", "next_hop": 70, "num": "0x7ad3664f", "position": {"altitude": 2141, "latitude": 33.229175, "location_source": "LOC_INTERNAL", "longitude": -107.993856, "time_offset_sec": 2018}, "public_key_hex": "b2bc9c7db94f5f5f0f6753219c59fdf7ab659d649d1c63bc059983b5025d7d51", "role": "CLIENT", "short_name": "R1OR", "snr": 12.0, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.352, "battery_level": 27, "channel_utilization": 4.5, "uptime_seconds": 25899, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 8573, "long_name": "Floating Stag", "next_hop": 0, "num": "0x7bb70bb7", "position": {"altitude": 1272, "latitude": 33.168645, "location_source": "LOC_INTERNAL", "longitude": -107.684837, "time_offset_sec": 8756}, "public_key_hex": "b6b88b3e8a9e389b8c41dd7b6a26bf2180090f59d4f1d8e577297aebe4b34562", "role": "CLIENT", "short_name": "FZ3H", "snr": 4.97, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.936, "battery_level": 27, "channel_utilization": 7.47, "uptime_seconds": 76225, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 2148, "long_name": "Rough Bear", "next_hop": 241, "num": "0x7c224653", "position": {"altitude": 1438, "latitude": 34.000152, "location_source": "LOC_INTERNAL", "longitude": -106.625412, "time_offset_sec": 2152}, "public_key_hex": "331ea14519572dfc8abde388ba5d1c29810e4c1934d08f9bf43f49cc8505a54b", "role": "CLIENT", "short_name": "RNUC", "snr": 7.3, "status": {"status": "offline-soon"}, "telemetry": {"air_util_tx": 0.75, "battery_level": 38, "channel_utilization": 9.21, "uptime_seconds": 407434, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 432, "long_name": "Burning Bronco", "next_hop": 0, "num": "0x7ccb0a96", "position": {"altitude": 1365, "latitude": 33.576907, "location_source": "LOC_INTERNAL", "longitude": -107.563729, "time_offset_sec": 581}, "public_key_hex": "8cca6b93f5b2f58f481c81480578391486d8f0e5bda344fe5dc6a29c447c10e7", "role": "TRACKER", "short_name": "BGCJ", "snr": 6.05, "status": null, "telemetry": {"air_util_tx": 0.056, "battery_level": 93, "channel_utilization": 7.59, "uptime_seconds": 165200, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 1428, "long_name": "Shady Lion", "next_hop": 0, "num": "0x7ce782e2", "position": {"altitude": 1380, "latitude": 32.431543, "location_source": "LOC_INTERNAL", "longitude": -107.604725, "time_offset_sec": 1458}, "public_key_hex": "0daf87c9019c3bb9741fd05905f67682b647dc97733fa5078e27df5fa05b93c5", "role": "CLIENT", "short_name": "SWX9", "snr": 8.03, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 1804, "long_name": "Mountain Cedar", "next_hop": 0, "num": "0x7e0d0b69", "position": {"altitude": 1321, "latitude": 33.11493, "location_source": "LOC_INTERNAL", "longitude": -107.449334, "time_offset_sec": 2074}, "public_key_hex": "bf5042d4e85efc712dd1e4681db857f2b3484e1739e897f2a49792ee0f18c0bc", "role": "CLIENT", "short_name": "M7J1", "snr": 0.98, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.639, "battery_level": 81, "channel_utilization": 3.58, "uptime_seconds": 19425, "voltage": 4.029}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 882, "long_name": "Rough Hawk", "next_hop": 32, "num": "0x7e39ee69", "position": {"altitude": 1278, "latitude": 33.405852, "location_source": "LOC_INTERNAL", "longitude": -106.491004, "time_offset_sec": 943}, "public_key_hex": "521e42d928118b4cdafb25bc69f8974f03ffc910c991069ec726dbb02ec7778c", "role": "CLIENT", "short_name": "RHCK", "snr": 8.01, "status": null, "telemetry": {"air_util_tx": 0.672, "battery_level": 55, "channel_utilization": 29.49, "uptime_seconds": 105577, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 788, "long_name": "Sleepy Iguana", "next_hop": 0, "num": "0x7e49f748", "position": {"altitude": 1316, "latitude": 33.409957, "location_source": "LOC_INTERNAL", "longitude": -107.430865, "time_offset_sec": 891}, "public_key_hex": "174cdc5204bdf752801c9abd6aaf17a5b9602b484f9ba55e4ec9a8b2e01f6adb", "role": "CLIENT", "short_name": "SDJ2", "snr": -5.74, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 11666, "long_name": "Howling Squirrel", "next_hop": 0, "num": "0x7eb8bc45", "position": {"altitude": 1520, "latitude": 32.659288, "location_source": "LOC_INTERNAL", "longitude": -106.233421, "time_offset_sec": 11833}, "public_key_hex": "d804b803ddb8b9a6bb456b2965da9eef06a6bc1db080049c4e278ed4860f292e", "role": "CLIENT_MUTE", "short_name": "H9UH", "snr": 8.35, "status": null, "telemetry": {"air_util_tx": 1.476, "battery_level": 32, "channel_utilization": 18.0, "uptime_seconds": 145120, "voltage": 3.588}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": {"barometric_pressure": 992.43, "iaq": 26, "relative_humidity": 70.77, "temperature": 16.27}, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1291, "long_name": "Tiny Seal", "next_hop": 242, "num": "0x7f039d52", "position": {"altitude": 1391, "latitude": 33.312521, "location_source": "LOC_INTERNAL", "longitude": -107.046926, "time_offset_sec": 1350}, "public_key_hex": "51db617b30735e34a4d75ad1725ef1b002ec1a9a38ef32d4b51acf9e8c7b146f", "role": "CLIENT", "short_name": "T82O", "snr": 9.81, "status": null, "telemetry": {"air_util_tx": 1.794, "battery_level": 68, "channel_utilization": 14.75, "uptime_seconds": 68126, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 356, "long_name": "Green Tortoise", "next_hop": 0, "num": "0x7f95833e", "position": {"altitude": 1431, "latitude": 32.123231, "location_source": "LOC_INTERNAL", "longitude": -107.638817, "time_offset_sec": 405}, "public_key_hex": "c925ceda2f3e4bbbf987e13974a3d3fa532794dd994730a4b784d9fbebd09784", "role": "CLIENT", "short_name": "GK1S", "snr": 1.37, "status": null, "telemetry": {"air_util_tx": 1.209, "battery_level": 94, "channel_utilization": 16.27, "uptime_seconds": 136843, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 3472, "long_name": "Sleepy Aspen", "next_hop": 0, "num": "0x7f98caa8", "position": {"altitude": 1866, "latitude": 33.495868, "location_source": "LOC_INTERNAL", "longitude": -108.063052, "time_offset_sec": 3488}, "public_key_hex": "0b2dc28d0c3b5d165ec9b161a0943737d6e2d88b545d5da717182f8f152e9e43", "role": "CLIENT", "short_name": "SX11", "snr": 8.03, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.845, "battery_level": 25, "channel_utilization": 16.27, "uptime_seconds": 165688, "voltage": 3.525}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 4141, "long_name": "Happy Arroyo", "next_hop": 111, "num": "0x7fe40bcc", "position": {"altitude": 1958, "latitude": 32.443587, "location_source": "LOC_INTERNAL", "longitude": -106.084226, "time_offset_sec": 4285}, "public_key_hex": "", "role": "CLIENT", "short_name": "HZAG", "snr": 8.71, "status": null, "telemetry": null}
|
||||
+501
@@ -0,0 +1,501 @@
|
||||
{"_meta": {"centroid": [33.1284, -107.2528], "count": 500, "coverage": {"environment": 0.25, "position": 0.85, "status": 0.4, "telemetry": 0.7}, "generated_at_iso": "1970-08-23T11:55:12Z", "last_heard_max_sec": 604800, "last_heard_mean_sec": 3600, "my_node_num_excluded": null, "seed": 20260512, "spread_km": 60.0, "version": 25}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3197, "long_name": "Frosty Whale", "next_hop": 173, "num": "0x00b40906", "position": {"altitude": 1519, "latitude": 32.151666, "location_source": "LOC_INTERNAL", "longitude": -107.6939, "time_offset_sec": 3317}, "public_key_hex": "3be3f3db2ea4843edb428e1d50d5f7096daf217bd4259709c44b7c5f7031a1c8", "role": "CLIENT", "short_name": "FL7K", "snr": 5.16, "status": null, "telemetry": {"air_util_tx": 0.197, "battery_level": 71, "channel_utilization": 2.45, "uptime_seconds": 61699, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 0, "hw_model": "TBEAM_1_WATT", "last_heard_offset_sec": 17404, "long_name": "Drifting Yucca", "next_hop": 0, "num": "0x011e2fed", "position": {"altitude": 1597, "latitude": 33.929344, "location_source": "LOC_INTERNAL", "longitude": -107.48397, "time_offset_sec": 17610}, "public_key_hex": "5e83f0b1e902e0609e31daa68131955ca54601b6a401ef40f3dd3075578e1d26", "role": "CLIENT", "short_name": "DGNX", "snr": 6.01, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 998.92, "iaq": 1, "relative_humidity": 41.97, "temperature": 37.71}, "hops_away": 2, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 3471, "long_name": "Shady Pike", "next_hop": 173, "num": "0x0189ac5e", "position": {"altitude": 1726, "latitude": 33.013084, "location_source": "LOC_INTERNAL", "longitude": -107.196206, "time_offset_sec": 3607}, "public_key_hex": "d7941aebdd024277a3c21efc85e57993d8619bae9d020e48a10698a63999009d", "role": "TRACKER", "short_name": "SVCS", "snr": 6.77, "status": {"status": "offline-soon"}, "telemetry": {"air_util_tx": 0.18, "battery_level": 88, "channel_utilization": 15.72, "uptime_seconds": 104301, "voltage": 4.092}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.77, "iaq": 66, "relative_humidity": 60.03, "temperature": 16.12}, "hops_away": 3, "hw_model": "RAK4631", "last_heard_offset_sec": 1955, "long_name": "Lost Cactus", "next_hop": 142, "num": "0x01b84c91", "position": null, "public_key_hex": "928626e03eabe5f6236aab36347b40dd833ba7903cc16fbe4b3871900a4c81ee", "role": "CLIENT", "short_name": "🗻", "snr": 4.24, "status": null, "telemetry": {"air_util_tx": 0.866, "battery_level": 50, "channel_utilization": 31.43, "uptime_seconds": 58418, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.26, "iaq": 51, "relative_humidity": 60.87, "temperature": 25.07}, "hops_away": 1, "hw_model": "HELTEC_VISION_MASTER_T190", "last_heard_offset_sec": 63, "long_name": "Forest Shark", "next_hop": 172, "num": "0x01d003a2", "position": {"altitude": 1242, "latitude": 33.298407, "location_source": "LOC_INTERNAL", "longitude": -107.374181, "time_offset_sec": 348}, "public_key_hex": "c638bc498f50956b06bdadd5a155b5ffed4efd86d4edec9e2ef33e578b7dcb26", "role": "CLIENT", "short_name": "FTDK", "snr": 2.09, "status": null, "telemetry": {"air_util_tx": 0.196, "battery_level": 79, "channel_utilization": 19.63, "uptime_seconds": 25163, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 2213, "long_name": "Sneaky Viper", "next_hop": 119, "num": "0x021d5a39", "position": {"altitude": 1650, "latitude": 32.828212, "location_source": "LOC_INTERNAL", "longitude": -106.562505, "time_offset_sec": 2411}, "public_key_hex": "1e20cd9b42884ea7b5a13ed9474886d9841c9102408fefac5d426f14ab3c26e6", "role": "CLIENT", "short_name": "ST00", "snr": 8.44, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 2.146, "battery_level": 45, "channel_utilization": 33.11, "uptime_seconds": 4177, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 34, "long_name": "Solar Sage", "next_hop": 0, "num": "0x02486b0c", "position": {"altitude": 1566, "latitude": 33.25455, "location_source": "LOC_INTERNAL", "longitude": -108.276457, "time_offset_sec": 282}, "public_key_hex": "639100e236b166787ce42d49d5ab889f3d678bc11fa947250247f89c2659cdde", "role": "CLIENT", "short_name": "🦂", "snr": 2.71, "status": null, "telemetry": {"air_util_tx": 0.818, "battery_level": 15, "channel_utilization": 10.26, "uptime_seconds": 14197, "voltage": 3.435}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.55, "iaq": 93, "relative_humidity": 61.52, "temperature": 22.54}, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 5878, "long_name": "Whispering Cougar", "next_hop": 119, "num": "0x02554ce8", "position": {"altitude": 943, "latitude": 34.283555, "location_source": "LOC_INTERNAL", "longitude": -107.74003, "time_offset_sec": 5911}, "public_key_hex": "9cd4142858ee82e04b22ba3b33f7c032b40063f39bc5c5a0d3f492636b598d9e", "role": "CLIENT", "short_name": "WOT9", "snr": 6.19, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 1577, "long_name": "Iron Whale", "next_hop": 0, "num": "0x0430fd13", "position": {"altitude": 1620, "latitude": 32.657264, "location_source": "LOC_INTERNAL", "longitude": -107.204545, "time_offset_sec": 1871}, "public_key_hex": "c9a747534744cb9ee1bd2c5b253182175bee5e5f2a5284f9530889836593852e", "role": "CLIENT", "short_name": "I189", "snr": 5.64, "status": null, "telemetry": {"air_util_tx": 0.121, "battery_level": 50, "channel_utilization": 18.75, "uptime_seconds": 49495, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 4591, "long_name": "Sneaky Heron", "next_hop": 138, "num": "0x04b75b6d", "position": {"altitude": 1287, "latitude": 33.452725, "location_source": "LOC_INTERNAL", "longitude": -106.460239, "time_offset_sec": 4757}, "public_key_hex": "11afa777f0189e7bc7f93f065be232df98e21f8b9ecdde49d738201a2c4956fc", "role": "CLIENT", "short_name": "🌊", "snr": 5.38, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 174, "long_name": "Sunny Pony", "next_hop": 108, "num": "0x05267c0d", "position": {"altitude": 1003, "latitude": 33.195449, "location_source": "LOC_INTERNAL", "longitude": -106.986062, "time_offset_sec": 439}, "public_key_hex": "7a195aad3491a11143e61ab8201b4ce1a8552c37985fb4a7d4b885c5cd5323be", "role": "CLIENT", "short_name": "SRBM", "snr": 7.29, "status": null, "telemetry": {"air_util_tx": 0.668, "battery_level": 23, "channel_utilization": 9.39, "uptime_seconds": 117938, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 5294, "long_name": "Lunar Gecko", "next_hop": 157, "num": "0x054f2796", "position": null, "public_key_hex": "201aff9fd3405ff131e325f089a44ccd8ce67184dae2015b4a10e142a1c4550f", "role": "CLIENT", "short_name": "LESX", "snr": 7.12, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.598, "battery_level": 14, "channel_utilization": 16.05, "uptime_seconds": 475034, "voltage": 3.426}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 5, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 1380, "long_name": "Howling Eagle", "next_hop": 35, "num": "0x058f496a", "position": {"altitude": 1347, "latitude": 33.202974, "location_source": "LOC_INTERNAL", "longitude": -107.351541, "time_offset_sec": 1541}, "public_key_hex": "11955ea65b430f745ead784933c3ac04798aa1f9f094fe49cc7fb3c91e50f08e", "role": "CLIENT", "short_name": "HZDF", "snr": 9.38, "status": null, "telemetry": {"air_util_tx": 1.512, "battery_level": 55, "channel_utilization": 8.96, "uptime_seconds": 88941, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 7137, "long_name": "Sunny Shark", "next_hop": 21, "num": "0x05b218d2", "position": {"altitude": 1536, "latitude": 33.202605, "location_source": "LOC_INTERNAL", "longitude": -107.383081, "time_offset_sec": 7193}, "public_key_hex": "99784925060b03ef3230387e5b124d347a7308525c830a113a654df744c0e836", "role": "CLIENT", "short_name": "🦋", "snr": 6.96, "status": null, "telemetry": {"air_util_tx": 0.001, "battery_level": 73, "channel_utilization": 6.35, "uptime_seconds": 95615, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1014.17, "iaq": 34, "relative_humidity": 36.77, "temperature": 27.02}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4830, "long_name": "Found Eagle", "next_hop": 0, "num": "0x05d3737a", "position": {"altitude": 1757, "latitude": 34.578404, "location_source": "LOC_INTERNAL", "longitude": -106.975336, "time_offset_sec": 4918}, "public_key_hex": "2ea628e36802c997cca7426463e167fa75986add373452a27cfa0210242e5268", "role": "CLIENT", "short_name": "FXWZ", "snr": 3.57, "status": null, "telemetry": {"air_util_tx": 0.362, "battery_level": 70, "channel_utilization": 1.49, "uptime_seconds": 12694, "voltage": 3.93}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 9839, "long_name": "Wandering Marmot", "next_hop": 0, "num": "0x05fc1a3f", "position": {"altitude": 1129, "latitude": 32.659091, "location_source": "LOC_INTERNAL", "longitude": -107.505247, "time_offset_sec": 9923}, "public_key_hex": "a337f9ab3ea8333215289724cbad941ae7621da8b8d6e3d5ab57e71524e5709a", "role": "CLIENT", "short_name": "W250", "snr": 2.53, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.586, "battery_level": 56, "channel_utilization": 12.14, "uptime_seconds": 42260, "voltage": 3.804}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.99, "iaq": 0, "relative_humidity": 71.41, "temperature": 15.52}, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 408, "long_name": "New Gecko", "next_hop": 61, "num": "0x066d6e76", "position": {"altitude": 1747, "latitude": 32.773832, "location_source": "LOC_INTERNAL", "longitude": -107.220236, "time_offset_sec": 698}, "public_key_hex": "", "role": "CLIENT", "short_name": "N2ZX", "snr": 0.58, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.313, "battery_level": 92, "channel_utilization": 3.14, "uptime_seconds": 15258, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 2782, "long_name": "Fast Yucca KX5XX", "next_hop": 141, "num": "0x06c7ea9f", "position": {"altitude": 1472, "latitude": 33.093005, "location_source": "LOC_INTERNAL", "longitude": -107.511463, "time_offset_sec": 2851}, "public_key_hex": "12c1f6217e378cb6f61533272c9641fd6eaf282c3180a3a71b1c08b1a89ffbf0", "role": "CLIENT", "short_name": "FLXR", "snr": 7.65, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 997.01, "iaq": 35, "relative_humidity": 61.92, "temperature": 24.34}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 692, "long_name": "Found Ridge", "next_hop": 0, "num": "0x06cab439", "position": {"altitude": 933, "latitude": 33.474999, "location_source": "LOC_INTERNAL", "longitude": -107.067398, "time_offset_sec": 845}, "public_key_hex": "", "role": "CLIENT", "short_name": "FDF7", "snr": -0.59, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "RAK4631", "last_heard_offset_sec": 638, "long_name": "Copper Bass", "next_hop": 154, "num": "0x06dda523", "position": {"altitude": 1395, "latitude": 33.438741, "location_source": "LOC_INTERNAL", "longitude": -108.072906, "time_offset_sec": 873}, "public_key_hex": "", "role": "CLIENT", "short_name": "CVYO", "snr": 11.55, "status": null, "telemetry": {"air_util_tx": 1.484, "battery_level": 13, "channel_utilization": 6.1, "uptime_seconds": 98410, "voltage": 3.417}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 112, "long_name": "Green Gecko", "next_hop": 78, "num": "0x072e0237", "position": {"altitude": 853, "latitude": 34.012327, "location_source": "LOC_INTERNAL", "longitude": -107.188724, "time_offset_sec": 262}, "public_key_hex": "700d510170e7fe912da2da8cbf930e43c6710829c8faa89af423910945e6265e", "role": "CLIENT", "short_name": "GGC5", "snr": 10.05, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 1245, "long_name": "Brave Bear", "next_hop": 0, "num": "0x0783e8d1", "position": {"altitude": 1537, "latitude": 33.749784, "location_source": "LOC_INTERNAL", "longitude": -107.841257, "time_offset_sec": 1414}, "public_key_hex": "178879e3e697a4c3c52ec66fbe8dd216cfa64ade0135a634cc8687ea3ade7320", "role": "TRACKER", "short_name": "BFRT", "snr": 3.14, "status": null, "telemetry": {"air_util_tx": 1.759, "battery_level": 30, "channel_utilization": 14.5, "uptime_seconds": 78399, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 2045, "long_name": "Stone Bear", "next_hop": 0, "num": "0x078cfba6", "position": null, "public_key_hex": "467443a858386925299334fbf59f7754583f47a8731763404a98c9b30a74d90d", "role": "CLIENT", "short_name": "SORT", "snr": 6.29, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.588, "battery_level": 79, "channel_utilization": 6.6, "uptime_seconds": 53720, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 5613, "long_name": "Frosty Elk", "next_hop": 0, "num": "0x07948431", "position": {"altitude": 1186, "latitude": 32.122731, "location_source": "LOC_INTERNAL", "longitude": -106.693574, "time_offset_sec": 5803}, "public_key_hex": "7133e1eda8cf2b46701bb55a1cdc136f4dd4efa26c296f92188c770b6f6b11c5", "role": "CLIENT", "short_name": "F4ZQ", "snr": 6.09, "status": null, "telemetry": {"air_util_tx": 0.318, "battery_level": 101, "channel_utilization": 8.56, "uptime_seconds": 76257, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 4207, "long_name": "Frosty Elk", "next_hop": 0, "num": "0x079d9ed4", "position": {"altitude": 1431, "latitude": 33.80937, "location_source": "LOC_INTERNAL", "longitude": -107.229029, "time_offset_sec": 4335}, "public_key_hex": "8c7857ee2dc180990083b56b1114163bacad59fd8f324a7dd6a3c52ce13880a9", "role": "TRACKER", "short_name": "FNZ9", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.514, "battery_level": 87, "channel_utilization": 7.82, "uptime_seconds": 5846, "voltage": 4.083}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 102, "long_name": "Wandering Hawk", "next_hop": 0, "num": "0x0839477d", "position": {"altitude": 1244, "latitude": 33.568907, "location_source": "LOC_INTERNAL", "longitude": -107.90779, "time_offset_sec": 292}, "public_key_hex": "4e12b947441a7467ea24d8ec904d0525483c6f4a5f53b73fafe1e1a1bb55414d", "role": "CLIENT", "short_name": "WD7D", "snr": 5.47, "status": null, "telemetry": {"air_util_tx": 0.391, "battery_level": 94, "channel_utilization": 4.9, "uptime_seconds": 106184, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 3873, "long_name": "Sneaky Elk", "next_hop": 0, "num": "0x087eacd5", "position": {"altitude": 1012, "latitude": 32.148225, "location_source": "LOC_INTERNAL", "longitude": -106.891906, "time_offset_sec": 3959}, "public_key_hex": "5dd12b3b478e5b2e86998d3785934c71d80505a4d650d72fc8f1a2f4b83c61f9", "role": "CLIENT_MUTE", "short_name": "SS28", "snr": 7.13, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 402, "long_name": "Stone Lion", "next_hop": 138, "num": "0x08b8aa97", "position": {"altitude": 1643, "latitude": 33.526974, "location_source": "LOC_INTERNAL", "longitude": -107.248283, "time_offset_sec": 581}, "public_key_hex": "c7a7d65cd7befa04cc4cdd2a7923f43797fc07eeb7816dd0a71aa4376daa9a8a", "role": "ROUTER", "short_name": "S461", "snr": 5.4, "status": {"status": "offline-soon"}, "telemetry": {"air_util_tx": 0.831, "battery_level": 70, "channel_utilization": 7.46, "uptime_seconds": 510, "voltage": 3.93}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "NOMADSTAR_METEOR_PRO", "last_heard_offset_sec": 585, "long_name": "Dusk Eagle", "next_hop": 53, "num": "0x0942c7fa", "position": {"altitude": 1276, "latitude": 33.564835, "location_source": "LOC_INTERNAL", "longitude": -107.990751, "time_offset_sec": 635}, "public_key_hex": "33d6722868eb0b3446af8787b9d79446a8a3af0a85a50f4e94d7bfcf2567872c", "role": "CLIENT", "short_name": "DW3D", "snr": 1.03, "status": null, "telemetry": {"air_util_tx": 1.126, "battery_level": 84, "channel_utilization": 11.33, "uptime_seconds": 67579, "voltage": 4.056}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 5824, "long_name": "Sleepy Iguana", "next_hop": 181, "num": "0x09681313", "position": {"altitude": 1536, "latitude": 33.940617, "location_source": "LOC_INTERNAL", "longitude": -107.400953, "time_offset_sec": 5935}, "public_key_hex": "7e768501c7c0b27a253cad9dfe04de9ea9f0fb7178cc52e749613e7a2448aacc", "role": "CLIENT", "short_name": "SPCZ", "snr": 8.54, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 9057, "long_name": "Drifting Bear", "next_hop": 0, "num": "0x099cef2d", "position": {"altitude": 1154, "latitude": 32.927688, "location_source": "LOC_INTERNAL", "longitude": -107.983437, "time_offset_sec": 9311}, "public_key_hex": "df026c53580a0014d8d0b0acf05b0a7699129b38ef54b62a6ad6b459f80cde4c", "role": "CLIENT_BASE", "short_name": "DUM8", "snr": 4.26, "status": null, "telemetry": {"air_util_tx": 0.22, "battery_level": 10, "channel_utilization": 20.01, "uptime_seconds": 4610, "voltage": 3.39}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 5657, "long_name": "Steel Eagle KE0EN", "next_hop": 218, "num": "0x09c2d2c5", "position": {"altitude": 1432, "latitude": 34.093196, "location_source": "LOC_INTERNAL", "longitude": -107.180771, "time_offset_sec": 5761}, "public_key_hex": "4a013f9630e13615cba93620efa713f6eb856dd9a0889ebf6d538a3fc1d8905b", "role": "CLIENT", "short_name": "S7YX", "snr": 3.85, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.252, "battery_level": 83, "channel_utilization": 8.77, "uptime_seconds": 22235, "voltage": 4.047}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 2759, "long_name": "Lunar Crow", "next_hop": 0, "num": "0x09f3a0c9", "position": {"altitude": 1513, "latitude": 32.201649, "location_source": "LOC_INTERNAL", "longitude": -107.870751, "time_offset_sec": 2857}, "public_key_hex": "ffd5dd9578ff4a710f98bd3726af95ddd74c7ed48f6bc326dd8c82fea467c688", "role": "CLIENT", "short_name": "L71T", "snr": 5.65, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.45, "battery_level": 62, "channel_utilization": 12.01, "uptime_seconds": 13126, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 365, "long_name": "Sneaky Yucca", "next_hop": 0, "num": "0x0a227668", "position": {"altitude": 1357, "latitude": 33.858842, "location_source": "LOC_INTERNAL", "longitude": -107.968186, "time_offset_sec": 458}, "public_key_hex": "a82a9214cba439128a6c66746ef3657b91d20818ace487f368054430497cd801", "role": "CLIENT", "short_name": "STUO", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.185, "battery_level": 93, "channel_utilization": 1.96, "uptime_seconds": 304258, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T096", "last_heard_offset_sec": 59, "long_name": "Lone Hawk", "next_hop": 61, "num": "0x0a26c767", "position": {"altitude": 1736, "latitude": 33.066119, "location_source": "LOC_INTERNAL", "longitude": -107.365383, "time_offset_sec": 299}, "public_key_hex": "1325d09f2302a1a82f853963373de3719162f3029523553cacd9a8c8ff924e58", "role": "CLIENT", "short_name": "LUNM", "snr": 5.04, "status": null, "telemetry": {"air_util_tx": 0.599, "battery_level": 26, "channel_utilization": 18.17, "uptime_seconds": 39452, "voltage": 3.534}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 5198, "long_name": "Slow Whale", "next_hop": 0, "num": "0x0a4e9ac7", "position": {"altitude": 1324, "latitude": 33.512009, "location_source": "LOC_INTERNAL", "longitude": -107.240115, "time_offset_sec": 5468}, "public_key_hex": "9f5080010b809a79bf8326674391a223a0ab14ad189d57f1659e8ba88a9910b0", "role": "CLIENT", "short_name": "SVG3", "snr": 7.72, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 6777, "long_name": "Soft Moose", "next_hop": 0, "num": "0x0a5b2072", "position": {"altitude": 1405, "latitude": 33.166582, "location_source": "LOC_INTERNAL", "longitude": -106.571049, "time_offset_sec": 7036}, "public_key_hex": "5dabc625e1757b630efd1bbe1dd2f31089b0e1dfc3929d72e5c45419748b997b", "role": "CLIENT", "short_name": "SDXX", "snr": 9.36, "status": null, "telemetry": {"air_util_tx": 0.91, "battery_level": 19, "channel_utilization": 4.61, "uptime_seconds": 51974, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 6114, "long_name": "Blue Iguana", "next_hop": 140, "num": "0x0ae73f47", "position": {"altitude": 1565, "latitude": 32.903508, "location_source": "LOC_INTERNAL", "longitude": -107.918895, "time_offset_sec": 6394}, "public_key_hex": "4824b9ca37d9e316d315bb42cb2dedb6d75125da6d11a12a3227a1f60b145f11", "role": "CLIENT", "short_name": "BSL5", "snr": 6.76, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 293, "long_name": "Misty Wolf", "next_hop": 23, "num": "0x0b094c92", "position": {"altitude": 1016, "latitude": 33.315313, "location_source": "LOC_INTERNAL", "longitude": -107.122867, "time_offset_sec": 406}, "public_key_hex": "1d08505017b6456355c7670bd0ce8c6701ea43b67ca338140306ea6afb5a0df7", "role": "CLIENT", "short_name": "MJK1", "snr": 8.08, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 9125, "long_name": "Storm Phoenix", "next_hop": 224, "num": "0x0b24a78e", "position": null, "public_key_hex": "", "role": "ROUTER_LATE", "short_name": "SIFA", "snr": 4.14, "status": null, "telemetry": {"air_util_tx": 0.06, "battery_level": 20, "channel_utilization": 8.81, "uptime_seconds": 94121, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1016, "long_name": "Giant Doe", "next_hop": 121, "num": "0x0b6940c2", "position": {"altitude": 1321, "latitude": 33.478159, "location_source": "LOC_INTERNAL", "longitude": -107.629302, "time_offset_sec": 1142}, "public_key_hex": "9b2dcb301b031c955820f4ccd600248598d76e606df1e1654374cfe95f426d1c", "role": "SENSOR", "short_name": "GM0W", "snr": 3.34, "status": {"status": "weak-signal"}, "telemetry": {"air_util_tx": 1.146, "battery_level": 57, "channel_utilization": 25.09, "uptime_seconds": 159619, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1008.0, "iaq": 68, "relative_humidity": 82.96, "temperature": 28.08}, "hops_away": 1, "hw_model": "RAK3312", "last_heard_offset_sec": 8146, "long_name": "Mountain Whale", "next_hop": 18, "num": "0x0bd266b2", "position": null, "public_key_hex": "88d5229035bc6d41e800ce92858bb0a641626aae0415ab68ebce9a716b4608c1", "role": "ROUTER", "short_name": "MNX3", "snr": 3.2, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.49, "battery_level": 16, "channel_utilization": 14.79, "uptime_seconds": 9989, "voltage": 3.444}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 7991, "long_name": "Loud Squirrel", "next_hop": 102, "num": "0x0c008042", "position": {"altitude": 1286, "latitude": 33.407558, "location_source": "LOC_INTERNAL", "longitude": -107.246507, "time_offset_sec": 8012}, "public_key_hex": "dc12d958c362cba44f5e51cd494b2d5146a2bc27a49f00bbe52568b48b12c0b0", "role": "TRACKER", "short_name": "LK99", "snr": 4.49, "status": null, "telemetry": {"air_util_tx": 1.485, "battery_level": 73, "channel_utilization": 28.13, "uptime_seconds": 4958, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 920, "long_name": "Shady Lynx", "next_hop": 219, "num": "0x0cf9d526", "position": {"altitude": 1597, "latitude": 32.392711, "location_source": "LOC_INTERNAL", "longitude": -107.605544, "time_offset_sec": 1003}, "public_key_hex": "", "role": "CLIENT", "short_name": "S8XM", "snr": 7.37, "status": null, "telemetry": {"air_util_tx": 0.901, "battery_level": 40, "channel_utilization": 7.94, "uptime_seconds": 66704, "voltage": 3.66}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 3355, "long_name": "Sharp Mamba", "next_hop": 0, "num": "0x0cfb3f42", "position": {"altitude": 1362, "latitude": 32.872047, "location_source": "LOC_INTERNAL", "longitude": -106.062756, "time_offset_sec": 3597}, "public_key_hex": "d2466d6c7a0b2fcdbc3e9e031fb15e9823d49974a8827414a95864e60704b09b", "role": "CLIENT", "short_name": "SHHU", "snr": 6.36, "status": null, "telemetry": {"air_util_tx": 0.318, "battery_level": 12, "channel_utilization": 14.33, "uptime_seconds": 240511, "voltage": 3.408}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 4138, "long_name": "Soft Seal", "next_hop": 0, "num": "0x0d1114f7", "position": {"altitude": 1159, "latitude": 32.862239, "location_source": "LOC_INTERNAL", "longitude": -107.364042, "time_offset_sec": 4194}, "public_key_hex": "0904dcbd8806913637e185a58f0a9702fe1dfd7eb1a299752031d53e87f3472e", "role": "CLIENT", "short_name": "🦉", "snr": 1.54, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1014.19, "iaq": 39, "relative_humidity": 31.48, "temperature": 22.47}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 5500, "long_name": "Rough Mole AB6UD", "next_hop": 0, "num": "0x0d1134f2", "position": {"altitude": 1246, "latitude": 32.923274, "location_source": "LOC_INTERNAL", "longitude": -107.365595, "time_offset_sec": 5501}, "public_key_hex": "e6f5ad1ee2cca9af4e6964dd650337302bd9f53d123ec3873458bbafceb4e51d", "role": "CLIENT", "short_name": "R6Q0", "snr": 0.35, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.109, "battery_level": 24, "channel_utilization": 9.18, "uptime_seconds": 305791, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 948, "long_name": "Sneaky Gecko", "next_hop": 0, "num": "0x0d6592a2", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "SM2S", "snr": 0.81, "status": null, "telemetry": {"air_util_tx": 0.139, "battery_level": 92, "channel_utilization": 15.72, "uptime_seconds": 114522, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 366, "long_name": "Loud Mamba", "next_hop": 0, "num": "0x0d968387", "position": {"altitude": 1407, "latitude": 32.519848, "location_source": "LOC_INTERNAL", "longitude": -107.380676, "time_offset_sec": 417}, "public_key_hex": "", "role": "CLIENT", "short_name": "LC5U", "snr": 4.14, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.571, "battery_level": 100, "channel_utilization": 19.43, "uptime_seconds": 80280, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1008.03, "iaq": 27, "relative_humidity": 63.2, "temperature": 30.0}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 254, "long_name": "Canyon Doe", "next_hop": 0, "num": "0x0da58b20", "position": {"altitude": 1054, "latitude": 33.143337, "location_source": "LOC_INTERNAL", "longitude": -107.818269, "time_offset_sec": 289}, "public_key_hex": "4114b0445f6a57bc0faf565f772acb9571fbbd5d1a7df7755692413b3eb71ce4", "role": "TRACKER", "short_name": "CBZJ", "snr": 10.44, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1007.83, "iaq": 73, "relative_humidity": 47.03, "temperature": 16.81}, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1829, "long_name": "White Pine", "next_hop": 0, "num": "0x0e2eefbc", "position": {"altitude": 1436, "latitude": 33.443858, "location_source": "LOC_INTERNAL", "longitude": -108.82553, "time_offset_sec": 1883}, "public_key_hex": "c7e997350b80f8ac4c89a2e48b6afc0af224c50ed253842f31e4c2e82d7e6dcb", "role": "CLIENT", "short_name": "WSBP", "snr": 7.32, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.15, "iaq": 65, "relative_humidity": 58.13, "temperature": 21.44}, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2998, "long_name": "Black Lion", "next_hop": 25, "num": "0x0e40250b", "position": {"altitude": 1280, "latitude": 33.201427, "location_source": "LOC_INTERNAL", "longitude": -106.820707, "time_offset_sec": 3195}, "public_key_hex": "0c9a4734918a056d180a803e6ca48645062b6b44249dc9ccef860793be97f881", "role": "CLIENT", "short_name": "B0WQ", "snr": 9.36, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.527, "battery_level": 71, "channel_utilization": 16.14, "uptime_seconds": 155116, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 9399, "long_name": "Drowsy Mustang", "next_hop": 214, "num": "0x0ea265d7", "position": {"altitude": 1393, "latitude": 32.898121, "location_source": "LOC_INTERNAL", "longitude": -107.524646, "time_offset_sec": 9491}, "public_key_hex": "", "role": "CLIENT_MUTE", "short_name": "D8ME", "snr": 5.32, "status": {"status": "running"}, "telemetry": {"air_util_tx": 1.48, "battery_level": 65, "channel_utilization": 10.72, "uptime_seconds": 15078, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 360, "long_name": "Iron Crow", "next_hop": 0, "num": "0x0ef13332", "position": {"altitude": 1469, "latitude": 32.917393, "location_source": "LOC_INTERNAL", "longitude": -106.817302, "time_offset_sec": 485}, "public_key_hex": "b43b345c37ed2ac879d0c1449f7f4a5c10b3cf8cdf5135b2b37014e44b8dbaa6", "role": "CLIENT", "short_name": "I5SF", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.214, "battery_level": 35, "channel_utilization": 29.6, "uptime_seconds": 51980, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 4082, "long_name": "Smooth Doe", "next_hop": 0, "num": "0x0f446c80", "position": {"altitude": 1122, "latitude": 31.767256, "location_source": "LOC_INTERNAL", "longitude": -106.825891, "time_offset_sec": 4255}, "public_key_hex": "8e0f9b2b699d27476d8b5e5c9e1a5735359eb7b192932dbc107024f5e83b69e1", "role": "CLIENT_MUTE", "short_name": "ST7O", "snr": 3.14, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "WISMESH_TAG", "last_heard_offset_sec": 390, "long_name": "Canyon Phoenix", "next_hop": 165, "num": "0x0f630f75", "position": {"altitude": 1089, "latitude": 33.100863, "location_source": "LOC_INTERNAL", "longitude": -107.714667, "time_offset_sec": 404}, "public_key_hex": "d0fc8f0d06868c85f6e4b9d1efd2d3ed2e8fcb2ca39d0b344bf9bf3a26e7b259", "role": "CLIENT", "short_name": "C20S", "snr": 4.37, "status": null, "telemetry": {"air_util_tx": 0.57, "battery_level": 50, "channel_utilization": 4.35, "uptime_seconds": 237778, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 17446, "long_name": "Lost Sage", "next_hop": 0, "num": "0x0f78f772", "position": {"altitude": 1565, "latitude": 32.065953, "location_source": "LOC_INTERNAL", "longitude": -108.155905, "time_offset_sec": 17681}, "public_key_hex": "4ea922a38c62d176fa256ae06bd26f606d273a33768a338fad8b14427e1b1ffa", "role": "CLIENT", "short_name": "LQ9J", "snr": 7.09, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.13, "iaq": 81, "relative_humidity": 75.96, "temperature": 25.99}, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 1013, "long_name": "Blue Fox", "next_hop": 229, "num": "0x0f8e526f", "position": {"altitude": 1412, "latitude": 32.720316, "location_source": "LOC_INTERNAL", "longitude": -107.264431, "time_offset_sec": 1257}, "public_key_hex": "327554b2882cddbbdd82666aeb0b35b2037f63ca79e616fbbb53668ccbbe8c2b", "role": "ROUTER", "short_name": "B3HK", "snr": 5.69, "status": null, "telemetry": {"air_util_tx": 1.508, "battery_level": 25, "channel_utilization": 21.97, "uptime_seconds": 106202, "voltage": 3.525}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 2928, "long_name": "Sharp Aspen", "next_hop": 0, "num": "0x0fde1eec", "position": {"altitude": 1449, "latitude": 33.530237, "location_source": "LOC_INTERNAL", "longitude": -107.771161, "time_offset_sec": 3067}, "public_key_hex": "98d7f97f098e04fd3d92350ff9907094ff5f48ef20523280e064d4fdaacd8e16", "role": "CLIENT", "short_name": "SCMN", "snr": 2.42, "status": null, "telemetry": {"air_util_tx": 1.011, "battery_level": 88, "channel_utilization": 9.51, "uptime_seconds": 27684, "voltage": 4.092}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1014.62, "iaq": 72, "relative_humidity": 76.4, "temperature": 19.6}, "hops_away": 0, "hw_model": "NOMADSTAR_METEOR_PRO", "last_heard_offset_sec": 8518, "long_name": "Copper Pony", "next_hop": 0, "num": "0x1018994e", "position": {"altitude": 1570, "latitude": 33.624451, "location_source": "LOC_INTERNAL", "longitude": -107.627157, "time_offset_sec": 8580}, "public_key_hex": "", "role": "ROUTER", "short_name": "CNKI", "snr": 2.18, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 30, "long_name": "Floating Falcon", "next_hop": 0, "num": "0x101b6e63", "position": {"altitude": 1221, "latitude": 33.737624, "location_source": "LOC_INTERNAL", "longitude": -108.391569, "time_offset_sec": 72}, "public_key_hex": "e165537fda9c4c72a40c6c4859db82bc37daff03108de62043fadd3b40f459fe", "role": "SENSOR", "short_name": "FH7Y", "snr": 4.88, "status": null, "telemetry": {"air_util_tx": 0.321, "battery_level": 23, "channel_utilization": 15.8, "uptime_seconds": 186477, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 2121, "long_name": "Copper Otter", "next_hop": 0, "num": "0x10205b63", "position": {"altitude": 1284, "latitude": 34.863075, "location_source": "LOC_INTERNAL", "longitude": -106.550002, "time_offset_sec": 2382}, "public_key_hex": "763052e1aa56f5a160c1d63923414ea597bd0031d639017e64f296dbde851a75", "role": "CLIENT", "short_name": "🌵", "snr": -0.94, "status": null, "telemetry": {"air_util_tx": 1.534, "battery_level": 24, "channel_utilization": 1.95, "uptime_seconds": 6014, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 7778, "long_name": "Black Hawk", "next_hop": 0, "num": "0x10227727", "position": {"altitude": 1462, "latitude": 32.90932, "location_source": "LOC_INTERNAL", "longitude": -107.285806, "time_offset_sec": 7995}, "public_key_hex": "ad0fe604a7094f2f89a7fc987fedd6017b03114ed474791054a31896f9542612", "role": "CLIENT", "short_name": "BB18", "snr": 2.25, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.78, "battery_level": 101, "channel_utilization": 12.64, "uptime_seconds": 1932, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1_EINK", "last_heard_offset_sec": 786, "long_name": "Old Cactus N56DI", "next_hop": 0, "num": "0x10b40519", "position": {"altitude": 1504, "latitude": 33.104311, "location_source": "LOC_INTERNAL", "longitude": -107.434206, "time_offset_sec": 1004}, "public_key_hex": "533b17828a9ede6fa74f3edb846843e8446717435b3a1dc27269e11009bcef6a", "role": "TAK_TRACKER", "short_name": "OCQ8", "snr": 0.49, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.125, "battery_level": 74, "channel_utilization": 17.91, "uptime_seconds": 124718, "voltage": 3.966}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 7537, "long_name": "Green Cedar", "next_hop": 0, "num": "0x10de9aae", "position": {"altitude": 723, "latitude": 33.326766, "location_source": "LOC_INTERNAL", "longitude": -108.003463, "time_offset_sec": 7750}, "public_key_hex": "e2d62f46d725f2949002b1684d3426c529ad503310aa76c51280ec5c806d14b3", "role": "ROUTER", "short_name": "GEJ1", "snr": 2.28, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2688, "long_name": "Black Sage", "next_hop": 0, "num": "0x11cf9223", "position": {"altitude": 1398, "latitude": 32.813263, "location_source": "LOC_INTERNAL", "longitude": -108.097167, "time_offset_sec": 2947}, "public_key_hex": "f2eb8a2e7daa401077ee6974e307733d39f767072e1c5bed2fd8a91c78170e31", "role": "CLIENT", "short_name": "BQUV", "snr": 12.0, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.672, "battery_level": 73, "channel_utilization": 3.24, "uptime_seconds": 243864, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1004.57, "iaq": 30, "relative_humidity": 35.69, "temperature": 12.18}, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 1969, "long_name": "Frosty Bison", "next_hop": 0, "num": "0x11d59092", "position": null, "public_key_hex": "ecb4eeafca0fa40bed7355c5fd9aa3b1cf3b50a590053b9224b2a3d751320538", "role": "CLIENT", "short_name": "FHZ2", "snr": 4.53, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.48, "battery_level": 42, "channel_utilization": 15.48, "uptime_seconds": 88977, "voltage": 3.678}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 5949, "long_name": "Wild Mustang", "next_hop": 0, "num": "0x11d7f212", "position": {"altitude": 1024, "latitude": 33.272749, "location_source": "LOC_INTERNAL", "longitude": -107.297537, "time_offset_sec": 6240}, "public_key_hex": "f54d9988a8cd53c53ee267318e0cae68e4093e5e2e6c0ea6b958e62dcf54a6a3", "role": "TRACKER", "short_name": "W2A5", "snr": 1.3, "status": {"status": "running"}, "telemetry": {"air_util_tx": 1.042, "battery_level": 39, "channel_utilization": 14.9, "uptime_seconds": 17083, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 3414, "long_name": "Storm Crane", "next_hop": 0, "num": "0x11fcd2c7", "position": null, "public_key_hex": "cb700ea5209ced2ce7fb56e78498359db8d0b212bf344767233e6a16d4ad8c54", "role": "CLIENT", "short_name": "S5DG", "snr": -1.79, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1017.06, "iaq": 56, "relative_humidity": 64.5, "temperature": 21.15}, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 1961, "long_name": "Tiny Tortoise", "next_hop": 0, "num": "0x124293c9", "position": {"altitude": 1593, "latitude": 33.348839, "location_source": "LOC_INTERNAL", "longitude": -106.734719, "time_offset_sec": 2028}, "public_key_hex": "", "role": "CLIENT", "short_name": "🐢", "snr": 0.06, "status": null, "telemetry": {"air_util_tx": 0.603, "battery_level": 29, "channel_utilization": 4.14, "uptime_seconds": 83202, "voltage": 3.561}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.97, "iaq": 55, "relative_humidity": 47.9, "temperature": 27.96}, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3815, "long_name": "Quick Lynx", "next_hop": 0, "num": "0x12793b94", "position": null, "public_key_hex": "4b71268babbada3e398b200e02f4f502412aba779546f65b4f1d6390e3c11c71", "role": "CLIENT", "short_name": "🦅", "snr": 1.71, "status": null, "telemetry": {"air_util_tx": 0.15, "battery_level": 24, "channel_utilization": 6.86, "uptime_seconds": 37207, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1008.45, "iaq": 28, "relative_humidity": 29.78, "temperature": 24.0}, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 1265, "long_name": "Gold Aspen", "next_hop": 0, "num": "0x12b1a994", "position": null, "public_key_hex": "c10646601f2cc6d352787547ab609271a62f40eb40f5ec3ebb4cee0b877a7a94", "role": "CLIENT", "short_name": "GAUX", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.824, "battery_level": 72, "channel_utilization": 14.07, "uptime_seconds": 78788, "voltage": 3.948}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 4582, "long_name": "Rough Sage W59QH", "next_hop": 111, "num": "0x134b790b", "position": {"altitude": 1199, "latitude": 33.051761, "location_source": "LOC_INTERNAL", "longitude": -107.200236, "time_offset_sec": 4881}, "public_key_hex": "5505535ae415d55b775ea3d8677bcc16a4362ce1534f3d95be20065271f9f5b1", "role": "CLIENT", "short_name": "RODU", "snr": 9.9, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 671, "long_name": "Frozen Elk", "next_hop": 0, "num": "0x1356e5a7", "position": {"altitude": 1282, "latitude": 32.367066, "location_source": "LOC_INTERNAL", "longitude": -107.325336, "time_offset_sec": 689}, "public_key_hex": "ba1368b21e97bc91b19adfbf5f0031ce456ff07aa56d6a8b6eec0ccc96799047", "role": "TAK_TRACKER", "short_name": "FUQL", "snr": 11.9, "status": null, "telemetry": {"air_util_tx": 0.443, "battery_level": 49, "channel_utilization": 6.68, "uptime_seconds": 41398, "voltage": 3.741}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 13755, "long_name": "Storm Moose", "next_hop": 0, "num": "0x1379b6f8", "position": {"altitude": 1283, "latitude": 33.488927, "location_source": "LOC_INTERNAL", "longitude": -107.17989, "time_offset_sec": 13977}, "public_key_hex": "94bcb6ebe3bc3ec012f4dbcf228e3439668758ca425184c26c68128fad5552b1", "role": "ROUTER_LATE", "short_name": "SO4X", "snr": 7.42, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.385, "battery_level": 36, "channel_utilization": 20.65, "uptime_seconds": 20292, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 3390, "long_name": "Wild Shark", "next_hop": 0, "num": "0x13aa96e0", "position": null, "public_key_hex": "522f72c0a046e007baedf483b49090d229c6cd95555aa3a8a9c4a72ff6bbffd0", "role": "CLIENT", "short_name": "WXOE", "snr": 8.25, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.552, "battery_level": 101, "channel_utilization": 10.44, "uptime_seconds": 28836, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 572, "long_name": "Slow Tortoise KE8BZ", "next_hop": 0, "num": "0x13c260ca", "position": {"altitude": 988, "latitude": 32.950608, "location_source": "LOC_INTERNAL", "longitude": -107.691689, "time_offset_sec": 751}, "public_key_hex": "de56d298c6d8b7322ebceeeaa5b5c220cd8f518819cb08fb98c054953ac0e43a", "role": "CLIENT", "short_name": "SJEL", "snr": 10.47, "status": null, "telemetry": {"air_util_tx": 2.227, "battery_level": 49, "channel_utilization": 17.7, "uptime_seconds": 95503, "voltage": 3.741}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 3627, "long_name": "Burning Moose", "next_hop": 11, "num": "0x1418f8cd", "position": {"altitude": 1294, "latitude": 33.659566, "location_source": "LOC_INTERNAL", "longitude": -108.238162, "time_offset_sec": 3734}, "public_key_hex": "22c0a92b37e0d813e0606b32cebbe17031ee9ab24370bfa0ec5c62c244fcca3e", "role": "ROUTER", "short_name": "🌊", "snr": 9.05, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 6908, "long_name": "Green Iguana", "next_hop": 189, "num": "0x14344f00", "position": {"altitude": 829, "latitude": 32.897047, "location_source": "LOC_INTERNAL", "longitude": -107.223164, "time_offset_sec": 7051}, "public_key_hex": "d1cbe0db99baa5af5dc3179d2bbdd50c2db5f98b1c338be18ec2c55f73836d38", "role": "CLIENT", "short_name": "GHVH", "snr": 3.87, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.306, "battery_level": 30, "channel_utilization": 12.84, "uptime_seconds": 72917, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 1071, "long_name": "Tall Wolf", "next_hop": 0, "num": "0x14d33250", "position": {"altitude": 1562, "latitude": 32.694901, "location_source": "LOC_INTERNAL", "longitude": -107.508362, "time_offset_sec": 1246}, "public_key_hex": "081e7a86105059118e56233bc00bcbd77dde78a465ea4759bef00ab5ca3e0181", "role": "CLIENT", "short_name": "TH3H", "snr": 2.73, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.842, "battery_level": 27, "channel_utilization": 8.35, "uptime_seconds": 56438, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 4481, "long_name": "Wandering Moose", "next_hop": 0, "num": "0x14eb1f2d", "position": {"altitude": 1654, "latitude": 33.730089, "location_source": "LOC_INTERNAL", "longitude": -107.361317, "time_offset_sec": 4625}, "public_key_hex": "8a4a31c8023223e5c508b31f3829c13008e82a957c1d15c38be451b797e3d8b5", "role": "CLIENT", "short_name": "W94X", "snr": 8.99, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.146, "battery_level": 89, "channel_utilization": 26.52, "uptime_seconds": 61941, "voltage": 4.101}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.5, "iaq": 76, "relative_humidity": 67.21, "temperature": 28.61}, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 9181, "long_name": "Silver Bear", "next_hop": 0, "num": "0x15008bbd", "position": {"altitude": 1303, "latitude": 33.304292, "location_source": "LOC_INTERNAL", "longitude": -106.704279, "time_offset_sec": 9318}, "public_key_hex": "e0e5fc0c2810e96cbdbaa94273448a433a56f85d833446f4a78dd567f24564af", "role": "CLIENT", "short_name": "SV3M", "snr": 5.67, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.252, "battery_level": 44, "channel_utilization": 10.76, "uptime_seconds": 20110, "voltage": 3.696}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1_EINK", "last_heard_offset_sec": 12488, "long_name": "Dawn Bison", "next_hop": 0, "num": "0x15079a96", "position": {"altitude": 1142, "latitude": 33.178914, "location_source": "LOC_INTERNAL", "longitude": -106.882896, "time_offset_sec": 12552}, "public_key_hex": "b19b49ec63540cbb0683e60d45179dcab274743512d5e9c81351057c625f031b", "role": "CLIENT", "short_name": "🗻", "snr": 4.12, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4732, "long_name": "Lost Oak", "next_hop": 85, "num": "0x151e3fb5", "position": {"altitude": 1556, "latitude": 33.030756, "location_source": "LOC_INTERNAL", "longitude": -107.105935, "time_offset_sec": 4801}, "public_key_hex": "b0c3b2db380cf1b42a013974b3936b6d8a03b9e36bee94abd5ee7030ed151f45", "role": "CLIENT", "short_name": "LZTS", "snr": 7.17, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1009.79, "iaq": 51, "relative_humidity": 27.03, "temperature": 12.69}, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 5740, "long_name": "Bright Juniper", "next_hop": 0, "num": "0x15dc23b2", "position": {"altitude": 868, "latitude": 32.860217, "location_source": "LOC_INTERNAL", "longitude": -108.123223, "time_offset_sec": 5988}, "public_key_hex": "cd14d907287a83262d5f58b428faad9d6f79832399ad9f1e38853c799756ae09", "role": "CLIENT", "short_name": "BS7U", "snr": 6.24, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.414, "battery_level": 39, "channel_utilization": 8.47, "uptime_seconds": 112298, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": {"barometric_pressure": 1014.5, "iaq": 107, "relative_humidity": 91.9, "temperature": 22.23}, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 510, "long_name": "Floating Fox", "next_hop": 102, "num": "0x16108cd9", "position": {"altitude": 1198, "latitude": 33.289005, "location_source": "LOC_INTERNAL", "longitude": -107.249121, "time_offset_sec": 649}, "public_key_hex": "4cdfc9b5d15351036ad5f84ff783a1ce059cdef45e08c87252221687624e4d51", "role": "CLIENT", "short_name": "FO2G", "snr": 7.94, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.415, "battery_level": 46, "channel_utilization": 6.21, "uptime_seconds": 27755, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 555, "long_name": "River Turtle", "next_hop": 0, "num": "0x162f1f2d", "position": {"altitude": 1048, "latitude": 33.456724, "location_source": "LOC_INTERNAL", "longitude": -107.166742, "time_offset_sec": 680}, "public_key_hex": "96b2115da7d505865a41d707f058f860536de871f4dc1a1ddfa730939dd14e4a", "role": "CLIENT_HIDDEN", "short_name": "RIBX", "snr": 10.55, "status": null, "telemetry": {"air_util_tx": 0.271, "battery_level": 95, "channel_utilization": 2.71, "uptime_seconds": 58824, "voltage": 4.155}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "THINKNODE_M5", "last_heard_offset_sec": 3463, "long_name": "Steel Trout", "next_hop": 183, "num": "0x163f2d40", "position": {"altitude": 1354, "latitude": 32.863185, "location_source": "LOC_INTERNAL", "longitude": -108.147876, "time_offset_sec": 3725}, "public_key_hex": "41838ad0fc0e569398ea924f689c3daa2d1057f363d35c8b444e6c536fe4d24f", "role": "CLIENT", "short_name": "SC17", "snr": 12.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 232, "long_name": "Whispering Lynx", "next_hop": 0, "num": "0x167494c0", "position": {"altitude": 1556, "latitude": 33.115465, "location_source": "LOC_INTERNAL", "longitude": -107.426901, "time_offset_sec": 281}, "public_key_hex": "c2478ff9f49a90e70290f8027b4c8e3a64849b721419e698728b4c6c1d1f0666", "role": "CLIENT", "short_name": "WFFL", "snr": 4.16, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 3.127, "battery_level": 54, "channel_utilization": 14.67, "uptime_seconds": 49601, "voltage": 3.786}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1560, "long_name": "Black Cougar", "next_hop": 28, "num": "0x16897d73", "position": {"altitude": 1673, "latitude": 32.91074, "location_source": "LOC_INTERNAL", "longitude": -108.369742, "time_offset_sec": 1624}, "public_key_hex": "c112995c30570dcea089ca8cc09e113877c301f2a02fa50cf3de16b175006618", "role": "CLIENT", "short_name": "BYO0", "snr": 8.38, "status": null, "telemetry": {"air_util_tx": 0.664, "battery_level": 14, "channel_utilization": 6.06, "uptime_seconds": 212294, "voltage": 3.426}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 2627, "long_name": "Storm Elk", "next_hop": 95, "num": "0x16b275f9", "position": null, "public_key_hex": "2a82e18a602e00282dedae0ec94bc011f1f63e6501c1154fdf4088e8f9e66221", "role": "CLIENT", "short_name": "SD28", "snr": 12.0, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.022, "battery_level": 96, "channel_utilization": 14.35, "uptime_seconds": 44369, "voltage": 4.164}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 4213, "long_name": "Blue Lion", "next_hop": 111, "num": "0x16b2a0d6", "position": {"altitude": 1426, "latitude": 32.811334, "location_source": "LOC_INTERNAL", "longitude": -107.621788, "time_offset_sec": 4332}, "public_key_hex": "8539ed4565ae84a1422af41bbf3b9931250e817a1afaced69f213a6deba68cd7", "role": "CLIENT", "short_name": "BOU8", "snr": 6.59, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1223, "long_name": "Wild Badger", "next_hop": 184, "num": "0x16c3bc1e", "position": {"altitude": 1210, "latitude": 31.911923, "location_source": "LOC_INTERNAL", "longitude": -106.914223, "time_offset_sec": 1415}, "public_key_hex": "9b5a6fbbad28e1bb34255eb4e1c06ebae5112d6865efe8f0f7300e8e615fcecd", "role": "CLIENT", "short_name": "WJIU", "snr": 8.91, "status": null, "telemetry": {"air_util_tx": 0.308, "battery_level": 68, "channel_utilization": 14.45, "uptime_seconds": 68262, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 3751, "long_name": "Lost Seal KQ5CO", "next_hop": 125, "num": "0x174ad192", "position": {"altitude": 1619, "latitude": 33.035231, "location_source": "LOC_INTERNAL", "longitude": -106.344659, "time_offset_sec": 3887}, "public_key_hex": "60c3cf0a8a24067b3dd13f67bf5fb744a064eff33318446e69a7173025035ba6", "role": "CLIENT", "short_name": "L3G4", "snr": 8.3, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.595, "battery_level": 68, "channel_utilization": 5.31, "uptime_seconds": 44253, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 3105, "long_name": "Dusk Bear", "next_hop": 0, "num": "0x1766b643", "position": null, "public_key_hex": "2a662dddcc43dbaf9307f399f96979586ec89075447f8dcd8d0770d24d8bb8c1", "role": "CLIENT", "short_name": "D06J", "snr": 2.87, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.311, "battery_level": 90, "channel_utilization": 23.58, "uptime_seconds": 63789, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 1090, "long_name": "Soft Mesa", "next_hop": 40, "num": "0x17e79e67", "position": {"altitude": 1287, "latitude": 32.365261, "location_source": "LOC_INTERNAL", "longitude": -106.634547, "time_offset_sec": 1289}, "public_key_hex": "ece402bde0b1a1d0d81cf6a77bb47ed8cdea95fe5c572fb26bb0e0be659b0b1d", "role": "CLIENT", "short_name": "SUXH", "snr": 6.42, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.641, "battery_level": 17, "channel_utilization": 20.0, "uptime_seconds": 1774, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 6549, "long_name": "Fast Pike", "next_hop": 0, "num": "0x18315641", "position": {"altitude": 1600, "latitude": 33.22812, "location_source": "LOC_INTERNAL", "longitude": -106.528869, "time_offset_sec": 6792}, "public_key_hex": "1a028f6e546ff000ccd3e5b381ea64df4a7c67fe8e48a3a7a5e7a6b5e6f31954", "role": "TRACKER", "short_name": "🌙", "snr": -1.78, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 233, "long_name": "Drowsy Beaver", "next_hop": 0, "num": "0x1879f157", "position": {"altitude": 1403, "latitude": 32.948675, "location_source": "LOC_INTERNAL", "longitude": -107.057998, "time_offset_sec": 236}, "public_key_hex": "69673da7dbee9e58d0582faf761ff65d3f53b504871de416fd3cd4bf756fb02b", "role": "CLIENT", "short_name": "D72I", "snr": 9.62, "status": null, "telemetry": {"air_util_tx": 0.401, "battery_level": 36, "channel_utilization": 35.24, "uptime_seconds": 96943, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 7571, "long_name": "Smooth Otter", "next_hop": 0, "num": "0x18e8f8e8", "position": {"altitude": 1321, "latitude": 32.871304, "location_source": "LOC_INTERNAL", "longitude": -106.837591, "time_offset_sec": 7854}, "public_key_hex": "", "role": "CLIENT", "short_name": "SWO2", "snr": 3.69, "status": {"status": "low-batt"}, "telemetry": {"air_util_tx": 0.199, "battery_level": 96, "channel_utilization": 2.78, "uptime_seconds": 172071, "voltage": 4.164}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1016.31, "iaq": 37, "relative_humidity": 87.49, "temperature": 33.24}, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 18931, "long_name": "Roving Falcon", "next_hop": 0, "num": "0x18ef3676", "position": null, "public_key_hex": "e7c21800cd0080b0283e471d96c9145398f52b79584122317b54c691dbb7f7f2", "role": "CLIENT", "short_name": "🦅", "snr": 4.74, "status": null, "telemetry": {"air_util_tx": 0.98, "battery_level": 25, "channel_utilization": 27.08, "uptime_seconds": 101477, "voltage": 3.525}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 994.67, "iaq": 57, "relative_humidity": 50.76, "temperature": 11.41}, "hops_away": 1, "hw_model": "T_ECHO", "last_heard_offset_sec": 3752, "long_name": "Wandering Adder", "next_hop": 201, "num": "0x1988381c", "position": {"altitude": 1575, "latitude": 33.57496, "location_source": "LOC_INTERNAL", "longitude": -107.855479, "time_offset_sec": 3771}, "public_key_hex": "9309172700d9b8c4dcdbfb6cb9a70f1667e3d1d4a63fa2f79b25c97c1fb27e78", "role": "CLIENT", "short_name": "W19E", "snr": 5.62, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 2950, "long_name": "Short Colt", "next_hop": 216, "num": "0x19f3157f", "position": {"altitude": 1615, "latitude": 32.464438, "location_source": "LOC_INTERNAL", "longitude": -107.616346, "time_offset_sec": 3081}, "public_key_hex": "eb6cfc06557874a6d463e4f4f2e108734d4a480588a123874b01145932dd322a", "role": "CLIENT", "short_name": "SG5Q", "snr": -0.24, "status": null, "telemetry": {"air_util_tx": 0.085, "battery_level": 23, "channel_utilization": 5.55, "uptime_seconds": 85090, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1780, "long_name": "Old Phoenix", "next_hop": 0, "num": "0x1a12e784", "position": {"altitude": 1200, "latitude": 33.519889, "location_source": "LOC_INTERNAL", "longitude": -107.338351, "time_offset_sec": 1808}, "public_key_hex": "d9ceee6f3c4b4da117cb125d2214bbd093339914243306a487dd72a3268df1d8", "role": "CLIENT", "short_name": "🐝", "snr": 6.02, "status": null, "telemetry": {"air_util_tx": 0.713, "battery_level": 62, "channel_utilization": 0.76, "uptime_seconds": 46755, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 181, "long_name": "Found Raven", "next_hop": 0, "num": "0x1b3b65e9", "position": {"altitude": 1293, "latitude": 33.288548, "location_source": "LOC_INTERNAL", "longitude": -106.793415, "time_offset_sec": 332}, "public_key_hex": "16abe3f61fba13eecc05937c951b3dc75f49c7a91b931f65cb78e51b71347a66", "role": "CLIENT", "short_name": "FGB8", "snr": 8.32, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 128, "long_name": "Tall Crow", "next_hop": 166, "num": "0x1b3dc88d", "position": {"altitude": 1529, "latitude": 33.179202, "location_source": "LOC_INTERNAL", "longitude": -107.497106, "time_offset_sec": 169}, "public_key_hex": "03eb9d64485bf32d9e0c759235d16d583bdd1ab21dea084426d8f84683de1d3f", "role": "CLIENT", "short_name": "TVOT", "snr": 7.57, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.232, "battery_level": 43, "channel_utilization": 16.23, "uptime_seconds": 62860, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2400, "long_name": "Storm Moose", "next_hop": 0, "num": "0x1b96157a", "position": {"altitude": 1109, "latitude": 32.378496, "location_source": "LOC_INTERNAL", "longitude": -107.954423, "time_offset_sec": 2652}, "public_key_hex": "6f495139fa0535f498dd4e452971c3b048303ef830853b0775562372d0aed5ef", "role": "CLIENT", "short_name": "SVDY", "snr": 3.46, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 660, "long_name": "River Adder", "next_hop": 23, "num": "0x1bb73d04", "position": {"altitude": 1641, "latitude": 33.68275, "location_source": "LOC_INTERNAL", "longitude": -106.336486, "time_offset_sec": 729}, "public_key_hex": "00d33760c0ae44ec4ed815ed2f8fb856de3dc8cba6d4a3fe87e864bb163c21fc", "role": "CLIENT", "short_name": "🦇", "snr": 8.36, "status": {"status": "weak-signal"}, "telemetry": {"air_util_tx": 0.259, "battery_level": 16, "channel_utilization": 10.86, "uptime_seconds": 148515, "voltage": 3.444}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1024.25, "iaq": 35, "relative_humidity": 29.15, "temperature": 10.43}, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 281, "long_name": "Tall Sage", "next_hop": 0, "num": "0x1bb8ec2e", "position": {"altitude": 1531, "latitude": 32.77112, "location_source": "LOC_INTERNAL", "longitude": -108.092313, "time_offset_sec": 401}, "public_key_hex": "", "role": "CLIENT", "short_name": "TYN9", "snr": 5.06, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.803, "battery_level": 50, "channel_utilization": 15.33, "uptime_seconds": 214193, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 5105, "long_name": "Desert Crane", "next_hop": 0, "num": "0x1c5d1282", "position": {"altitude": 1015, "latitude": 32.714988, "location_source": "LOC_INTERNAL", "longitude": -107.149688, "time_offset_sec": 5361}, "public_key_hex": "31ac1d441f3a1497f3fe0c690feb1be2c7e1ae206e75076adb0266cbb3aa6db4", "role": "CLIENT", "short_name": "DPH4", "snr": 4.23, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.329, "battery_level": 79, "channel_utilization": 24.0, "uptime_seconds": 118737, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 1510, "long_name": "Red Adder", "next_hop": 0, "num": "0x1c65ab38", "position": {"altitude": 1119, "latitude": 32.218937, "location_source": "LOC_INTERNAL", "longitude": -107.683511, "time_offset_sec": 1670}, "public_key_hex": "526c2de137bac9ced28684319ac3c88652bec2857257494a88ce4953cde8f35d", "role": "ROUTER_LATE", "short_name": "RCQ5", "snr": 5.32, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 1734, "long_name": "Silent Owl", "next_hop": 0, "num": "0x1cd35371", "position": null, "public_key_hex": "760e3df9c2c870447ce712166b29f524bfef1f96d810b291ac9fea3a958d7bf5", "role": "CLIENT", "short_name": "SQ2L", "snr": 4.4, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2380, "long_name": "Shady Viper", "next_hop": 35, "num": "0x1d034814", "position": {"altitude": 1558, "latitude": 32.964971, "location_source": "LOC_INTERNAL", "longitude": -106.457279, "time_offset_sec": 2402}, "public_key_hex": "e5942d09ce2a6eb922b60ce70af7f117c247e8e31b9f8f86beab1e90557ccfbc", "role": "CLIENT", "short_name": "SDKU", "snr": 3.29, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.374, "battery_level": 83, "channel_utilization": 4.34, "uptime_seconds": 52904, "voltage": 4.047}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_ECHO", "last_heard_offset_sec": 6228, "long_name": "White Hawk", "next_hop": 186, "num": "0x1d250bf1", "position": {"altitude": 1580, "latitude": 33.745509, "location_source": "LOC_INTERNAL", "longitude": -106.22969, "time_offset_sec": 6390}, "public_key_hex": "34e2a11726966b49cadf79e782bb6411b20db72dc599384e90036ec2aa948b5f", "role": "CLIENT", "short_name": "🌲", "snr": 6.15, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.717, "battery_level": 69, "channel_utilization": 17.24, "uptime_seconds": 4879, "voltage": 3.921}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 1115, "long_name": "Silent Hare", "next_hop": 104, "num": "0x1d7b308f", "position": {"altitude": 1699, "latitude": 32.964606, "location_source": "LOC_INTERNAL", "longitude": -106.834004, "time_offset_sec": 1146}, "public_key_hex": "7d6fc2a73a3613727447a5213e5b28f2d2a6daf1bc9d37fb85af98f924060068", "role": "CLIENT", "short_name": "SEF4", "snr": 9.34, "status": null, "telemetry": {"air_util_tx": 0.141, "battery_level": 19, "channel_utilization": 7.71, "uptime_seconds": 24330, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 3021, "long_name": "Dusk Ridge", "next_hop": 0, "num": "0x1d8a404d", "position": {"altitude": 1272, "latitude": 32.667972, "location_source": "LOC_INTERNAL", "longitude": -107.58155, "time_offset_sec": 3245}, "public_key_hex": "", "role": "SENSOR", "short_name": "DA2X", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.123, "battery_level": 77, "channel_utilization": 8.91, "uptime_seconds": 100385, "voltage": 3.993}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 7594, "long_name": "Loud Moose KE2CF", "next_hop": 0, "num": "0x1d922594", "position": {"altitude": 1512, "latitude": 33.685806, "location_source": "LOC_INTERNAL", "longitude": -106.311204, "time_offset_sec": 7624}, "public_key_hex": "668759f4208b636bc246eb4c674b149e6580afba036d0521aa4bd644f533016d", "role": "CLIENT", "short_name": "🦋", "snr": 8.05, "status": {"status": "online"}, "telemetry": {"air_util_tx": 1.086, "battery_level": 87, "channel_utilization": 10.32, "uptime_seconds": 53171, "voltage": 4.083}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1005.76, "iaq": 48, "relative_humidity": 84.51, "temperature": 25.52}, "hops_away": 2, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 390, "long_name": "Dusk Falcon", "next_hop": 90, "num": "0x1e48de96", "position": {"altitude": 1754, "latitude": 32.922495, "location_source": "LOC_INTERNAL", "longitude": -107.761571, "time_offset_sec": 566}, "public_key_hex": "9cfad118faa52d6a962cfe920c3223a2435ed86716ec4fff20ee094bc3be9247", "role": "CLIENT", "short_name": "DSY7", "snr": 8.44, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.288, "battery_level": 65, "channel_utilization": 6.55, "uptime_seconds": 72604, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 4374, "long_name": "Silver Trout", "next_hop": 24, "num": "0x1e55127e", "position": null, "public_key_hex": "fad8bd37ec2fd7cb5f22e6c9bd0d59315ce728a1809408904587d3ee07c0de2c", "role": "CLIENT", "short_name": "SAGX", "snr": 2.95, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.309, "battery_level": 58, "channel_utilization": 12.57, "uptime_seconds": 5476, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 2906, "long_name": "Floating Salmon", "next_hop": 178, "num": "0x1ec65817", "position": {"altitude": 1642, "latitude": 32.054247, "location_source": "LOC_INTERNAL", "longitude": -107.714698, "time_offset_sec": 2991}, "public_key_hex": "2eece35b6bbf4844a5b4fa626cf6b911072f493897cbdac4dc6b51c9cdbac750", "role": "CLIENT", "short_name": "FQR6", "snr": 0.63, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1003.98, "iaq": 89, "relative_humidity": 93.47, "temperature": 35.52}, "hops_away": 4, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1356, "long_name": "Steel Lion", "next_hop": 70, "num": "0x1edcadc8", "position": {"altitude": 1132, "latitude": 32.809096, "location_source": "LOC_INTERNAL", "longitude": -108.059756, "time_offset_sec": 1643}, "public_key_hex": "", "role": "CLIENT_MUTE", "short_name": "🔥", "snr": 9.65, "status": null, "telemetry": {"air_util_tx": 1.187, "battery_level": 12, "channel_utilization": 13.29, "uptime_seconds": 8597, "voltage": 3.408}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1011.68, "iaq": 0, "relative_humidity": 85.78, "temperature": 20.85}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 19869, "long_name": "Quick Colt", "next_hop": 0, "num": "0x1f3c099d", "position": {"altitude": 1528, "latitude": 33.641254, "location_source": "LOC_INTERNAL", "longitude": -107.003798, "time_offset_sec": 19950}, "public_key_hex": "a93259deaa48ea388df51930b660b4db7ad3205f39fdaa796cada03c5f1d94ca", "role": "CLIENT", "short_name": "Q1T3", "snr": 7.01, "status": null, "telemetry": {"air_util_tx": 1.197, "battery_level": 22, "channel_utilization": 2.57, "uptime_seconds": 17868, "voltage": 3.498}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 4696, "long_name": "Green Owl", "next_hop": 0, "num": "0x1fa1ecc4", "position": {"altitude": 1704, "latitude": 33.608806, "location_source": "LOC_INTERNAL", "longitude": -107.078775, "time_offset_sec": 4921}, "public_key_hex": "d8f7463ef6088318f1a337f9d78745f2414d7a5e1ef74a7b3a027f11a21a5b22", "role": "SENSOR", "short_name": "G70L", "snr": 4.43, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.747, "battery_level": 94, "channel_utilization": 42.31, "uptime_seconds": 40259, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.37, "iaq": 88, "relative_humidity": 39.53, "temperature": 19.13}, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 2032, "long_name": "Hidden Cedar", "next_hop": 153, "num": "0x1fd14395", "position": {"altitude": 1345, "latitude": 34.041731, "location_source": "LOC_INTERNAL", "longitude": -107.545165, "time_offset_sec": 2280}, "public_key_hex": "d49a566319a2b467494d96d04c294b9b0f25f3e517570b3ef67921e55391baf4", "role": "CLIENT", "short_name": "HZUZ", "snr": -3.41, "status": null, "telemetry": {"air_util_tx": 1.013, "battery_level": 59, "channel_utilization": 17.63, "uptime_seconds": 11804, "voltage": 3.831}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 3757, "long_name": "Wandering Beaver", "next_hop": 0, "num": "0x1ff10642", "position": {"altitude": 1336, "latitude": 33.635048, "location_source": "LOC_INTERNAL", "longitude": -108.082046, "time_offset_sec": 3847}, "public_key_hex": "1d69bdcc66ded4d1fd928233ff5e4a69dfb31c3e0def2e355642a4c02a066c10", "role": "CLIENT", "short_name": "WC3M", "snr": 11.11, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 8655, "long_name": "Old Owl", "next_hop": 0, "num": "0x1ff9fc94", "position": {"altitude": 1630, "latitude": 32.689108, "location_source": "LOC_INTERNAL", "longitude": -107.204299, "time_offset_sec": 8694}, "public_key_hex": "", "role": "CLIENT_MUTE", "short_name": "OEFS", "snr": 6.12, "status": null, "telemetry": {"air_util_tx": 0.495, "battery_level": 35, "channel_utilization": 25.26, "uptime_seconds": 67098, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 5112, "long_name": "Black Cougar", "next_hop": 58, "num": "0x20584ab0", "position": {"altitude": 1650, "latitude": 32.332627, "location_source": "LOC_INTERNAL", "longitude": -106.842898, "time_offset_sec": 5277}, "public_key_hex": "24fb486fdca3f123deb52e67730c80d56e73da33f0567a5a9bce05dd7a9ba977", "role": "ROUTER", "short_name": "BCZC", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.388, "battery_level": 46, "channel_utilization": 11.56, "uptime_seconds": 31821, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1423, "long_name": "Sky Doe", "next_hop": 25, "num": "0x205d8291", "position": {"altitude": 1394, "latitude": 33.553239, "location_source": "LOC_INTERNAL", "longitude": -106.866388, "time_offset_sec": 1523}, "public_key_hex": "a8cabf3339c05e04fe61bf45c061bb0bcd2d724d58d6817a9577e9ee08d203a1", "role": "CLIENT", "short_name": "S7B4", "snr": 10.74, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.014, "battery_level": 93, "channel_utilization": 6.16, "uptime_seconds": 1547, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1231, "long_name": "Steel Juniper", "next_hop": 0, "num": "0x21033a28", "position": {"altitude": 1264, "latitude": 33.356136, "location_source": "LOC_INTERNAL", "longitude": -106.614493, "time_offset_sec": 1239}, "public_key_hex": "f2a9858f9df95595e2297da50cd6ff39f6dc6a98e95a3d1301ccf60268dcc006", "role": "CLIENT", "short_name": "SIMB", "snr": 2.0, "status": null, "telemetry": {"air_util_tx": 0.783, "battery_level": 101, "channel_utilization": 4.42, "uptime_seconds": 38270, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 5800, "long_name": "Gold Pony", "next_hop": 90, "num": "0x217fcefe", "position": {"altitude": 1327, "latitude": 33.145461, "location_source": "LOC_INTERNAL", "longitude": -106.737792, "time_offset_sec": 5964}, "public_key_hex": "e2f5c1574f518f68f6feb70ec37f88df48bcd397b0de92f000afe8f3aefb38d3", "role": "CLIENT", "short_name": "GMEF", "snr": 8.53, "status": null, "telemetry": {"air_util_tx": 0.161, "battery_level": 88, "channel_utilization": 24.95, "uptime_seconds": 202680, "voltage": 4.092}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SENSECAP_INDICATOR", "last_heard_offset_sec": 530, "long_name": "Iron Yucca", "next_hop": 0, "num": "0x218bfe2c", "position": {"altitude": 1722, "latitude": 33.303433, "location_source": "LOC_INTERNAL", "longitude": -106.970972, "time_offset_sec": 647}, "public_key_hex": "ed244b20dc77f39a789646b51e74048b10380d945510652c1681a60b27a05cb9", "role": "CLIENT", "short_name": "IZ30", "snr": 10.09, "status": null, "telemetry": {"air_util_tx": 2.038, "battery_level": 43, "channel_utilization": 5.35, "uptime_seconds": 84598, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 4028, "long_name": "Desert Yucca", "next_hop": 216, "num": "0x22152f01", "position": {"altitude": 1490, "latitude": 32.902735, "location_source": "LOC_INTERNAL", "longitude": -108.083973, "time_offset_sec": 4073}, "public_key_hex": "4b59cc1528251d21d567bf07f1c171c781b9ffe3c08fa95446a7b3d58e8107a6", "role": "CLIENT_MUTE", "short_name": "🦋", "snr": 9.79, "status": null, "telemetry": {"air_util_tx": 0.558, "battery_level": 64, "channel_utilization": 13.37, "uptime_seconds": 84244, "voltage": 3.876}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 3662, "long_name": "New Cobra", "next_hop": 252, "num": "0x225663cf", "position": {"altitude": 1321, "latitude": 33.636697, "location_source": "LOC_INTERNAL", "longitude": -107.268583, "time_offset_sec": 3843}, "public_key_hex": "35b56c0424b193ef053313349bcae772fce851ea41d3dccd93597a98601f2654", "role": "CLIENT", "short_name": "🐢", "snr": 7.75, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.601, "battery_level": 80, "channel_utilization": 29.33, "uptime_seconds": 32338, "voltage": 4.02}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1000.51, "iaq": 84, "relative_humidity": 14.79, "temperature": 40.91}, "hops_away": 2, "hw_model": "T_ECHO", "last_heard_offset_sec": 309, "long_name": "Brave Juniper", "next_hop": 9, "num": "0x22681b09", "position": {"altitude": 1313, "latitude": 33.196654, "location_source": "LOC_INTERNAL", "longitude": -106.997179, "time_offset_sec": 575}, "public_key_hex": "0c5ce9a2ae203f5698b67669463c7650d7898547aba0ed6acc456e3d24b1a401", "role": "CLIENT", "short_name": "BPP2", "snr": 3.84, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.797, "battery_level": 62, "channel_utilization": 9.31, "uptime_seconds": 349552, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 7718, "long_name": "Desert Cobra", "next_hop": 0, "num": "0x22d7d309", "position": {"altitude": 1311, "latitude": 33.255675, "location_source": "LOC_INTERNAL", "longitude": -107.444882, "time_offset_sec": 7959}, "public_key_hex": "90b5dee4acb9274a52c640a2b854603a543a4be718e7ae478212bf60cc53a252", "role": "CLIENT", "short_name": "DFPE", "snr": -0.05, "status": {"status": "no-gps"}, "telemetry": {"air_util_tx": 1.058, "battery_level": 40, "channel_utilization": 35.25, "uptime_seconds": 5417, "voltage": 3.66}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 6737, "long_name": "Silver Cedar", "next_hop": 0, "num": "0x22d9762a", "position": null, "public_key_hex": "ca92433f107ebf95e44c6286a649d0d559a0c10e1cbe12194b02fd05a1a0be7f", "role": "CLIENT", "short_name": "SOC1", "snr": 2.98, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 1.834, "battery_level": 65, "channel_utilization": 10.13, "uptime_seconds": 92911, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 1038, "long_name": "Sneaky Mustang", "next_hop": 250, "num": "0x22fa3ede", "position": {"altitude": 998, "latitude": 33.530425, "location_source": "LOC_INTERNAL", "longitude": -108.118632, "time_offset_sec": 1071}, "public_key_hex": "cd67de99c01cef3a4bccf8be77fdc9867e9d36140ed85dd4db2580090a8b12a4", "role": "CLIENT", "short_name": "S4ZQ", "snr": 7.23, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 11870, "long_name": "Drifting Squirrel", "next_hop": 0, "num": "0x230de5d6", "position": null, "public_key_hex": "207f473e204ad579961c472b68f36b5c2417f263d527abfad579c2c1fbd2292d", "role": "CLIENT", "short_name": "DJWM", "snr": 9.27, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.127, "battery_level": 10, "channel_utilization": 3.15, "uptime_seconds": 24031, "voltage": 3.39}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1030.51, "iaq": 42, "relative_humidity": 54.16, "temperature": 29.92}, "hops_away": 3, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 5016, "long_name": "Smooth Marmot", "next_hop": 126, "num": "0x2375e66b", "position": {"altitude": 1412, "latitude": 32.61115, "location_source": "LOC_INTERNAL", "longitude": -107.686434, "time_offset_sec": 5113}, "public_key_hex": "f050748fd0d4f37779d5eb745a51fca43f61111f3631fe587e8236676090dac6", "role": "CLIENT", "short_name": "STBK", "snr": -0.04, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.332, "battery_level": 57, "channel_utilization": 18.05, "uptime_seconds": 5906, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M6", "last_heard_offset_sec": 694, "long_name": "Sky Doe", "next_hop": 0, "num": "0x237668c0", "position": {"altitude": 1742, "latitude": 32.685164, "location_source": "LOC_INTERNAL", "longitude": -107.454203, "time_offset_sec": 855}, "public_key_hex": "eb89e19cbd4889d2ceaee0559bc54eade7dd1f3e572d58d67aa5eb5e4f75c750", "role": "CLIENT", "short_name": "SR0J", "snr": 6.47, "status": {"status": "offline-soon"}, "telemetry": {"air_util_tx": 0.496, "battery_level": 19, "channel_utilization": 7.6, "uptime_seconds": 31164, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1002.49, "iaq": 24, "relative_humidity": 62.45, "temperature": 35.82}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 1384, "long_name": "Whispering Salmon", "next_hop": 0, "num": "0x23c98a6d", "position": {"altitude": 1614, "latitude": 33.614918, "location_source": "LOC_INTERNAL", "longitude": -107.429103, "time_offset_sec": 1647}, "public_key_hex": "c43fa3accf988f4100b88711423b7661a756eb33315f6c2ddcb88c60cb65e663", "role": "CLIENT_MUTE", "short_name": "W297", "snr": 4.03, "status": null, "telemetry": {"air_util_tx": 1.534, "battery_level": 31, "channel_utilization": 2.71, "uptime_seconds": 126260, "voltage": 3.579}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 6333, "long_name": "Dawn Owl AE5DW", "next_hop": 0, "num": "0x23f41648", "position": {"altitude": 1149, "latitude": 32.670635, "location_source": "LOC_INTERNAL", "longitude": -106.966118, "time_offset_sec": 6611}, "public_key_hex": "d19c786d7447228d443f44206e3df23dcbe9bdca14d6f251b0fc27b3e7c597e1", "role": "CLIENT", "short_name": "DV8L", "snr": 5.4, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 2484, "long_name": "Roving Elk", "next_hop": 214, "num": "0x24595fd0", "position": {"altitude": 704, "latitude": 33.437723, "location_source": "LOC_INTERNAL", "longitude": -106.824987, "time_offset_sec": 2523}, "public_key_hex": "7199ef6b37f76349421ef6bfb82b4bb5f0d5a6e009d6fc769c02310b836c412e", "role": "CLIENT", "short_name": "R1XY", "snr": 10.11, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.412, "battery_level": 47, "channel_utilization": 16.49, "uptime_seconds": 43935, "voltage": 3.723}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 5231, "long_name": "Steel Phoenix", "next_hop": 67, "num": "0x24ef37f2", "position": {"altitude": 881, "latitude": 33.382287, "location_source": "LOC_INTERNAL", "longitude": -106.683959, "time_offset_sec": 5469}, "public_key_hex": "9d7e3a32de5ddba3bd77fb3156f58e94be20aeb4b559655b9338c4618c655d17", "role": "LOST_AND_FOUND", "short_name": "🔥", "snr": 4.36, "status": null, "telemetry": {"air_util_tx": 0.683, "battery_level": 36, "channel_utilization": 2.5, "uptime_seconds": 8316, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1023.29, "iaq": 67, "relative_humidity": 56.81, "temperature": 16.91}, "hops_away": 2, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 2457, "long_name": "Hidden Ridge", "next_hop": 121, "num": "0x255e347c", "position": {"altitude": 1191, "latitude": 32.831029, "location_source": "LOC_INTERNAL", "longitude": -107.8584, "time_offset_sec": 2463}, "public_key_hex": "a55a7dcc277e8487d135f7b6f73a8a5378f36d8a105f6c5bf432119cee055d00", "role": "CLIENT", "short_name": "HRX0", "snr": 2.36, "status": {"status": "offline-soon"}, "telemetry": {"air_util_tx": 0.061, "battery_level": 14, "channel_utilization": 40.63, "uptime_seconds": 19839, "voltage": 3.426}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 4139, "long_name": "Lost Eagle", "next_hop": 0, "num": "0x25aa8181", "position": {"altitude": 1358, "latitude": 32.614367, "location_source": "LOC_INTERNAL", "longitude": -107.462449, "time_offset_sec": 4389}, "public_key_hex": "6bbe98fbf5844c3f15b8ecd858fb92bf91e29a76dc7feb61708038e5c45044a2", "role": "CLIENT", "short_name": "LECX", "snr": 4.8, "status": null, "telemetry": {"air_util_tx": 1.039, "battery_level": 101, "channel_utilization": 14.91, "uptime_seconds": 29832, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 6403, "long_name": "Stone Seal", "next_hop": 187, "num": "0x25da47be", "position": {"altitude": 1445, "latitude": 34.003693, "location_source": "LOC_INTERNAL", "longitude": -106.236603, "time_offset_sec": 6660}, "public_key_hex": "552c56b590e2bdd964dbfc232a14a69afad16be2625e6fec65844d55c535eb99", "role": "CLIENT", "short_name": "SLY8", "snr": -0.39, "status": null, "telemetry": {"air_util_tx": 0.525, "battery_level": 76, "channel_utilization": 30.06, "uptime_seconds": 5620, "voltage": 3.984}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1011.6, "iaq": 0, "relative_humidity": 37.72, "temperature": 13.49}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 904, "long_name": "Floating Wolf", "next_hop": 0, "num": "0x25e5b378", "position": {"altitude": 1299, "latitude": 32.597977, "location_source": "LOC_INTERNAL", "longitude": -107.462789, "time_offset_sec": 972}, "public_key_hex": "1619e3db54a4d1696fd63d79a7e2b0a35858ae97ec30bf98fc49550db1a9e8cc", "role": "CLIENT", "short_name": "FLL7", "snr": 1.1, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.401, "battery_level": 32, "channel_utilization": 11.97, "uptime_seconds": 73909, "voltage": 3.588}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 2381, "long_name": "Shady Hawk", "next_hop": 0, "num": "0x25eea078", "position": {"altitude": 1548, "latitude": 32.966234, "location_source": "LOC_INTERNAL", "longitude": -107.004419, "time_offset_sec": 2406}, "public_key_hex": "497a86eed0ca8ad7804229c089b8fd5fbf48839bfd625d02d6872418fa92e852", "role": "CLIENT", "short_name": "SIC9", "snr": 8.74, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.207, "battery_level": 50, "channel_utilization": 12.97, "uptime_seconds": 19430, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1081, "long_name": "Happy Sage", "next_hop": 203, "num": "0x2667d1f8", "position": {"altitude": 1552, "latitude": 33.095074, "location_source": "LOC_INTERNAL", "longitude": -107.13094, "time_offset_sec": 1281}, "public_key_hex": "0c000b564a9244a33072f13726b86b280a1200a0670fad5dcfa817889d69ad42", "role": "CLIENT", "short_name": "H1X4", "snr": 5.61, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 6, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1085, "long_name": "Frosty Phoenix NM1FH", "next_hop": 196, "num": "0x26ca2f96", "position": {"altitude": 1514, "latitude": 32.151259, "location_source": "LOC_INTERNAL", "longitude": -106.862516, "time_offset_sec": 1238}, "public_key_hex": "ce6fcb85cc3e972ffdf1d82ae23f7c1d5a770cbc578fc1574fa7fd665fa6f62e", "role": "CLIENT", "short_name": "FQCL", "snr": 8.5, "status": null, "telemetry": {"air_util_tx": 0.334, "battery_level": 41, "channel_utilization": 18.85, "uptime_seconds": 421633, "voltage": 3.669}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1005.01, "iaq": 68, "relative_humidity": 88.46, "temperature": 26.35}, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 7290, "long_name": "Burning Mustang", "next_hop": 1, "num": "0x27425ae4", "position": null, "public_key_hex": "000e54002f8c0ab1480cbba098217703d232c413abb1ea40e45354716a1eb986", "role": "CLIENT", "short_name": "BN4S", "snr": 6.09, "status": null, "telemetry": {"air_util_tx": 0.245, "battery_level": 22, "channel_utilization": 8.9, "uptime_seconds": 138472, "voltage": 3.498}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4044, "long_name": "Misty Mesa", "next_hop": 166, "num": "0x28189553", "position": {"altitude": 1530, "latitude": 34.237739, "location_source": "LOC_INTERNAL", "longitude": -107.293962, "time_offset_sec": 4114}, "public_key_hex": "7bd4aad3389db08c0ab3187703ad4b8890f3894fe5336f6bf207aa86a2fb6d0d", "role": "CLIENT_BASE", "short_name": "MWNZ", "snr": 3.45, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.33, "iaq": 17, "relative_humidity": 48.69, "temperature": 12.51}, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 5033, "long_name": "Whispering Mesa", "next_hop": 0, "num": "0x286532a5", "position": {"altitude": 1587, "latitude": 33.100997, "location_source": "LOC_INTERNAL", "longitude": -107.578256, "time_offset_sec": 5317}, "public_key_hex": "408f11c37752fb1225f8f6ac0e07af1bc9525af15356cddad6afddbb9cdfd572", "role": "CLIENT_MUTE", "short_name": "WDPB", "snr": 2.02, "status": null, "telemetry": {"air_util_tx": 0.463, "battery_level": 28, "channel_utilization": 3.29, "uptime_seconds": 6869, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 3696, "long_name": "Slow Eagle", "next_hop": 0, "num": "0x2871d2c3", "position": {"altitude": 1315, "latitude": 32.745414, "location_source": "LOC_INTERNAL", "longitude": -107.895128, "time_offset_sec": 3752}, "public_key_hex": "1fff7ed20f2ff6f158999d61b02e3e18377cd1bf32017465ce2458bb7688205d", "role": "CLIENT", "short_name": "🦅", "snr": 5.72, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.312, "battery_level": 18, "channel_utilization": 4.14, "uptime_seconds": 78516, "voltage": 3.462}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 6358, "long_name": "Canyon Bluff", "next_hop": 0, "num": "0x28b78c55", "position": {"altitude": 1051, "latitude": 33.951996, "location_source": "LOC_INTERNAL", "longitude": -108.109663, "time_offset_sec": 6621}, "public_key_hex": "a4748a9966ad143c72cb65ab661b9fa78bc7197600d4149361c1c9fe7790290d", "role": "CLIENT", "short_name": "CUWK", "snr": 2.14, "status": null, "telemetry": {"air_util_tx": 1.151, "battery_level": 101, "channel_utilization": 1.72, "uptime_seconds": 78558, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 2160, "long_name": "Frozen Yucca", "next_hop": 0, "num": "0x2902b034", "position": {"altitude": 2009, "latitude": 32.901567, "location_source": "LOC_INTERNAL", "longitude": -107.182626, "time_offset_sec": 2266}, "public_key_hex": "0a82dcd2a4fbdfd1209f04d2784e33b87e9391a976b92d6d20846aeb4fb48032", "role": "CLIENT", "short_name": "F9PB", "snr": 7.53, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.388, "battery_level": 100, "channel_utilization": 30.03, "uptime_seconds": 127938, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 593, "long_name": "Copper Bluff", "next_hop": 0, "num": "0x294b4ff2", "position": null, "public_key_hex": "95f367b92ac6236e41ff536b3800fee8d14ff4482c678b7a9d3e44d11935e382", "role": "CLIENT", "short_name": "C9HF", "snr": 5.99, "status": null, "telemetry": {"air_util_tx": 0.024, "battery_level": 97, "channel_utilization": 11.51, "uptime_seconds": 16495, "voltage": 4.173}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2761, "long_name": "Frozen Crane", "next_hop": 237, "num": "0x29735d36", "position": {"altitude": 1161, "latitude": 32.957968, "location_source": "LOC_INTERNAL", "longitude": -107.416665, "time_offset_sec": 2855}, "public_key_hex": "d38c2fc7f95c3e4306a68f3ec9ca5eb4c6a4327154b73e50e32adf4f62e71ef9", "role": "TRACKER", "short_name": "F9K1", "snr": 2.16, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.263, "battery_level": 71, "channel_utilization": 12.68, "uptime_seconds": 65271, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1017.33, "iaq": 60, "relative_humidity": 87.04, "temperature": 11.48}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 2916, "long_name": "Happy Moose", "next_hop": 0, "num": "0x29c9c9f0", "position": {"altitude": 1068, "latitude": 33.048205, "location_source": "LOC_INTERNAL", "longitude": -107.182212, "time_offset_sec": 3211}, "public_key_hex": "754a5b838bc5dba0743c1fe2cb527abaacfdcd0eb9956be4def3c4101e79d1e7", "role": "ROUTER", "short_name": "H9AT", "snr": 6.68, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.627, "battery_level": 30, "channel_utilization": 0.9, "uptime_seconds": 83541, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 11816, "long_name": "Short Juniper", "next_hop": 0, "num": "0x29d4f0d6", "position": {"altitude": 1657, "latitude": 33.175304, "location_source": "LOC_INTERNAL", "longitude": -107.340189, "time_offset_sec": 12064}, "public_key_hex": "82daf0da564c692a844e39c61351aba1af6df06fbefb5b8d1067fab533e88f20", "role": "CLIENT", "short_name": "SBUX", "snr": 7.39, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 11612, "long_name": "Sky Eagle", "next_hop": 0, "num": "0x29d75e55", "position": {"altitude": 1363, "latitude": 32.634353, "location_source": "LOC_INTERNAL", "longitude": -106.629271, "time_offset_sec": 11653}, "public_key_hex": "", "role": "CLIENT", "short_name": "SCET", "snr": 4.27, "status": null, "telemetry": {"air_util_tx": 0.121, "battery_level": 76, "channel_utilization": 16.94, "uptime_seconds": 10144, "voltage": 3.984}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 191, "long_name": "Sneaky Bear", "next_hop": 192, "num": "0x2a7eafe7", "position": {"altitude": 1050, "latitude": 33.006934, "location_source": "LOC_INTERNAL", "longitude": -107.180556, "time_offset_sec": 377}, "public_key_hex": "aa4c4b6ce50afd0f90e0c74aad3074c4a3e9bc1f4b7cd7eedacc37c98728d84e", "role": "CLIENT", "short_name": "S2BM", "snr": 1.17, "status": null, "telemetry": {"air_util_tx": 0.268, "battery_level": 70, "channel_utilization": 0.88, "uptime_seconds": 62363, "voltage": 3.93}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 9242, "long_name": "Frozen Lion", "next_hop": 0, "num": "0x2a8ddd3b", "position": {"altitude": 1289, "latitude": 33.408764, "location_source": "LOC_INTERNAL", "longitude": -106.941602, "time_offset_sec": 9277}, "public_key_hex": "36e68f8e390af3201a9c29dfee18c770ea4e06bd0d29bc6103ee153c9a53cbb7", "role": "CLIENT", "short_name": "FUET", "snr": 8.86, "status": null, "telemetry": {"air_util_tx": 0.142, "battery_level": 30, "channel_utilization": 10.54, "uptime_seconds": 67024, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 1682, "long_name": "Quick Lion", "next_hop": 0, "num": "0x2afcb0c0", "position": {"altitude": 1064, "latitude": 33.062992, "location_source": "LOC_INTERNAL", "longitude": -108.242545, "time_offset_sec": 1920}, "public_key_hex": "", "role": "ROUTER", "short_name": "QCH1", "snr": 9.0, "status": null, "telemetry": {"air_util_tx": 0.025, "battery_level": 36, "channel_utilization": 5.1, "uptime_seconds": 864, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 7, "environment": null, "hops_away": 0, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 1956, "long_name": "Sunny Lynx", "next_hop": 0, "num": "0x2b347222", "position": {"altitude": 1296, "latitude": 33.162348, "location_source": "LOC_INTERNAL", "longitude": -107.196228, "time_offset_sec": 2042}, "public_key_hex": "e926767fbe87024d4b7a0af44cae50c39e8c8c5755616a8e59b20d80b9944b15", "role": "CLIENT", "short_name": "SAHV", "snr": 11.11, "status": null, "telemetry": {"air_util_tx": 0.501, "battery_level": 77, "channel_utilization": 8.54, "uptime_seconds": 228867, "voltage": 3.993}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 2557, "long_name": "Silent Pony", "next_hop": 112, "num": "0x2b34a346", "position": {"altitude": 1489, "latitude": 33.126571, "location_source": "LOC_INTERNAL", "longitude": -106.378904, "time_offset_sec": 2563}, "public_key_hex": "f5a89f662c2f3a5f55d478e7c2f33ec9e7f4359b7891a3c229689ea8c0c7d68c", "role": "SENSOR", "short_name": "SL36", "snr": 0.9, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": null, "hops_away": 5, "hw_model": "THINKNODE_M6", "last_heard_offset_sec": 9092, "long_name": "Giant Stag", "next_hop": 227, "num": "0x2b5e052c", "position": {"altitude": 1221, "latitude": 34.103918, "location_source": "LOC_INTERNAL", "longitude": -108.470304, "time_offset_sec": 9124}, "public_key_hex": "6a5ec94870af7d5b5e665de3bd732d04638d7af8fd295b7ac2d69fdc1846163b", "role": "CLIENT", "short_name": "GDT6", "snr": 5.84, "status": null, "telemetry": {"air_util_tx": 0.161, "battery_level": 23, "channel_utilization": 24.35, "uptime_seconds": 52091, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 997.51, "iaq": 54, "relative_humidity": 66.77, "temperature": 21.53}, "hops_away": 6, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 11094, "long_name": "Canyon Turtle", "next_hop": 103, "num": "0x2ba1d378", "position": {"altitude": 1078, "latitude": 32.838468, "location_source": "LOC_INTERNAL", "longitude": -107.571802, "time_offset_sec": 11359}, "public_key_hex": "f463d51e7aaecb4d1428131e72f3b7ea449e6a8d5c22855800867a43e5b10af4", "role": "CLIENT", "short_name": "CMWK", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.194, "battery_level": 86, "channel_utilization": 14.99, "uptime_seconds": 9072, "voltage": 4.074}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 3, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 6651, "long_name": "Tiny Mole", "next_hop": 0, "num": "0x2c3b2dcc", "position": {"altitude": 1171, "latitude": 33.265768, "location_source": "LOC_INTERNAL", "longitude": -107.158108, "time_offset_sec": 6708}, "public_key_hex": "95fefb0414af73334daa18eb1d43b1ef42aed034cc4f34ad42485ae89a6e836a", "role": "CLIENT_BASE", "short_name": "TFKN", "snr": -0.19, "status": null, "telemetry": {"air_util_tx": 1.682, "battery_level": 43, "channel_utilization": 3.54, "uptime_seconds": 333245, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1313, "long_name": "Silent Bison", "next_hop": 95, "num": "0x2c44806f", "position": {"altitude": 1673, "latitude": 33.332687, "location_source": "LOC_INTERNAL", "longitude": -108.403882, "time_offset_sec": 1392}, "public_key_hex": "a1dbb254d365eb8ce1df7ae1ada160c03eaa9dd8eab64d6b86364e347cb732c4", "role": "CLIENT", "short_name": "SO8Q", "snr": 3.28, "status": null, "telemetry": {"air_util_tx": 0.161, "battery_level": 12, "channel_utilization": 2.33, "uptime_seconds": 1147, "voltage": 3.408}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1023.9, "iaq": 70, "relative_humidity": 57.26, "temperature": 23.7}, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 371, "long_name": "Green Shark", "next_hop": 0, "num": "0x2c4f0a46", "position": {"altitude": 1198, "latitude": 32.459029, "location_source": "LOC_INTERNAL", "longitude": -106.520458, "time_offset_sec": 566}, "public_key_hex": "25e2d778098b7e11fcd2dcca143d2b3fb92888c4310bc5116dafa090525f627b", "role": "TAK", "short_name": "GGZ8", "snr": 8.81, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 3647, "long_name": "Sky Crow", "next_hop": 0, "num": "0x2cc1b44c", "position": {"altitude": 1766, "latitude": 33.038761, "location_source": "LOC_INTERNAL", "longitude": -107.317439, "time_offset_sec": 3896}, "public_key_hex": "74f1a468c5ffc4551aa890f3adfaad3c6b546c3037d647a496242260916f9c25", "role": "CLIENT", "short_name": "🐺", "snr": 11.05, "status": null, "telemetry": {"air_util_tx": 0.47, "battery_level": 23, "channel_utilization": 2.74, "uptime_seconds": 129382, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 10432, "long_name": "Old Juniper", "next_hop": 0, "num": "0x2cdbaab1", "position": {"altitude": 1133, "latitude": 33.066707, "location_source": "LOC_INTERNAL", "longitude": -107.620177, "time_offset_sec": 10514}, "public_key_hex": "24325838a5b348c60c12a6de6da9f9d56f0b91cdb4e35ae05d8d7d7b0b60145e", "role": "TRACKER", "short_name": "OTGD", "snr": 1.91, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 786, "long_name": "Drifting Shark", "next_hop": 0, "num": "0x2cffb46b", "position": {"altitude": 1769, "latitude": 33.111323, "location_source": "LOC_INTERNAL", "longitude": -107.792845, "time_offset_sec": 809}, "public_key_hex": "7b2d36372449f86224e31c825f0b83df3242c51e6590d8ee43fdd3d3b6eb2cf4", "role": "CLIENT_MUTE", "short_name": "DMHA", "snr": 2.03, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1002.26, "iaq": 60, "relative_humidity": 52.62, "temperature": 10.59}, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 2156, "long_name": "Bright Pike", "next_hop": 0, "num": "0x2d097018", "position": {"altitude": 1961, "latitude": 32.728872, "location_source": "LOC_INTERNAL", "longitude": -107.467619, "time_offset_sec": 2242}, "public_key_hex": "4032dc18f7ac255f8b45e6d0de53c9e26a87773fe1fa144d0bead43fcfef6dc0", "role": "CLIENT", "short_name": "BLUO", "snr": 9.96, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.296, "battery_level": 26, "channel_utilization": 5.76, "uptime_seconds": 352047, "voltage": 3.534}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1297, "long_name": "White Seal", "next_hop": 203, "num": "0x2d467a1e", "position": {"altitude": 606, "latitude": 32.590838, "location_source": "LOC_INTERNAL", "longitude": -108.031101, "time_offset_sec": 1423}, "public_key_hex": "cc3909ddca959ec85a5651226abfe0466c75711dabfdd4c95c1eace8fa95eaed", "role": "CLIENT", "short_name": "WS08", "snr": 6.64, "status": null, "telemetry": {"air_util_tx": 1.644, "battery_level": 66, "channel_utilization": 8.71, "uptime_seconds": 55069, "voltage": 3.894}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1019.43, "iaq": 55, "relative_humidity": 55.36, "temperature": 21.12}, "hops_away": 0, "hw_model": "MINI_EPAPER_S3", "last_heard_offset_sec": 8020, "long_name": "Rough Bison", "next_hop": 0, "num": "0x2da1c655", "position": {"altitude": 652, "latitude": 33.586611, "location_source": "LOC_INTERNAL", "longitude": -108.107971, "time_offset_sec": 8318}, "public_key_hex": "91b61115d77af4176a7c0816507b80a701deffeb9c71c1f1129aa08ec3e2b8b2", "role": "CLIENT", "short_name": "REY0", "snr": 7.9, "status": null, "telemetry": {"air_util_tx": 0.83, "battery_level": 35, "channel_utilization": 11.3, "uptime_seconds": 47328, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 996.72, "iaq": 31, "relative_humidity": 58.79, "temperature": 13.99}, "hops_away": 0, "hw_model": "THINKNODE_M5", "last_heard_offset_sec": 6928, "long_name": "Floating Falcon", "next_hop": 0, "num": "0x2dff7287", "position": {"altitude": 1298, "latitude": 34.609606, "location_source": "LOC_INTERNAL", "longitude": -106.295517, "time_offset_sec": 7087}, "public_key_hex": "ec530a9f8866ba7523aaea2a20934463ce8a143ddc553e3e85a2d2173007b4a6", "role": "CLIENT", "short_name": "F1BM", "snr": 5.16, "status": null, "telemetry": {"air_util_tx": 0.682, "battery_level": 101, "channel_utilization": 15.94, "uptime_seconds": 19307, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2915, "long_name": "Red Coyote", "next_hop": 0, "num": "0x2e0aa0ef", "position": null, "public_key_hex": "ccfcbc27c57006045a1608fbe32bd1835b6e618d32a1ed4ee86794fb98e9de8b", "role": "CLIENT", "short_name": "R4CS", "snr": 10.93, "status": null, "telemetry": {"air_util_tx": 0.68, "battery_level": 99, "channel_utilization": 19.5, "uptime_seconds": 52545, "voltage": 4.191}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1010.3, "iaq": 45, "relative_humidity": 38.32, "temperature": 25.02}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 2771, "long_name": "Shady Owl", "next_hop": 0, "num": "0x2e5a3396", "position": {"altitude": 1560, "latitude": 33.447145, "location_source": "LOC_INTERNAL", "longitude": -107.147302, "time_offset_sec": 2916}, "public_key_hex": "595bd7bc432fac984064d001e07e153d1a37856a9b1c4425f1a595cd99912bea", "role": "CLIENT", "short_name": "SKRA", "snr": 1.49, "status": null, "telemetry": {"air_util_tx": 1.042, "battery_level": 38, "channel_utilization": 2.38, "uptime_seconds": 4539, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3479, "long_name": "Lost Eagle", "next_hop": 0, "num": "0x2ed3f2c9", "position": {"altitude": 1409, "latitude": 34.047196, "location_source": "LOC_INTERNAL", "longitude": -107.411467, "time_offset_sec": 3584}, "public_key_hex": "901e5d5764542878fd2435626e367e76eb6a8db9ad51ff6de3d1019ff78017fd", "role": "CLIENT", "short_name": "LB5Y", "snr": 4.57, "status": null, "telemetry": {"air_util_tx": 0.888, "battery_level": 85, "channel_utilization": 5.94, "uptime_seconds": 12625, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 2410, "long_name": "Lunar Falcon", "next_hop": 0, "num": "0x2f0f3244", "position": {"altitude": 1196, "latitude": 33.385031, "location_source": "LOC_INTERNAL", "longitude": -108.171963, "time_offset_sec": 2489}, "public_key_hex": "f8615cc5faf7f35fbaddfa32d3e468615a58d7e9113d905271d96d4fa5b0c767", "role": "CLIENT", "short_name": "LCNT", "snr": 6.43, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": {"barometric_pressure": 1008.36, "iaq": 66, "relative_humidity": 32.6, "temperature": 24.35}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 13431, "long_name": "Roving Otter", "next_hop": 0, "num": "0x2f2b0cc3", "position": {"altitude": 1502, "latitude": 33.075668, "location_source": "LOC_INTERNAL", "longitude": -106.545429, "time_offset_sec": 13708}, "public_key_hex": "3fe4d1db3059e2ba5734e2442ad2f192e141d6d02a1000d1124ec34bfe4969c0", "role": "CLIENT", "short_name": "RYEF", "snr": 2.93, "status": {"status": "running"}, "telemetry": {"air_util_tx": 1.196, "battery_level": 85, "channel_utilization": 11.67, "uptime_seconds": 43860, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 4490, "long_name": "Red Cobra", "next_hop": 178, "num": "0x2f335cb1", "position": {"altitude": 1284, "latitude": 32.952871, "location_source": "LOC_INTERNAL", "longitude": -106.45338, "time_offset_sec": 4598}, "public_key_hex": "d38ba579c9c86914d7a46952920363d445a63077cec64ed8eedd2f530f199aac", "role": "CLIENT", "short_name": "R8FE", "snr": 10.84, "status": null, "telemetry": {"air_util_tx": 0.271, "battery_level": 30, "channel_utilization": 5.39, "uptime_seconds": 16626, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1006.84, "iaq": 107, "relative_humidity": 86.07, "temperature": 26.78}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1556, "long_name": "Wild Mamba", "next_hop": 0, "num": "0x2f4a24be", "position": {"altitude": 1283, "latitude": 34.196781, "location_source": "LOC_INTERNAL", "longitude": -108.081588, "time_offset_sec": 1742}, "public_key_hex": "d302285d86ef2cfd19f365606d60be646a461dc86436bd75682ff8c97b9d5629", "role": "CLIENT", "short_name": "WIR0", "snr": 6.83, "status": null, "telemetry": {"air_util_tx": 0.218, "battery_level": 99, "channel_utilization": 10.27, "uptime_seconds": 22497, "voltage": 4.191}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 8074, "long_name": "Short Mustang", "next_hop": 0, "num": "0x2f8041b6", "position": {"altitude": 1629, "latitude": 32.430916, "location_source": "LOC_INTERNAL", "longitude": -106.903131, "time_offset_sec": 8241}, "public_key_hex": "ca5b2ba5ef9580e07e2f24bc7255e9051a47e7051993a3f64d6208f2506d9d98", "role": "CLIENT", "short_name": "SPK3", "snr": 3.93, "status": {"status": "low-batt"}, "telemetry": {"air_util_tx": 0.103, "battery_level": 92, "channel_utilization": 24.36, "uptime_seconds": 89728, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.39, "iaq": 77, "relative_humidity": 67.81, "temperature": 16.65}, "hops_away": 1, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 293, "long_name": "Tiny Bluff", "next_hop": 222, "num": "0x2f9511af", "position": {"altitude": 1078, "latitude": 33.394593, "location_source": "LOC_INTERNAL", "longitude": -107.04634, "time_offset_sec": 572}, "public_key_hex": "5852c0696fce46ca89d723cd0a4f45a629c2536cfbee3e14deb0fda6e827e2fe", "role": "CLIENT", "short_name": "TBIV", "snr": -0.85, "status": null, "telemetry": {"air_util_tx": 1.387, "battery_level": 96, "channel_utilization": 4.88, "uptime_seconds": 30393, "voltage": 4.164}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1010.15, "iaq": 73, "relative_humidity": 69.95, "temperature": 25.35}, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 3521, "long_name": "Silent Hawk NM2XD", "next_hop": 25, "num": "0x2fddcc45", "position": {"altitude": 951, "latitude": 33.560012, "location_source": "LOC_INTERNAL", "longitude": -107.640412, "time_offset_sec": 3551}, "public_key_hex": "4cfdd5c01b12f81cd4f90801d290858e46ea16f21e300805ffe48bd02412bdf8", "role": "ROUTER", "short_name": "SBQM", "snr": 12.0, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.015, "battery_level": 26, "channel_utilization": 12.25, "uptime_seconds": 439034, "voltage": 3.534}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1017.89, "iaq": 36, "relative_humidity": 86.12, "temperature": 32.4}, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 2517, "long_name": "Howling Fox", "next_hop": 102, "num": "0x2ffc54b6", "position": null, "public_key_hex": "94381db82f1ffdc0ce3680dc424a2550f0e3186e33a46b5dbfaf012b8c982c73", "role": "CLIENT", "short_name": "HGU8", "snr": 3.02, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1010.67, "iaq": 34, "relative_humidity": 51.43, "temperature": 31.02}, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 755, "long_name": "Old Lion", "next_hop": 0, "num": "0x3019784d", "position": null, "public_key_hex": "dd70ca76b27534160accff9cfbffa89c475ed6a0df324753942cd9670946b7cd", "role": "CLIENT", "short_name": "🦇", "snr": 4.62, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1010.06, "iaq": 15, "relative_humidity": 61.18, "temperature": 24.46}, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 2640, "long_name": "Steel Aspen", "next_hop": 34, "num": "0x302276b1", "position": {"altitude": 1389, "latitude": 32.178993, "location_source": "LOC_INTERNAL", "longitude": -106.728026, "time_offset_sec": 2827}, "public_key_hex": "10903a4d8c75b7d8d645b213d4bd7bf2e7510008b1058338040e1899ffb88905", "role": "CLIENT", "short_name": "🐝", "snr": -3.89, "status": null, "telemetry": {"air_util_tx": 0.033, "battery_level": 95, "channel_utilization": 16.84, "uptime_seconds": 85688, "voltage": 4.155}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.87, "iaq": 26, "relative_humidity": 38.89, "temperature": 22.29}, "hops_away": 4, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 1055, "long_name": "Canyon Heron", "next_hop": 59, "num": "0x302347c6", "position": {"altitude": 1500, "latitude": 33.175513, "location_source": "LOC_INTERNAL", "longitude": -108.193238, "time_offset_sec": 1148}, "public_key_hex": "13f20da3a657fce0204811efc1ae982c95ccaebb7261a0e5366262f686f210de", "role": "ROUTER", "short_name": "CL68", "snr": 3.4, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.501, "battery_level": 19, "channel_utilization": 13.65, "uptime_seconds": 55497, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1081, "long_name": "Slow Moose", "next_hop": 61, "num": "0x307e37f5", "position": {"altitude": 1723, "latitude": 32.61456, "location_source": "LOC_INTERNAL", "longitude": -107.310215, "time_offset_sec": 1181}, "public_key_hex": "a16dc21e89c07d49ecfc537b7806c9bbc0188f7defbd5cef438c5e43e8ec9ce3", "role": "CLIENT", "short_name": "SJ62", "snr": 4.57, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 2493, "long_name": "Sharp Phoenix", "next_hop": 36, "num": "0x30b51075", "position": {"altitude": 1704, "latitude": 33.168725, "location_source": "LOC_INTERNAL", "longitude": -106.951606, "time_offset_sec": 2683}, "public_key_hex": "35c7f7d6bb4712bb032246ffc038364d6369f4039bbae66478bf682e95c45d45", "role": "CLIENT", "short_name": "SU0J", "snr": 10.27, "status": null, "telemetry": {"air_util_tx": 0.51, "battery_level": 11, "channel_utilization": 26.69, "uptime_seconds": 39780, "voltage": 3.399}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 9096, "long_name": "Drowsy Bronco", "next_hop": 0, "num": "0x30efe3b4", "position": {"altitude": 1245, "latitude": 33.106372, "location_source": "LOC_INTERNAL", "longitude": -106.7179, "time_offset_sec": 9270}, "public_key_hex": "b0e4d9ce00bb3ffc3f329d3ab02eef180478951cc871cf8971b26c0decf26d0c", "role": "CLIENT", "short_name": "DE6W", "snr": 6.29, "status": null, "telemetry": {"air_util_tx": 1.628, "battery_level": 72, "channel_utilization": 10.45, "uptime_seconds": 29813, "voltage": 3.948}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1017.35, "iaq": 91, "relative_humidity": 43.65, "temperature": 18.36}, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 357, "long_name": "Dawn Coyote", "next_hop": 116, "num": "0x30f40c60", "position": {"altitude": 1338, "latitude": 32.549872, "location_source": "LOC_INTERNAL", "longitude": -106.643419, "time_offset_sec": 626}, "public_key_hex": "69ff9ea49887dbb9f206994b2aea3aa7c1428110ad50b54c0dd8ea32b716a4ba", "role": "CLIENT", "short_name": "DFTL", "snr": 0.96, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 1.038, "battery_level": 24, "channel_utilization": 10.63, "uptime_seconds": 54787, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 754, "long_name": "Loud Bass", "next_hop": 0, "num": "0x30fa4c03", "position": {"altitude": 1498, "latitude": 33.853065, "location_source": "LOC_INTERNAL", "longitude": -107.51827, "time_offset_sec": 854}, "public_key_hex": "74834eb0e774da1724557df897960e7f153351e1b4361a69dc4dc2528c302b06", "role": "ROUTER_LATE", "short_name": "LKB1", "snr": 6.84, "status": null, "telemetry": {"air_util_tx": 0.676, "battery_level": 98, "channel_utilization": 8.2, "uptime_seconds": 1124, "voltage": 4.182}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3800, "long_name": "Bright Trout", "next_hop": 19, "num": "0x31328c71", "position": {"altitude": 1732, "latitude": 32.840214, "location_source": "LOC_INTERNAL", "longitude": -107.137479, "time_offset_sec": 4085}, "public_key_hex": "7fc6cad0e2b814bcb1d3fb7f070e737a17c3197f48d16abb9c1a7775155f7d66", "role": "ROUTER", "short_name": "BGVZ", "snr": 3.4, "status": null, "telemetry": {"air_util_tx": 0.948, "battery_level": 23, "channel_utilization": 6.91, "uptime_seconds": 168843, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 4140, "long_name": "Loud Bear", "next_hop": 0, "num": "0x314173e9", "position": {"altitude": 1218, "latitude": 32.880206, "location_source": "LOC_INTERNAL", "longitude": -107.441087, "time_offset_sec": 4236}, "public_key_hex": "c92c8f7bae844f67fa71181009c39e75cf5f68d22de5de67d2a621cfddf57114", "role": "CLIENT", "short_name": "LBDB", "snr": 3.82, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 70, "long_name": "Old Stag", "next_hop": 0, "num": "0x3148a12f", "position": {"altitude": 1020, "latitude": 33.518529, "location_source": "LOC_INTERNAL", "longitude": -106.755016, "time_offset_sec": 93}, "public_key_hex": "4b902cc9230b4885370f304fe23a6075f3faf48bf9b57467c2db1fb8622dcb30", "role": "CLIENT", "short_name": "OJ86", "snr": 4.7, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 1791, "long_name": "Loud Adder", "next_hop": 0, "num": "0x31a6a1e3", "position": {"altitude": 1118, "latitude": 32.634524, "location_source": "LOC_INTERNAL", "longitude": -107.177717, "time_offset_sec": 2041}, "public_key_hex": "299a024b5ba48817147c466d268a5f1a19df19cc757ba4226c9c6f37f7a3f2e9", "role": "CLIENT", "short_name": "LMAU", "snr": 1.54, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.824, "battery_level": 70, "channel_utilization": 8.51, "uptime_seconds": 23034, "voltage": 3.93}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 5550, "long_name": "Desert Cedar", "next_hop": 0, "num": "0x31fff78e", "position": {"altitude": 1916, "latitude": 32.538876, "location_source": "LOC_INTERNAL", "longitude": -107.03072, "time_offset_sec": 5711}, "public_key_hex": "0781a7eb508fc0c538cdd99780f491250f7e5325a63d37fc8ef3664f671991e3", "role": "CLIENT", "short_name": "DGCN", "snr": 7.54, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.935, "battery_level": 17, "channel_utilization": 9.45, "uptime_seconds": 35953, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1024.25, "iaq": 45, "relative_humidity": 30.09, "temperature": 23.56}, "hops_away": 2, "hw_model": "SEEED_WIO_TRACKER_L1_EINK", "last_heard_offset_sec": 1401, "long_name": "Frozen Heron", "next_hop": 103, "num": "0x3210af50", "position": {"altitude": 1273, "latitude": 33.848447, "location_source": "LOC_INTERNAL", "longitude": -107.514036, "time_offset_sec": 1503}, "public_key_hex": "e1e001184ee54ec7c0646a98c35b4561ecec57f3c2f4c10a6885cb6024dffca8", "role": "ROUTER", "short_name": "F5HQ", "snr": 1.84, "status": null, "telemetry": {"air_util_tx": 0.24, "battery_level": 75, "channel_utilization": 24.19, "uptime_seconds": 117051, "voltage": 3.975}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 2567, "long_name": "Brave Pony", "next_hop": 117, "num": "0x32919c69", "position": {"altitude": 1493, "latitude": 33.621492, "location_source": "LOC_INTERNAL", "longitude": -107.271347, "time_offset_sec": 2582}, "public_key_hex": "6dd3bcfb3a06e79a64f2d91f880a7abb3091eca24f9ab291a1e97ae18c61c383", "role": "CLIENT", "short_name": "BP7J", "snr": 9.61, "status": null, "telemetry": {"air_util_tx": 0.122, "battery_level": 71, "channel_utilization": 6.1, "uptime_seconds": 21430, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 2961, "long_name": "Sunny Bronco", "next_hop": 0, "num": "0x32dc1a76", "position": {"altitude": 1417, "latitude": 32.755904, "location_source": "LOC_INTERNAL", "longitude": -107.013091, "time_offset_sec": 3254}, "public_key_hex": "cc1303af364737c5f324c4b320d8c2072124475f10becb923fbfd08708f05021", "role": "CLIENT_MUTE", "short_name": "STBX", "snr": 4.13, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 2894, "long_name": "Storm Mole", "next_hop": 22, "num": "0x32f499a6", "position": {"altitude": 1596, "latitude": 33.814613, "location_source": "LOC_INTERNAL", "longitude": -107.14183, "time_offset_sec": 2962}, "public_key_hex": "7ff62f65c7a67fc4f7852b3faf04002d94522d7dbc22e36be2298374214b0e0d", "role": "CLIENT", "short_name": "SLZ8", "snr": 6.07, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 6321, "long_name": "Silver Bass", "next_hop": 204, "num": "0x330f06d8", "position": {"altitude": 1549, "latitude": 33.241452, "location_source": "LOC_INTERNAL", "longitude": -108.032326, "time_offset_sec": 6536}, "public_key_hex": "5e71597621bf7267184483913b770267bec132efc5d34f75b37e177dd6503eb6", "role": "CLIENT", "short_name": "🌵", "snr": 0.32, "status": null, "telemetry": {"air_util_tx": 0.219, "battery_level": 24, "channel_utilization": 14.56, "uptime_seconds": 6201, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 301, "long_name": "White Doe", "next_hop": 0, "num": "0x33b5c384", "position": {"altitude": 1916, "latitude": 33.342177, "location_source": "LOC_INTERNAL", "longitude": -107.48488, "time_offset_sec": 380}, "public_key_hex": "8dd111fc91dce2a62afa71e4612e4b0778102f5a46d545bdf04b3228dedd608e", "role": "CLIENT_HIDDEN", "short_name": "WENR", "snr": 4.32, "status": null, "telemetry": {"air_util_tx": 0.292, "battery_level": 62, "channel_utilization": 11.98, "uptime_seconds": 54735, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 4872, "long_name": "White Bass", "next_hop": 0, "num": "0x33b6711f", "position": {"altitude": 1275, "latitude": 33.221311, "location_source": "LOC_INTERNAL", "longitude": -107.068917, "time_offset_sec": 5022}, "public_key_hex": "12f331b46977af65cefbbe75943be108c668439b5079ebce47b4ec175f489364", "role": "CLIENT", "short_name": "W3X2", "snr": 11.91, "status": null, "telemetry": {"air_util_tx": 1.203, "battery_level": 50, "channel_utilization": 14.41, "uptime_seconds": 170602, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1790, "long_name": "Soft Pike", "next_hop": 0, "num": "0x348294ad", "position": {"altitude": 710, "latitude": 33.598746, "location_source": "LOC_INTERNAL", "longitude": -108.138437, "time_offset_sec": 1922}, "public_key_hex": "a0f3c269ce8c86665e461ddb1b37ee7705907807ec0ef1f5f9f279c407a0b20c", "role": "CLIENT", "short_name": "🌙", "snr": 7.01, "status": null, "telemetry": {"air_util_tx": 0.308, "battery_level": 65, "channel_utilization": 19.14, "uptime_seconds": 42664, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 2941, "long_name": "Tiny Badger", "next_hop": 38, "num": "0x34bf5541", "position": {"altitude": 1620, "latitude": 32.840343, "location_source": "LOC_INTERNAL", "longitude": -107.512689, "time_offset_sec": 2966}, "public_key_hex": "2a86016d6430e85b0dac4d8e4880491e5312197853d46a24c8871c40188a30db", "role": "CLIENT", "short_name": "TZ4C", "snr": 11.35, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.031, "battery_level": 23, "channel_utilization": 28.95, "uptime_seconds": 147350, "voltage": 3.507}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 5891, "long_name": "New Otter", "next_hop": 0, "num": "0x3543fc07", "position": {"altitude": 1414, "latitude": 34.551594, "location_source": "LOC_INTERNAL", "longitude": -106.134115, "time_offset_sec": 6027}, "public_key_hex": "85b64f1767c97c79d4cb15d12be49ab011e3f85011e21c44d81220b4b86283ca", "role": "CLIENT", "short_name": "N97N", "snr": 11.67, "status": null, "telemetry": {"air_util_tx": 0.612, "battery_level": 77, "channel_utilization": 1.99, "uptime_seconds": 242076, "voltage": 3.993}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 125, "long_name": "Sharp Lion", "next_hop": 141, "num": "0x359c376f", "position": {"altitude": 1180, "latitude": 33.557869, "location_source": "LOC_INTERNAL", "longitude": -107.626806, "time_offset_sec": 138}, "public_key_hex": "", "role": "CLIENT_BASE", "short_name": "SN7T", "snr": 6.47, "status": {"status": "low-batt"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1929, "long_name": "Silent Cedar", "next_hop": 157, "num": "0x35e89ead", "position": null, "public_key_hex": "1489005e7bb07aa8e8695ad0e92706492e40be9c37ccc50f6837ef4d79d03224", "role": "CLIENT", "short_name": "🐢", "snr": 5.67, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 4919, "long_name": "White Ridge", "next_hop": 132, "num": "0x37294851", "position": {"altitude": 979, "latitude": 32.075937, "location_source": "LOC_INTERNAL", "longitude": -107.008495, "time_offset_sec": 5058}, "public_key_hex": "", "role": "CLIENT", "short_name": "WEDP", "snr": 4.49, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 9052, "long_name": "Steel Hawk", "next_hop": 0, "num": "0x372e6f8c", "position": {"altitude": 1797, "latitude": 32.753516, "location_source": "LOC_INTERNAL", "longitude": -108.284545, "time_offset_sec": 9141}, "public_key_hex": "0db68598c24a5862f20efb3157d1f202e4cd43a6a2f7e0ee459016a8a0e3dd9f", "role": "CLIENT", "short_name": "S6VC", "snr": 4.3, "status": null, "telemetry": {"air_util_tx": 0.959, "battery_level": 85, "channel_utilization": 5.57, "uptime_seconds": 20709, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 5158, "long_name": "Solar Adder", "next_hop": 0, "num": "0x375dc1a6", "position": {"altitude": 1177, "latitude": 32.721335, "location_source": "LOC_INTERNAL", "longitude": -107.359895, "time_offset_sec": 5417}, "public_key_hex": "4ba37ff4b9c69ae5c1a759d095d2a09ffd342c2e0110e36027a95c4c76e507b8", "role": "CLIENT", "short_name": "SF6A", "snr": 3.63, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.469, "battery_level": 62, "channel_utilization": 3.24, "uptime_seconds": 68574, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 12875, "long_name": "Sky Pony", "next_hop": 104, "num": "0x379552d2", "position": {"altitude": 1489, "latitude": 33.789051, "location_source": "LOC_INTERNAL", "longitude": -107.119094, "time_offset_sec": 13111}, "public_key_hex": "39e8349e42d7f60352c7aba9e07d6e1b988ecc43d1cb0cb5e102ca97dcb53838", "role": "CLIENT", "short_name": "SP46", "snr": 4.12, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 80, "long_name": "Misty Oak", "next_hop": 67, "num": "0x37b9fab4", "position": {"altitude": 1393, "latitude": 33.269396, "location_source": "LOC_INTERNAL", "longitude": -107.181673, "time_offset_sec": 245}, "public_key_hex": "94e90eec8fe7d12cac241d6fc450a724f1aef2901f60e60abf775639accddddc", "role": "CLIENT", "short_name": "MBZW", "snr": 8.79, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 2543, "long_name": "Dusk Cobra", "next_hop": 176, "num": "0x37dcc34a", "position": {"altitude": 1496, "latitude": 33.340681, "location_source": "LOC_INTERNAL", "longitude": -107.083151, "time_offset_sec": 2608}, "public_key_hex": "8180ae7681f78718a0e3fa25494d569ed1918cbde94dcbd79303d1c5a2558f4b", "role": "LOST_AND_FOUND", "short_name": "🗻", "snr": 1.02, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.035, "battery_level": 68, "channel_utilization": 6.26, "uptime_seconds": 133154, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 3263, "long_name": "Canyon Oak", "next_hop": 0, "num": "0x380f2b0e", "position": {"altitude": 1501, "latitude": 33.048196, "location_source": "LOC_INTERNAL", "longitude": -106.975414, "time_offset_sec": 3425}, "public_key_hex": "ed94a403602015b926702b5631037268e97e122f8dd719158872f828ed258411", "role": "ROUTER_LATE", "short_name": "CR99", "snr": 10.68, "status": null, "telemetry": {"air_util_tx": 0.66, "battery_level": 53, "channel_utilization": 1.08, "uptime_seconds": 90754, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 5718, "long_name": "Shady Crane", "next_hop": 12, "num": "0x38422b34", "position": {"altitude": 1398, "latitude": 31.939514, "location_source": "LOC_INTERNAL", "longitude": -107.647159, "time_offset_sec": 5875}, "public_key_hex": "39a05519ebcc12e828091b42c58f65fae034f53defcbdd07a204f7f568a7c0cc", "role": "CLIENT", "short_name": "SXAJ", "snr": 12.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 2528, "long_name": "Blue Doe", "next_hop": 0, "num": "0x390512a7", "position": {"altitude": 1173, "latitude": 32.38155, "location_source": "LOC_INTERNAL", "longitude": -107.627094, "time_offset_sec": 2567}, "public_key_hex": "f84aa57ff1dbbe3f4b025ddf092f0ba21b802f99cbb58781e31440bb56737ded", "role": "CLIENT", "short_name": "BBXY", "snr": 5.5, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2557, "long_name": "Sky Mustang", "next_hop": 0, "num": "0x39ec6ec9", "position": {"altitude": 1173, "latitude": 33.228267, "location_source": "LOC_INTERNAL", "longitude": -107.236545, "time_offset_sec": 2770}, "public_key_hex": "", "role": "CLIENT", "short_name": "SRG8", "snr": 7.55, "status": null, "telemetry": {"air_util_tx": 0.059, "battery_level": 94, "channel_utilization": 6.87, "uptime_seconds": 34215, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.82, "iaq": 0, "relative_humidity": 37.18, "temperature": 23.8}, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 512, "long_name": "Stone Arroyo", "next_hop": 0, "num": "0x3a813d75", "position": {"altitude": 1630, "latitude": 33.686901, "location_source": "LOC_INTERNAL", "longitude": -107.651696, "time_offset_sec": 694}, "public_key_hex": "8017e8ecf12f6b8730615c2897daaacae5ab46e6a6576c920d924f063965ae4b", "role": "CLIENT", "short_name": "SH40", "snr": 10.31, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.037, "battery_level": 101, "channel_utilization": 14.66, "uptime_seconds": 148162, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.96, "iaq": 52, "relative_humidity": 75.04, "temperature": 32.27}, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_T190", "last_heard_offset_sec": 959, "long_name": "Sneaky Otter", "next_hop": 0, "num": "0x3a84b3a1", "position": {"altitude": 1511, "latitude": 32.411666, "location_source": "LOC_INTERNAL", "longitude": -107.110419, "time_offset_sec": 1253}, "public_key_hex": "37223366dd83347f2e8414b53f15c66b66906ee6a0fc2a3785df2d41398d867f", "role": "CLIENT", "short_name": "SKAE", "snr": 3.55, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 15749, "long_name": "Tall Dolphin", "next_hop": 175, "num": "0x3a8c2376", "position": {"altitude": 1897, "latitude": 32.405617, "location_source": "LOC_INTERNAL", "longitude": -107.218084, "time_offset_sec": 15903}, "public_key_hex": "a97f23293aba138f94d8b449e493576ee5cbff156159ec9d1d3d69f5dab54c69", "role": "CLIENT_HIDDEN", "short_name": "TLMT", "snr": 3.79, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_ECHO", "last_heard_offset_sec": 230, "long_name": "Dusk Tortoise", "next_hop": 177, "num": "0x3ad732db", "position": {"altitude": 1555, "latitude": 33.345739, "location_source": "LOC_INTERNAL", "longitude": -106.721907, "time_offset_sec": 255}, "public_key_hex": "f3b5b31e8f4319acb8e7dc8c4ca2d16c559207fd0bd08c8676e317d8abd219b4", "role": "CLIENT", "short_name": "DJ1E", "snr": 2.32, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 286, "long_name": "Storm Lion", "next_hop": 0, "num": "0x3b2a4f38", "position": {"altitude": 1485, "latitude": 32.976493, "location_source": "LOC_INTERNAL", "longitude": -107.309679, "time_offset_sec": 570}, "public_key_hex": "", "role": "CLIENT", "short_name": "🌲", "snr": 2.6, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.719, "battery_level": 22, "channel_utilization": 2.8, "uptime_seconds": 84144, "voltage": 3.498}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 1135, "long_name": "Loud Pike", "next_hop": 0, "num": "0x3bbdf785", "position": {"altitude": 1010, "latitude": 31.796316, "location_source": "LOC_INTERNAL", "longitude": -106.618855, "time_offset_sec": 1417}, "public_key_hex": "", "role": "CLIENT", "short_name": "L8HA", "snr": 6.95, "status": null, "telemetry": {"air_util_tx": 2.242, "battery_level": 13, "channel_utilization": 13.33, "uptime_seconds": 179196, "voltage": 3.417}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 833, "long_name": "Sunny Turtle", "next_hop": 0, "num": "0x3bec6322", "position": null, "public_key_hex": "a36c93ac8c31e0e366057b42995c278bce4f58303e61ffd82c69d9b6d19c036f", "role": "CLIENT", "short_name": "S8R7", "snr": 10.81, "status": null, "telemetry": {"air_util_tx": 0.106, "battery_level": 24, "channel_utilization": 5.61, "uptime_seconds": 74568, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3132, "long_name": "Storm Hare", "next_hop": 0, "num": "0x3c260256", "position": {"altitude": 1173, "latitude": 33.178951, "location_source": "LOC_INTERNAL", "longitude": -107.43765, "time_offset_sec": 3375}, "public_key_hex": "019a1fcd102952418ce2c96a9ca88b32060dac7f35c9607bbc7b3089a6b7e424", "role": "CLIENT", "short_name": "S7F4", "snr": 5.59, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.565, "battery_level": 101, "channel_utilization": 8.06, "uptime_seconds": 123017, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 495, "long_name": "Dusk Coyote", "next_hop": 30, "num": "0x3c7ec803", "position": null, "public_key_hex": "f5b3257469beb79d06461d9ced20fd6b0f6adcc88af8819ac75733c11b234e58", "role": "CLIENT", "short_name": "DI41", "snr": 9.76, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.13, "battery_level": 101, "channel_utilization": 3.31, "uptime_seconds": 18934, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 20329, "long_name": "Gold Lynx", "next_hop": 120, "num": "0x3cabd975", "position": {"altitude": 1276, "latitude": 32.83804, "location_source": "LOC_INTERNAL", "longitude": -106.289307, "time_offset_sec": 20449}, "public_key_hex": "94f93fe3d04a4a8512b2c1ef538ab300915f177f802f195115d6f9c503d8993c", "role": "CLIENT", "short_name": "G2NU", "snr": 3.88, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 6311, "long_name": "Dawn Yucca", "next_hop": 178, "num": "0x3cb174cf", "position": {"altitude": 909, "latitude": 32.290424, "location_source": "LOC_INTERNAL", "longitude": -107.492656, "time_offset_sec": 6417}, "public_key_hex": "05022520e20550639ca28ef8dc8355cab42603a05ee1faa2c11c265f29cc13a7", "role": "CLIENT_MUTE", "short_name": "DU03", "snr": 4.56, "status": {"status": "running"}, "telemetry": {"air_util_tx": 2.419, "battery_level": 37, "channel_utilization": 13.49, "uptime_seconds": 43314, "voltage": 3.633}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 2703, "long_name": "Sharp Phoenix", "next_hop": 167, "num": "0x3d43985f", "position": {"altitude": 1576, "latitude": 32.197037, "location_source": "LOC_INTERNAL", "longitude": -107.449257, "time_offset_sec": 2818}, "public_key_hex": "3860815ade9fc1911beef95e37cebde1e0ebd7a1f7d8aa1ea78d897b81eb8e06", "role": "CLIENT", "short_name": "SZ7I", "snr": 7.09, "status": null, "telemetry": {"air_util_tx": 0.263, "battery_level": 13, "channel_utilization": 24.4, "uptime_seconds": 4251, "voltage": 3.417}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1008.74, "iaq": 53, "relative_humidity": 47.2, "temperature": 8.43}, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1209, "long_name": "Red Arroyo", "next_hop": 0, "num": "0x3de79994", "position": null, "public_key_hex": "df6274b7cd63d960c7bb72d541153cf794f2d5ad2f9083f283318c2c0d68edff", "role": "CLIENT", "short_name": "RWS6", "snr": 3.89, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 546, "long_name": "Stone Cedar", "next_hop": 0, "num": "0x3e1c0ece", "position": {"altitude": 1595, "latitude": 32.885307, "location_source": "LOC_INTERNAL", "longitude": -107.535058, "time_offset_sec": 744}, "public_key_hex": "c051f4a8daa982873bc2fec7e2647eec49fb097e704e5cfc695b83c1874e8fe7", "role": "CLIENT", "short_name": "SIQG", "snr": -1.15, "status": null, "telemetry": {"air_util_tx": 1.175, "battery_level": 42, "channel_utilization": 2.81, "uptime_seconds": 484989, "voltage": 3.678}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 4819, "long_name": "Sky Gecko", "next_hop": 49, "num": "0x3e2e20ef", "position": {"altitude": 1115, "latitude": 33.106531, "location_source": "LOC_INTERNAL", "longitude": -107.458733, "time_offset_sec": 5073}, "public_key_hex": "c9b9950e932fb3c1ccc7abca60fe1dad4f7653cd9df0412f81e913d143765ca1", "role": "ROUTER", "short_name": "SMVM", "snr": 9.61, "status": null, "telemetry": {"air_util_tx": 0.047, "battery_level": 84, "channel_utilization": 13.63, "uptime_seconds": 91994, "voltage": 4.056}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1509, "long_name": "Shady Crow", "next_hop": 158, "num": "0x3e345006", "position": {"altitude": 861, "latitude": 34.091143, "location_source": "LOC_INTERNAL", "longitude": -108.017718, "time_offset_sec": 1701}, "public_key_hex": "12720e4479465a158aa2c3bb3a70d7f6fada35bd731846f6daf1e93d11f2cd3c", "role": "CLIENT", "short_name": "SPIY", "snr": 7.95, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 1604, "long_name": "Happy Bronco", "next_hop": 0, "num": "0x3e37d297", "position": {"altitude": 1028, "latitude": 33.487795, "location_source": "LOC_INTERNAL", "longitude": -108.381078, "time_offset_sec": 1706}, "public_key_hex": "", "role": "SENSOR", "short_name": "HQS2", "snr": 0.69, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 5, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 279, "long_name": "Sneaky Bison", "next_hop": 72, "num": "0x3e82ffd3", "position": {"altitude": 1935, "latitude": 32.577825, "location_source": "LOC_INTERNAL", "longitude": -107.174252, "time_offset_sec": 441}, "public_key_hex": "7347bc043dc918381d40d8b051a320d52f9dfcfc8821248023171d01c1fdf9bd", "role": "CLIENT", "short_name": "STTN", "snr": 1.29, "status": null, "telemetry": {"air_util_tx": 1.068, "battery_level": 101, "channel_utilization": 13.55, "uptime_seconds": 13102, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": {"barometric_pressure": 1007.63, "iaq": 32, "relative_humidity": 51.24, "temperature": 31.2}, "hops_away": 0, "hw_model": "SEEED_SOLAR_NODE", "last_heard_offset_sec": 988, "long_name": "Wild Mamba", "next_hop": 0, "num": "0x3ef69851", "position": {"altitude": 1700, "latitude": 32.488215, "location_source": "LOC_INTERNAL", "longitude": -106.708983, "time_offset_sec": 1278}, "public_key_hex": "2ef11dda5453dbf8030bc69942f50d41949d9ade3b08fdf988a1898b81931b64", "role": "CLIENT", "short_name": "WK4I", "snr": 3.82, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 7395, "long_name": "Giant Sage", "next_hop": 0, "num": "0x3f4e40a6", "position": {"altitude": 1124, "latitude": 33.227145, "location_source": "LOC_INTERNAL", "longitude": -107.497745, "time_offset_sec": 7540}, "public_key_hex": "7e5a41230314362f9bc3bcc353dc039bfda14de95c0630eb9b08e6901ea33242", "role": "CLIENT", "short_name": "GC2V", "snr": 4.58, "status": null, "telemetry": {"air_util_tx": 2.102, "battery_level": 54, "channel_utilization": 15.97, "uptime_seconds": 580, "voltage": 3.786}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "RAK4631", "last_heard_offset_sec": 8505, "long_name": "Sky Iguana", "next_hop": 107, "num": "0x3f5be4f1", "position": {"altitude": 1117, "latitude": 33.851012, "location_source": "LOC_INTERNAL", "longitude": -107.749199, "time_offset_sec": 8548}, "public_key_hex": "3a799fffecacb9692386c0d7af0284d392b541bde45ffc3e3a2c4251b375e3c6", "role": "CLIENT", "short_name": "S7XR", "snr": 3.26, "status": null, "telemetry": {"air_util_tx": 0.187, "battery_level": 11, "channel_utilization": 0.88, "uptime_seconds": 11913, "voltage": 3.399}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "THINKNODE_M5", "last_heard_offset_sec": 5551, "long_name": "Smooth Lynx", "next_hop": 152, "num": "0x3faae676", "position": {"altitude": 1090, "latitude": 33.621759, "location_source": "LOC_INTERNAL", "longitude": -107.207629, "time_offset_sec": 5624}, "public_key_hex": "6b2df0142fbbd3f0ecae48cbfaa5e226e48ea965f68ee21c0a3529fa681f395f", "role": "CLIENT_MUTE", "short_name": "SLVN", "snr": 8.14, "status": {"status": "weak-signal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 5055, "long_name": "Copper Falcon", "next_hop": 0, "num": "0x3fc1c32e", "position": {"altitude": 1715, "latitude": 33.357183, "location_source": "LOC_INTERNAL", "longitude": -107.29983, "time_offset_sec": 5266}, "public_key_hex": "a8f177a6bf45a3cd0482142ed187d183d34690fa7cad78fccd2ff3c0b9d8ca6b", "role": "CLIENT", "short_name": "CA61", "snr": 3.39, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.372, "battery_level": 42, "channel_utilization": 3.79, "uptime_seconds": 29867, "voltage": 3.678}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 2065, "long_name": "Copper Sage", "next_hop": 81, "num": "0x407a7762", "position": {"altitude": 1109, "latitude": 33.584418, "location_source": "LOC_INTERNAL", "longitude": -106.875554, "time_offset_sec": 2266}, "public_key_hex": "ff0e287fb0024ef961b17b6282670664bc449f0d9514473a1759fd62e9ffc1b5", "role": "CLIENT", "short_name": "CI0W", "snr": 10.75, "status": null, "telemetry": {"air_util_tx": 0.134, "battery_level": 73, "channel_utilization": 13.32, "uptime_seconds": 183501, "voltage": 3.957}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 893, "long_name": "Wild Stag", "next_hop": 0, "num": "0x409da0b9", "position": {"altitude": 1599, "latitude": 32.899158, "location_source": "LOC_INTERNAL", "longitude": -107.531942, "time_offset_sec": 1062}, "public_key_hex": "d3ba91f50140e489296741e63741c381ea2e523f01bcb47917e7a70f117c5d3e", "role": "CLIENT", "short_name": "WFNN", "snr": 9.37, "status": null, "telemetry": {"air_util_tx": 0.551, "battery_level": 78, "channel_utilization": 23.83, "uptime_seconds": 223308, "voltage": 4.002}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3013, "long_name": "White Shark", "next_hop": 166, "num": "0x421ed9cd", "position": null, "public_key_hex": "39b00b0a5880ab4c581c7c992a3c288d353c77eac7d0022b16f17fd53a3fb148", "role": "ROUTER", "short_name": "WWUV", "snr": 10.36, "status": null, "telemetry": {"air_util_tx": 1.01, "battery_level": 60, "channel_utilization": 15.15, "uptime_seconds": 8298, "voltage": 3.84}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 4705, "long_name": "Lone Dolphin", "next_hop": 143, "num": "0x425b3167", "position": {"altitude": 1324, "latitude": 32.239844, "location_source": "LOC_INTERNAL", "longitude": -107.204437, "time_offset_sec": 4903}, "public_key_hex": "67f16b834d9faa0218e6a172b5ce1a425d13a53cbdc7ccdeda6033ff5c16de4c", "role": "CLIENT", "short_name": "🦉", "snr": 1.71, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 798, "long_name": "Red Mamba", "next_hop": 0, "num": "0x42634862", "position": {"altitude": 1468, "latitude": 32.720221, "location_source": "LOC_INTERNAL", "longitude": -106.922564, "time_offset_sec": 833}, "public_key_hex": "5054c78aa27629488c6868c7f84b2fbb0301ea781f67b574855d49887628d8d2", "role": "ROUTER", "short_name": "RL00", "snr": 2.64, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.19, "battery_level": 83, "channel_utilization": 4.48, "uptime_seconds": 4820, "voltage": 4.047}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 1498, "long_name": "Quick Mesa", "next_hop": 0, "num": "0x42b4cb24", "position": {"altitude": 1632, "latitude": 32.996339, "location_source": "LOC_INTERNAL", "longitude": -106.079693, "time_offset_sec": 1708}, "public_key_hex": "94142bb7f81006173eab540143cd692017d4d1919bd1fd17804d604c46170991", "role": "CLIENT", "short_name": "Q633", "snr": 1.13, "status": null, "telemetry": {"air_util_tx": 0.753, "battery_level": 54, "channel_utilization": 11.35, "uptime_seconds": 35851, "voltage": 3.786}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 3, "environment": {"barometric_pressure": 991.66, "iaq": 73, "relative_humidity": 63.16, "temperature": 4.66}, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 606, "long_name": "Happy Colt", "next_hop": 172, "num": "0x42cd002f", "position": {"altitude": 1024, "latitude": 33.287455, "location_source": "LOC_INTERNAL", "longitude": -107.171553, "time_offset_sec": 814}, "public_key_hex": "9eedd9572ef82c539e9c4beb86ae45642d6f8f49f81003d17f329f9f15fd0fad", "role": "CLIENT", "short_name": "HIX1", "snr": 9.97, "status": null, "telemetry": {"air_util_tx": 0.805, "battery_level": 60, "channel_utilization": 6.72, "uptime_seconds": 19641, "voltage": 3.84}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1024.37, "iaq": 71, "relative_humidity": 62.97, "temperature": 16.45}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 10218, "long_name": "Blue Yucca", "next_hop": 0, "num": "0x42cfc132", "position": {"altitude": 788, "latitude": 33.702353, "location_source": "LOC_INTERNAL", "longitude": -106.776716, "time_offset_sec": 10278}, "public_key_hex": "c171e7e65e37259d6b0d1a755d35368bae1ce20bad2834362ed7229a8afefe7f", "role": "CLIENT", "short_name": "🐝", "snr": 5.07, "status": null, "telemetry": {"air_util_tx": 0.941, "battery_level": 34, "channel_utilization": 9.39, "uptime_seconds": 35246, "voltage": 3.606}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 3792, "long_name": "Old Cobra", "next_hop": 0, "num": "0x42dffc4f", "position": {"altitude": 1697, "latitude": 32.803374, "location_source": "LOC_INTERNAL", "longitude": -107.081468, "time_offset_sec": 4018}, "public_key_hex": "c392a2e251f6983101813a5237cbbe02b5119ed360f037d0f33585bcd10f8e60", "role": "CLIENT", "short_name": "OQWC", "snr": -0.14, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "THINKNODE_M6", "last_heard_offset_sec": 5042, "long_name": "Lunar Mustang", "next_hop": 169, "num": "0x433523d0", "position": null, "public_key_hex": "d253a965504c9632d3ce55d66e9c5b67be335079e309154915c60c23ef0973c8", "role": "CLIENT", "short_name": "LEBO", "snr": -3.74, "status": {"status": "online"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK", "last_heard_offset_sec": 4528, "long_name": "White Stag", "next_hop": 3, "num": "0x43fbf777", "position": {"altitude": 1537, "latitude": 33.003847, "location_source": "LOC_INTERNAL", "longitude": -106.629808, "time_offset_sec": 4634}, "public_key_hex": "23c075dac68fa78051ed99c03bc904cb534f9c2acb50882aaf93b271b9fa265e", "role": "SENSOR", "short_name": "🐝", "snr": 7.42, "status": null, "telemetry": {"air_util_tx": 1.153, "battery_level": 82, "channel_utilization": 20.87, "uptime_seconds": 15486, "voltage": 4.038}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2199, "long_name": "Storm Seal", "next_hop": 0, "num": "0x447b9432", "position": {"altitude": 1839, "latitude": 33.347711, "location_source": "LOC_INTERNAL", "longitude": -106.773231, "time_offset_sec": 2322}, "public_key_hex": "44c2ce2da5b8ed09ffb3ec7675c09be2aaba7f89fe9a28d4ff96bddf00eca6e2", "role": "CLIENT", "short_name": "🦇", "snr": 2.56, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 3, "hw_model": "MUZI_R1_NEO", "last_heard_offset_sec": 9703, "long_name": "Gold Whale", "next_hop": 52, "num": "0x4495d3e7", "position": {"altitude": 1687, "latitude": 33.139536, "location_source": "LOC_INTERNAL", "longitude": -107.128211, "time_offset_sec": 9823}, "public_key_hex": "", "role": "CLIENT", "short_name": "G5TG", "snr": 5.28, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 4433, "long_name": "Bright Lynx", "next_hop": 0, "num": "0x4585a4d7", "position": {"altitude": 1508, "latitude": 34.392774, "location_source": "LOC_INTERNAL", "longitude": -107.622506, "time_offset_sec": 4707}, "public_key_hex": "7363006b39bc789935aef32753e5a6b2463f253c6fc81b247dbb4e2c6ee6d8cc", "role": "CLIENT", "short_name": "BEX9", "snr": 5.31, "status": null, "telemetry": {"air_util_tx": 0.123, "battery_level": 93, "channel_utilization": 3.01, "uptime_seconds": 45971, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "RAK4631", "last_heard_offset_sec": 3304, "long_name": "Stone Ridge", "next_hop": 104, "num": "0x464a3092", "position": null, "public_key_hex": "236a842d43af5200d5851b93149ceb271d6401c0d1f9869b4d03ff2b6d9b3504", "role": "CLIENT", "short_name": "S3FD", "snr": 5.87, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.119, "battery_level": 93, "channel_utilization": 6.64, "uptime_seconds": 83, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.56, "iaq": 69, "relative_humidity": 59.24, "temperature": 22.74}, "hops_away": 2, "hw_model": "RAK4631", "last_heard_offset_sec": 1878, "long_name": "Old Coyote", "next_hop": 161, "num": "0x465a3f45", "position": null, "public_key_hex": "589dd2ce3e852dfcebc8383716b51049fedf5958d4c47bfd4fcd8180753c9265", "role": "CLIENT", "short_name": "ODG9", "snr": 12.0, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 996.74, "iaq": 30, "relative_humidity": 22.68, "temperature": 19.77}, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1650, "long_name": "Howling Bear", "next_hop": 26, "num": "0x46829c18", "position": {"altitude": 1186, "latitude": 32.485891, "location_source": "LOC_INTERNAL", "longitude": -106.568956, "time_offset_sec": 1650}, "public_key_hex": "20b91aa8f3a37933c469b27ca1ef6b2ead041017c665e224db5815bcf0ceee06", "role": "CLIENT", "short_name": "HAMZ", "snr": 8.05, "status": null, "telemetry": {"air_util_tx": 0.672, "battery_level": 101, "channel_utilization": 11.65, "uptime_seconds": 108535, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 3288, "long_name": "Smooth Wolf", "next_hop": 0, "num": "0x46d3022f", "position": {"altitude": 1030, "latitude": 33.623966, "location_source": "LOC_INTERNAL", "longitude": -106.987025, "time_offset_sec": 3366}, "public_key_hex": "02d902254db70ef843d914b4a652673b97d5f83869476410da2b0b0d9e3c6bdf", "role": "CLIENT", "short_name": "🐺", "snr": 2.34, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 2607, "long_name": "Iron Otter", "next_hop": 0, "num": "0x4723794e", "position": {"altitude": 1665, "latitude": 33.972929, "location_source": "LOC_INTERNAL", "longitude": -107.536336, "time_offset_sec": 2621}, "public_key_hex": "e50842a4c37590a895eb24393289b2cd43f3389d75f651610d1ce8a7a6363c8d", "role": "CLIENT", "short_name": "I5UV", "snr": 10.29, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.857, "battery_level": 65, "channel_utilization": 34.29, "uptime_seconds": 40045, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 5635, "long_name": "Loud Mole", "next_hop": 189, "num": "0x4745fcb5", "position": {"altitude": 1856, "latitude": 32.963171, "location_source": "LOC_INTERNAL", "longitude": -106.7369, "time_offset_sec": 5846}, "public_key_hex": "eaa31929e29386358a971950bcc69f76cc6e63d7864bb0f333a017881d62798d", "role": "LOST_AND_FOUND", "short_name": "LIM1", "snr": 1.13, "status": {"status": "online"}, "telemetry": {"air_util_tx": 2.709, "battery_level": 94, "channel_utilization": 28.13, "uptime_seconds": 43337, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 10027, "long_name": "Slow Iguana", "next_hop": 0, "num": "0x4747fb52", "position": null, "public_key_hex": "7f4c72ed406a733349893efe8a0954be75f10614bc89b504591a34a42d8ed1d6", "role": "CLIENT", "short_name": "SL24", "snr": 3.13, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.22, "battery_level": 51, "channel_utilization": 2.01, "uptime_seconds": 78386, "voltage": 3.759}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1015.49, "iaq": 29, "relative_humidity": 47.55, "temperature": 17.24}, "hops_away": 1, "hw_model": "XIAO_NRF52_KIT", "last_heard_offset_sec": 3124, "long_name": "Fast Adder", "next_hop": 44, "num": "0x47bce2ba", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "FKTF", "snr": 10.59, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.198, "battery_level": 62, "channel_utilization": 26.66, "uptime_seconds": 43070, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 2073, "long_name": "River Marmot", "next_hop": 0, "num": "0x47c71459", "position": null, "public_key_hex": "422de85eb248649548c824bd9d138293e7d7acce9420b4306f742fcf27da6c94", "role": "CLIENT", "short_name": "🦂", "snr": 9.33, "status": null, "telemetry": {"air_util_tx": 1.326, "battery_level": 38, "channel_utilization": 11.01, "uptime_seconds": 93587, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 987, "long_name": "Howling Gecko", "next_hop": 0, "num": "0x47fb66b1", "position": {"altitude": 1262, "latitude": 33.495338, "location_source": "LOC_INTERNAL", "longitude": -107.307232, "time_offset_sec": 1179}, "public_key_hex": "432574e8c84556bb7f265bb9dd4ffd3b0f85ffea3869b0accdfc47ba65c74b35", "role": "CLIENT", "short_name": "HBFQ", "snr": 5.38, "status": null, "telemetry": {"air_util_tx": 0.999, "battery_level": 14, "channel_utilization": 3.54, "uptime_seconds": 222947, "voltage": 3.426}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 3776, "long_name": "Smooth Cactus", "next_hop": 0, "num": "0x483a694f", "position": {"altitude": 1465, "latitude": 33.427641, "location_source": "LOC_INTERNAL", "longitude": -107.340998, "time_offset_sec": 3834}, "public_key_hex": "f58d38baf30c2b9e7f5352fb608cc61cb8db6fecf43580b179335c916d09c71e", "role": "CLIENT", "short_name": "SCF7", "snr": 4.95, "status": {"status": "weak-signal"}, "telemetry": {"air_util_tx": 0.783, "battery_level": 92, "channel_utilization": 2.41, "uptime_seconds": 122984, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.77, "iaq": 25, "relative_humidity": 19.83, "temperature": 26.33}, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4438, "long_name": "Howling Oak", "next_hop": 189, "num": "0x483c475f", "position": {"altitude": 1427, "latitude": 33.889055, "location_source": "LOC_INTERNAL", "longitude": -107.334535, "time_offset_sec": 4520}, "public_key_hex": "ec54f6144acd6f5a389097a1cdc454e25216a542e339aa758bfd6b5bf6999666", "role": "CLIENT", "short_name": "HP27", "snr": 8.16, "status": null, "telemetry": {"air_util_tx": 0.619, "battery_level": 24, "channel_utilization": 14.2, "uptime_seconds": 76704, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 1418, "long_name": "Brave Raven", "next_hop": 54, "num": "0x483eca7c", "position": {"altitude": 1152, "latitude": 32.978165, "location_source": "LOC_INTERNAL", "longitude": -108.217729, "time_offset_sec": 1425}, "public_key_hex": "e78392b59682d33d2ada4cec85466d16393a3d8d71d2a36d145658c25933367d", "role": "CLIENT", "short_name": "🐢", "snr": 2.07, "status": null, "telemetry": {"air_util_tx": 0.22, "battery_level": 53, "channel_utilization": 11.54, "uptime_seconds": 407445, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 1383, "long_name": "Happy Seal", "next_hop": 132, "num": "0x4854156c", "position": {"altitude": 1442, "latitude": 33.606331, "location_source": "LOC_INTERNAL", "longitude": -106.600787, "time_offset_sec": 1589}, "public_key_hex": "81a71b86b569f004e34cf904aa6e8c5412423ce32aa4183ba187c4fe734b1d14", "role": "CLIENT", "short_name": "HENS", "snr": 9.18, "status": null, "telemetry": {"air_util_tx": 0.237, "battery_level": 58, "channel_utilization": 9.9, "uptime_seconds": 47487, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 1018, "long_name": "Sneaky Cougar", "next_hop": 0, "num": "0x487d2471", "position": null, "public_key_hex": "ebcc5fc22b928136fea4b0ca3c1f5094674e47bbb2eb8e6ffe7874fab8b28f58", "role": "CLIENT", "short_name": "SAKC", "snr": 4.16, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 394, "long_name": "Shady Cobra", "next_hop": 145, "num": "0x48e901f1", "position": {"altitude": 1380, "latitude": 32.872246, "location_source": "LOC_INTERNAL", "longitude": -107.212042, "time_offset_sec": 510}, "public_key_hex": "ad5b6379d1d65f12df4d33cee476e17dff44ce1efb71a74fc55d35a7333c6b52", "role": "SENSOR", "short_name": "SQ69", "snr": 4.21, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.444, "battery_level": 60, "channel_utilization": 15.12, "uptime_seconds": 19835, "voltage": 3.84}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 10719, "long_name": "Brave Otter", "next_hop": 201, "num": "0x490313b5", "position": {"altitude": 1593, "latitude": 33.112042, "location_source": "LOC_INTERNAL", "longitude": -107.730151, "time_offset_sec": 11017}, "public_key_hex": "69e40f351dc853e1aa8fdfc5b913dc73439febe73e3290cbfd2c43b1f6768963", "role": "CLIENT", "short_name": "B8SD", "snr": 5.64, "status": null, "telemetry": {"air_util_tx": 0.148, "battery_level": 44, "channel_utilization": 8.24, "uptime_seconds": 79369, "voltage": 3.696}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 3047, "long_name": "Shady Crow", "next_hop": 0, "num": "0x491344f0", "position": {"altitude": 1296, "latitude": 32.046758, "location_source": "LOC_INTERNAL", "longitude": -107.505796, "time_offset_sec": 3118}, "public_key_hex": "35b6a7167c3329e4dd884e4463bd0f229255e32375d05bc0e72154777e8f5182", "role": "CLIENT", "short_name": "🦉", "snr": 4.89, "status": null, "telemetry": {"air_util_tx": 0.678, "battery_level": 45, "channel_utilization": 28.61, "uptime_seconds": 123590, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2664, "long_name": "Bright Moose", "next_hop": 249, "num": "0x49150ca0", "position": {"altitude": 1185, "latitude": 33.136873, "location_source": "LOC_INTERNAL", "longitude": -107.306155, "time_offset_sec": 2827}, "public_key_hex": "18c470234ec7dd4cd49ccedb8655a11d095fba920d7d695148d569eccfb2e596", "role": "CLIENT", "short_name": "B7Y5", "snr": 7.32, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3760, "long_name": "Happy Bison", "next_hop": 112, "num": "0x49a0c8c0", "position": {"altitude": 1093, "latitude": 34.141068, "location_source": "LOC_INTERNAL", "longitude": -107.324738, "time_offset_sec": 3785}, "public_key_hex": "776c6f1ea4fe04fabd21ef54f4cd6e04772041a21b83dc4e465e5b9c925207f8", "role": "CLIENT_BASE", "short_name": "H31Z", "snr": 4.87, "status": null, "telemetry": {"air_util_tx": 0.722, "battery_level": 53, "channel_utilization": 15.65, "uptime_seconds": 73213, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3548, "long_name": "Sunny Phoenix", "next_hop": 0, "num": "0x4a0630d7", "position": null, "public_key_hex": "8e095643dbfb5687150b592ce52b655bed7182e40bb3805bd5603021bd5b7973", "role": "CLIENT", "short_name": "S67Z", "snr": 5.25, "status": null, "telemetry": {"air_util_tx": 0.862, "battery_level": 33, "channel_utilization": 11.21, "uptime_seconds": 16708, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.65, "iaq": 65, "relative_humidity": 15.51, "temperature": 14.0}, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 1691, "long_name": "Quick Crane", "next_hop": 48, "num": "0x4a7fcb93", "position": {"altitude": 1567, "latitude": 32.745722, "location_source": "LOC_INTERNAL", "longitude": -106.880759, "time_offset_sec": 1734}, "public_key_hex": "", "role": "CLIENT", "short_name": "Q2LX", "snr": 7.11, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 189, "long_name": "Lunar Doe", "next_hop": 201, "num": "0x4ab21734", "position": {"altitude": 1001, "latitude": 33.468173, "location_source": "LOC_INTERNAL", "longitude": -105.934058, "time_offset_sec": 308}, "public_key_hex": "43bfb69a2f86b78000b259d576f8f0352e1e420c5930882690f4281487248bf8", "role": "CLIENT", "short_name": "LO5R", "snr": 3.23, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.37, "battery_level": 10, "channel_utilization": 12.63, "uptime_seconds": 511662, "voltage": 3.39}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 770, "long_name": "Brave Hawk", "next_hop": 0, "num": "0x4ae230a9", "position": {"altitude": 1082, "latitude": 32.741686, "location_source": "LOC_INTERNAL", "longitude": -107.136659, "time_offset_sec": 859}, "public_key_hex": "49be2f27b080df1a5ed4aa7c30212ae34b6f568f173eb78f9ffb3c36344663ff", "role": "CLIENT", "short_name": "BHTB", "snr": 10.44, "status": null, "telemetry": {"air_util_tx": 0.278, "battery_level": 34, "channel_utilization": 4.11, "uptime_seconds": 2907, "voltage": 3.606}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1957, "long_name": "Canyon Oak", "next_hop": 227, "num": "0x4aec3038", "position": null, "public_key_hex": "9dda018df6f1e6ff27f619de60411bb27aa73ce0846de74d5dca1f01036f1c40", "role": "ROUTER_LATE", "short_name": "C9DX", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.298, "battery_level": 31, "channel_utilization": 7.99, "uptime_seconds": 69430, "voltage": 3.579}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 532, "long_name": "Steel Otter", "next_hop": 2, "num": "0x4b187b87", "position": {"altitude": 1352, "latitude": 33.03721, "location_source": "LOC_INTERNAL", "longitude": -108.066405, "time_offset_sec": 807}, "public_key_hex": "c121485dc7bab5d818080c7cbc32f96e2c987d362b00b55221713ad65663f8fb", "role": "SENSOR", "short_name": "S3JY", "snr": 2.94, "status": null, "telemetry": {"air_util_tx": 2.114, "battery_level": 52, "channel_utilization": 20.96, "uptime_seconds": 188564, "voltage": 3.768}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1001.93, "iaq": 13, "relative_humidity": 52.34, "temperature": 15.56}, "hops_away": 1, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 134, "long_name": "Canyon Cobra", "next_hop": 134, "num": "0x4b370785", "position": null, "public_key_hex": "7dfb7417dcd46e90037e97d0f81ff2b4f617fff6e1a29914bad7d1b3176f7dcd", "role": "CLIENT", "short_name": "🌲", "snr": 10.14, "status": null, "telemetry": {"air_util_tx": 0.746, "battery_level": 36, "channel_utilization": 17.54, "uptime_seconds": 155984, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3533, "long_name": "Old Fox", "next_hop": 102, "num": "0x4b4a8774", "position": {"altitude": 1648, "latitude": 32.894622, "location_source": "LOC_INTERNAL", "longitude": -107.582707, "time_offset_sec": 3707}, "public_key_hex": "2083f83ca66ec5c091f40bf757c6e331bf8606a420feb3fc92b57235b882bb92", "role": "CLIENT", "short_name": "OE96", "snr": 12.0, "status": {"status": "running"}, "telemetry": {"air_util_tx": 1.557, "battery_level": 93, "channel_utilization": 6.69, "uptime_seconds": 54186, "voltage": 4.137}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 804, "long_name": "Slow Falcon", "next_hop": 0, "num": "0x4b637625", "position": {"altitude": 797, "latitude": 32.688944, "location_source": "LOC_INTERNAL", "longitude": -107.674431, "time_offset_sec": 1069}, "public_key_hex": "366f4d2e6562fbac0a875f3952383cf43fcfa929fa89e39209d2a851be5d7af2", "role": "CLIENT", "short_name": "SJLO", "snr": 6.1, "status": null, "telemetry": {"air_util_tx": 0.317, "battery_level": 46, "channel_utilization": 12.34, "uptime_seconds": 14979, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 651, "long_name": "Drifting Moose", "next_hop": 0, "num": "0x4b77d530", "position": {"altitude": 1052, "latitude": 34.148328, "location_source": "LOC_INTERNAL", "longitude": -106.696747, "time_offset_sec": 951}, "public_key_hex": "88e3e6b2d5c4b4d573209e2f060a2dc9bd860f8c7dfc2d1606cec8fb242d741f", "role": "CLIENT", "short_name": "DLDU", "snr": 1.83, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.374, "battery_level": 69, "channel_utilization": 17.12, "uptime_seconds": 2429, "voltage": 3.921}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 4768, "long_name": "Old Heron", "next_hop": 0, "num": "0x4b9ee1da", "position": {"altitude": 1137, "latitude": 32.759026, "location_source": "LOC_INTERNAL", "longitude": -106.833195, "time_offset_sec": 5043}, "public_key_hex": "321a0086ee8d59658a42927710adab144ce483f912b38f64b04a39c6ac3e5e56", "role": "CLIENT", "short_name": "OU7P", "snr": 2.01, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.15, "iaq": 20, "relative_humidity": 82.06, "temperature": 30.37}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2498, "long_name": "Stone Elk", "next_hop": 0, "num": "0x4c1991e8", "position": {"altitude": 1529, "latitude": 33.014879, "location_source": "LOC_INTERNAL", "longitude": -107.731089, "time_offset_sec": 2507}, "public_key_hex": "3a08be45aa8e7346b7a03740e9f3896a1bbbe608bcebac459509fd6d0d9dad12", "role": "CLIENT", "short_name": "SLAD", "snr": 0.7, "status": null, "telemetry": {"air_util_tx": 0.548, "battery_level": 78, "channel_utilization": 9.59, "uptime_seconds": 96023, "voltage": 4.002}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.94, "iaq": 20, "relative_humidity": 45.95, "temperature": 6.92}, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 2408, "long_name": "Silver Eagle", "next_hop": 0, "num": "0x4c7f99b5", "position": {"altitude": 1566, "latitude": 33.220154, "location_source": "LOC_INTERNAL", "longitude": -107.446302, "time_offset_sec": 2438}, "public_key_hex": "", "role": "CLIENT", "short_name": "SJ3H", "snr": 2.03, "status": null, "telemetry": {"air_util_tx": 0.399, "battery_level": 28, "channel_utilization": 10.79, "uptime_seconds": 120116, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 445, "long_name": "Misty Shark", "next_hop": 158, "num": "0x4d7dce61", "position": null, "public_key_hex": "63b65466fcaa7cbf18700e7f8ad84289fce0c5fd56946276c2ea83ac02181fe5", "role": "CLIENT", "short_name": "MJTP", "snr": 3.32, "status": null, "telemetry": {"air_util_tx": 0.552, "battery_level": 96, "channel_utilization": 3.19, "uptime_seconds": 175167, "voltage": 4.164}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1017.59, "iaq": 96, "relative_humidity": 49.5, "temperature": 17.54}, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2601, "long_name": "Lost Coyote", "next_hop": 181, "num": "0x4d8f946b", "position": {"altitude": 1546, "latitude": 32.611293, "location_source": "LOC_INTERNAL", "longitude": -107.455073, "time_offset_sec": 2653}, "public_key_hex": "b9c191e8ea6bb9ecb384049a7b09f63a790d954b2cafdd62574c11b184f01a25", "role": "CLIENT", "short_name": "L8A8", "snr": 6.98, "status": null, "telemetry": {"air_util_tx": 1.42, "battery_level": 87, "channel_utilization": 9.8, "uptime_seconds": 31218, "voltage": 4.083}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 3, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3719, "long_name": "Tiny Ridge", "next_hop": 101, "num": "0x4ddc2937", "position": {"altitude": 1220, "latitude": 33.794181, "location_source": "LOC_INTERNAL", "longitude": -107.157124, "time_offset_sec": 3787}, "public_key_hex": "afc8355d4a457c73c1d8cdb22be6ad3e572dc59b71d37c9315c1218c1a43e86a", "role": "CLIENT_MUTE", "short_name": "TTGP", "snr": 4.87, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 5483, "long_name": "Desert Coyote", "next_hop": 0, "num": "0x4e049a8c", "position": {"altitude": 1375, "latitude": 33.384528, "location_source": "LOC_INTERNAL", "longitude": -106.995136, "time_offset_sec": 5607}, "public_key_hex": "35dbef035ae32ba475e26ee69d99bf1ac15e4e53888d43e836eb1cc0e4ed0d6e", "role": "ROUTER", "short_name": "D1LD", "snr": 10.32, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2179, "long_name": "Storm Cedar", "next_hop": 0, "num": "0x4e2d03c2", "position": {"altitude": 1149, "latitude": 32.9513, "location_source": "LOC_INTERNAL", "longitude": -105.825534, "time_offset_sec": 2430}, "public_key_hex": "c6130f6febbc11c8cf77f3ba078643cb45f047ab748286f7b5a539c05ab8a573", "role": "SENSOR", "short_name": "SQEE", "snr": -4.28, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1019.48, "iaq": 57, "relative_humidity": 35.31, "temperature": 26.64}, "hops_away": 0, "hw_model": "MINI_EPAPER_S3", "last_heard_offset_sec": 1885, "long_name": "Sharp Cougar", "next_hop": 0, "num": "0x4f11eba6", "position": {"altitude": 1055, "latitude": 34.90857, "location_source": "LOC_INTERNAL", "longitude": -107.03887, "time_offset_sec": 2026}, "public_key_hex": "642a6cfd1d98bde0fbd3cf4ba1dc04905aa2afee856cd74d63fa80aaa8dee2db", "role": "CLIENT_MUTE", "short_name": "S9A0", "snr": 7.29, "status": null, "telemetry": {"air_util_tx": 0.912, "battery_level": 101, "channel_utilization": 30.44, "uptime_seconds": 129550, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 263, "long_name": "Black Pony", "next_hop": 0, "num": "0x4f31fb85", "position": {"altitude": 1136, "latitude": 31.978064, "location_source": "LOC_INTERNAL", "longitude": -107.569375, "time_offset_sec": 431}, "public_key_hex": "d92a31657f1e0db1f6a5d74d754a2b580d6600b61b5b6ca7b3d69a0bd0cb3949", "role": "ROUTER", "short_name": "BY4G", "snr": 2.29, "status": null, "telemetry": {"air_util_tx": 0.592, "battery_level": 100, "channel_utilization": 6.02, "uptime_seconds": 118228, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 3440, "long_name": "Sunny Wolf", "next_hop": 0, "num": "0x4f3ebf31", "position": {"altitude": 1551, "latitude": 34.316344, "location_source": "LOC_INTERNAL", "longitude": -107.365624, "time_offset_sec": 3568}, "public_key_hex": "ba99e2e6c1767c7f5c5cea3c46b275d14fbaa71c263ae33cfc398b326b557d36", "role": "CLIENT", "short_name": "SWFP", "snr": 8.75, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 543, "long_name": "Misty Bronco", "next_hop": 0, "num": "0x4f4ef47e", "position": {"altitude": 1373, "latitude": 32.574421, "location_source": "LOC_INTERNAL", "longitude": -107.359873, "time_offset_sec": 645}, "public_key_hex": "9d04d02a9ba7d657dbedeab50802d6490f4769c81d66b0d15fe7969a5bdd267d", "role": "CLIENT", "short_name": "MCXF", "snr": 9.47, "status": null, "telemetry": {"air_util_tx": 0.412, "battery_level": 31, "channel_utilization": 12.38, "uptime_seconds": 20434, "voltage": 3.579}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 998.74, "iaq": 63, "relative_humidity": 13.22, "temperature": 20.18}, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 9351, "long_name": "Smooth Crow", "next_hop": 0, "num": "0x4f9acebc", "position": {"altitude": 1108, "latitude": 33.287967, "location_source": "LOC_INTERNAL", "longitude": -107.163393, "time_offset_sec": 9381}, "public_key_hex": "3b816d65102ecf171e0f0f555ce6d8ca4e1ce7ba32367bb9990380c957139d8f", "role": "CLIENT_MUTE", "short_name": "S71S", "snr": 3.07, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.195, "battery_level": 20, "channel_utilization": 26.92, "uptime_seconds": 13428, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 609, "long_name": "Tall Tortoise", "next_hop": 100, "num": "0x4fc83db2", "position": {"altitude": 1896, "latitude": 33.09145, "location_source": "LOC_INTERNAL", "longitude": -106.624006, "time_offset_sec": 618}, "public_key_hex": "f1e091acacb9615e6e8cd0715fc858ad89a9db10fd73fd685419d23c57c6346e", "role": "SENSOR", "short_name": "T2JM", "snr": 8.7, "status": null, "telemetry": {"air_util_tx": 0.314, "battery_level": 55, "channel_utilization": 14.87, "uptime_seconds": 92459, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1016.25, "iaq": 31, "relative_humidity": 77.58, "temperature": 22.82}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1425, "long_name": "Mountain Cedar", "next_hop": 0, "num": "0x4ff32b5e", "position": {"altitude": 1836, "latitude": 33.221978, "location_source": "LOC_INTERNAL", "longitude": -107.595271, "time_offset_sec": 1646}, "public_key_hex": "0befe882264538237ac3631c6941146633a8ce0c571e3c0622d7e9e304aa7929", "role": "CLIENT", "short_name": "MFHA", "snr": 10.17, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.355, "battery_level": 81, "channel_utilization": 12.59, "uptime_seconds": 77130, "voltage": 4.029}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M5", "last_heard_offset_sec": 3454, "long_name": "Happy Shark", "next_hop": 0, "num": "0x5023db81", "position": null, "public_key_hex": "31c73ee9d269a171330ac84440f6645be805a47ddcdb2203b8de2434358f8bea", "role": "CLIENT", "short_name": "HM7R", "snr": 6.0, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.27, "battery_level": 90, "channel_utilization": 8.95, "uptime_seconds": 171267, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 7469, "long_name": "Silent Hawk", "next_hop": 0, "num": "0x508c60c8", "position": {"altitude": 1288, "latitude": 33.001922, "location_source": "LOC_INTERNAL", "longitude": -106.259455, "time_offset_sec": 7716}, "public_key_hex": "ea2bd595e1b796e42fbc44297d60e0531956cd4f3317b6076ce57122fe6f3c86", "role": "CLIENT", "short_name": "SJ20", "snr": 7.66, "status": null, "telemetry": {"air_util_tx": 0.505, "battery_level": 35, "channel_utilization": 7.29, "uptime_seconds": 86288, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.16, "iaq": 40, "relative_humidity": 88.93, "temperature": 37.22}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 3740, "long_name": "Blue Mustang", "next_hop": 0, "num": "0x50b05df0", "position": {"altitude": 1427, "latitude": 33.410179, "location_source": "LOC_INTERNAL", "longitude": -108.284215, "time_offset_sec": 3925}, "public_key_hex": "31d87416d15fe6e101ebd01b7baf580e279a2ce822dd91ab6bee7b596a369e16", "role": "CLIENT", "short_name": "B6GZ", "snr": 2.12, "status": null, "telemetry": {"air_util_tx": 0.898, "battery_level": 86, "channel_utilization": 27.07, "uptime_seconds": 22298, "voltage": 4.074}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1022.43, "iaq": 101, "relative_humidity": 60.39, "temperature": 33.91}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 9252, "long_name": "Found Owl", "next_hop": 0, "num": "0x50dd87e3", "position": {"altitude": 1131, "latitude": 32.556593, "location_source": "LOC_INTERNAL", "longitude": -106.934135, "time_offset_sec": 9491}, "public_key_hex": "336592c4a40d750aa4670787e036588046d62213f3b2f4f95089098bf027a8c0", "role": "ROUTER", "short_name": "F1AY", "snr": 7.97, "status": null, "telemetry": {"air_util_tx": 0.814, "battery_level": 64, "channel_utilization": 9.45, "uptime_seconds": 119563, "voltage": 3.876}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "THINKNODE_M6", "last_heard_offset_sec": 3505, "long_name": "Drifting Pine", "next_hop": 99, "num": "0x51491b42", "position": {"altitude": 1242, "latitude": 33.247823, "location_source": "LOC_INTERNAL", "longitude": -107.706515, "time_offset_sec": 3676}, "public_key_hex": "51ae2f4e7f9b3ff151af4228978d1f2ac23e77da14966f3571c0558c1bfd8b32", "role": "CLIENT", "short_name": "DRWU", "snr": 4.55, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.528, "battery_level": 30, "channel_utilization": 4.94, "uptime_seconds": 40074, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 7461, "long_name": "Howling Tortoise", "next_hop": 0, "num": "0x519de129", "position": {"altitude": 1614, "latitude": 31.546025, "location_source": "LOC_INTERNAL", "longitude": -107.668609, "time_offset_sec": 7581}, "public_key_hex": "50ebe012c33ad935d8424042de6fd34b213b0a944a8f5a39976340707b12d6e9", "role": "CLIENT", "short_name": "H9JH", "snr": 5.57, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.689, "battery_level": 50, "channel_utilization": 21.52, "uptime_seconds": 57636, "voltage": 3.75}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.45, "iaq": 44, "relative_humidity": 31.78, "temperature": 24.49}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1163, "long_name": "Sleepy Marmot", "next_hop": 0, "num": "0x5218f3e7", "position": {"altitude": 1546, "latitude": 33.195416, "location_source": "LOC_INTERNAL", "longitude": -107.135683, "time_offset_sec": 1343}, "public_key_hex": "02c70a7f0ab1ac7229915f55dca20206d905785472e3f0ca8141c099c5f4eabc", "role": "CLIENT", "short_name": "SFPW", "snr": 6.81, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 5932, "long_name": "Green Fox", "next_hop": 0, "num": "0x52bbeaa5", "position": {"altitude": 1757, "latitude": 33.602032, "location_source": "LOC_INTERNAL", "longitude": -107.223963, "time_offset_sec": 6146}, "public_key_hex": "f5d1637c5e0550a7e697bafade0d7ba7212668e331751150942c30406402f419", "role": "CLIENT", "short_name": "GGPI", "snr": 7.64, "status": null, "telemetry": {"air_util_tx": 0.445, "battery_level": 38, "channel_utilization": 3.86, "uptime_seconds": 86972, "voltage": 3.642}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 2971, "long_name": "Copper Arroyo", "next_hop": 251, "num": "0x52f652ad", "position": {"altitude": 1286, "latitude": 32.62743, "location_source": "LOC_INTERNAL", "longitude": -108.063504, "time_offset_sec": 2982}, "public_key_hex": "9f32ddcda90bdf3992ae1d16f9c8c0c78a24b2d55d9555e8e724102164235d36", "role": "CLIENT", "short_name": "CTSY", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.141, "battery_level": 15, "channel_utilization": 25.22, "uptime_seconds": 56615, "voltage": 3.435}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 51, "long_name": "Rough Adder", "next_hop": 0, "num": "0x53436ec5", "position": {"altitude": 1632, "latitude": 33.309706, "location_source": "LOC_INTERNAL", "longitude": -107.307216, "time_offset_sec": 170}, "public_key_hex": "534da73a459a3dfb53510163cff89e5bf20db3ab6c68859462650a786d0497a9", "role": "CLIENT", "short_name": "ROEV", "snr": -0.98, "status": null, "telemetry": {"air_util_tx": 0.826, "battery_level": 68, "channel_utilization": 9.48, "uptime_seconds": 204231, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 435, "long_name": "White Fox", "next_hop": 160, "num": "0x538bf9f2", "position": null, "public_key_hex": "2a08f094239016f205dc2e4016631515b4f53c37c8fb4c9b3a00c01c1e406fd5", "role": "CLIENT", "short_name": "WXY6", "snr": 0.77, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.373, "battery_level": 95, "channel_utilization": 2.63, "uptime_seconds": 252920, "voltage": 4.155}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1019.75, "iaq": 15, "relative_humidity": 30.92, "temperature": 23.8}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1261, "long_name": "Silent Badger", "next_hop": 0, "num": "0x543b382e", "position": {"altitude": 1286, "latitude": 32.104133, "location_source": "LOC_INTERNAL", "longitude": -107.468768, "time_offset_sec": 1454}, "public_key_hex": "88f5c63f5d5b2454974415eae24d6cb018dbca877aebea64f6bdec0af4a81df8", "role": "CLIENT_MUTE", "short_name": "SA9A", "snr": 4.66, "status": null, "telemetry": {"air_util_tx": 1.007, "battery_level": 37, "channel_utilization": 5.87, "uptime_seconds": 137381, "voltage": 3.633}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 2245, "long_name": "Dusk Viper", "next_hop": 0, "num": "0x547cee65", "position": null, "public_key_hex": "dd298a5996289aa6a82c9182024d6441e0238cab10420dea868ffc4ab5a1e8a4", "role": "ROUTER", "short_name": "🌊", "snr": 11.2, "status": {"status": "offline-soon"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 999.33, "iaq": 55, "relative_humidity": 89.01, "temperature": 15.19}, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4943, "long_name": "Green Colt", "next_hop": 198, "num": "0x54c245c7", "position": {"altitude": 1305, "latitude": 33.409967, "location_source": "LOC_INTERNAL", "longitude": -107.484346, "time_offset_sec": 5110}, "public_key_hex": "6171d755c496ec8dfa3186bfb9c5928c40cbc5712b5a955cdbd961a45e43d4a9", "role": "CLIENT", "short_name": "GGIS", "snr": 0.62, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 4821, "long_name": "Silent Hawk", "next_hop": 0, "num": "0x552e163e", "position": null, "public_key_hex": "6ea41c3cbf15684f04cca490e24fde2dd9a375478de136630bd2a6c08062fa3c", "role": "CLIENT", "short_name": "SM6L", "snr": 6.71, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.208, "battery_level": 28, "channel_utilization": 7.63, "uptime_seconds": 33588, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1013.66, "iaq": 32, "relative_humidity": 45.54, "temperature": 29.04}, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 5132, "long_name": "Giant Ridge", "next_hop": 0, "num": "0x5542038a", "position": {"altitude": 1711, "latitude": 32.932384, "location_source": "LOC_INTERNAL", "longitude": -107.960917, "time_offset_sec": 5188}, "public_key_hex": "253fdd50e950a675c5f8392966b54b49c6f2990f9f06b7788199c1b2b46a3eb4", "role": "CLIENT", "short_name": "GYDB", "snr": 8.34, "status": null, "telemetry": {"air_util_tx": 0.425, "battery_level": 97, "channel_utilization": 12.63, "uptime_seconds": 160275, "voltage": 4.173}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1025.23, "iaq": 51, "relative_humidity": 100.0, "temperature": 21.58}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 2309, "long_name": "Frosty Wolf", "next_hop": 0, "num": "0x55734537", "position": {"altitude": 937, "latitude": 33.393204, "location_source": "LOC_INTERNAL", "longitude": -107.850923, "time_offset_sec": 2530}, "public_key_hex": "7e4652e64745f9818402f3468dde97348c5ce54112110556f75dee40bb04e1f8", "role": "TAK_TRACKER", "short_name": "FIQP", "snr": 4.64, "status": null, "telemetry": {"air_util_tx": 1.204, "battery_level": 53, "channel_utilization": 8.74, "uptime_seconds": 140213, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2239, "long_name": "Short Yucca", "next_hop": 0, "num": "0x55799f51", "position": {"altitude": 1188, "latitude": 33.614414, "location_source": "LOC_INTERNAL", "longitude": -107.219677, "time_offset_sec": 2515}, "public_key_hex": "899cfaecd22f954af1d43f588273dee5002e72549b71e8881c18129b8d6c0c00", "role": "CLIENT", "short_name": "SGJO", "snr": 6.31, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.113, "battery_level": 57, "channel_utilization": 2.77, "uptime_seconds": 51030, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 227, "long_name": "Canyon Adder", "next_hop": 43, "num": "0x55fc07e7", "position": {"altitude": 1751, "latitude": 33.220694, "location_source": "LOC_INTERNAL", "longitude": -107.741995, "time_offset_sec": 500}, "public_key_hex": "7a74561a78f535fc95f5890d163818433e9fca44121884787afec20f5aea5ab7", "role": "CLIENT", "short_name": "C5NQ", "snr": 2.48, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.674, "battery_level": 20, "channel_utilization": 8.32, "uptime_seconds": 320816, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 6828, "long_name": "Short Mesa", "next_hop": 0, "num": "0x5607821b", "position": {"altitude": 1447, "latitude": 32.264792, "location_source": "LOC_INTERNAL", "longitude": -107.556886, "time_offset_sec": 6995}, "public_key_hex": "a147c79d0822db6eaf78c8ab0555df6d23b5b4a43dc7921fd5a6bc575c87a39e", "role": "CLIENT", "short_name": "S5IO", "snr": 9.31, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.259, "battery_level": 100, "channel_utilization": 6.39, "uptime_seconds": 59620, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1539, "long_name": "Silent Hare", "next_hop": 0, "num": "0x560ca843", "position": {"altitude": 1427, "latitude": 32.975042, "location_source": "LOC_INTERNAL", "longitude": -106.346847, "time_offset_sec": 1663}, "public_key_hex": "", "role": "CLIENT", "short_name": "🦉", "snr": 5.35, "status": null, "telemetry": {"air_util_tx": 0.756, "battery_level": 98, "channel_utilization": 6.16, "uptime_seconds": 249833, "voltage": 4.182}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 4080, "long_name": "Stone Raven", "next_hop": 0, "num": "0x56801f9c", "position": {"altitude": 1224, "latitude": 33.196324, "location_source": "LOC_INTERNAL", "longitude": -107.29657, "time_offset_sec": 4236}, "public_key_hex": "b542b16c88d335f3c205f46dbaaa5a989fc3eb898e77f4c75e5d5f330ba9b7e6", "role": "ROUTER_LATE", "short_name": "SKCP", "snr": 6.2, "status": null, "telemetry": {"air_util_tx": 0.715, "battery_level": 58, "channel_utilization": 3.8, "uptime_seconds": 356181, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 4671, "long_name": "White Turtle", "next_hop": 178, "num": "0x56cf95b6", "position": {"altitude": 1680, "latitude": 32.541764, "location_source": "LOC_INTERNAL", "longitude": -106.914057, "time_offset_sec": 4815}, "public_key_hex": "55d8e0e2e4054e8fa5887db5caa1688ee1b6aec13d6119ddf7041c377d2246e4", "role": "CLIENT", "short_name": "W1A4", "snr": 0.79, "status": null, "telemetry": {"air_util_tx": 0.28, "battery_level": 51, "channel_utilization": 14.89, "uptime_seconds": 188313, "voltage": 3.759}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "T_DECK", "last_heard_offset_sec": 2526, "long_name": "Howling Lion", "next_hop": 61, "num": "0x57300ef4", "position": {"altitude": 1344, "latitude": 32.916285, "location_source": "LOC_INTERNAL", "longitude": -107.634001, "time_offset_sec": 2775}, "public_key_hex": "50e4eb0ba899c8f757558ce2bbfd5e09dc232399450fe3ca3348399d708020a9", "role": "CLIENT", "short_name": "HLE2", "snr": 8.41, "status": null, "telemetry": {"air_util_tx": 0.033, "battery_level": 42, "channel_utilization": 16.39, "uptime_seconds": 122991, "voltage": 3.678}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 3214, "long_name": "Sleepy Cougar", "next_hop": 0, "num": "0x57352c4b", "position": {"altitude": 1371, "latitude": 32.55933, "location_source": "LOC_INTERNAL", "longitude": -106.986804, "time_offset_sec": 3408}, "public_key_hex": "29b6a9c3fec1a346e1de89825cac16cf8b6d4d33f98f52683a2ebd010888147c", "role": "CLIENT", "short_name": "SL29", "snr": 12.0, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.216, "battery_level": 94, "channel_utilization": 9.51, "uptime_seconds": 48903, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 7817, "long_name": "Black Bear", "next_hop": 0, "num": "0x575d1b79", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "B1JA", "snr": 5.49, "status": null, "telemetry": {"air_util_tx": 1.738, "battery_level": 27, "channel_utilization": 5.98, "uptime_seconds": 24654, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 5513, "long_name": "Steel Tortoise", "next_hop": 0, "num": "0x57b6c2de", "position": {"altitude": 1210, "latitude": 32.811198, "location_source": "LOC_INTERNAL", "longitude": -106.890358, "time_offset_sec": 5527}, "public_key_hex": "3d54a4ebaa20e77dbec8f7a781cb0af6eca5563258e5e5055faab72aeb6af2af", "role": "CLIENT", "short_name": "SGEA", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.971, "battery_level": 47, "channel_utilization": 15.67, "uptime_seconds": 26310, "voltage": 3.723}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 7086, "long_name": "Iron Bronco", "next_hop": 210, "num": "0x5832817c", "position": {"altitude": 1569, "latitude": 32.821781, "location_source": "LOC_INTERNAL", "longitude": -107.140782, "time_offset_sec": 7225}, "public_key_hex": "6c423146b02c426a5dbb7de0c0fc3477a71514105884410b96f512704755c326", "role": "CLIENT", "short_name": "IFIP", "snr": 3.97, "status": null, "telemetry": {"air_util_tx": 1.422, "battery_level": 16, "channel_utilization": 10.66, "uptime_seconds": 63821, "voltage": 3.444}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 1692, "long_name": "Giant Cobra", "next_hop": 0, "num": "0x583c5a5b", "position": {"altitude": 1032, "latitude": 32.813046, "location_source": "LOC_INTERNAL", "longitude": -107.794138, "time_offset_sec": 1832}, "public_key_hex": "962a97a7b5b4aac4806156eb170f177fd5d9ca9e2b4e8d248b5d63fce6d1cac3", "role": "CLIENT", "short_name": "GMJ4", "snr": 3.52, "status": null, "telemetry": {"air_util_tx": 1.012, "battery_level": 45, "channel_utilization": 7.19, "uptime_seconds": 35130, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 126, "long_name": "Fast Lynx KQ3LW", "next_hop": 6, "num": "0x5849da73", "position": null, "public_key_hex": "1a6b6734b2bf587c354c48c13c1e62fa72dc166cae43dc8c870a5b6716245d94", "role": "CLIENT_MUTE", "short_name": "FI93", "snr": 3.16, "status": null, "telemetry": {"air_util_tx": 0.643, "battery_level": 32, "channel_utilization": 23.9, "uptime_seconds": 43328, "voltage": 3.588}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 2425, "long_name": "Storm Trout", "next_hop": 0, "num": "0x586b6883", "position": {"altitude": 1236, "latitude": 32.815968, "location_source": "LOC_INTERNAL", "longitude": -107.542767, "time_offset_sec": 2443}, "public_key_hex": "7dd18327c0124af056634160508731d3378308b0569a4aff289026f9d66f5d3a", "role": "CLIENT", "short_name": "SO1Y", "snr": 0.63, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1021.67, "iaq": 36, "relative_humidity": 41.59, "temperature": 16.27}, "hops_away": 1, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 1775, "long_name": "Quick Cedar W58AU", "next_hop": 6, "num": "0x588f87cf", "position": {"altitude": 1230, "latitude": 33.116822, "location_source": "LOC_INTERNAL", "longitude": -107.137944, "time_offset_sec": 1982}, "public_key_hex": "", "role": "CLIENT", "short_name": "QZB0", "snr": 8.87, "status": null, "telemetry": {"air_util_tx": 1.663, "battery_level": 53, "channel_utilization": 4.08, "uptime_seconds": 9436, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 6678, "long_name": "Fast Hare", "next_hop": 0, "num": "0x58d23c2d", "position": {"altitude": 1440, "latitude": 32.920805, "location_source": "LOC_INTERNAL", "longitude": -107.680564, "time_offset_sec": 6831}, "public_key_hex": "3ff2802508ac7af3b8942c20093932e9eb77600233079968709f921deae7eff1", "role": "CLIENT", "short_name": "FHT9", "snr": 1.98, "status": null, "telemetry": {"air_util_tx": 0.243, "battery_level": 97, "channel_utilization": 6.41, "uptime_seconds": 22521, "voltage": 4.173}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3336, "long_name": "Shady Oak", "next_hop": 171, "num": "0x58d3d466", "position": {"altitude": 1483, "latitude": 34.052506, "location_source": "LOC_INTERNAL", "longitude": -106.57317, "time_offset_sec": 3348}, "public_key_hex": "3258a0e7fe246f0fe1761c5048fa6021095d0542f89b268515c4870f243a99df", "role": "CLIENT", "short_name": "SZA8", "snr": 6.74, "status": null, "telemetry": {"air_util_tx": 0.674, "battery_level": 57, "channel_utilization": 13.16, "uptime_seconds": 69659, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4108, "long_name": "Wild Coyote", "next_hop": 0, "num": "0x590a2b97", "position": null, "public_key_hex": "93580e89ee97f6ac8b0a84305b15d8466cfe969d7843c699bf09320798f99c79", "role": "CLIENT", "short_name": "WBM7", "snr": 4.44, "status": null, "telemetry": {"air_util_tx": 0.406, "battery_level": 19, "channel_utilization": 21.25, "uptime_seconds": 76854, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 5000, "long_name": "Loud Lion", "next_hop": 0, "num": "0x59493fdf", "position": {"altitude": 1707, "latitude": 34.040154, "location_source": "LOC_INTERNAL", "longitude": -108.243014, "time_offset_sec": 5114}, "public_key_hex": "f2d8ed013f3fa29c7e9c4e9629c17cd1b63a73f25b10e816502eb1dbcc563033", "role": "CLIENT", "short_name": "L15F", "snr": 1.09, "status": null, "telemetry": {"air_util_tx": 1.901, "battery_level": 79, "channel_utilization": 3.25, "uptime_seconds": 214227, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 2673, "long_name": "Roving Lion", "next_hop": 0, "num": "0x5b10e9b7", "position": {"altitude": 1321, "latitude": 32.38925, "location_source": "LOC_INTERNAL", "longitude": -107.069099, "time_offset_sec": 2942}, "public_key_hex": "820b486c6495e1ad09e7dab117a1b7552c829fe3d8caf278e8a5facfaf187699", "role": "CLIENT", "short_name": "🐺", "snr": 12.0, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.929, "battery_level": 11, "channel_utilization": 15.13, "uptime_seconds": 80013, "voltage": 3.399}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1026.63, "iaq": 31, "relative_humidity": 79.54, "temperature": 15.58}, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 290, "long_name": "Tall Yucca", "next_hop": 96, "num": "0x5b55f45c", "position": {"altitude": 1730, "latitude": 33.727681, "location_source": "LOC_INTERNAL", "longitude": -106.987046, "time_offset_sec": 291}, "public_key_hex": "18f90c990661567a0a1c270b184201979599918f0cdc8bed391f5020d26537d1", "role": "CLIENT", "short_name": "TG7E", "snr": 7.53, "status": null, "telemetry": {"air_util_tx": 1.309, "battery_level": 66, "channel_utilization": 4.61, "uptime_seconds": 46397, "voltage": 3.894}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 784, "long_name": "Roving Eagle", "next_hop": 0, "num": "0x5b6a8991", "position": {"altitude": 1247, "latitude": 32.625816, "location_source": "LOC_INTERNAL", "longitude": -107.227175, "time_offset_sec": 788}, "public_key_hex": "8e7bc4885275255e537cdfc9e064b2191291f5b6cb57c39c04058d2142ed1563", "role": "CLIENT", "short_name": "R8M5", "snr": 3.96, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.088, "battery_level": 80, "channel_utilization": 9.12, "uptime_seconds": 27191, "voltage": 4.02}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 1110, "long_name": "Sleepy Stag", "next_hop": 195, "num": "0x5b739989", "position": {"altitude": 1427, "latitude": 33.273372, "location_source": "LOC_INTERNAL", "longitude": -107.3321, "time_offset_sec": 1348}, "public_key_hex": "1ce344c6d332c8b1114014e33ae347fca6cd6cfd7e4fd16b8e4443d8efc5c833", "role": "CLIENT", "short_name": "SDQS", "snr": 0.77, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1009.97, "iaq": 48, "relative_humidity": 63.94, "temperature": 23.68}, "hops_away": 1, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 4754, "long_name": "Forest Viper", "next_hop": 55, "num": "0x5be84694", "position": {"altitude": 1526, "latitude": 32.959566, "location_source": "LOC_INTERNAL", "longitude": -106.82651, "time_offset_sec": 4837}, "public_key_hex": "a9ff29378ab3ec7a94f0db45d3946a7a53f1d6b00589dfbfb468b1e1a645f9c4", "role": "CLIENT", "short_name": "FW2V", "snr": 4.24, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 8689, "long_name": "Desert Bronco KE7JW", "next_hop": 0, "num": "0x5bfa9b5d", "position": null, "public_key_hex": "53c3ffd3c55d07a4d8f7f166c59d89c8c478b360e979ff35165f8393fcfa3dad", "role": "CLIENT", "short_name": "DUXZ", "snr": 6.21, "status": null, "telemetry": {"air_util_tx": 0.351, "battery_level": 20, "channel_utilization": 0.33, "uptime_seconds": 24488, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 4542, "long_name": "Misty Badger", "next_hop": 0, "num": "0x5c3e3c24", "position": {"altitude": 1513, "latitude": 32.539944, "location_source": "LOC_INTERNAL", "longitude": -107.688447, "time_offset_sec": 4628}, "public_key_hex": "d852643bcc812b122022a8f74545d621041f1a97fa673eee510cd1e6df8ecf34", "role": "CLIENT", "short_name": "MK6S", "snr": 4.44, "status": null, "telemetry": {"air_util_tx": 1.826, "battery_level": 58, "channel_utilization": 8.23, "uptime_seconds": 41859, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 990.78, "iaq": 13, "relative_humidity": 31.97, "temperature": 33.84}, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 687, "long_name": "Storm Marmot", "next_hop": 203, "num": "0x5c6c119b", "position": {"altitude": 1533, "latitude": 33.031282, "location_source": "LOC_INTERNAL", "longitude": -107.060367, "time_offset_sec": 967}, "public_key_hex": "75a45d69da1eeb52542819b3c001d565ed11b2c2ad052cc2de47646093b9432f", "role": "CLIENT_MUTE", "short_name": "S56P", "snr": 1.13, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.034, "battery_level": 10, "channel_utilization": 13.92, "uptime_seconds": 187090, "voltage": 3.39}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": {"barometric_pressure": 1020.15, "iaq": 49, "relative_humidity": 1.43, "temperature": 23.6}, "hops_away": 2, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 115, "long_name": "Black Iguana", "next_hop": 140, "num": "0x5c9456dd", "position": {"altitude": 1617, "latitude": 32.482529, "location_source": "LOC_INTERNAL", "longitude": -107.675732, "time_offset_sec": 332}, "public_key_hex": "1e691bcc62abda1c9683428874c1091c84b63e69634536bf3d8489d13cf6eedc", "role": "CLIENT", "short_name": "BBYR", "snr": -3.93, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1015.86, "iaq": 53, "relative_humidity": 55.86, "temperature": 20.66}, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 7992, "long_name": "Forest Sage AB0MP", "next_hop": 0, "num": "0x5ca89e4b", "position": {"altitude": 1114, "latitude": 34.16658, "location_source": "LOC_INTERNAL", "longitude": -107.3823, "time_offset_sec": 8103}, "public_key_hex": "139f86aa39c096a4a822b810729ab498ac7bed52377414053b5531c9ba304c2a", "role": "CLIENT", "short_name": "F40B", "snr": 2.22, "status": null, "telemetry": {"air_util_tx": 0.59, "battery_level": 85, "channel_utilization": 10.92, "uptime_seconds": 20989, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 3557, "long_name": "Steel Salmon", "next_hop": 0, "num": "0x5cac2114", "position": null, "public_key_hex": "abbf4e7d6641963ea122c46fec3d2a40a5fb0c206d45d4b7ad78f93ce1cd43d9", "role": "CLIENT", "short_name": "SNWR", "snr": 3.81, "status": {"status": "weak-signal"}, "telemetry": {"air_util_tx": 0.608, "battery_level": 62, "channel_utilization": 20.97, "uptime_seconds": 134297, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1010.84, "iaq": 27, "relative_humidity": 38.56, "temperature": 25.6}, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 2748, "long_name": "Floating Cactus", "next_hop": 135, "num": "0x5cb62187", "position": null, "public_key_hex": "a084a8b6835d6b60e7d808d2f2f65d8b9bd0c2d04fc671113fcc7892f237043f", "role": "ROUTER", "short_name": "🔥", "snr": 12.0, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.303, "battery_level": 39, "channel_utilization": 2.65, "uptime_seconds": 55285, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1701, "long_name": "Sky Colt", "next_hop": 0, "num": "0x5cc29b65", "position": null, "public_key_hex": "2388ec408d9ea1bffa23ddd1e15904454b425c5dade36c7df2dac7d9b3743516", "role": "CLIENT", "short_name": "SRG7", "snr": 6.88, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1029.5, "iaq": 0, "relative_humidity": 14.37, "temperature": 20.27}, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 404, "long_name": "Gold Adder", "next_hop": 0, "num": "0x5cf883f9", "position": {"altitude": 708, "latitude": 32.310391, "location_source": "LOC_INTERNAL", "longitude": -107.199345, "time_offset_sec": 531}, "public_key_hex": "a3cba5d695fe9cadd1b43c91a6584666c33c100d68a8822591dd56f31c623587", "role": "CLIENT", "short_name": "G5FD", "snr": 2.09, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 8072, "long_name": "White Bass", "next_hop": 204, "num": "0x5d1ca7a6", "position": {"altitude": 896, "latitude": 32.738831, "location_source": "LOC_INTERNAL", "longitude": -108.47479, "time_offset_sec": 8103}, "public_key_hex": "56bfeeb88dbe57343040b5bb2f1a4f3cb8e2c10bff76a7ba4d48926e272276a3", "role": "CLIENT", "short_name": "WACF", "snr": 1.84, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4767, "long_name": "Stone Phoenix", "next_hop": 0, "num": "0x5d39a6a5", "position": {"altitude": 1139, "latitude": 33.583354, "location_source": "LOC_INTERNAL", "longitude": -106.054177, "time_offset_sec": 4881}, "public_key_hex": "3c81ba017e67241f3fdc52b7be3210a44fb821859e56dacc070d1ea02fc21bc4", "role": "CLIENT", "short_name": "SE5T", "snr": 8.28, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.244, "battery_level": 76, "channel_utilization": 19.13, "uptime_seconds": 171242, "voltage": 3.984}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 2, "environment": {"barometric_pressure": 1024.29, "iaq": 79, "relative_humidity": 64.85, "temperature": 37.25}, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 60, "long_name": "Iron Shark", "next_hop": 0, "num": "0x5d3edcd2", "position": {"altitude": 1966, "latitude": 33.271395, "location_source": "LOC_INTERNAL", "longitude": -107.208199, "time_offset_sec": 203}, "public_key_hex": "2438dbee39cdbcb7e7cee34b4241d2b4e40094d3605b27641922c41e426eef5e", "role": "CLIENT", "short_name": "IHT6", "snr": -4.25, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1668, "long_name": "Sharp Wolf", "next_hop": 0, "num": "0x5d5700e6", "position": {"altitude": 1779, "latitude": 33.019747, "location_source": "LOC_INTERNAL", "longitude": -106.850405, "time_offset_sec": 1696}, "public_key_hex": "614c80100854937c661183623c445e5b1edd065a92e042d738f3b6514114dade", "role": "CLIENT", "short_name": "S202", "snr": 8.1, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.566, "battery_level": 66, "channel_utilization": 1.53, "uptime_seconds": 113792, "voltage": 3.894}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "T_DECK", "last_heard_offset_sec": 305, "long_name": "Drifting Cobra", "next_hop": 103, "num": "0x5d6fa8cf", "position": null, "public_key_hex": "a3c7e53a06cabf0e12a289cd6b0707782abc901592e02e9398dce6d0178d2243", "role": "CLIENT", "short_name": "DIA1", "snr": -1.89, "status": null, "telemetry": {"air_util_tx": 0.204, "battery_level": 30, "channel_utilization": 21.66, "uptime_seconds": 17851, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 4, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1210, "long_name": "Sneaky Bison", "next_hop": 94, "num": "0x5e6b5cfa", "position": {"altitude": 1516, "latitude": 32.420087, "location_source": "LOC_INTERNAL", "longitude": -107.710817, "time_offset_sec": 1299}, "public_key_hex": "7c7cbb5c42d3ca7961b038d38015e7bb1263f66fdd8324fabbd03edbb9c7140d", "role": "CLIENT_HIDDEN", "short_name": "SCIN", "snr": 3.97, "status": null, "telemetry": {"air_util_tx": 1.823, "battery_level": 22, "channel_utilization": 1.18, "uptime_seconds": 35076, "voltage": 3.498}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.75, "iaq": 89, "relative_humidity": 47.18, "temperature": 22.24}, "hops_away": 0, "hw_model": "SEEED_WIO_TRACKER_L1", "last_heard_offset_sec": 8410, "long_name": "Sunny Colt", "next_hop": 0, "num": "0x5e924b24", "position": {"altitude": 1628, "latitude": 32.631907, "location_source": "LOC_INTERNAL", "longitude": -106.731651, "time_offset_sec": 8620}, "public_key_hex": "b3ad636860caf01eaab96dc15ea0bd89285807900ea8ba40e742fa93a6aaf49c", "role": "CLIENT", "short_name": "S35S", "snr": 3.93, "status": null, "telemetry": {"air_util_tx": 1.236, "battery_level": 46, "channel_utilization": 11.87, "uptime_seconds": 5201, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1016.66, "iaq": 29, "relative_humidity": 42.67, "temperature": 24.87}, "hops_away": 1, "hw_model": "RAK4631", "last_heard_offset_sec": 5740, "long_name": "Quick Seal", "next_hop": 178, "num": "0x5f0f76c2", "position": {"altitude": 1464, "latitude": 32.363427, "location_source": "LOC_INTERNAL", "longitude": -106.984259, "time_offset_sec": 5823}, "public_key_hex": "196f793d29b19686a7dff432ed32f1a5e8798b390d26501419c264242c8d34ae", "role": "CLIENT", "short_name": "Q8CY", "snr": -2.67, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 4986, "long_name": "Dusk Bison", "next_hop": 0, "num": "0x5f147185", "position": {"altitude": 1140, "latitude": 32.142654, "location_source": "LOC_INTERNAL", "longitude": -107.324034, "time_offset_sec": 5047}, "public_key_hex": "fe392798be3ed2ed2c220c44dcaa1ed24ae2a0149caea4930cc9dad75fefeb51", "role": "CLIENT_MUTE", "short_name": "🦅", "snr": 9.35, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 1, "hw_model": "THINKNODE_M1", "last_heard_offset_sec": 741, "long_name": "Desert Phoenix", "next_hop": 119, "num": "0x5f2f9f9b", "position": {"altitude": 1055, "latitude": 33.208668, "location_source": "LOC_INTERNAL", "longitude": -106.618164, "time_offset_sec": 908}, "public_key_hex": "82da9cbc77e469d33e7d805403e01d2dfe35dd84debc5e1cecc3cf8f90bf841e", "role": "CLIENT", "short_name": "DXVI", "snr": -2.18, "status": null, "telemetry": {"air_util_tx": 0.025, "battery_level": 100, "channel_utilization": 2.22, "uptime_seconds": 476020, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1025.44, "iaq": 0, "relative_humidity": 59.94, "temperature": 22.55}, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 4320, "long_name": "Fast Coyote", "next_hop": 241, "num": "0x5f47ac30", "position": {"altitude": 1135, "latitude": 33.264005, "location_source": "LOC_INTERNAL", "longitude": -107.339905, "time_offset_sec": 4565}, "public_key_hex": "26f5501c4afeac4b12f5f99ac41b267d5cc5de96e888c3b05d5bc3c5198ed694", "role": "CLIENT", "short_name": "FT8E", "snr": 6.11, "status": {"status": "no-gps"}, "telemetry": {"air_util_tx": 1.158, "battery_level": 92, "channel_utilization": 1.46, "uptime_seconds": 90355, "voltage": 4.128}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 4821, "long_name": "Wandering Cougar", "next_hop": 6, "num": "0x5fc9d6fa", "position": {"altitude": 984, "latitude": 32.634939, "location_source": "LOC_INTERNAL", "longitude": -107.25922, "time_offset_sec": 4949}, "public_key_hex": "4494a75e3e4d3a7a44ca2944ef45e436d1ce973daeed6b46333a647a7acd28d8", "role": "CLIENT_MUTE", "short_name": "W99T", "snr": 4.11, "status": {"status": "rebooted"}, "telemetry": {"air_util_tx": 0.117, "battery_level": 71, "channel_utilization": 4.77, "uptime_seconds": 42360, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 309, "long_name": "Howling Falcon", "next_hop": 43, "num": "0x60a76bbe", "position": null, "public_key_hex": "0cea29776523ebb2443ec07bf5059a2aa62733d12a7f229decfdec3d11cd17a5", "role": "TRACKER", "short_name": "HRDU", "snr": 6.41, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.714, "battery_level": 95, "channel_utilization": 0.97, "uptime_seconds": 280797, "voltage": 4.155}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1020.15, "iaq": 65, "relative_humidity": 76.09, "temperature": 28.15}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2001, "long_name": "Silent Shark", "next_hop": 0, "num": "0x60acb50d", "position": null, "public_key_hex": "d7d3f2084a457d171e595de3ceaed0362aa8a82840b030aeaa33c37a569b34e6", "role": "CLIENT", "short_name": "SG2A", "snr": 10.32, "status": {"status": "OK"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 2071, "long_name": "Dawn Oak", "next_hop": 205, "num": "0x60d52493", "position": null, "public_key_hex": "", "role": "CLIENT", "short_name": "D57H", "snr": 2.67, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.845, "battery_level": 57, "channel_utilization": 20.85, "uptime_seconds": 23577, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 408, "long_name": "Found Bronco", "next_hop": 16, "num": "0x6126e11d", "position": {"altitude": 1713, "latitude": 32.328562, "location_source": "LOC_INTERNAL", "longitude": -107.831678, "time_offset_sec": 622}, "public_key_hex": "5f1ede392e100f37d57c2c855a75ce067b5fa69354e7452a7de5c8f1f9ffd16f", "role": "CLIENT", "short_name": "FUAB", "snr": 4.63, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 6386, "long_name": "Soft Dolphin", "next_hop": 89, "num": "0x6134fee3", "position": {"altitude": 1455, "latitude": 33.08712, "location_source": "LOC_INTERNAL", "longitude": -107.295562, "time_offset_sec": 6664}, "public_key_hex": "0fd08b5fd2e1567150219561d969bec29ef910c420efcd5279c7aea5045e5e49", "role": "TAK_TRACKER", "short_name": "S1JZ", "snr": 2.74, "status": {"status": "low-batt"}, "telemetry": {"air_util_tx": 0.143, "battery_level": 43, "channel_utilization": 6.63, "uptime_seconds": 24021, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3325, "long_name": "River Bluff", "next_hop": 57, "num": "0x6143515c", "position": {"altitude": 1670, "latitude": 33.826539, "location_source": "LOC_INTERNAL", "longitude": -107.469218, "time_offset_sec": 3368}, "public_key_hex": "32c1968b4e8bd4c56e62032c9d4a5864196bc83a8687fd629425a8f087d0d1d3", "role": "CLIENT", "short_name": "RL69", "snr": 4.78, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.25, "battery_level": 90, "channel_utilization": 20.09, "uptime_seconds": 248749, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 1133, "long_name": "Blue Eagle", "next_hop": 0, "num": "0x614c0e01", "position": null, "public_key_hex": "2c031998d927fa7b524f85fc64952609201c92ab2b966956ffbf4b09be0a610f", "role": "CLIENT", "short_name": "B2AH", "snr": 7.76, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.083, "battery_level": 43, "channel_utilization": 4.71, "uptime_seconds": 3918, "voltage": 3.687}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 3, "environment": {"barometric_pressure": 995.19, "iaq": 48, "relative_humidity": 57.07, "temperature": 18.89}, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 1317, "long_name": "Sharp Arroyo", "next_hop": 90, "num": "0x618a05ed", "position": {"altitude": 1170, "latitude": 32.531093, "location_source": "LOC_INTERNAL", "longitude": -106.511411, "time_offset_sec": 1427}, "public_key_hex": "dccf199986299e0640f02a00b8c256b76490edaba14d24498565d137f68d05f9", "role": "CLIENT", "short_name": "ST9J", "snr": -0.14, "status": null, "telemetry": {"air_util_tx": 0.181, "battery_level": 71, "channel_utilization": 4.5, "uptime_seconds": 5744, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 4758, "long_name": "Brave Otter", "next_hop": 0, "num": "0x619f7ad2", "position": null, "public_key_hex": "03b2fd5dcfc651ab528b2d5a6f09af129027c756cf7ca681d21723adfc1c1958", "role": "CLIENT", "short_name": "BIC2", "snr": 8.86, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 1.417, "battery_level": 41, "channel_utilization": 7.37, "uptime_seconds": 6155, "voltage": 3.669}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1027.12, "iaq": 73, "relative_humidity": 51.07, "temperature": 25.8}, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3530, "long_name": "Burning Tortoise", "next_hop": 119, "num": "0x61d754f1", "position": {"altitude": 1192, "latitude": 32.554766, "location_source": "LOC_INTERNAL", "longitude": -107.670555, "time_offset_sec": 3766}, "public_key_hex": "112ff04d11fd11f1683100afd66c85cc606fa7280884df18a08e1f03593db308", "role": "CLIENT", "short_name": "B87D", "snr": 5.43, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.132, "battery_level": 45, "channel_utilization": 7.09, "uptime_seconds": 23000, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "THINKNODE_M2", "last_heard_offset_sec": 6314, "long_name": "Roving Hawk", "next_hop": 79, "num": "0x61ecc35f", "position": null, "public_key_hex": "", "role": "ROUTER", "short_name": "🐺", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 1.251, "battery_level": 16, "channel_utilization": 21.55, "uptime_seconds": 11974, "voltage": 3.444}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 1614, "long_name": "Mountain Pony", "next_hop": 0, "num": "0x62a08a3d", "position": {"altitude": 1014, "latitude": 32.731474, "location_source": "LOC_INTERNAL", "longitude": -107.127675, "time_offset_sec": 1734}, "public_key_hex": "", "role": "CLIENT", "short_name": "MLWS", "snr": 4.12, "status": null, "telemetry": {"air_util_tx": 0.103, "battery_level": 31, "channel_utilization": 10.83, "uptime_seconds": 18147, "voltage": 3.579}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 1753, "long_name": "Black Mustang", "next_hop": 0, "num": "0x62aeb717", "position": null, "public_key_hex": "7a34ed04662ba4469e5698e6b22f196b5f1efaf282452f44b28ccd54a47e0c8c", "role": "TAK", "short_name": "B15H", "snr": 2.04, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 3452, "long_name": "Shady Pony", "next_hop": 0, "num": "0x62e16e9d", "position": {"altitude": 1345, "latitude": 33.943778, "location_source": "LOC_INTERNAL", "longitude": -107.49073, "time_offset_sec": 3628}, "public_key_hex": "4e5d5b74d7a2614f86b3ae23ef6f4e59d1cd9aefda74648d4eedcf000ffc7800", "role": "CLIENT", "short_name": "S38R", "snr": 2.68, "status": null, "telemetry": {"air_util_tx": 1.361, "battery_level": 81, "channel_utilization": 6.38, "uptime_seconds": 100368, "voltage": 4.029}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 7272, "long_name": "Lost Bison", "next_hop": 90, "num": "0x63122ae7", "position": {"altitude": 1500, "latitude": 32.953909, "location_source": "LOC_INTERNAL", "longitude": -107.21476, "time_offset_sec": 7292}, "public_key_hex": "a627fe57fe5478e2ff8c532fecad12d3037ee2adfca23980da9d6eb9ece0d5f9", "role": "TRACKER", "short_name": "LIRB", "snr": -0.65, "status": null, "telemetry": {"air_util_tx": 0.872, "battery_level": 49, "channel_utilization": 2.93, "uptime_seconds": 51703, "voltage": 3.741}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 3216, "long_name": "Drowsy Eagle", "next_hop": 49, "num": "0x6410b8ec", "position": {"altitude": 1310, "latitude": 33.010542, "location_source": "LOC_INTERNAL", "longitude": -106.403195, "time_offset_sec": 3354}, "public_key_hex": "bee10be3a33bb9e755411dd0e03149befe46868e7378bb35817edad58d5b462f", "role": "CLIENT", "short_name": "DI1D", "snr": 4.99, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 10233, "long_name": "Frozen Ridge K16WD", "next_hop": 249, "num": "0x643d413d", "position": {"altitude": 1318, "latitude": 32.853899, "location_source": "LOC_INTERNAL", "longitude": -108.16199, "time_offset_sec": 10354}, "public_key_hex": "08e5488221ef6ddb8dbb1586d41011ecc00fb8b74cc10c5639bc95e5c3151403", "role": "CLIENT", "short_name": "FVA6", "snr": 6.26, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.709, "battery_level": 59, "channel_utilization": 17.97, "uptime_seconds": 42772, "voltage": 3.831}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 6694, "long_name": "Dawn Seal", "next_hop": 27, "num": "0x6450c3fb", "position": {"altitude": 1376, "latitude": 32.920828, "location_source": "LOC_INTERNAL", "longitude": -107.07977, "time_offset_sec": 6772}, "public_key_hex": "7c6657dca30be8e3ece68a504aa2d304d472eb3e4e2fef885d848af1e7431f81", "role": "ROUTER", "short_name": "DS11", "snr": -0.65, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.58, "iaq": 69, "relative_humidity": 90.68, "temperature": 14.54}, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 2177, "long_name": "Drowsy Raven", "next_hop": 0, "num": "0x645bc141", "position": {"altitude": 1036, "latitude": 32.917715, "location_source": "LOC_INTERNAL", "longitude": -107.72338, "time_offset_sec": 2382}, "public_key_hex": "5e0ad59c291ae909ad1fc13618f9e5b3fde276352e6e1511bab5b86a615afa18", "role": "CLIENT", "short_name": "DCNF", "snr": 2.28, "status": null, "telemetry": {"air_util_tx": 0.216, "battery_level": 17, "channel_utilization": 5.1, "uptime_seconds": 72205, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1005.46, "iaq": 43, "relative_humidity": 100.0, "temperature": 5.43}, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 4079, "long_name": "Soft Pine", "next_hop": 0, "num": "0x655b61d3", "position": {"altitude": 962, "latitude": 33.818742, "location_source": "LOC_INTERNAL", "longitude": -107.759172, "time_offset_sec": 4092}, "public_key_hex": "d0cbe0fe25b919f38550f3f9561074c0f06aaff8a124b75435b9ac36166366dd", "role": "CLIENT_MUTE", "short_name": "S6K0", "snr": 3.41, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.811, "battery_level": 75, "channel_utilization": 11.02, "uptime_seconds": 104419, "voltage": 3.975}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1005.64, "iaq": 113, "relative_humidity": 27.29, "temperature": 14.93}, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1527, "long_name": "Slow Mamba", "next_hop": 0, "num": "0x65684a77", "position": {"altitude": 1479, "latitude": 32.519337, "location_source": "LOC_INTERNAL", "longitude": -107.090347, "time_offset_sec": 1738}, "public_key_hex": "768177b124e3416bc53827cbe3eac32d2a122c7652347034363b64d03145f34e", "role": "CLIENT", "short_name": "SK3G", "snr": 2.76, "status": null, "telemetry": {"air_util_tx": 1.46, "battery_level": 100, "channel_utilization": 5.93, "uptime_seconds": 321087, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 87, "long_name": "Sunny Sage", "next_hop": 0, "num": "0x6597cc10", "position": {"altitude": 1022, "latitude": 32.086383, "location_source": "LOC_INTERNAL", "longitude": -107.871811, "time_offset_sec": 167}, "public_key_hex": "f8b0606a45371cf8eecf6ceca7086ab921f4aff7289ab5bafef1dfb1500b5d4b", "role": "ROUTER", "short_name": "🌲", "snr": 7.83, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.025, "battery_level": 20, "channel_utilization": 2.73, "uptime_seconds": 227551, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 5629, "long_name": "River Oak", "next_hop": 0, "num": "0x65a37178", "position": {"altitude": 1177, "latitude": 33.73494, "location_source": "LOC_INTERNAL", "longitude": -108.007027, "time_offset_sec": 5778}, "public_key_hex": "e57787d4990caeada3d54663b25f50e756b11bf275913378f1b5414fa34e00d1", "role": "CLIENT", "short_name": "RBKG", "snr": 8.68, "status": null, "telemetry": {"air_util_tx": 0.108, "battery_level": 45, "channel_utilization": 19.0, "uptime_seconds": 40769, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.81, "iaq": 101, "relative_humidity": 25.77, "temperature": 14.31}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1165, "long_name": "Wild Ridge", "next_hop": 0, "num": "0x65a7b407", "position": {"altitude": 1447, "latitude": 33.54226, "location_source": "LOC_INTERNAL", "longitude": -106.895898, "time_offset_sec": 1228}, "public_key_hex": "58b7fca526c5ed88a273674f7fac0ae3b9be0ce980c0775fb1bc0ce3ea5cca37", "role": "CLIENT", "short_name": "WXI2", "snr": 9.41, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.174, "battery_level": 58, "channel_utilization": 23.06, "uptime_seconds": 67457, "voltage": 3.822}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 5989, "long_name": "Lunar Cougar", "next_hop": 0, "num": "0x65af31dc", "position": {"altitude": 1074, "latitude": 32.16723, "location_source": "LOC_INTERNAL", "longitude": -107.285076, "time_offset_sec": 6182}, "public_key_hex": "cda47ed74da113fd8789dfd518c2d5b2f93df64c481784585fb10dcec1c0eeb2", "role": "TRACKER", "short_name": "LC1J", "snr": 4.62, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 2437, "long_name": "Rough Falcon", "next_hop": 0, "num": "0x65b43e72", "position": {"altitude": 1280, "latitude": 33.265534, "location_source": "LOC_INTERNAL", "longitude": -107.716745, "time_offset_sec": 2715}, "public_key_hex": "6d4ec445477406994a457799137b4911d74d6d2e6a79c3826a5cf6569ba64af8", "role": "CLIENT", "short_name": "RUFF", "snr": 3.32, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.123, "battery_level": 82, "channel_utilization": 6.32, "uptime_seconds": 6122, "voltage": 4.038}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "THINKNODE_M3", "last_heard_offset_sec": 337, "long_name": "Shady Ridge", "next_hop": 0, "num": "0x667d0bb4", "position": null, "public_key_hex": "f912306b415f7aa4da19556f1bfb6c9f7fb0c1e29580742078b4493e6c8350c7", "role": "CLIENT", "short_name": "S004", "snr": -0.57, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.624, "battery_level": 33, "channel_utilization": 15.8, "uptime_seconds": 5462, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "T_DECK", "last_heard_offset_sec": 966, "long_name": "River Mole", "next_hop": 78, "num": "0x66a9fcd8", "position": null, "public_key_hex": "92ae245a2ed70681ef79621614b8385429e6547a47a1acd7a7435296cba6606f", "role": "CLIENT", "short_name": "R7QG", "snr": 9.6, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.625, "battery_level": 61, "channel_utilization": 12.36, "uptime_seconds": 17187, "voltage": 3.849}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 603, "long_name": "Misty Pony", "next_hop": 0, "num": "0x66d201f9", "position": {"altitude": 1399, "latitude": 32.243637, "location_source": "LOC_INTERNAL", "longitude": -107.161281, "time_offset_sec": 608}, "public_key_hex": "b32f4a02bb6ba87dd709eaab8aa2def30eea43948da6a88d3e6757ad37fb1bf1", "role": "CLIENT", "short_name": "MHGL", "snr": 2.23, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.079, "battery_level": 46, "channel_utilization": 7.39, "uptime_seconds": 8797, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1615, "long_name": "Loud Mamba", "next_hop": 0, "num": "0x670a401a", "position": {"altitude": 1125, "latitude": 33.386315, "location_source": "LOC_INTERNAL", "longitude": -107.348963, "time_offset_sec": 1654}, "public_key_hex": "3e988b2487f8f86c5b4fc2ff8603ef75d3d603f382bf747a85aa34e2cb56d887", "role": "CLIENT_HIDDEN", "short_name": "LC74", "snr": 10.26, "status": null, "telemetry": {"air_util_tx": 0.634, "battery_level": 11, "channel_utilization": 3.11, "uptime_seconds": 170109, "voltage": 3.399}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 6984, "long_name": "Gold Cobra", "next_hop": 0, "num": "0x67828a9d", "position": {"altitude": 1484, "latitude": 33.31937, "location_source": "LOC_INTERNAL", "longitude": -107.462128, "time_offset_sec": 7121}, "public_key_hex": "1d9d963537b6a842696639308d55414c42b2838310bdcbae7fb4f98dd2746a94", "role": "CLIENT", "short_name": "GPD1", "snr": 9.74, "status": null, "telemetry": {"air_util_tx": 0.886, "battery_level": 28, "channel_utilization": 8.35, "uptime_seconds": 41548, "voltage": 3.552}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 1311, "long_name": "Sneaky Beaver", "next_hop": 0, "num": "0x67a11808", "position": {"altitude": 1033, "latitude": 32.927237, "location_source": "LOC_INTERNAL", "longitude": -106.917122, "time_offset_sec": 1522}, "public_key_hex": "64497ae071d0dbfd1f006af9604dea39b494ae4699108704d05b53296e8c1f4a", "role": "CLIENT", "short_name": "SMW2", "snr": 7.89, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": {"barometric_pressure": 1015.83, "iaq": 0, "relative_humidity": 48.56, "temperature": 27.84}, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 882, "long_name": "White Bronco", "next_hop": 0, "num": "0x67a2fc22", "position": {"altitude": 1194, "latitude": 32.069484, "location_source": "LOC_INTERNAL", "longitude": -107.0902, "time_offset_sec": 955}, "public_key_hex": "d319e98a163b943a6b1b51e7aab152d35ac96d09299a8db902c58b1d4189a7af", "role": "CLIENT", "short_name": "WUCR", "snr": 0.46, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 3686, "long_name": "Found Falcon", "next_hop": 0, "num": "0x67e769d2", "position": {"altitude": 1633, "latitude": 33.0328, "location_source": "LOC_INTERNAL", "longitude": -107.240522, "time_offset_sec": 3771}, "public_key_hex": "", "role": "CLIENT", "short_name": "FYOX", "snr": 1.48, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.625, "battery_level": 99, "channel_utilization": 14.36, "uptime_seconds": 68022, "voltage": 4.191}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1005.59, "iaq": 65, "relative_humidity": 17.76, "temperature": 23.78}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1971, "long_name": "Old Mole", "next_hop": 0, "num": "0x67eda7d6", "position": {"altitude": 1498, "latitude": 32.629438, "location_source": "LOC_INTERNAL", "longitude": -107.269653, "time_offset_sec": 2004}, "public_key_hex": "de38ac98f05428ca2820bbf54be72fbf3e773c07cbf9f6521663752458828e84", "role": "CLIENT_HIDDEN", "short_name": "OU8V", "snr": 12.0, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.562, "battery_level": 101, "channel_utilization": 24.43, "uptime_seconds": 101944, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1003.69, "iaq": 50, "relative_humidity": 44.1, "temperature": 36.37}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 642, "long_name": "Solar Adder", "next_hop": 0, "num": "0x67f48cb5", "position": {"altitude": 1656, "latitude": 32.913674, "location_source": "LOC_INTERNAL", "longitude": -106.957759, "time_offset_sec": 831}, "public_key_hex": "4790c1b4bad3100ca34cee84c30174dbde73988aa7001d48062877f667403831", "role": "ROUTER", "short_name": "S545", "snr": 12.0, "status": {"status": "active"}, "telemetry": {"air_util_tx": 2.037, "battery_level": 96, "channel_utilization": 8.25, "uptime_seconds": 77101, "voltage": 4.164}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 9835, "long_name": "Roving Seal", "next_hop": 233, "num": "0x67f6ee87", "position": {"altitude": 1634, "latitude": 33.779854, "location_source": "LOC_INTERNAL", "longitude": -107.994639, "time_offset_sec": 10090}, "public_key_hex": "", "role": "CLIENT", "short_name": "RVBJ", "snr": 6.89, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1012.06, "iaq": 44, "relative_humidity": 21.17, "temperature": 26.13}, "hops_away": 2, "hw_model": "T_DECK", "last_heard_offset_sec": 873, "long_name": "Silent Sage", "next_hop": 207, "num": "0x68ca0085", "position": {"altitude": 1681, "latitude": 33.090398, "location_source": "LOC_INTERNAL", "longitude": -106.850398, "time_offset_sec": 902}, "public_key_hex": "03d21555e5d10eecc12e3c033d17a459bb3c9026c3dbcc184e964a83fc0dcd01", "role": "CLIENT", "short_name": "SN2Y", "snr": 7.28, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.444, "battery_level": 70, "channel_utilization": 5.6, "uptime_seconds": 61549, "voltage": 3.93}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 3693, "long_name": "Forest Badger", "next_hop": 0, "num": "0x68d21cdf", "position": {"altitude": 1447, "latitude": 33.60625, "location_source": "LOC_INTERNAL", "longitude": -106.763408, "time_offset_sec": 3789}, "public_key_hex": "7f22ca57b404fd315a02e7194c02927b8d4a320c3033cd12538b534f38d4a8d5", "role": "CLIENT", "short_name": "F15P", "snr": 1.36, "status": {"status": "offline-soon"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 332, "long_name": "Silver Phoenix", "next_hop": 0, "num": "0x68db9a79", "position": {"altitude": 1436, "latitude": 31.863364, "location_source": "LOC_INTERNAL", "longitude": -106.745974, "time_offset_sec": 537}, "public_key_hex": "7a137a5511c4e21236e69c3fb0d586fa63661d3fe65a8784edd645ffa4e15791", "role": "CLIENT", "short_name": "SRI5", "snr": 6.07, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4_R8", "last_heard_offset_sec": 9910, "long_name": "Dusk Stag", "next_hop": 0, "num": "0x69160996", "position": {"altitude": 1315, "latitude": 33.006478, "location_source": "LOC_INTERNAL", "longitude": -107.60747, "time_offset_sec": 10209}, "public_key_hex": "cd222db4c4df04c04f1c1b67e04e462fb177603fd08e04a1d8f824d614e45211", "role": "LOST_AND_FOUND", "short_name": "D6EE", "snr": 5.47, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.367, "battery_level": 101, "channel_utilization": 11.29, "uptime_seconds": 6571, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 11782, "long_name": "Bright Bison", "next_hop": 31, "num": "0x69443905", "position": {"altitude": 1701, "latitude": 33.557304, "location_source": "LOC_INTERNAL", "longitude": -107.395231, "time_offset_sec": 11794}, "public_key_hex": "1ca60e46542920bb906736a8c09af6753e9167b44b680de0f336b4412bc8fc55", "role": "CLIENT", "short_name": "BQCF", "snr": 4.62, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER", "last_heard_offset_sec": 1846, "long_name": "Tiny Juniper", "next_hop": 0, "num": "0x69512c9f", "position": null, "public_key_hex": "eb11d406cb1e6b5e57ef2f4efc4067ed893d42159e2f0e2e8f2601a6b2a5b833", "role": "CLIENT", "short_name": "TPGU", "snr": 0.42, "status": null, "telemetry": {"air_util_tx": 0.913, "battery_level": 54, "channel_utilization": 10.96, "uptime_seconds": 44889, "voltage": 3.786}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.11, "iaq": 100, "relative_humidity": 56.47, "temperature": 15.63}, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 3973, "long_name": "Lunar Dolphin", "next_hop": 0, "num": "0x695ea36c", "position": {"altitude": 1448, "latitude": 33.04932, "location_source": "LOC_INTERNAL", "longitude": -107.173961, "time_offset_sec": 4082}, "public_key_hex": "0c64449dad15645f70042cc3dfbb5945ec54ee542633d3aa52f202c28acae1d9", "role": "CLIENT", "short_name": "LHPX", "snr": 6.42, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.169, "battery_level": 42, "channel_utilization": 27.83, "uptime_seconds": 417701, "voltage": 3.678}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1009.43, "iaq": 60, "relative_humidity": 67.3, "temperature": 20.41}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 5682, "long_name": "Tall Crow", "next_hop": 0, "num": "0x69959909", "position": {"altitude": 1130, "latitude": 33.247049, "location_source": "LOC_INTERNAL", "longitude": -106.725418, "time_offset_sec": 5705}, "public_key_hex": "7f6b52ee6b5b005b1954b6e317df1945cce6286c5b2af22c27d5686014f7ee82", "role": "CLIENT", "short_name": "TKRG", "snr": 6.74, "status": null, "telemetry": {"air_util_tx": 0.838, "battery_level": 101, "channel_utilization": 10.31, "uptime_seconds": 250687, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 93, "long_name": "Wild Eagle", "next_hop": 0, "num": "0x6a88a937", "position": {"altitude": 1337, "latitude": 32.669787, "location_source": "LOC_INTERNAL", "longitude": -106.905899, "time_offset_sec": 212}, "public_key_hex": "7ce8c5001d1a00b459ab45aa033fdaa12ac6ef42d56893003c3dd834794e9e36", "role": "ROUTER_LATE", "short_name": "WM0N", "snr": 9.78, "status": null, "telemetry": {"air_util_tx": 1.126, "battery_level": 52, "channel_utilization": 4.5, "uptime_seconds": 206846, "voltage": 3.768}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 7581, "long_name": "River Coyote", "next_hop": 0, "num": "0x6ac03524", "position": {"altitude": 1459, "latitude": 33.131279, "location_source": "LOC_INTERNAL", "longitude": -108.035675, "time_offset_sec": 7612}, "public_key_hex": "68fd7afb6a2f7dbd732fa013bd181143f906cc65ce33d2810edb0c68897a0c04", "role": "SENSOR", "short_name": "RDYM", "snr": 4.5, "status": null, "telemetry": {"air_util_tx": 0.238, "battery_level": 98, "channel_utilization": 12.09, "uptime_seconds": 9279, "voltage": 4.182}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1010.02, "iaq": 47, "relative_humidity": 55.69, "temperature": 28.1}, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 3598, "long_name": "Frosty Arroyo", "next_hop": 0, "num": "0x6b1505a9", "position": {"altitude": 1280, "latitude": 32.289692, "location_source": "LOC_INTERNAL", "longitude": -106.779069, "time_offset_sec": 3868}, "public_key_hex": "d95c1db8d26a35d1a1a5752866b6414b5f8faba9fc2af8dbdd0dc2ad5fec4de2", "role": "CLIENT", "short_name": "FE2N", "snr": 10.7, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.781, "battery_level": 75, "channel_utilization": 22.77, "uptime_seconds": 72827, "voltage": 3.975}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 998.23, "iaq": 33, "relative_humidity": 62.25, "temperature": 36.78}, "hops_away": 0, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 1330, "long_name": "Floating Squirrel", "next_hop": 0, "num": "0x6b2270e8", "position": {"altitude": 1528, "latitude": 33.389223, "location_source": "LOC_INTERNAL", "longitude": -108.196442, "time_offset_sec": 1470}, "public_key_hex": "31c03b0d9636fe3b833df53b00d92a7e4e5988e7b807c8ba426665808dace31a", "role": "CLIENT_BASE", "short_name": "F0FM", "snr": 0.13, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.645, "battery_level": 94, "channel_utilization": 11.86, "uptime_seconds": 7143, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3090, "long_name": "Smooth Salmon", "next_hop": 0, "num": "0x6b2dce01", "position": {"altitude": 1409, "latitude": 33.100448, "location_source": "LOC_INTERNAL", "longitude": -107.92389, "time_offset_sec": 3305}, "public_key_hex": "16d1f968b4035b6edf635237f3f01138827927a9848d967d6f31350688cf188a", "role": "CLIENT_HIDDEN", "short_name": "SB4A", "snr": 11.58, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.347, "battery_level": 85, "channel_utilization": 8.17, "uptime_seconds": 36591, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 332, "long_name": "Steel Cedar", "next_hop": 0, "num": "0x6bf30f37", "position": null, "public_key_hex": "88808c58de5b8c4058b666a6db248d8e8ad33045d9df74a5d1a67a98f984706e", "role": "TRACKER", "short_name": "SX1D", "snr": 7.31, "status": {"status": "active"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 2903, "long_name": "Bright Sage", "next_hop": 0, "num": "0x6c8fbc50", "position": {"altitude": 1586, "latitude": 32.350879, "location_source": "LOC_INTERNAL", "longitude": -107.05561, "time_offset_sec": 3201}, "public_key_hex": "461cee2e618fcdeaf759daa978b36ff4eb4d437a83214fea59ea82808297c0ce", "role": "CLIENT_HIDDEN", "short_name": "BPBG", "snr": 4.0, "status": null, "telemetry": {"air_util_tx": 0.289, "battery_level": 85, "channel_utilization": 0.63, "uptime_seconds": 46196, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "MINI_EPAPER_S3", "last_heard_offset_sec": 723, "long_name": "Silver Eagle", "next_hop": 42, "num": "0x6cc140c8", "position": {"altitude": 1298, "latitude": 32.802868, "location_source": "LOC_INTERNAL", "longitude": -108.108877, "time_offset_sec": 731}, "public_key_hex": "5d1514847335f3e3b783bf340990baf1bfeeaeaee6d9f4932894d3d7c82ae65f", "role": "CLIENT", "short_name": "🌵", "snr": 2.76, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 14385, "long_name": "Whispering Falcon", "next_hop": 0, "num": "0x6ce558ad", "position": {"altitude": 1567, "latitude": 32.115722, "location_source": "LOC_INTERNAL", "longitude": -106.926052, "time_offset_sec": 14454}, "public_key_hex": "ba072d449993d9384e2f544d437f138e6032a506820882ad23ad764c7c44582d", "role": "CLIENT", "short_name": "W1DJ", "snr": 6.37, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.819, "battery_level": 55, "channel_utilization": 20.22, "uptime_seconds": 73068, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 4890, "long_name": "Bright Cedar", "next_hop": 0, "num": "0x6cfdd4fa", "position": {"altitude": 979, "latitude": 32.657349, "location_source": "LOC_INTERNAL", "longitude": -107.084846, "time_offset_sec": 4950}, "public_key_hex": "332d8658cbbab5eaf335b8bbc848934f402374a08c32f6b79ac32eed2601a08c", "role": "ROUTER_LATE", "short_name": "🐝", "snr": 8.44, "status": null, "telemetry": {"air_util_tx": 0.878, "battery_level": 85, "channel_utilization": 1.34, "uptime_seconds": 58815, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 663, "long_name": "Hidden Turtle", "next_hop": 0, "num": "0x6d6fe2cd", "position": {"altitude": 710, "latitude": 33.357273, "location_source": "LOC_INTERNAL", "longitude": -107.306769, "time_offset_sec": 730}, "public_key_hex": "d2984b2719ee23d69b70e70e9b71e8732b5fa3cd148e7376a3a118932268725b", "role": "CLIENT_MUTE", "short_name": "HAD7", "snr": 5.8, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 565, "long_name": "Mountain Dolphin", "next_hop": 0, "num": "0x6d9575e4", "position": {"altitude": 1204, "latitude": 32.968638, "location_source": "LOC_INTERNAL", "longitude": -108.392893, "time_offset_sec": 632}, "public_key_hex": "2b4948dd23b6acce3f6c8789c371cebc0d6d18e9a738600587c303089115e3cf", "role": "SENSOR", "short_name": "MVX9", "snr": 7.42, "status": null, "telemetry": {"air_util_tx": 1.062, "battery_level": 47, "channel_utilization": 7.94, "uptime_seconds": 33895, "voltage": 3.723}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 2618, "long_name": "Tiny Bear", "next_hop": 0, "num": "0x6db0eeb2", "position": {"altitude": 1228, "latitude": 32.430937, "location_source": "LOC_INTERNAL", "longitude": -107.468988, "time_offset_sec": 2827}, "public_key_hex": "", "role": "CLIENT_BASE", "short_name": "TE9P", "snr": 10.5, "status": null, "telemetry": {"air_util_tx": 1.415, "battery_level": 70, "channel_utilization": 10.78, "uptime_seconds": 109124, "voltage": 3.93}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1004.51, "iaq": 30, "relative_humidity": 51.2, "temperature": 5.59}, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 7056, "long_name": "Red Phoenix", "next_hop": 67, "num": "0x6ebad280", "position": {"altitude": 1406, "latitude": 32.587193, "location_source": "LOC_INTERNAL", "longitude": -107.864844, "time_offset_sec": 7077}, "public_key_hex": "4b37206cf6c334f1c025ed1eca12c826ff2e72fcf5b24a4d12ea0349f3754647", "role": "TAK", "short_name": "RSBL", "snr": 12.0, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.068, "battery_level": 101, "channel_utilization": 4.58, "uptime_seconds": 9420, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 748, "long_name": "Happy Mole AE7OR", "next_hop": 0, "num": "0x6f29b8be", "position": {"altitude": 1471, "latitude": 34.024057, "location_source": "LOC_INTERNAL", "longitude": -107.980924, "time_offset_sec": 956}, "public_key_hex": "76d9fbe59a5ec44941574d30735c45f34b0df3b859aa31f4842e316c16493060", "role": "CLIENT", "short_name": "HEAD", "snr": 1.7, "status": null, "telemetry": {"air_util_tx": 2.07, "battery_level": 89, "channel_utilization": 2.5, "uptime_seconds": 4008, "voltage": 4.101}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1002.52, "iaq": 43, "relative_humidity": 27.02, "temperature": 34.46}, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1148, "long_name": "Short Mesa WD3LM", "next_hop": 0, "num": "0x6fdf60c0", "position": null, "public_key_hex": "7b8a79f2d32792730b51bb3c49fe52b7f32f9c9d0bdad2e236613e5c370f180b", "role": "CLIENT_MUTE", "short_name": "SPEK", "snr": 7.16, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_VISION_MASTER_E290", "last_heard_offset_sec": 3653, "long_name": "Gold Tortoise", "next_hop": 0, "num": "0x70091536", "position": {"altitude": 1231, "latitude": 33.215614, "location_source": "LOC_INTERNAL", "longitude": -107.454934, "time_offset_sec": 3927}, "public_key_hex": "b8bc1238d599bb552daa9bb5854170725e73440aa97a5313a3e809bedf1d2492", "role": "CLIENT", "short_name": "GCR4", "snr": 0.56, "status": null, "telemetry": {"air_util_tx": 0.849, "battery_level": 20, "channel_utilization": 0.54, "uptime_seconds": 72907, "voltage": 3.48}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 7, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 10347, "long_name": "Desert Whale", "next_hop": 122, "num": "0x7049f212", "position": {"altitude": 1563, "latitude": 34.863907, "location_source": "LOC_INTERNAL", "longitude": -107.289982, "time_offset_sec": 10465}, "public_key_hex": "0804773e1c6637bfe1ad68e033090eb8785d465d6488aae6e91153313cbfef52", "role": "CLIENT", "short_name": "DYV8", "snr": 6.21, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 6547, "long_name": "Fast Mesa", "next_hop": 0, "num": "0x706b89dc", "position": {"altitude": 575, "latitude": 33.070382, "location_source": "LOC_INTERNAL", "longitude": -108.658227, "time_offset_sec": 6632}, "public_key_hex": "3c2e7b204295221ff20739d229fa58cc2a0c918643510c1d9c142af4261a6d40", "role": "CLIENT", "short_name": "FELZ", "snr": 7.66, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.176, "battery_level": 62, "channel_utilization": 3.76, "uptime_seconds": 35160, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": {"barometric_pressure": 1011.87, "iaq": 25, "relative_humidity": 37.46, "temperature": 22.71}, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 759, "long_name": "Short Arroyo", "next_hop": 0, "num": "0x7073c568", "position": {"altitude": 1474, "latitude": 32.796892, "location_source": "LOC_INTERNAL", "longitude": -107.986938, "time_offset_sec": 787}, "public_key_hex": "3b06ebd249feb4db8a046d8240f1e6133def1f6681f363341bf1c2d8f35ed253", "role": "CLIENT", "short_name": "SG6Y", "snr": -1.58, "status": null, "telemetry": {"air_util_tx": 0.647, "battery_level": 97, "channel_utilization": 7.86, "uptime_seconds": 205366, "voltage": 4.173}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 481, "long_name": "Green Iguana", "next_hop": 100, "num": "0x70b7aa80", "position": {"altitude": 1720, "latitude": 32.751094, "location_source": "LOC_INTERNAL", "longitude": -106.105514, "time_offset_sec": 528}, "public_key_hex": "c8b9feca4dcd64a53b1f15a8e13c3e717bcc5acb72ff3f8d0087dd65f2386e66", "role": "ROUTER_LATE", "short_name": "GLC3", "snr": -1.8, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.327, "battery_level": 59, "channel_utilization": 2.49, "uptime_seconds": 135440, "voltage": 3.831}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1007.52, "iaq": 47, "relative_humidity": 32.24, "temperature": 26.78}, "hops_away": 2, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 3893, "long_name": "Mountain Squirrel", "next_hop": 14, "num": "0x70d95891", "position": {"altitude": 1127, "latitude": 33.675798, "location_source": "LOC_INTERNAL", "longitude": -106.883999, "time_offset_sec": 4149}, "public_key_hex": "ca4724e421c616c133571de521b110fb8cc4985681e29399d41b8268fea05b4f", "role": "CLIENT", "short_name": "MYSF", "snr": 9.69, "status": null, "telemetry": {"air_util_tx": 0.899, "battery_level": 17, "channel_utilization": 7.19, "uptime_seconds": 137688, "voltage": 3.453}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1509, "long_name": "Gold Adder", "next_hop": 0, "num": "0x71459999", "position": null, "public_key_hex": "01cf4af2928b1fbf274405bf3a17299e7eb73fcdf03ebe8d2efc9e94eb8d179a", "role": "CLIENT", "short_name": "🐺", "snr": 4.63, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1018.3, "iaq": 0, "relative_humidity": 37.74, "temperature": 32.73}, "hops_away": 2, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 1959, "long_name": "Silver Crane", "next_hop": 171, "num": "0x716da39c", "position": null, "public_key_hex": "089363a405cd19f55b88e76e0863663d8d3e6d3b7171d011af89b4f36712a86a", "role": "SENSOR", "short_name": "SIC8", "snr": 0.57, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.419, "battery_level": 45, "channel_utilization": 8.68, "uptime_seconds": 22858, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO_PLUS", "last_heard_offset_sec": 521, "long_name": "Floating Raven", "next_hop": 0, "num": "0x719e4ee9", "position": {"altitude": 913, "latitude": 32.781272, "location_source": "LOC_INTERNAL", "longitude": -107.211215, "time_offset_sec": 682}, "public_key_hex": "7b1afdaebc952249765689c2fe0bf4d41d795822a16e836f1804a636466e1e2e", "role": "CLIENT", "short_name": "F9I9", "snr": -2.23, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.537, "battery_level": 61, "channel_utilization": 6.8, "uptime_seconds": 192545, "voltage": 3.849}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1049, "long_name": "White Turtle", "next_hop": 0, "num": "0x71aaa863", "position": {"altitude": 1203, "latitude": 33.654085, "location_source": "LOC_INTERNAL", "longitude": -107.263033, "time_offset_sec": 1288}, "public_key_hex": "06ce4a8ec0671200fd3e72a02d2eaef7bb9fce10f617f888700875783d85f0c4", "role": "TAK", "short_name": "W0FC", "snr": 2.43, "status": null, "telemetry": {"air_util_tx": 0.083, "battery_level": 85, "channel_utilization": 16.67, "uptime_seconds": 3562, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 2, "environment": null, "hops_away": 1, "hw_model": "HELTEC_VISION_MASTER_E213", "last_heard_offset_sec": 7605, "long_name": "Roving Mamba", "next_hop": 198, "num": "0x7200a1bc", "position": {"altitude": 1097, "latitude": 33.406264, "location_source": "LOC_INTERNAL", "longitude": -107.479595, "time_offset_sec": 7665}, "public_key_hex": "e7e9b3f098033f014a7d0062c6a3794f5b49913c7850b2cdf972aa6a91cba3e7", "role": "CLIENT", "short_name": "RN5I", "snr": 5.81, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.728, "battery_level": 55, "channel_utilization": 12.42, "uptime_seconds": 18866, "voltage": 3.795}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 3093, "long_name": "Tall Fox", "next_hop": 0, "num": "0x722ac5db", "position": {"altitude": 1158, "latitude": 33.754065, "location_source": "LOC_INTERNAL", "longitude": -108.142416, "time_offset_sec": 3181}, "public_key_hex": "5cb8569f357f1f12a62c7b23d01001c4c2cf4c4c307bff7539251865d9c6ef62", "role": "CLIENT_MUTE", "short_name": "TX3E", "snr": 5.35, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1015.37, "iaq": 62, "relative_humidity": 46.84, "temperature": 18.62}, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 705, "long_name": "Iron Bison", "next_hop": 38, "num": "0x72317d7d", "position": {"altitude": 1278, "latitude": 31.93437, "location_source": "LOC_INTERNAL", "longitude": -106.770818, "time_offset_sec": 894}, "public_key_hex": "c536dc8aa9f544bec26fa64ec02f33e0bcf3deea7d40cf8a5929d274da9bc02f", "role": "CLIENT", "short_name": "IHD7", "snr": 9.44, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 1.918, "battery_level": 81, "channel_utilization": 32.59, "uptime_seconds": 147281, "voltage": 4.029}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 16666, "long_name": "Fast Bluff", "next_hop": 0, "num": "0x729d57b3", "position": {"altitude": 1409, "latitude": 33.656311, "location_source": "LOC_INTERNAL", "longitude": -106.945183, "time_offset_sec": 16956}, "public_key_hex": "b16d739c9bba5da22bcefeb30daf4237c1f4c197c1e77068ad2b4931537a3003", "role": "CLIENT", "short_name": "FUAM", "snr": 12.0, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.428, "battery_level": 11, "channel_utilization": 17.7, "uptime_seconds": 19864, "voltage": 3.399}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": {"barometric_pressure": 1011.24, "iaq": 23, "relative_humidity": 81.21, "temperature": 19.5}, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 5391, "long_name": "Silver Beaver", "next_hop": 0, "num": "0x72b29372", "position": {"altitude": 1302, "latitude": 33.3054, "location_source": "LOC_INTERNAL", "longitude": -107.445822, "time_offset_sec": 5586}, "public_key_hex": "f90d811e288681ef2a86d709b512b1cc9e8e624d92909a6d231d1af70a0d974b", "role": "CLIENT", "short_name": "S1Y7", "snr": 10.79, "status": {"status": "rebooted"}, "telemetry": {"air_util_tx": 0.455, "battery_level": 68, "channel_utilization": 35.0, "uptime_seconds": 2721, "voltage": 3.912}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1491, "long_name": "Canyon Bronco K18XM", "next_hop": 0, "num": "0x72d1bb31", "position": {"altitude": 930, "latitude": 33.770664, "location_source": "LOC_INTERNAL", "longitude": -107.936809, "time_offset_sec": 1728}, "public_key_hex": "7db1ca17780b8192e9ce77f648a76bdac0335f6b491e361061312ffe945dd6ec", "role": "CLIENT", "short_name": "CS1D", "snr": 4.57, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WSL_V3", "last_heard_offset_sec": 711, "long_name": "Hidden Adder", "next_hop": 204, "num": "0x72d3c9b3", "position": {"altitude": 1352, "latitude": 33.038897, "location_source": "LOC_INTERNAL", "longitude": -106.605698, "time_offset_sec": 769}, "public_key_hex": "3d9d763bb87710e4ae804eb54774f01048d3f31ca450d0a73e92a4711573f388", "role": "CLIENT", "short_name": "HS9T", "snr": 7.44, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 5321, "long_name": "Forest Salmon", "next_hop": 0, "num": "0x737bd88b", "position": null, "public_key_hex": "e75e5375a6229f57a6512e866a3623e41e94cd991cc0b97d8f2001f99bdbc078", "role": "CLIENT", "short_name": "F33J", "snr": 9.6, "status": null, "telemetry": {"air_util_tx": 0.409, "battery_level": 33, "channel_utilization": 26.34, "uptime_seconds": 45767, "voltage": 3.597}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 3514, "long_name": "Silent Cobra", "next_hop": 235, "num": "0x747b0108", "position": {"altitude": 1113, "latitude": 33.698573, "location_source": "LOC_INTERNAL", "longitude": -107.099209, "time_offset_sec": 3804}, "public_key_hex": "dad3762673a4b521a6bd3333dab99ceed5937dc9e9d8600368bfdb4548aea00d", "role": "CLIENT", "short_name": "SOJ4", "snr": 5.22, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1001.85, "iaq": 0, "relative_humidity": 47.93, "temperature": 37.57}, "hops_away": 2, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1330, "long_name": "Sharp Tortoise", "next_hop": 58, "num": "0x7482140b", "position": {"altitude": 1593, "latitude": 32.909753, "location_source": "LOC_INTERNAL", "longitude": -106.843692, "time_offset_sec": 1452}, "public_key_hex": "", "role": "CLIENT", "short_name": "🐝", "snr": 7.68, "status": null, "telemetry": {"air_util_tx": 0.68, "battery_level": 71, "channel_utilization": 0.74, "uptime_seconds": 72228, "voltage": 3.939}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 1092, "long_name": "Brave Mustang", "next_hop": 244, "num": "0x74ebe68c", "position": {"altitude": 1066, "latitude": 32.57088, "location_source": "LOC_INTERNAL", "longitude": -107.594198, "time_offset_sec": 1171}, "public_key_hex": "d9c6c924ebe255ffe2f30f4c2b9eb082ee58d1a1a262561b187b55266fcbb44d", "role": "CLIENT", "short_name": "BE1L", "snr": 5.5, "status": null, "telemetry": {"air_util_tx": 0.452, "battery_level": 22, "channel_utilization": 13.31, "uptime_seconds": 31995, "voltage": 3.498}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1441, "long_name": "Desert Pony", "next_hop": 95, "num": "0x75819b22", "position": {"altitude": 1642, "latitude": 32.7144, "location_source": "LOC_INTERNAL", "longitude": -107.293768, "time_offset_sec": 1496}, "public_key_hex": "1274ed4d155eeb387db100a0e9580f1aabcda5da48109168d5e07b54911f21bb", "role": "CLIENT", "short_name": "DL22", "snr": 7.75, "status": null, "telemetry": {"air_util_tx": 0.226, "battery_level": 94, "channel_utilization": 4.3, "uptime_seconds": 315832, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 996.53, "iaq": 40, "relative_humidity": 50.26, "temperature": 22.3}, "hops_away": 5, "hw_model": "RAK4631", "last_heard_offset_sec": 2362, "long_name": "Smooth Tortoise", "next_hop": 26, "num": "0x7592642f", "position": null, "public_key_hex": "38a59621a06369e8a4d9663f50092f8aecc501ca6dd7352ac42412bc24596225", "role": "CLIENT", "short_name": "SUJT", "snr": 11.04, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.582, "battery_level": 30, "channel_utilization": 3.46, "uptime_seconds": 106194, "voltage": 3.57}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1017.05, "iaq": 62, "relative_humidity": 20.66, "temperature": 9.6}, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 1159, "long_name": "Tall Coyote W55FU", "next_hop": 0, "num": "0x75a976fd", "position": {"altitude": 1390, "latitude": 33.082615, "location_source": "LOC_INTERNAL", "longitude": -107.834629, "time_offset_sec": 1184}, "public_key_hex": "c8e6356f5c2ec5c35fa7af9760899bdbd760dd00b9a9f086c4825488224473eb", "role": "CLIENT", "short_name": "TPUY", "snr": 0.29, "status": null, "telemetry": {"air_util_tx": 0.858, "battery_level": 24, "channel_utilization": 18.79, "uptime_seconds": 65427, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 180, "long_name": "Drowsy Shark", "next_hop": 0, "num": "0x763c51ea", "position": {"altitude": 1481, "latitude": 33.016754, "location_source": "LOC_INTERNAL", "longitude": -107.701299, "time_offset_sec": 191}, "public_key_hex": "a2baacc65e28e34b920f93d0abc6f84e2ae418686c165383044e49207f2b4a0b", "role": "CLIENT", "short_name": "D9ZH", "snr": 5.81, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1011.31, "iaq": 47, "relative_humidity": 36.93, "temperature": 17.88}, "hops_away": 2, "hw_model": "T_ECHO", "last_heard_offset_sec": 4592, "long_name": "Sneaky Cobra", "next_hop": 220, "num": "0x76635e54", "position": {"altitude": 1344, "latitude": 32.812135, "location_source": "LOC_INTERNAL", "longitude": -106.951662, "time_offset_sec": 4618}, "public_key_hex": "c3e38660851abef6d4d6c2c8601ab5bb35ff68b6c4b177a60e8d04e407fe33ad", "role": "CLIENT", "short_name": "SPPX", "snr": 6.52, "status": {"status": "nominal"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 1060, "long_name": "Dusk Bluff", "next_hop": 0, "num": "0x766e4007", "position": {"altitude": 1436, "latitude": 33.156057, "location_source": "LOC_INTERNAL", "longitude": -106.498656, "time_offset_sec": 1208}, "public_key_hex": "37169d6a09d2b2db79c9b8b86c17d07c152858ee0b7525ebd9016fd0267202d7", "role": "ROUTER", "short_name": "DC7V", "snr": 0.68, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.551, "battery_level": 53, "channel_utilization": 9.38, "uptime_seconds": 33734, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": {"barometric_pressure": 1003.64, "iaq": 0, "relative_humidity": 50.28, "temperature": 34.94}, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 16708, "long_name": "River Badger AE6LR", "next_hop": 0, "num": "0x7688d7e7", "position": {"altitude": 1237, "latitude": 32.381937, "location_source": "LOC_INTERNAL", "longitude": -107.515037, "time_offset_sec": 16823}, "public_key_hex": "41c0a3c812d4ec19ff44c06827616f99d7abbdd73833cfd91bcce1ad42efe016", "role": "CLIENT", "short_name": "RRCQ", "snr": 12.0, "status": {"status": "online"}, "telemetry": {"air_util_tx": 0.406, "battery_level": 77, "channel_utilization": 25.9, "uptime_seconds": 107176, "voltage": 3.993}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 18686, "long_name": "Lost Hawk", "next_hop": 0, "num": "0x768e79f3", "position": {"altitude": 1538, "latitude": 33.305402, "location_source": "LOC_INTERNAL", "longitude": -107.245097, "time_offset_sec": 18952}, "public_key_hex": "96035f83127debc01a20a7a4b63c31312fea59bd4370277ea568d84df8395412", "role": "ROUTER", "short_name": "LWVP", "snr": 6.53, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1013.87, "iaq": 61, "relative_humidity": 37.63, "temperature": 29.91}, "hops_away": 2, "hw_model": "T_DECK", "last_heard_offset_sec": 6606, "long_name": "Misty Trout", "next_hop": 150, "num": "0x770fc426", "position": {"altitude": 1020, "latitude": 33.669337, "location_source": "LOC_INTERNAL", "longitude": -106.755362, "time_offset_sec": 6878}, "public_key_hex": "930cdc2003db4ef1733bf34ac04a138c23c7bb8a2ed96066c855426fca86a6d9", "role": "CLIENT", "short_name": "MSZG", "snr": -3.72, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.747, "battery_level": 24, "channel_utilization": 16.84, "uptime_seconds": 38808, "voltage": 3.516}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 2359, "long_name": "Sleepy Juniper", "next_hop": 0, "num": "0x771bf8a9", "position": null, "public_key_hex": "23649b698d1a3c04a949bd6be3103325b8968dac4c3bc37384daeee403e2f4c7", "role": "TAK_TRACKER", "short_name": "S4XI", "snr": 9.37, "status": null, "telemetry": {"air_util_tx": 1.043, "battery_level": 39, "channel_utilization": 23.22, "uptime_seconds": 81536, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 5, "environment": null, "hops_away": 0, "hw_model": "MUZI_BASE", "last_heard_offset_sec": 1611, "long_name": "Short Beaver", "next_hop": 0, "num": "0x776c4708", "position": {"altitude": 1255, "latitude": 33.763903, "location_source": "LOC_INTERNAL", "longitude": -106.466717, "time_offset_sec": 1794}, "public_key_hex": "94b30977c1b425535001b8805ad1ec5a0e84248f5fb9131898e21fe072f9c47f", "role": "CLIENT", "short_name": "SNUD", "snr": 8.49, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 1.003, "battery_level": 84, "channel_utilization": 27.72, "uptime_seconds": 127743, "voltage": 4.056}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TLORA_T3_S3", "last_heard_offset_sec": 2426, "long_name": "Solar Owl", "next_hop": 182, "num": "0x777ac886", "position": {"altitude": 1439, "latitude": 32.592907, "location_source": "LOC_INTERNAL", "longitude": -108.330483, "time_offset_sec": 2636}, "public_key_hex": "eb0873f4bd12f8341cb06ab37dc50cae3f2a8bb9a8cfa28e34dc54d5d2d452ef", "role": "CLIENT", "short_name": "STDJ", "snr": 11.84, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 801, "long_name": "Bright Arroyo", "next_hop": 0, "num": "0x77cfa02a", "position": {"altitude": 1750, "latitude": 33.411219, "location_source": "LOC_INTERNAL", "longitude": -107.565288, "time_offset_sec": 1093}, "public_key_hex": "3e34f1df51c60337e3c4a9c95ee60b7c6569afa18657d7f39074699c28871df3", "role": "SENSOR", "short_name": "BE6T", "snr": 12.0, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.178, "battery_level": 39, "channel_utilization": 5.08, "uptime_seconds": 16174, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 54, "long_name": "Lunar Lynx", "next_hop": 121, "num": "0x780294e9", "position": {"altitude": 1659, "latitude": 33.962318, "location_source": "LOC_INTERNAL", "longitude": -107.544268, "time_offset_sec": 272}, "public_key_hex": "36a4b7e10bf625cc0d160a943b884634f5706f0346e8c47c8f7472f8e35d06f7", "role": "CLIENT", "short_name": "LXIE", "snr": 2.4, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 8650, "long_name": "Iron Heron", "next_hop": 0, "num": "0x780d26b0", "position": {"altitude": 1282, "latitude": 31.893481, "location_source": "LOC_INTERNAL", "longitude": -107.055716, "time_offset_sec": 8868}, "public_key_hex": "4d12ba69521f0d49c49e3493c9cd972617c2bb8a21c5f6e25880f232ed3b15b7", "role": "CLIENT", "short_name": "I1HT", "snr": 6.74, "status": null, "telemetry": {"air_util_tx": 0.545, "battery_level": 57, "channel_utilization": 6.47, "uptime_seconds": 131248, "voltage": 3.813}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 7, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 3022, "long_name": "Brave Pike", "next_hop": 14, "num": "0x78496d44", "position": {"altitude": 1186, "latitude": 32.456601, "location_source": "LOC_INTERNAL", "longitude": -107.308159, "time_offset_sec": 3076}, "public_key_hex": "29ec8065252760321e5f90962332bf1efb623e658da09d3dfc645d1fae66edff", "role": "ROUTER", "short_name": "BQFF", "snr": -0.68, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 1519, "long_name": "Tiny Colt", "next_hop": 0, "num": "0x785ece6e", "position": null, "public_key_hex": "7dfbd80205fbaf4ddff7df764b803f0005634acb464f15225ad6aa34312ea8d3", "role": "CLIENT", "short_name": "TVAA", "snr": 5.55, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 253, "long_name": "Misty Shark", "next_hop": 123, "num": "0x78b1b84d", "position": {"altitude": 1392, "latitude": 33.329327, "location_source": "LOC_INTERNAL", "longitude": -107.092506, "time_offset_sec": 305}, "public_key_hex": "b6ede5d696727422a7c14921b90c89447fa29f5b891055f8115646aa4c006a86", "role": "CLIENT", "short_name": "MF0Y", "snr": 4.07, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP", "last_heard_offset_sec": 2345, "long_name": "Happy Arroyo", "next_hop": 0, "num": "0x790a2d18", "position": {"altitude": 998, "latitude": 32.548507, "location_source": "LOC_INTERNAL", "longitude": -107.187949, "time_offset_sec": 2571}, "public_key_hex": "c1ddda5281f7fc5380a8b40685bf0de8b4e7013e437cea71c0e3b8b1a6bf9859", "role": "CLIENT", "short_name": "🌵", "snr": 1.56, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_WIRELESS_TRACKER_V2", "last_heard_offset_sec": 1518, "long_name": "Stone Mesa", "next_hop": 0, "num": "0x796bac87", "position": {"altitude": 1532, "latitude": 33.331451, "location_source": "LOC_INTERNAL", "longitude": -107.20749, "time_offset_sec": 1780}, "public_key_hex": "11a1106709cbbe68eaea431f5e1ac3f48426a89045d7437e2be1b0b8a5a32833", "role": "CLIENT_HIDDEN", "short_name": "S0E7", "snr": -0.62, "status": {"status": "running"}, "telemetry": {"air_util_tx": 0.559, "battery_level": 25, "channel_utilization": 8.11, "uptime_seconds": 53807, "voltage": 3.525}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "WISMESH_TAP_V2", "last_heard_offset_sec": 3964, "long_name": "Rough Squirrel", "next_hop": 0, "num": "0x79c7fc54", "position": {"altitude": 1184, "latitude": 33.847673, "location_source": "LOC_INTERNAL", "longitude": -108.399321, "time_offset_sec": 4180}, "public_key_hex": "e1128e0570df2d4c6c06b509ced92d7866f742c8bb5acd5d2bae222907026f91", "role": "CLIENT", "short_name": "🌙", "snr": 10.86, "status": null, "telemetry": {"air_util_tx": 0.064, "battery_level": 53, "channel_utilization": 9.05, "uptime_seconds": 44833, "voltage": 3.777}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1024.35, "iaq": 0, "relative_humidity": 66.53, "temperature": 19.84}, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 3455, "long_name": "Hidden Shark", "next_hop": 0, "num": "0x79cd3168", "position": {"altitude": 1341, "latitude": 32.735161, "location_source": "LOC_INTERNAL", "longitude": -106.941718, "time_offset_sec": 3596}, "public_key_hex": "fd66c9774c563a92c870ae6bed5b6c15f0cf0ac084cbf32a808f6bca6722dd8b", "role": "CLIENT", "short_name": "H4VJ", "snr": 4.53, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 1671, "long_name": "Shady Mesa", "next_hop": 0, "num": "0x79fc6f8a", "position": null, "public_key_hex": "b320b94d3ccdee790157a8246ea0cc68e5895bd513718f81d6966fb017b47f80", "role": "CLIENT", "short_name": "SFKE", "snr": 4.16, "status": {"status": "running"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": true}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "T_LORA_PAGER", "last_heard_offset_sec": 9430, "long_name": "Quick Cactus", "next_hop": 136, "num": "0x7a37ec20", "position": {"altitude": 1345, "latitude": 33.129057, "location_source": "LOC_INTERNAL", "longitude": -107.822688, "time_offset_sec": 9676}, "public_key_hex": "a193c1c68016952a5df22fa46a732a0ea80e4a48deb6885d347b682254b31a31", "role": "CLIENT", "short_name": "QYEE", "snr": 7.7, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.338, "battery_level": 19, "channel_utilization": 6.92, "uptime_seconds": 94670, "voltage": 3.471}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": true, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_MESH_NODE_T114", "last_heard_offset_sec": 707, "long_name": "Old Salmon", "next_hop": 2, "num": "0x7a4281ac", "position": null, "public_key_hex": "af9afb469b819caf92b67458302e74913fa1c1b6dc88d609d214ad58d57c2ef4", "role": "TAK", "short_name": "OHVD", "snr": 5.82, "status": null, "telemetry": {"air_util_tx": 0.285, "battery_level": 36, "channel_utilization": 9.9, "uptime_seconds": 227163, "voltage": 3.624}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 253, "long_name": "Giant Oak", "next_hop": 0, "num": "0x7a623dbf", "position": null, "public_key_hex": "ba3ad195e706e514fe3959becb2fd6597ceb8568d36346aa8b4508de8117b30f", "role": "ROUTER", "short_name": "GGR6", "snr": 2.92, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.421, "battery_level": 29, "channel_utilization": 0.77, "uptime_seconds": 155995, "voltage": 3.561}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 1, "environment": null, "hops_away": 1, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 1624, "long_name": "Whispering Yucca", "next_hop": 133, "num": "0x7afc3bdf", "position": {"altitude": 941, "latitude": 32.651888, "location_source": "LOC_INTERNAL", "longitude": -107.339859, "time_offset_sec": 1856}, "public_key_hex": "", "role": "CLIENT_HIDDEN", "short_name": "W4IF", "snr": 9.03, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "XIAO_NRF52_KIT", "last_heard_offset_sec": 232, "long_name": "Blue Seal", "next_hop": 0, "num": "0x7b03173d", "position": {"altitude": 1281, "latitude": 32.477974, "location_source": "LOC_INTERNAL", "longitude": -106.900437, "time_offset_sec": 327}, "public_key_hex": "ab4e63b10fbced513a64d35b04cec016deeb42571ae924b08766be75c75e43fd", "role": "CLIENT", "short_name": "BS9G", "snr": 3.11, "status": {"status": "nominal"}, "telemetry": {"air_util_tx": 0.137, "battery_level": 27, "channel_utilization": 8.86, "uptime_seconds": 7446, "voltage": 3.543}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 8541, "long_name": "Lone Stag", "next_hop": 0, "num": "0x7b14a1a4", "position": {"altitude": 1618, "latitude": 32.00989, "location_source": "LOC_INTERNAL", "longitude": -106.960274, "time_offset_sec": 8759}, "public_key_hex": "b708eaa3003a627f770f3195d5f12d3aed2a7c6798ad50a5c6af1d9615a68d56", "role": "CLIENT_HIDDEN", "short_name": "LBRG", "snr": -2.17, "status": null, "telemetry": {"air_util_tx": 0.396, "battery_level": 46, "channel_utilization": 19.14, "uptime_seconds": 80209, "voltage": 3.714}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 15532, "long_name": "Stone Cactus", "next_hop": 0, "num": "0x7b85c491", "position": {"altitude": 948, "latitude": 32.596386, "location_source": "LOC_INTERNAL", "longitude": -107.908213, "time_offset_sec": 15772}, "public_key_hex": "62134c353256dadbae4f680097ea9caabb1680b3659cc1f02c7e4fb0282bf43a", "role": "CLIENT", "short_name": "S7WW", "snr": 5.23, "status": null, "telemetry": {"air_util_tx": 0.344, "battery_level": 100, "channel_utilization": 2.77, "uptime_seconds": 153615, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 748, "long_name": "Dawn Elk", "next_hop": 0, "num": "0x7b95d6c4", "position": {"altitude": 1375, "latitude": 32.814726, "location_source": "LOC_INTERNAL", "longitude": -106.699569, "time_offset_sec": 989}, "public_key_hex": "fef6fe0964937dedb0900268ee852637e0059457bf110d028629e4ccf5f8a717", "role": "CLIENT", "short_name": "🦋", "snr": 5.62, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.362, "battery_level": 62, "channel_utilization": 4.13, "uptime_seconds": 83278, "voltage": 3.858}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": true, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1868, "long_name": "Floating Cactus", "next_hop": 78, "num": "0x7b9832ba", "position": {"altitude": 1422, "latitude": 34.760079, "location_source": "LOC_INTERNAL", "longitude": -107.083786, "time_offset_sec": 2123}, "public_key_hex": "f3b168f6cb1dc13894a84e6bcea3815232cfe5b60279401832cc51b2cbbf5200", "role": "CLIENT", "short_name": "FZ3S", "snr": 7.67, "status": {"status": "ready"}, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 4, "environment": {"barometric_pressure": 996.03, "iaq": 103, "relative_humidity": 43.23, "temperature": 27.85}, "hops_away": 2, "hw_model": "HELTEC_MESH_SOLAR", "last_heard_offset_sec": 2349, "long_name": "Soft Coyote", "next_hop": 141, "num": "0x7c4cf82e", "position": null, "public_key_hex": "39c6faeae76d6831292d9828d401b5247709ae3353bc99be2be6217976f15711", "role": "CLIENT", "short_name": "S34Q", "snr": 0.27, "status": {"status": "active"}, "telemetry": {"air_util_tx": 1.144, "battery_level": 79, "channel_utilization": 11.78, "uptime_seconds": 90955, "voltage": 4.011}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1019.12, "iaq": 55, "relative_humidity": 50.17, "temperature": 20.67}, "hops_away": 0, "hw_model": "HELTEC_MESH_POCKET", "last_heard_offset_sec": 661, "long_name": "Forest Dolphin", "next_hop": 0, "num": "0x7cb88352", "position": {"altitude": 857, "latitude": 33.336533, "location_source": "LOC_INTERNAL", "longitude": -107.606652, "time_offset_sec": 706}, "public_key_hex": "cdcaa7a375d26a24dbcb6d7685fc62a5562c1536f9520e0d85396f5710c90aa8", "role": "CLIENT", "short_name": "FN6X", "snr": 0.74, "status": {"status": "active"}, "telemetry": {"air_util_tx": 0.37, "battery_level": 61, "channel_utilization": 9.89, "uptime_seconds": 372510, "voltage": 3.849}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 8676, "long_name": "Brave Iguana", "next_hop": 0, "num": "0x7cf15b3e", "position": {"altitude": 1886, "latitude": 33.819257, "location_source": "LOC_INTERNAL", "longitude": -107.739619, "time_offset_sec": 8913}, "public_key_hex": "6a9c754541b4c2c875a2aadbe922f57c641582c36a2a9b89c8df1e3d22c37eff", "role": "CLIENT", "short_name": "B4LT", "snr": 5.51, "status": {"status": "no-gps"}, "telemetry": {"air_util_tx": 1.236, "battery_level": 45, "channel_utilization": 10.01, "uptime_seconds": 137576, "voltage": 3.705}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 2052, "long_name": "Whispering Doe", "next_hop": 0, "num": "0x7cf4966c", "position": {"altitude": 874, "latitude": 32.846001, "location_source": "LOC_INTERNAL", "longitude": -105.956593, "time_offset_sec": 2166}, "public_key_hex": "", "role": "CLIENT", "short_name": "W99D", "snr": 3.4, "status": null, "telemetry": {"air_util_tx": 0.56, "battery_level": 52, "channel_utilization": 3.85, "uptime_seconds": 38863, "voltage": 3.768}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": true, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": true, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 2, "hw_model": "HELTEC_V3", "last_heard_offset_sec": 902, "long_name": "White Raven AB5MG", "next_hop": 13, "num": "0x7cfd5051", "position": null, "public_key_hex": "9fa2378b9ce7198261d695d5316c1a3a011cbff32b7b4da3384cff071f968cd0", "role": "CLIENT", "short_name": "WPPH", "snr": 8.19, "status": null, "telemetry": {"air_util_tx": 0.506, "battery_level": 35, "channel_utilization": 18.87, "uptime_seconds": 1244, "voltage": 3.615}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": true, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 4, "hw_model": "LILYGO_TBEAM_S3_CORE", "last_heard_offset_sec": 1198, "long_name": "Silver Heron", "next_hop": 89, "num": "0x7d3454ac", "position": {"altitude": 1641, "latitude": 33.593007, "location_source": "LOC_INTERNAL", "longitude": -106.858867, "time_offset_sec": 1487}, "public_key_hex": "604c7af1d4a56fc9dfbdd54eeb7226cf7fc5656527578340139441eb8f043dbc", "role": "CLIENT", "short_name": "🌊", "snr": 9.21, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.806, "battery_level": 89, "channel_utilization": 14.05, "uptime_seconds": 89908, "voltage": 4.101}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "HELTEC_V4", "last_heard_offset_sec": 828, "long_name": "Red Whale", "next_hop": 0, "num": "0x7d3bc64f", "position": {"altitude": 1496, "latitude": 34.091942, "location_source": "LOC_INTERNAL", "longitude": -107.54737, "time_offset_sec": 950}, "public_key_hex": "ba0b75e6258532e1ad5ceb53688bfb9b4d3cecd04313dddc9ae54137b24d6911", "role": "CLIENT", "short_name": "RNTA", "snr": 7.01, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "RAK4631", "last_heard_offset_sec": 3539, "long_name": "Iron Hawk", "next_hop": 0, "num": "0x7d47b6f8", "position": {"altitude": 1822, "latitude": 33.069863, "location_source": "LOC_INTERNAL", "longitude": -107.612415, "time_offset_sec": 3805}, "public_key_hex": "71715cfe83cd82673ad031905c7b9eec55503ded0490c5ca8f0046d378f539c6", "role": "ROUTER_LATE", "short_name": "IMDI", "snr": 12.0, "status": null, "telemetry": {"air_util_tx": 0.067, "battery_level": 39, "channel_utilization": 0.73, "uptime_seconds": 110889, "voltage": 3.651}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_ECHO", "last_heard_offset_sec": 6745, "long_name": "Copper Pine", "next_hop": 0, "num": "0x7daa6446", "position": {"altitude": 1432, "latitude": 32.883057, "location_source": "LOC_INTERNAL", "longitude": -107.857615, "time_offset_sec": 6792}, "public_key_hex": "228c311d5844991c731e338c97905d4686b4c5378a15a6139e7e304ada2e6387", "role": "CLIENT", "short_name": "C25O", "snr": 7.52, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.331, "battery_level": 65, "channel_utilization": 21.64, "uptime_seconds": 159410, "voltage": 3.885}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "TRACKER_T1000_E", "last_heard_offset_sec": 1289, "long_name": "Wild Moose", "next_hop": 0, "num": "0x7e293a9a", "position": null, "public_key_hex": "ef0471274ab1c232c164127963b28415db71aab7ad8988010856ba77ee9363de", "role": "TRACKER", "short_name": "WJXL", "snr": 2.36, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 0.295, "battery_level": 94, "channel_utilization": 19.23, "uptime_seconds": 148622, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "HELTEC_WIRELESS_PAPER", "last_heard_offset_sec": 5811, "long_name": "Silver Cobra", "next_hop": 80, "num": "0x7e98136c", "position": {"altitude": 1176, "latitude": 33.644568, "location_source": "LOC_INTERNAL", "longitude": -107.51554, "time_offset_sec": 6021}, "public_key_hex": "d7d36933041896a386b15c785f552e08f16d4b3ee6687e6dc1e2d38aaf8b0a7d", "role": "CLIENT", "short_name": "SQ5M", "snr": 8.33, "status": {"status": "ready"}, "telemetry": {"air_util_tx": 0.554, "battery_level": 85, "channel_utilization": 5.06, "uptime_seconds": 30228, "voltage": 4.065}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "T_DECK", "last_heard_offset_sec": 704, "long_name": "Lone Mesa", "next_hop": 0, "num": "0x7ee205cc", "position": {"altitude": 1332, "latitude": 33.586042, "location_source": "LOC_INTERNAL", "longitude": -106.953368, "time_offset_sec": 910}, "public_key_hex": "46f79ed30865e398a21c801f213cfc61e58902b85278f7d515b82b8e2e3adba6", "role": "CLIENT", "short_name": "🦂", "snr": 10.02, "status": {"status": "OK"}, "telemetry": {"air_util_tx": 1.145, "battery_level": 90, "channel_utilization": 4.22, "uptime_seconds": 219179, "voltage": 4.11}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 812, "long_name": "Giant Hawk", "next_hop": 125, "num": "0x7ef09a0c", "position": {"altitude": 1360, "latitude": 33.718694, "location_source": "LOC_INTERNAL", "longitude": -106.810279, "time_offset_sec": 1080}, "public_key_hex": "6a912d9ac3abc3be3bded33ebdc06539f3fd4099e33b3638b20bc9d4278749ab", "role": "CLIENT", "short_name": "GSLL", "snr": 7.27, "status": null, "telemetry": {"air_util_tx": 1.274, "battery_level": 94, "channel_utilization": 13.89, "uptime_seconds": 34740, "voltage": 4.146}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 1011.47, "iaq": 77, "relative_humidity": 57.86, "temperature": 19.64}, "hops_away": 1, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 1465, "long_name": "Quick Viper", "next_hop": 254, "num": "0x7f2c9c94", "position": {"altitude": 1504, "latitude": 32.929298, "location_source": "LOC_INTERNAL", "longitude": -107.529246, "time_offset_sec": 1691}, "public_key_hex": "98da664d4852437c7b699b87e5214d9433223984ac1a39af47f3b0e20ef19703", "role": "CLIENT", "short_name": "🦋", "snr": 1.03, "status": null, "telemetry": null}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 3, "hw_model": "RAK4631", "last_heard_offset_sec": 1370, "long_name": "Old Cobra", "next_hop": 96, "num": "0x7f3ce610", "position": {"altitude": 1386, "latitude": 32.584843, "location_source": "LOC_INTERNAL", "longitude": -106.99496, "time_offset_sec": 1565}, "public_key_hex": "46a346b7947ec2d961097e2481c3cd2a29897afb36065ace08b3affac3b0ec4e", "role": "CLIENT", "short_name": "OMR8", "snr": 1.81, "status": null, "telemetry": {"air_util_tx": 0.343, "battery_level": 101, "channel_utilization": 5.13, "uptime_seconds": 184861, "voltage": 4.2}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": true, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": null, "hops_away": 0, "hw_model": "SEEED_SOLAR_NODE", "last_heard_offset_sec": 3375, "long_name": "Copper Mole", "next_hop": 0, "num": "0x7fa9346f", "position": {"altitude": 1088, "latitude": 34.023624, "location_source": "LOC_INTERNAL", "longitude": -107.080344, "time_offset_sec": 3654}, "public_key_hex": "7f351bcc32b693e11f10c794f30fb986a1f701ac9ff394960f3e65af8b4c1e08", "role": "CLIENT", "short_name": "CSX2", "snr": 5.51, "status": {"status": "running"}, "telemetry": {"air_util_tx": 1.156, "battery_level": 31, "channel_utilization": 4.14, "uptime_seconds": 126619, "voltage": 3.579}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 0, "environment": {"barometric_pressure": 986.27, "iaq": 104, "relative_humidity": 61.42, "temperature": 17.96}, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 2608, "long_name": "Quick Beaver", "next_hop": 0, "num": "0x7fb6b696", "position": {"altitude": 1176, "latitude": 33.375736, "location_source": "LOC_INTERNAL", "longitude": -107.702107, "time_offset_sec": 2853}, "public_key_hex": "bbe249baf336d614c31e98d0c3c773de70725991c1244746ab04fcdea1afe730", "role": "CLIENT", "short_name": "Q8U6", "snr": 1.83, "status": null, "telemetry": {"air_util_tx": 0.264, "battery_level": 99, "channel_utilization": 3.68, "uptime_seconds": 59962, "voltage": 4.191}}
|
||||
{"bitfield": {"has_is_unmessagable": true, "has_user": true, "is_favorite": false, "is_ignored": false, "is_key_manually_verified": false, "is_licensed": false, "is_muted": false, "is_unmessagable": false, "via_mqtt": false}, "channel": 3, "environment": null, "hops_away": 0, "hw_model": "T_DECK_PRO", "last_heard_offset_sec": 5106, "long_name": "Sunny Adder", "next_hop": 0, "num": "0x7fd909ad", "position": {"altitude": 1437, "latitude": 32.788073, "location_source": "LOC_INTERNAL", "longitude": -107.233327, "time_offset_sec": 5249}, "public_key_hex": "80c21b4a6f7e3955bfeb3626b0e7d8af8e884e4afd5af927ed6d4787d43d0131", "role": "CLIENT", "short_name": "SDJF", "snr": 8.38, "status": {"status": "nominal"}, "telemetry": null}
|
||||
+1001
File diff suppressed because it is too large
Load Diff
+2001
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user