88 lines
3.8 KiB
Markdown
88 lines
3.8 KiB
Markdown
# Changelog
|
||
|
||
## 1.0.0 — 2026-04-22
|
||
|
||
### Breaking
|
||
|
||
- **Upgraded to the DongLoRa Protocol v2 wire format.** The 0.x line spoke the legacy
|
||
v0.1 protocol with hardcoded tag constants (`TAG_OK=4`, `TAG_ERROR=5`,
|
||
`CMD_TAG_SET_CONFIG=2`, …) that don't exist in v1.0. This release is
|
||
wire-incompatible with 0.x clients and firmware.
|
||
- **Pins `donglora>=1.0.0`** and uses its `donglora.frame` /
|
||
`donglora.events` / `donglora.commands` modules as the wire-level
|
||
backbone. No more in-package reimplementation of the wire protocol.
|
||
- **Structural rewrite** mirroring the Rust sibling
|
||
[`donglora-mux`](https://crates.io/crates/donglora-mux) one-to-one:
|
||
`donglora_mux/{daemon,intercept,session,tagmap}.py` replace the single
|
||
601-line `__init__.py`.
|
||
|
||
### Added
|
||
|
||
- **`TagMapper`** — per-command device-tag allocator. Enables concurrent
|
||
out-of-order command/response correlation for multi-client setups
|
||
(the v0.1-era single-oneshot-response slot is gone).
|
||
- **Full `§13.2` multi-client `SET_CONFIG` semantics** — tracks the
|
||
`(client_id, modulation)` lock owner. Cross-client calls synthesize:
|
||
- `OK(APPLIED / MINE, params)` when the caller owns the lock
|
||
(forwarded to dongle).
|
||
- `OK(ALREADY_MATCHED / OTHER, current)` when another client holds
|
||
the lock and the requested params byte-match.
|
||
- `OK(LOCKED_MISMATCH / OTHER, current)` when they differ; `current`
|
||
echoes the lock owner's actual modulation.
|
||
- Lock auto-releases when the owning client disconnects.
|
||
- **TX loopback fan-out (`§13.4`)** — on `TX_DONE(TRANSMITTED)` the mux
|
||
synthesizes an `RX` frame with `origin = LOCAL_LOOPBACK` and sends it
|
||
to every connected client except the transmitter.
|
||
- **Async `ERR(EFRAME)` fan-out** — if the dongle emits async framing
|
||
errors (tag 0), they're broadcast to all clients rather than swallowed.
|
||
- **Per-client TX cache** on `ClientSession` so the loopback fan-out
|
||
doesn't need the sender to re-buffer the payload.
|
||
- **Socket and serial file locking** (`fcntl.flock`) to prevent multiple
|
||
mux instances from fighting over the same dongle or socket path.
|
||
- **500 ms keepalive** matching `PROTOCOL.md §3.4` (2× margin on the
|
||
1 s inactivity timer). Replaces the 5 s cadence from 0.x.
|
||
- **Clean ^C / SIGTERM shutdown** — per-client handler tasks are tracked
|
||
and cancelled on shutdown, so blocked `reader.read()` calls no longer
|
||
pin the event loop open.
|
||
|
||
### Removed
|
||
|
||
- Every v0.1 wire constant and the inline `cobs` + raw `serial.Serial`
|
||
frame codec.
|
||
- The single `pending_response: asyncio.Future` slot (fundamentally
|
||
incompatible with v1.0's per-tag concurrency).
|
||
|
||
### Test coverage
|
||
|
||
- 28 tests (up from 11):
|
||
- `test_tagmap.py` (6) — tag allocator behaviour.
|
||
- `test_intercept.py` (13) — every branch of the `decide()` matrix,
|
||
including the `ALREADY_MATCHED` / `LOCKED_MISMATCH` synthesis paths.
|
||
- `test_multiclient.py` (9) — asyncio-driven daemon dispatch tests:
|
||
concurrent tag routing, `SET_CONFIG` lock installation, TX loopback
|
||
to peer clients, lock release on disconnect, unsolicited-frame drop.
|
||
|
||
## 0.2.0 — 2026-04-08
|
||
|
||
### Features
|
||
|
||
- **PyPI-ready metadata** — full classifiers, URLs, keywords, readme. Publishable
|
||
as `donglora-mux`.
|
||
- **Interception test suite** — 11 tests ported from the Rust mux covering
|
||
SetConfig locking, StartRx/StopRx reference counting, and passthrough
|
||
behaviour.
|
||
- **GitHub Actions CI** — ruff lint + format, pytest on Python 3.10–3.13, PyPI
|
||
publish on tag.
|
||
- **Strict ruff linting** — E, W, F, I, UP, B, C4, ARG, SIM, RUF rules.
|
||
|
||
### Fixes
|
||
|
||
- Fixed broken dependency path (`../../clients/python` → `../client-py`).
|
||
- Fixed dependency name (`donglora-python` → `donglora`).
|
||
- Applied `contextlib.suppress` where ruff/SIM105 flagged try-except-pass.
|
||
- Collapsed nested `if` per ruff/SIM102.
|
||
|
||
## 0.1.0 — 2026-04-06
|
||
|
||
Initial release.
|