mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-11 03:12:51 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ab04907be | |||
| cb98211445 | |||
| afb303af78 |
@@ -65,3 +65,4 @@ syncpi.sh
|
||||
|
||||
# Docker
|
||||
/data
|
||||
Installing
|
||||
|
||||
@@ -503,10 +503,19 @@ if [ -n "$ARCH_TAG" ]; then
|
||||
"$VENV_PIP" install --find-links "${WHEEL_BASE}/index.html" --no-cache-dir "pycryptodome>=3.23.0" "PyNaCl>=1.5.0" cffi "pyyaml>=6.0.0" 2>/dev/null || true
|
||||
fi
|
||||
# ---- Install pymc_repeater from git ----
|
||||
exec "$VENV_PIP" install \
|
||||
if "$VENV_PIP" install \
|
||||
--upgrade \
|
||||
--no-cache-dir \
|
||||
"pymc_repeater[hardware] @ git+https://github.com/rightup/pyMC_Repeater.git@${CHANNEL}"
|
||||
"pymc_repeater[hardware] @ git+https://github.com/rightup/pyMC_Repeater.git@${CHANNEL}"; then
|
||||
# Keep web/OTA updates aligned with manage.sh install/upgrade defaults.
|
||||
RADIO_BASE_URL="https://raw.githubusercontent.com/rightup/pyMC_Repeater/${CHANNEL}"
|
||||
RADIO_STORAGE_DIR="/var/lib/pymc_repeater"
|
||||
mkdir -p "$RADIO_STORAGE_DIR"
|
||||
wget -qO "$RADIO_STORAGE_DIR/radio-settings.json" "${RADIO_BASE_URL}/radio-settings.json" 2>/dev/null || true
|
||||
wget -qO "$RADIO_STORAGE_DIR/radio-presets.json" "${RADIO_BASE_URL}/radio-presets.json" 2>/dev/null || true
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
UPGRADEEOF
|
||||
chmod 0755 /usr/local/bin/pymc-do-upgrade
|
||||
|
||||
@@ -907,10 +916,19 @@ python3 -m pip uninstall -y pymc_core 2>/dev/null || true
|
||||
"$VENV_PIP" install --find-links "${WHEEL_BASE}/index.html" --no-cache-dir "pycryptodome>=3.23.0" "PyNaCl>=1.5.0" cffi "pyyaml>=6.0.0" 2>/dev/null || true
|
||||
fi
|
||||
# ---- Install pymc_repeater from git ----
|
||||
exec "$VENV_PIP" install \
|
||||
if "$VENV_PIP" install \
|
||||
--upgrade \
|
||||
--no-cache-dir \
|
||||
"pymc_repeater[hardware] @ git+https://github.com/rightup/pyMC_Repeater.git@${CHANNEL}"
|
||||
"pymc_repeater[hardware] @ git+https://github.com/rightup/pyMC_Repeater.git@${CHANNEL}"; then
|
||||
# Keep web/OTA updates aligned with manage.sh install/upgrade defaults.
|
||||
RADIO_BASE_URL="https://raw.githubusercontent.com/rightup/pyMC_Repeater/${CHANNEL}"
|
||||
RADIO_STORAGE_DIR="/var/lib/pymc_repeater"
|
||||
mkdir -p "$RADIO_STORAGE_DIR"
|
||||
wget -qO "$RADIO_STORAGE_DIR/radio-settings.json" "${RADIO_BASE_URL}/radio-settings.json" 2>/dev/null || true
|
||||
wget -qO "$RADIO_STORAGE_DIR/radio-presets.json" "${RADIO_BASE_URL}/radio-presets.json" 2>/dev/null || true
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
UPGRADEEOF
|
||||
chmod 0755 /usr/local/bin/pymc-do-upgrade
|
||||
echo " ✓ Permissions updated"
|
||||
|
||||
@@ -70,7 +70,7 @@ class PacketRecord:
|
||||
origin_id=origin_id,
|
||||
timestamp=dt.isoformat(),
|
||||
type="PACKET",
|
||||
direction="rx",
|
||||
direction="tx" if bool(packet_record.get("transmitted", False)) else "rx",
|
||||
time=dt.strftime("%H:%M:%S"),
|
||||
date=dt.strftime("%-d/%-m/%Y"),
|
||||
len=str(len(packet_record["raw_packet"]) // 2),
|
||||
|
||||
+9
-2
@@ -1083,8 +1083,15 @@ class RepeaterDaemon:
|
||||
route_type="flood",
|
||||
)
|
||||
|
||||
# Send via dispatcher
|
||||
await self.dispatcher.send_packet(packet, wait_for_ack=False)
|
||||
# Route advert TX through router injection so it flows through engine/storage/MQTT.
|
||||
if not self.router:
|
||||
logger.error("Cannot send advert: router not initialized")
|
||||
return False
|
||||
|
||||
send_ok = await self.router.inject_packet(packet, wait_for_ack=False)
|
||||
if not send_ok:
|
||||
logger.error("Failed to inject advert packet for TX")
|
||||
return False
|
||||
|
||||
if self.repeater_handler:
|
||||
self.repeater_handler.mark_seen(packet)
|
||||
|
||||
@@ -4,7 +4,8 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import pytest
|
||||
|
||||
from repeater.companion.constants import STATS_TYPE_CORE, STATS_TYPE_PACKETS, STATS_TYPE_RADIO
|
||||
from repeater.main import RepeaterDaemon, main as repeater_main
|
||||
from repeater.main import RepeaterDaemon
|
||||
from repeater.main import main as repeater_main
|
||||
|
||||
|
||||
class _FakeIdentity:
|
||||
@@ -212,7 +213,7 @@ async def test_register_identity_everywhere_calls_helpers_and_respects_collision
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_advert_branches_and_success_path():
|
||||
async def test_send_advert_branches_and_success_paths():
|
||||
daemon = RepeaterDaemon(_base_config(), radio=object())
|
||||
|
||||
# Missing dispatcher/local identity
|
||||
@@ -231,12 +232,19 @@ async def test_send_advert_branches_and_success_path():
|
||||
get_repeater_location=lambda: {"latitude": 9.1, "longitude": 8.2, "source": "gps"}
|
||||
)
|
||||
|
||||
# Router is now required for advert TX processing path.
|
||||
daemon.router = None
|
||||
assert await daemon.send_advert() is False
|
||||
|
||||
daemon.router = SimpleNamespace(inject_packet=AsyncMock(return_value=True))
|
||||
|
||||
packet = SimpleNamespace(calculate_packet_hash=lambda: b"\xab" * 16)
|
||||
with patch("pymc_core.protocol.PacketBuilder.create_advert", return_value=packet):
|
||||
ok = await daemon.send_advert()
|
||||
|
||||
assert ok is True
|
||||
daemon.dispatcher.send_packet.assert_awaited_once_with(packet, wait_for_ack=False)
|
||||
daemon.router.inject_packet.assert_awaited_once_with(packet, wait_for_ack=False)
|
||||
daemon.dispatcher.send_packet.assert_not_called()
|
||||
daemon.repeater_handler.mark_seen.assert_called_once_with(packet)
|
||||
daemon.dispatcher.packet_filter.track_packet.assert_called_once()
|
||||
|
||||
|
||||
@@ -236,3 +236,40 @@ def test_mqtt_published_packet_legacy_mqtt_format_uses_singular_packet_topic():
|
||||
payload_dict = json.loads(captured[0]["payload"])
|
||||
# Duration still flows through correctly even on the legacy topic
|
||||
assert int(payload_dict["duration"]) > 0
|
||||
|
||||
|
||||
def test_mqtt_published_packet_direction_uses_transmitted_flag_for_tx():
|
||||
"""When engine marks packet_record.transmitted=True, published direction must be tx."""
|
||||
config = _make_config(format_value="letsmesh", iata_code="LAX")
|
||||
public_key_hex = "11" * 32
|
||||
identity = _FakeIdentity(public_key_hex)
|
||||
|
||||
pusher = MeshCoreToMqttPusher(local_identity=identity, config=config)
|
||||
captured = _attach_capturing_client(pusher.connections[0])
|
||||
|
||||
raw_bytes = bytes(range(12))
|
||||
airtime_mgr = AirtimeManager(config)
|
||||
packet_record = {
|
||||
"timestamp": 1700000000.0,
|
||||
"type": 8,
|
||||
"route": 1,
|
||||
"rssi": -88,
|
||||
"snr": 6.0,
|
||||
"score": 0.4,
|
||||
"payload_length": 6,
|
||||
"packet_hash": "FACEFEED" + "00" * 4,
|
||||
"raw_packet": raw_bytes.hex(),
|
||||
"airtime_ms": airtime_mgr.calculate_airtime(len(raw_bytes)),
|
||||
"transmitted": True,
|
||||
}
|
||||
|
||||
record = PacketRecord.from_packet_record(
|
||||
packet_record, origin="test-node", origin_id=public_key_hex.upper()
|
||||
)
|
||||
assert record is not None
|
||||
|
||||
pusher.publish_packet(record.to_dict())
|
||||
|
||||
assert len(captured) == 1
|
||||
payload_dict = json.loads(captured[0]["payload"])
|
||||
assert payload_dict["direction"] == "tx"
|
||||
|
||||
Reference in New Issue
Block a user