Releases: tesseractAZ/Switchboard
Release list
v0.43.0
Busy-guard on the handset announce endpoint: an announcement is no longer
originated to a phone that is already on a call.
POST /api/announce/{ext}now reads the target's device state
(DEVICE_STATE(PJSIP/<ext>)via a channel-less AMIGetvar) immediately
before the originate. When the phone is on or being offered a call
(INUSE/RINGING/RINGINUSE/BUSY/ONHOLD), the call is not
placed: an INVITE to a busy phone cannot auto-answer (the intercom
Call-Infoheader acts only on an idle device) and instead rings the
handset as call waiting — a phantom ring right after an alarm announcement
when a duplicate dispatch reaches a handset still in its announce call.- A skipped announcement is logged and reported as
{"ok": true, "skipped": "busy", "device_state": …}(HTTP 200), so callers
treat it as handled rather than re-queueing identical content behind the
call in progress. - The guard fails open — an unreadable device state (AMI hiccup) never blocks
an announcement — and applies only to the announce endpoint: ordinary
inbound/outbound calling, paging, test rings and wake-up calls are
unchanged. - New
ami.get_device_state/ami.device_busyhelpers, covered by
wire-level and truth-table tests intests/test_webui.py.
v0.42.3 — repo cleanup
Repository cleanup: unused code removed and documentation reconciled with the
source. No add-on behaviour changes.
- Removed three unused helpers with no callers —
ami.get_contacts,
ami.get_channels, andmwi_store.all_flags— each superseded by
ami.get_status_bundle/mwi_store.exts. Their underlying*_from_blocks
parsers remain in use and are unaffected. - Dropped an obsolete
DOCS.md§14 troubleshooting row describing a web-terminal
ValueErrorat start as harmless; that startup race was eliminated in 0.29.1, so
such aValueErrornow indicates a genuine fault rather than a benign one. - Removed the inert
armv7base image frombuild.yaml; the manifest, README, and
DOCS all declareamd64/aarch64only. - Corrected a stale
switchboard-configcomment that described the tmpfs AMI secret
file as0600; it is created0640, asSECURITY.mdalready documents. - Genericized the SIP-trunk POP hostname in the test fixtures to
losangeles.voip.ms,
matching the documentation and completing the 0.42.2 hygiene pass.
v0.42.2 — public-repo hygiene + one-way-audio detection
Rolls up everything since v0.42.0: a call-quality detection fix, a full
public-repository hygiene pass, and documentation with screenshots.
Fixes
One-way audio is now detected when a direction is near dead, not only fully dead.
A live call scored excellent despite receiving a single inbound RTP packet across
38 seconds — the caller's audio never reached the PBX. The detector treated a
direction as dead only at exactly zero packets, so one stray packet read as healthy.
A direction carrying fewer than ten packets over a whole call is now dead (a live
G.711 leg streams ~50 packets/s even in silence). The guard requiring >50 packets on
the live side still prevents a call-setup blip from false-alarming.
Public-repository hygiene
This repository is public; it no longer carries real-home details.
- Every real LAN address is genericised to the example range
192.168.1.xacross the
documentation, the shipped monitors' fallback defaults, the tools, and the test
fixtures. Behaviour is unchanged — a live install overrides these. - The documented
cordless_ipdefault is corrected to"", matching the manifest. - Vulnerability reports route through a GitHub Security Advisory rather than a
personal email; the maintainer field no longer carries a personal address. - The internal maintainer handoff (
STATUS.md) is removed;README.mdand
switchboard/DOCS.mdare the reference. wp826-cli.expreadsWP826_HOSTinstead of a hard-coded address.
No secret was ever committed — the history is pickaxe-clean.
Documentation
- The README is rewritten for the current feature set.
- Screenshots of the dashboard and the operator console, rendered from the real
interfaces with generic example data, appear in the README and in DOCS. scripts/build-screenshots.pyregenerates both deterministically; a CI job fails
if a change breaks the generator, and a release refreshes the committed pair so
the images cannot lag a release.- The printable manual attached below embeds each screenshot once rather than twice.
Documentation downloads
Switchboard-Documentation-v0.42.2.docx and .pdf are attached to this release:
the README, the security posture, and the complete system reference as one document.
v0.42.0
Documentation: a generated Word + PDF manual, built and attached automatically.
scripts/build-docs-docx.py assembles README + SECURITY + DOCS.md into one
printable manual — an editable .docx (via pandoc) and a reader-friendly
.pdf (via LibreOffice, no LaTeX) — and a new docs CI workflow builds both on
every push/PR (uploaded as an artifact, so a DOCS.md that stops converting fails
the check) and attaches them to each published GitHub Release. Same process the
ecoflow-panel and zwave repos use.
The reference itself was refreshed for the current release: the real
prefix-free direct-dial GXW dial plan and the SSH CLI config path (with
the P85 = 3 s rotary-safe send delay), the full-screen operator console with
its trunk-registration / STT-health / per-room-RTT signals, and a new
"Reproducing on new hardware" chapter (rebuild from a release tag; restore
config from a Home Assistant backup).
v0.41.1
Fix: saying just "time" to the operator now reaches the talking clock.
Whisper transcribed a caller's "time" request correctly (heard Time.), but the
operator's clock trigger only listed multi-word forms ("the time", "what time
is it", "time please"…) and not the bare word "time", so the most natural
utterance matched nothing and the operator gave up. Added time (plus "the
current time" / "got the time") to the clock phrases. Whole-word matching still
protects fragments — "anytime", "overtime", "bedtime" don't trip it — and a
confident room match still wins first, so nothing named after the word is
shadowed. No other feature had a bare-word gap (weather/power/directory/announce/
page/intercom all already resolve as single words).
v0.41.0
Full-screen operator console + the new signals mirrored into the TUI.
The telnet/browser console now uses the whole terminal width instead of a
72-column board floating in the middle: the rules span the full width, the header
puts the title on the left and the online/on-call/clock stats on the right edge,
and each room row right-aligns its live link RTT (idle qualify round-trip) and
any notable contact status. A compact status line under the header shows the same
two signals the web dashboard got in 0.40.0 — trunk SIP registration (green
Registered / red Unregistered / grey Unknown) and resident-STT health (green
resident / amber CLI fallback). It's still vertically centered and degrades
cleanly on a narrow window (the right-aligned detail drops rather than wrapping).
The trunk-registration and STT-health reads are on their own 20s throttle (they
each cost an extra AMI login / loopback probe), separate from the ~3s board poll,
so console AMI churn stays negligible. No behavior change to calls or routing.
v0.40.0
Dashboard: trunk registration status + resident-STT health.
The header sub-line now shows two things it never surfaced before:
- Trunk registration — for an enabled outside line, its live SIP registration
state (green Registered, red Rejected/Unregistered, grey Unknown), read via
the AMIPJSIPShowRegistrationsOutboundaction. A dead trunk registration
(wrong secret, NAT pinhole, provider down) kills all PSTN calls but was
previously invisible in the UI. - Resident STT — an amber "STT: CLI fallback" when the whisper-server isn't
answering (so voice recognition is silently running on the slow per-call
whisper-cli); quiet when it's resident or intentionally disabled.
Both ride their own longer-TTL caches (20s / 8s) so the hot /api/status path and
its 3-tuple bundle are untouched; the whisper probe is a 1s loopback GET. Completes
the dashboard observability alongside the per-phone link health added in 0.39.0.
(The telnet/browser TUI mirror of these is coming next.)
v0.39.0
Dashboard now shows per-phone link health (contact status + round-trip time).
Each registered, idle room card now displays its SIP contact status and idle
round-trip time (e.g. "Reachable · 2.4 ms") beneath the status pill — the data was
already collected (AMI ContactList RoundtripUsec, surfaced in /api/status) but never
rendered. At a glance you can now see which handsets are responding quickly and spot
a laggy one (e.g. the Wi-Fi cordless) without opening logs. Rendering-only change to
the web dashboard; no new polling. (Trunk-registration status and whisper-server
health indicators are coming next.)
v0.38.0
Voice operator feels snappier — the ~1s recognition now hides behind the prompt.
Every voice flow (dial-0 operator, dial-a-status 45, directory 411, announce 46,
wake-up 42, automation 43) recorded your speech, played "one moment," and then
ran whisper — so the ~1s of recognition was dead air added after the prompt. Now
the transcription is started (as a background process) just BEFORE the "one moment"
prompt and collected just after, so the ~0.9s of prompt audio plays OVER the
inference instead of before it. That shaves roughly the prompt's length off every
voice interaction, cutting the post-speech wait from ~2s to ~1s.
Safe by construction: the STT child reads its input from the recorded WAV file
(stdin=/dev/null) and only talks to the loopback whisper-server — it never touches
the AGI's stdin/stdout, so it runs concurrently with the (blocking) prompt playback
without any chance of corrupting the Asterisk AGI protocol stream. The 25s STT
backstop, error handling, and the recognition result contract are unchanged.
v0.37.0
Operator greeting now advertises the voice features, plus public-repo hygiene.
- Greeting — dial-0 now says "Say a room name, or ask for the time, weather,
directory, or lights," so the voice features (added in 0.33.0) are discoverable
instead of hidden. Inbound outside calls ring only the cordless by default now
(inbound_extexample trimmed to a single ext; re-add a second to group-ring). - Public-readiness (the add-on is open-source): genericized the example
provider username and caller-ID in the test fixtures to fictional values
(100000_switchboard, a 555-0100 reserved number) so no real account identifier
or home phone number ships in the repo; neutralized the shipped default
cordless_ip(was a real-home address) to empty (device-health auto-follows the
cordless's registration anyway); hardened.gitignoreagainst accidentally
committing secrets/state. No secret was ever committed — nothing required
rotation (verified against full git history).