Skip to content

Releases: tesseractAZ/Switchboard

v0.43.0

Choose a tag to compare

@tesseractAZ tesseractAZ released this 29 Jul 00:09
cf9e29c

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 AMI Getvar) 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-Info header 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_busy helpers, covered by
    wire-level and truth-table tests in tests/test_webui.py.

v0.42.3 — repo cleanup

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27
d08c84d

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, and mwi_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
    ValueError at start as harmless; that startup race was eliminated in 0.29.1, so
    such a ValueError now indicates a genuine fault rather than a benign one.
  • Removed the inert armv7 base image from build.yaml; the manifest, README, and
    DOCS all declare amd64/aarch64 only.
  • Corrected a stale switchboard-config comment that described the tmpfs AMI secret
    file as 0600; it is created 0640, as SECURITY.md already 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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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.x across the
    documentation, the shipped monitors' fallback defaults, the tools, and the test
    fixtures. Behaviour is unchanged — a live install overrides these.
  • The documented cordless_ip default 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.md and
    switchboard/DOCS.md are the reference.
  • wp826-cli.exp reads WP826_HOST instead 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.py regenerates 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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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 AMI PJSIPShowRegistrationsOutbound action. 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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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

Choose a tag to compare

@tesseractAZ tesseractAZ released this 28 Jul 17:27

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_ext example 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 .gitignore against accidentally
    committing secrets/state. No secret was ever committed — nothing required
    rotation (verified against full git history).