Releases: n-IA-hane/esphome-intercom
Release list
2026.8.2-dev pre-release: WE HAVE A F...ING ESPHOME VIDEOPHONE!
2026.8.2-dev pre-release: WE HAVE A F...ING ESPHOME VIDEOPHONE!
2026.8.2-dev remains an active pre-release and work in progress. The tag and
HACS archive may be refreshed while field qualification continues. Use
2026.8.0 when you need the current stable release.
🔧 What 2026.8.2-dev adds
The original P4 videophone milestone is all still here. Since that first
development candidate, the project has also received a deep consolidation of
the PBX, SIP and media lifecycles:
- one generation-owned call session now governs direct, trunk, forwarded,
grouped, conference, browser and Assist calls; - answer, bridge activation, losing-leg settlement, projection, rollback and
termination now use shared authoritative primitives instead of parallel call
paths; - public idle is published only after call-scoped tasks, media owners, relays,
sockets and RTP reservations have crossed the cleanup barrier; - SIP interoperability now includes reliable provisional responses and PRACK,
initial and in-dialog delayed offers, RFC 4028 session refresh, remote forks
and late 2xx settlement, REFER/NOTIFY, stronger Digest authentication,
RFC 3263 discovery, TLS and IPv6; - registered clients, trunks and local endpoints share the same atomic
audio/video renegotiation mechanics, including audio-first video add, remove
and re-add without sacrificing the established audio call; - forwarding, ring groups, conference calls, DTMF routing and browser phones
retain one call owner and release failed or losing destinations without
disturbing the remaining members; - the P4 JPEG and H.264 paths gained bounded presentation work, bidirectional
cadence stabilization, post-call LVGL recovery and one-shot framebuffer
capture for direct physical-panel verification; - the ESP voice assistant is now documented as an independent optional device
capability, separate from a SIP call to Home Assistant Assist; - the local qualification toolkit now covers real Home Assistant, Chromium,
SIPp, baresip, registered clients, trunks, S3/P4 hardware, firmware metadata
and post-call quiescence without running jobs automatically on GitHub pushes.
This is still a development release. The stable baseline remains 2026.8.0.
🏠 Home Assistant and VoIP Stack
- The Home Assistant audio and video media paths now share one lifecycle for
authenticated ownership, replacement barriers, live-session resolution,
bridge leases, cleanup and final state publication. - Physical ESPHome phones and Home Assistant softphones now use the same
device_idsource selector. The service dispatches each phone through its
native adapter, and an ESP service response is no longer resolved again as
a browser phone. - The new
packages/voip/ha_phone.yamlpackage provides the complete physical
phone surface in one include: discovery entities, native call-control
actions and the Home Assistant managed phonebook. - SIP routing identity and presentation identity remain separate. Stable URI
users route calls, while standard quoted display names preserve spaces from
ESP friendly names, local accounts and external callers. RFC 4916 connected
identity lets the caller learn the resolved callee after answer. - SIP-to-SIP bridges negotiate each leg independently. Compatible codecs stay
on direct RTP, while the optional bounded FFmpeg fallback converts an
incompatible active direction to H.264, VP8 or RTP/JPEG as required by the
receiving endpoint. - An established audio call can add, remove and add video again through
standard in-dialog re-INVITE without restarting its audio relay. RTP profile,
direction, payload type and codec changes commit as one media generation. - Transcoded SIP audio is paced by its negotiated packet time, and immediate
redial waits for the previous bounded transcoder to release its slot. - Negotiated RFC 4733 DTMF payload types and media direction are retained
through direct, bridged and trunk call paths. - Browser ownership follows the logical phone, committed SIP transaction,
active call and media direction. Dashboard reloads and multiple cards can
hand media over without creating a second call owner. - Mixed browser and SIP ring groups let the first valid answer win, cancel the
losing INVITEs and settle every remaining leg. - Inbound routing, forwarding, conference ringing, SIP bridge handling and
browser media claims now use focused lifecycle owners whileCallRegistry
remains the observable compatibility index. - In-dialog video activation now follows the bridge relay that is actually active, not merely the presence of a dormant
recvonlySDP media line. Re-INVITEs preserve the negotiated RTP audio codec and payload instead of accidentally changing PCMA, PCMU, G.722 or Opus into L16. - Completed dialogs retain a bounded SIP transaction tombstone. A delayed retransmission of the original INVITE now receives
481 Call/Transaction Does Not Existand cannot reclaim a softphone after BYE. - Unexpected teardown failures are surfaced instead of being silently hidden,
and completed calls release their sessions, sockets, bridges and RTP ports. - FRITZ!Box REGISTER now uses the bare registrar URI, keeps the account AOR in
To and From, refreshes CSeq and Via branch after authentication, follows SIP
Timer F and runs without blocking Home Assistant setup. - FRITZ!Box interoperability has been improved for authenticated registration
and internal service dial strings such as**621. Thanks to
@ruedigerhanses-stack for the careful hardware testing and iterative
validation in issue #95. - RTP audio with valid packet boundaries different from negotiated
ptimeis
accumulated and reframed by the shared PCM converter. This fixes the
reported FRITZ!Box PCMA/16000 320-byte input without forcing a codec change. - ESPHome phonebook delivery now tracks exact content per device. Reconnects
restore only the restarted phone, while duplicate callbacks and unchanged
setup retries no longer republish the roster to every ESP.
🎬 Browser video worker and card
-
JPEG encoding and decoding, plus H.264 and VP8 WebCodecs decoding when the
browser supports it, can run in a dedicated worker instead of blocking the
card UI. -
Worker generations prevent stale frames from a replaced call from reaching
the screen.VideoFrameandImageBitmapobjects are closed on success,
replacement and error paths. -
The card editor and view builder are now focused modules without changing
the public card configuration. -
The card remains the GUI and media surface of a logical softphone. The
softphone itself continues to exist and ring independently of an open card. -
Browser calls verify secure context, Web Audio support and microphone access
before signaling. A web view withoutgetUserMediano longer accepts a call
and immediately tears it down with BYE. -
Thanks to @csfrussa, browser microphone downsampling now applies a
fourth-order Butterworth anti-alias filter before decimation. In Chromium
validation it reduced a 12 kHz tone folding into 4 kHz by 23.4 dB while the
wanted 1 kHz signal remained unchanged. -
A
Microphone anti-alias filterswitch in the card's idleOptionspanel
lets each browser and logical phone compare the filtered and legacy paths.
It is enabled by default, applies from the next call and adds no filtering
cost when the browser and negotiated transmit rates already match. -
Browser softphones can now enumerate and remember the preferred microphone,
speaker and camera for each browser and logical phone. If no preference has
been selected, the existing browser defaults remain unchanged. -
Media devices can be changed while a call is ringing or established. Camera
replacement preserves the active SIP session and disposes the superseded
capture track instead of creating a parallel sender lifecycle. -
The card keeps one responsive
Optionssurface in idle, audio calls and
video calls. During video it becomes a compact settings icon inside the call
bar, while the opaque options view replaces the card content for clear and
accessible device selection. -
The existing hangup control now reacts subtly to received audio level without
overriding its responsive dimensions or adding a second audio capture path.
🎥 WE HAVE A F...ING ESPHOME VIDEOPHONE!
Everything started when @Psix-anp shared a working ESP32-P4 camera build based
on the ESPHome camera work by @youkorr. What can I say, half a second later I
was already experimenting with bidirectional SIP video on ESPHome.
And we made it. A P4 can now be a real audio and video SIP endpoint, including
bidirectional video on its own display. Better still, the JPEG path is light
enough to live inside the full P4 profile alongside Micro Wake Word, Voice
Assistant, AFE echo cancellation, LVGL, TTS and Sendspin.
This opens the door to custom ESPHome videophones and doorbells with echo
cancellation, full P4 voice-assistant tablets, and direct room-to-room audio
and video calls between P4 devices.
The implementation stays close to the accelerated Espressif media paths:
esp_video2.3.0 owns the MIPI-CSI and V4L2 camera pipeline. JPEG SIP TX
borrows the camera's already encoded access unit, so it does not capture or
encode the same frame a second time.- The JPEG camera follows Espressif's V4L2 startup practice and discards the
first two CSI buffers afterSTREAMON. Cross-codec calls therefore begin
with a color-stable JPEG and VP8 keyframe instead of publishing a transient
ISP startup frame. - The H...
2026.8.1-dev pre-release: consolidated calls and the P4 videophone
2026.8.1-dev pre-release: consolidated calls and the P4 videophone
2026.8.1-dev remains an active pre-release and work in progress. The tag and
HACS archive may be refreshed while field qualification continues. Use
2026.8.0 when you need the current stable release.
🏠 Home Assistant and VoIP Stack
- The Home Assistant audio and video media paths now share one lifecycle for
authenticated ownership, replacement barriers, live-session resolution,
bridge leases, cleanup and final state publication. - Physical ESPHome phones and Home Assistant softphones now use the same
device_idsource selector. The service dispatches each phone through its
native adapter, and an ESP service response is no longer resolved again as
a browser phone. - The new
packages/voip/ha_phone.yamlpackage provides the complete physical
phone surface in one include: discovery entities, native call-control
actions and the Home Assistant managed phonebook. - SIP routing identity and presentation identity remain separate. Stable URI
users route calls, while standard quoted display names preserve spaces from
ESP friendly names, local accounts and external callers. RFC 4916 connected
identity lets the caller learn the resolved callee after answer. - SIP-to-SIP bridges negotiate each leg independently. Compatible codecs stay
on direct RTP, while the optional bounded FFmpeg fallback converts an
incompatible active direction to H.264, VP8 or RTP/JPEG as required by the
receiving endpoint. - An established audio call can add, remove and add video again through
standard in-dialog re-INVITE without restarting its audio relay. RTP profile,
direction, payload type and codec changes commit as one media generation. - Transcoded SIP audio is paced by its negotiated packet time, and immediate
redial waits for the previous bounded transcoder to release its slot. - Negotiated RFC 4733 DTMF payload types and media direction are retained
through direct, bridged and trunk call paths. - Browser ownership follows the logical phone, committed SIP transaction,
active call and media direction. Dashboard reloads and multiple cards can
hand media over without creating a second call owner. - Mixed browser and SIP ring groups let the first valid answer win, cancel the
losing INVITEs and settle every remaining leg. - Inbound routing, forwarding, conference ringing, SIP bridge handling and
browser media claims now use focused lifecycle owners whileCallRegistry
remains the observable compatibility index. - In-dialog video activation now follows the bridge relay that is actually active, not merely the presence of a dormant
recvonlySDP media line. Re-INVITEs preserve the negotiated RTP audio codec and payload instead of accidentally changing PCMA, PCMU, G.722 or Opus into L16. - Completed dialogs retain a bounded SIP transaction tombstone. A delayed retransmission of the original INVITE now receives
481 Call/Transaction Does Not Existand cannot reclaim a softphone after BYE. - Unexpected teardown failures are surfaced instead of being silently hidden,
and completed calls release their sessions, sockets, bridges and RTP ports. - FRITZ!Box REGISTER now uses the bare registrar URI, keeps the account AOR in
To and From, refreshes CSeq and Via branch after authentication, follows SIP
Timer F and runs without blocking Home Assistant setup. - FRITZ!Box interoperability has been improved for authenticated registration
and internal service dial strings such as**621. Thanks to
@ruedigerhanses-stack for the careful hardware testing and iterative
validation in issue #95. - RTP audio with valid packet boundaries different from negotiated
ptimeis
accumulated and reframed by the shared PCM converter. This fixes the
reported FRITZ!Box PCMA/16000 320-byte input without forcing a codec change. - ESPHome phonebook delivery now tracks exact content per device. Reconnects
restore only the restarted phone, while duplicate callbacks and unchanged
setup retries no longer republish the roster to every ESP.
🎬 Browser video worker and card
- JPEG encoding and decoding, plus H.264 and VP8 WebCodecs decoding when the
browser supports it, can run in a dedicated worker instead of blocking the
card UI. - Worker generations prevent stale frames from a replaced call from reaching
the screen.VideoFrameandImageBitmapobjects are closed on success,
replacement and error paths. - The card editor and view builder are now focused modules without changing
the public card configuration. - The card remains the GUI and media surface of a logical softphone. The
softphone itself continues to exist and ring independently of an open card. - Browser calls verify secure context, Web Audio support and microphone access
before signaling. A web view withoutgetUserMediano longer accepts a call
and immediately tears it down with BYE. - Thanks to @csfrussa, browser microphone downsampling now applies a
fourth-order Butterworth anti-alias filter before decimation. In Chromium
validation it reduced a 12 kHz tone folding into 4 kHz by 23.4 dB while the
wanted 1 kHz signal remained unchanged. - A
Microphone anti-alias filterswitch in the card's idleOptionspanel
lets each browser and logical phone compare the filtered and legacy paths.
It is enabled by default, applies from the next call and adds no filtering
cost when the browser and negotiated transmit rates already match.
🎥 WE HAVE A F...ING ESPHOME VIDEOPHONE!
Everything started when @Psix-anp shared a working ESP32-P4 camera build based
on the ESPHome camera work by @youkorr. What can I say, half a second later I
was already experimenting with bidirectional SIP video on ESPHome.
And we made it. A P4 can now be a real audio and video SIP endpoint, including
bidirectional video on its own display. Better still, the JPEG path is light
enough to live inside the full P4 profile alongside Micro Wake Word, Voice
Assistant, AFE echo cancellation, LVGL, TTS and Sendspin.
This opens the door to custom ESPHome videophones and doorbells with echo
cancellation, full P4 voice-assistant tablets, and direct room-to-room audio
and video calls between P4 devices.
The implementation stays close to the accelerated Espressif media paths:
esp_video2.3.0 owns the MIPI-CSI and V4L2 camera pipeline. JPEG SIP TX
borrows the camera's already encoded access unit, so it does not capture or
encode the same frame a second time.- The JPEG camera follows Espressif's V4L2 startup practice and discards the
first two CSI buffers afterSTREAMON. Cross-codec calls therefore begin
with a color-stable JPEG and VP8 keyframe instead of publishing a transient
ISP startup frame. - The H.264 profile uses
esp_h2641.3.6 for the ESP32-P4 hardware encoder and
software decoder, withesp_image_effects1.1.0 for its optimized I420
conversion. - ESP-IDF PPA handles scaling, rotation and color conversion. Hardware JPEG
decode and direct MIPI DSI presentation keep the P4 display path lean. - JPEG and H.264 are gated at compile time. A JPEG firmware does not include
the H.264 media path, and the H.264 firmware does not include JPEG SIP video.
The P4 profiles are now:
- the canonical full AFE JPEG videophone,
waveshare-p4-touch-full-afe-landscape-videophone-jpeg.yaml; - the lightweight bidirectional RTP/JPEG videophone,
waveshare-p4-touch-videophone-jpeg.yaml; - the experimental bidirectional H.264 videophone,
waveshare-p4-touch-videophone-h264.yaml.
H.264 remains experimental in this pre-release. JPEG is the current full P4
baseline.
⌨️ Smaller ESPHome improvements
The compact Spotpear profile now has a dedicated touch keypad that keeps
contact navigation and direct extension dialing on the device.
Existing ESP audio profiles keep their native PCM transport and audio-stack
behavior. This update does not replace the audio backend or reduce configured
sample rates. P4 codec selection remains compile-time gated and its media tasks
remain event driven.
⚠️ Breaking change
The former P4 full landscape profile was promoted to the canonical full JPEG
videophone and renamed:
yamls/full-experience/single-bus/waveshare-p4-touch-full-afe-landscape.yaml
is now:
yamls/full-experience/single-bus/waveshare-p4-touch-full-afe-landscape-videophone-jpeg.yaml
Review the complete
breaking changes
before updating an existing installation.
✅ Qualification
1351Python tests plus126subtests passed.- Ruff passed for the custom component, ESPHome components, tests and tools.
- Every shipped JavaScript file passed syntax validation.
- The canonical full JPEG profile completed repeated real HA-to-P4 calls with
bidirectional audio and video, zero reported RTP loss or media drops, clean
BYE handling and quiescent call-scoped resource cleanup. - A Zoiper-style PCMA and VP8 call completed audio-to-video re-INVITE through
the HA B2BUA and its JPEG fallback. The first returned VP8 frame was a valid,
color-stable keyframe; all four audio/video RTP flows reported zero packet
loss and both JPEG depacketizers reported zero dropped access units. - The dedicated JPEG and experimental H.264 P4 profiles have both completed
real bidirectional audio and video calls on hardware. - Real
voip_stack.callser...
2026.8.0: Native HA Video Phones, Multi-Room Calling And PBX Routing
2026.8.0: Native HA Video Phones, Multi-Room Calling And PBX Routing
Important
Read the Breaking Changes
before upgrading. This
release deliberately removes development-only aliases and may require
reconfiguring VoIP Stack and updating existing automations.
2026.8.0 is the release where VoIP Stack stops looking like one clever
intercom and starts behaving like a small, Home Assistant-native communications
system. ESP devices, browser phones, wall tablets, standard SIP accounts,
Assist, ring groups, conference rooms and an optional provider trunk now share
one local roster and one standards-based SIP/SDP/RTP call model.
This note contains only the user-visible delta since stable 2026.7.1.
🏠 One Home Assistant, Every Phone
Home Assistant can now host multiple logical browser phones. Keep the migrated
default phone, then add Kitchen, Reception, Office or any other room from
Settings > Devices & services > VoIP Stack > Add phone. Bind one
ha_softphone card and one real browser/tablet session to each Device.
- Every HA phone has its own name, extension, DND, availability, groups, video
capability and call state. - Browser phones and registered SIP accounts are native integration Devices
with scoped state, connectivity, controls and call Event Entities. - ESP phones remain their existing ESPHome Devices. VoIP Stack discovers and
routes them without creating duplicates. - Logical phones share the same SIP listeners and dynamic RTP pool; they do not
reserve one server port set per room. - Two browser phones can call each other locally with independent audio and
video. External legs remain normal SIP/SDP/RTP. - One endpoint owns one call. A second caller receives
486 Busy Here, while
calls to other phones continue independently. - A logical phone may keep ringing while its kiosk browser is offline, allowing
missed-call, timeout and fallback automations to remain meaningful.
🎥 SIP Video Calling
SIP video is supported in web browsers, the Home Assistant Companion app,
standard SIP video door stations, SIP video phones and softphones, including
compatible routes through a PBX or trunk. ESPHome endpoints intentionally
remain audio-only.
- Direct receive supports H.264, VP8 and RTP/JPEG without decoding or
re-encoding video on the HA server. - Compatible H.264 and VP8 calls can send the browser camera when both the
integration-wide permission and that phone's persistent Send Camera
switch are on. Auto Answer and Send Camera are phone settings stored
by the integration, so clearing browser cache does not reset them; microphone
and camera permission remains local to the browser that owns the call media. - Optional bounded FFmpeg receive converts H.263, H.263-1998 or H.265 to VP8.
It is receive-only, single-slot and never records an intermediate file. - Incoming and outgoing calls support
sendrecv,sendonly,recvonlyand
inactivedirections. - Direct calls and video-capable trunk/PBX routes use the same media contract.
Authenticated retries preserve the complete audio/video offer. - A direct HA-browser dialog can accept compatible peer-initiated video
add/remove, hold/resume and RTP endpoint changes through re-INVITE or UPDATE. - RTP/AVP remains the compatible default. Negotiated AVPF adds compound RTCP
reports and bounded PLI/FIR key-frame recovery. - Camera denial, decoder failure, unsupported video or optional transcoder
failure does not tear down compatible audio. - The card keeps video inside its configured Lovelace geometry, preserves the
native aspect ratio and moves identity, duration and Hang Up into a compact
translucent bottom bar. - Detailed media counters appear only with backend debug and Extended
information enabled.
Video remains opt-in and requires HTTPS, a compatible browser and an RTP path
permitted by the local firewall. Read
SIP Video
for codec, network and privacy requirements.
🧭 Initial Preview: Automation-Native PBX Routing
Home Assistant automations can now influence an inbound call at explicit
decision points instead of replacing the normal dial plan.
Warning
This is a preliminary, disabled-by-default API. Event fields, decision
timing and service semantics may change in a future release while more real
PBX installations shape the final contract. The central phonebook, explicit
extensions and configured fallback destination remain the stable default.
- Direct inbound mode can expose a short
route_requesteddecision before the
configured target. - DTMF mode gives explicit extension digits priority and exposes automation
routing only when no digits were entered. ingressandorigindistinguish provider/PBX trunk calls from local
extension calls without relying on internal ownership fields.voip_stack.select_inbound_destinationselects the initial target.voip_stack.forwardmoves an already ringing or connected HA-owned call to
another phone, ESP, registered endpoint, group or Assist pipeline.- Forward failures can
resume, terminate or return busy; a replaced ringing
SIP leg receives a real CANCEL. - Per-phone state Sensor and call Event Entities support native HA triggers
such asringing for 30 seconds, without helper timers or Call-ID templates
for the ordinary single-call case. - Once Assist owns the call, compatible in-dialog audio updates remain live;
video re-offers are answered explicitly withm=video 0because the Assist
endpoint is audio-only, rather than interrupting TTS or stranding the dialog. - Revision and sequence guards remain available for concurrent or multi-stage
routing policies.
Yes: an unanswered door station can now be forwarded to Assist. What Assist
says is controlled by your pipeline prompt. Good judgement is still not
included.
Copyable native-UI automation examples are in
Automation Dial Plan.
👥 Ring Groups And Conference Groups
Groups now re-enter the same canonical PBX dispatcher as every other target.
They are no longer special routing shortcuts with subtly different media or
ownership rules.
- A ring group calls all eligible members in parallel.
- The first answer wins; losing early dialogs receive CANCEL and a confirmed
losing dialog receives BYE. - The originating endpoint is excluded when it is also a group member.
- DND, disabled endpoints, busy state and endpoint capability are applied
before fan-out. - Browser phones, ESP endpoints and registered SIP accounts can share one
group without forcing audio-only members to negotiate video. - A conference group is a real HA-hosted focus: members join the same mixed
room, optional invitees ring, and the room closes when the last participant
leaves.
☎️ A More Predictable SIP/PBX Core
Most of this release is invisible when it works, which is precisely the point.
The former monolithic runtime has been decomposed around explicit call,
transaction, dialog, route and media ownership primitives.
- A logical call owns its generation, legs, tasks, media reservations and one
cancellation-safe cleanup barrier. - SIP dialogs follow Call-ID and tags, not the lifetime or source port of one
TCP connection. Established trunk calls survive a replacement flow. - Inbound answer is transactional: prepare resources, send the final response,
commit media or roll everything back. - Audio and video media updates commit atomically against the current call
generation. A late re-INVITE cannot leave the two directions on different
revisions. - State transitions are monotonic. Late provisional responses or stale browser
callbacks cannot resurrect a terminated card. - Hang Up remains available through calling, connecting and remote ringing.
SIP CANCEL,487 Request Terminated, ACK and crossed-200BYE handling now
share one lifecycle. - Remote Contact and Record-Route state are retained for correct in-dialog ACK,
BYE, INFO, UPDATE and re-INVITE routing through proxies. - Cleanup is idempotent and releases dialogs, tasks, media owners, relays, RTP
reservations and optional transcoder slots before the endpoint returns idle.
🔢 DTMF, Registered Phones And Trunks
- Initial trunk extension selection supports negotiated RFC 4733
telephone-eventand compatible SIP INFO digits. - Established HA-owned bridges publish one experimental
voip_stack.dtmf
occurrence per RFC 4733 or SIP INFO key for door/gate automations. - Direct ESP-to-ESP m...
2026.7.1: Assist By Phone, Groups And A Stronger VoIP Stack
2026.7.1: Assist By Phone, Groups And A Stronger VoIP Stack
2026.7.1 contains the changes made after stable 2026.7.0. It expands
the Home Assistant phone system with real ring groups and conference rooms,
makes the Lovelace card more useful for both HA and ESP phones, and hardens the
call and audio paths for everyday use.
🏠 VoIP Stack / Home Assistant
- ☎️ The existing HA softphone can now publish its own extension, ring-group
membership, conference-group membership and conference-ringing preference. - 🔄 Changes made from the card or services are reflected through HA's virtual
endpoint and republished to the shared phonebook. - 💾 HA softphone extension, DND and group settings are persisted in the
integration config entry. The card writes through the softphone service and
reads the canonical snapshot back; the values survive integration reloads
and full Home Assistant restarts. - 🧑💼 Registered SIP phones are first-class endpoints. Their accounts can use
extensions and join the same ring or conference groups as HA and ESP devices. - 🌍 Incoming calls are not limited to callers stored in the phonebook.
Reachable and protocol-compatible SIP callers can ring HA or an ESP, subject
to normal destination, busy and DND checks. - 🧭 Extensions, group contacts, registered phones, ESP endpoints and optional
trunk routes all resolve through the same dial plan. - 🧹 Endpoint departure, reboot and roster-service timing are handled
automatically, so stale contacts and missed phonebook pushes are less likely.
🗣️ Call Home Assistant Assist By Phone
Enable Include voice assistant, choose HA's preferred Assist pipeline or a
specific one, and assign an extension. The selected assistant appears in the
same phonebook used by every other destination; no extension is assumed or
reserved automatically.
The optional second setup step keeps the SIP extension explicit and supports
both HA's preferred Assist pipeline and a specifically selected pipeline.
- 📞 ESP phones, locally registered SIP clients, direct compatible SIP callers
and external callers arriving through a trunk can call the assistant. - 👋 The assistant receives the SIP caller identity as its first turn and can
greet first. A matching phonebook name is used when available; otherwise the
original caller string or number is preserved. - 🔁 After speaking, it listens for the caller, runs STT and the conversation
agent, streams the TTS reply back into the same call, and repeats until the
caller hangs up. Conversation context is retained between turns. - 🧩 VoIP Stack runs the selected native HA pipeline, so its existing STT,
conversation agent, TTS, language and voice settings remain authoritative.
Piper, Wyoming, cloud providers and local HA agents use the same route without
provider-specific code. - 🏠 No separate Home Assistant VoIP integration, second SIP port or generated
Assist satellite is required.
🎛️ Lovelace Card
- 🪞 ESP mirror cards keep their original meaning: each card represents one ESP
phone and uses that ESP's own call controls and synchronized phonebook. - ⌨️ ESP mirror cards now include a keypad for a manual phonebook name, SIP URI,
extension or number without overwriting the contact selected on the device. - ⚙️ The Options view exposes Auto Answer, DND, extension, ring groups,
conference groups and conference ringing when the selected endpoint supports
them. - 📐 Option labels share one left edge; fields, selectors and checkboxes share a
clean right-hand control column. - 🏠 HA softphone cards expose the same HA-owned extension and group settings
without creating a second routing or call-state engine in the browser. - 📒 The main card now has a phonebook mode backed by the canonical HA roster.
It sorts contacts alphabetically, uses the available Sections grid area and
scrolls only when the roster is longer than that area. ↔️ ESP mirror, HA softphone and phonebook modes support native Home Assistant
Sections resizing. Their controls respond to both card width and height, and
an omittednameortitleleaves no empty header row.- 🔔 Ringtone, DND and terminal call state no longer race normal card refresh or
cleanup paths. - 🔌 ESP mirror availability follows the bound Home Assistant entity live. A
card becomes unavailable when its ESP leaves and recovers when it returns,
without a dashboard refresh or polling timer. - 🖱️ Internal card scrolling hands the remaining wheel/trackpad movement back
to the dashboard at the card boundary. - 🎨 Native contact selectors remain readable on both light and dark themes,
including the operating-system popup rows. - 🔒 Caller and destination labels are inserted as text, so SIP display strings
cannot become card markup.
ESP mirror card with keypad, Auto Answer, DND, extension and group controls
expanded.
🔔 Ring Groups
Call a group such as RG Home and every available member rings together.
Home Assistant, ESP phones, registered SIP endpoints and manual contacts can all
participate.
The first member to answer wins. Other early legs are cancelled, and a late
answer cannot steal or duplicate the established call. Group contacts are
created from live membership and disappear when no endpoint declares them. The
card keeps the group name while dialing/ringing, then shows the actual endpoint
that answered, including the Home Assistant softphone.
🎙️ Conference Groups
Home Assistant can now host SIP conference rooms. Calling a contact such as
CG Home joins that room; members with conference ringing enabled can also be
invited when the room starts.
The conference focus mixes the active participants, applies additional headroom
as the room grows and cleans up invited legs and media ports when the owner or
last participant leaves.
📒 Phonebook And Dial Plan
- Group fields accept comma-separated membership, so one phone can belong to
more than one room or ring group. - The roster advertises directional media capabilities and the fields needed by
HA, ESP and registered endpoints. - Numeric inbound routing uses the same phonebook extensions instead of a
separate static route table. - HA refreshes the roster when an ESP's phonebook service appears after reboot,
closing the window where discovery could finish before the service was ready. - SIP authentication retries rebuild the transaction correctly for stricter
PBX and FRITZ!Box implementations.
📞 More Predictable Calls
- SIP transactions now match the correct Call-ID, CSeq, branch, dialog tags and
peer before changing a call. - Retransmissions, decline, cancel, busy, answer, hangup and immediate redial
have deterministic ownership and cleanup. - The Hang Up action remains available while an outbound call is still in
calling, including unreachable or slow external destinations. - A cancel that crosses a successful answer is completed with the proper
acknowledgement and teardown instead of leaving a ghost call. - Reused SIP/TCP connections serialize outgoing messages and keep pending work
bounded. - Unsupported hold or codec-changing re-INVITE requests receive
488 Not Acceptable Herewithout destroying the call already in progress. - Incoming digit routing accepts both standard RTP
telephone-eventand the
widely deployed legacy SIP INFO DTMF representation, including four-digit
trunk destinations entered at human speed. - During an established HA-bridged call, each RFC 4733 or compatible SIP INFO
DTMF key is also exposed asvoip_stack.dtmffor automations. This event
path is separate from pre-answer extension routing and never transfers the
call by itself. - Ring-group legs, conference members, RTP ports, registrations and transaction
caches all have explicit limits instead of growing without bound.
🧪 Experimental: Use Phone Keys In Home Assistant Automations
Caution
In-call DTMF automation events are experimental in 2026.7.1. Validate the
caller/source filters and the target action in a safe environment before
using phone keys for gates, locks or other security-sensitive controls.
During a call bridged by Home Assistant, every DTMF key now fires one
voip_stack.dtmf event. This makes practical actions such as press 1 to open
the gate possible while the conversation and audio continue normally.
triggers:
- trigger: event
event_type: voip_stack.dtmf
event_data:
source: Cordless
digit: "1"
actions:
- action: cover.open_cover
target:
entity_id: cover.gateEvents include caller, callee, source leg, digit, both call IDs and transport,
so automations can be restricted to the intended phone and active call. This is
not a second dial plan: initial extension collection remains separate and an
in-call key never transfers a call by itself. Detection is entirely HA-side;
no parser or additional real-time work is added to ESP firmware.
🔊 Audio And ESP Real-Time Performance
- Browser audio has one owner per call and uses stateful codecs, absolute pacing
and bounded queues. A short FIFO jitter buffer smooths Chrome microphone
bursts instead of discarding adjacent speech frames. - PCMA 8 kHz, L16 48 kHz and Opus 48 kHz paths are used where the endpoints on
that leg support them. - PCMA/G.711 handset audio now preserves the source speech level...
2026.7.0
2026.7.0: ESPHome devices are VoIP phones now
This release contains the largest call-control change
in the project so far: ESP devices and Home Assistant have been migrated from
the old project call model to SIP/SDP/RTP VoIP.
Use it when you are ready to move to the SIP/VoIP stack. If a device is
installed somewhere where temporary audio glitches or call-routing regressions
would be a problem, test the matching YAML on spare hardware first.
This release changes the project category
Yes, you read that correctly. ESP devices are now SIP phones and Home Assistant
is now a SIP softphone, router/B2BUA, RTP bridge/resampler and optional trunk
client. VoIP Stack has been migrated with them, so Home Assistant is not a
sidecar around an intercom protocol anymore: it participates in real VoIP call
flows.
This does not remove the simple door intercom use case. An ESP can still be a
one-button door intercom or room intercom with the same user workflow: call, answer,
talk, hang up. It is now a real SIP/VoIP phone underneath, so the same device
can also join direct SIP calls, HA-routed calls, registered softphone calls and
trunk calls when those paths are configured.
That means this release can do things that previously required an external PBX
such as Asterisk:
- ESP devices can call each other with SIP identities from the shared
phonebook. - ESP devices can call Home Assistant as an independent softphone.
- Home Assistant can call ESP devices from the Lovelace softphone card,
automations, Assist intents or services. - Home Assistant can bridge calls between ESPs, registered softphones and trunk
legs while preserving SIP call state and terminal reasons. - A standard softphone such as Zoiper, Linphone, baresip or pjsua can register
to Home Assistant as a local SIP account and become a phonebook contact. - Home Assistant can register one optional provider/PBX trunk. External numbers
and provider inbound calls can be routed through VoIP Stack. - With a trunk configured, ESPs and Home Assistant can place and receive
external calls without carrying Asterisk beside the integration. - Audio quality is negotiated per direction. A device can receive the best
compatible speaker format available on that leg while transmitting whatever
its microphone path can actually produce, for example 48 kHz receive audio
with a 16 kHz AFE/AEC microphone return. - SIP status, DND, busy, decline, cancel, BYE, incompatible media and routing
failures are propagated as call reasons instead of being hidden behind a
project-specific state machine.
This is intentionally a breaking migration. ESP firmware does not REGISTER to a
provider/PBX and does not require SIP auth. ESPs are local SIP user agents; HA
is the central router/B2BUA and optional trunk endpoint. The old proprietary
VoIP call-control path is not a compatibility layer.
🏠 VoIP Stack / Home Assistant
Home Assistant is now a real SIP softphone, not only a controller for ESP
devices. It can place and receive calls for itself, place calls on behalf of ESP
endpoints, ring from ESP calls, ring from standard SIP clients such as Zoiper,
Linphone or bareSIP, and ring from a public phone number when an optional SIP
trunk is registered. The card shows those incoming calls, answers or declines
them, and can open a keypad/manual target view for a phonebook name, SIP URI,
extension or number.
- 🗣️ VoIP Stack now is able to call other VoIP contacts via Voice
Assistant. An optional Assist intent adapter can be enabled from the
VoIP Stack setup/reconfigure dialog. With the provided custom sentences,
voice satellites can call a phonebook contact, hang up, answer or decline
using the satellite that heard the sentence. Spoken contact names are resolved
dynamically against the live phonebook, socall kitchen speakercan resolve
to the canonicalKitchen Speakercontact without changing low-level
VoIP contact matching. If no phonebook contact matches, the adapter can also call
the single VoIP device assigned to a matching Home Assistant area name.
Multiple VoIP devices per area are intentionally not voice-dialed in this
release; group/area calls are left for a later release. - 🧬 Added negotiated PCM audio formats to the HA SIP softphone/bridge stack.
- 🔁 Audio formats are now per direction, not one global device format:
tx_formatdescribes device-to-wire audio andrx_formatdescribes
wire-to-device audio. - 🎚️ HA can bridge endpoints with different TX/RX formats by converting the
audio explicitly instead of pretending every call is 16 kHz/s16/mono. - 🧮 HA bridge conversion is vectorized through NumPy and uses anti-aliased
sample-rate conversion for downsampling. - 🧩 SIP/SDP offer-answer now carries explicit PCM audio capabilities.
- 🧭 HA now validates endpoint-declared formats before routing calls. Incompatible
peers fail with a readableincompatible_audio_formatreason instead of
producing distorted audio. - 📡 SIP/TCP and SIP/UDP endpoints understand the negotiated format selected
for each call leg. - 🌉 HA SIP bridge legs preserve the original call identity when routing toward
the destination, so caller/destination labels stay stable across TCP ↔ UDP
and mixed-format bridges. - 🚪 Inbound SIP INVITE messages do not require the caller to exist in the
callee phonebook. The phonebook is the outbound dial plan; inbound
caller/destination identity comes from SIP headers. This keeps VPN/routed
callers and HA bridges working without pre-seeding every callee phonebook. - ☎️ VoIP Stack can optionally register one SIP trunk account. When
disabled, trunk code is inactive. When enabled, unresolved outbound numbers
can route through the trunk and inbound provider calls can select HA or a
local phonebook target with RFC2833/telephone-event DTMF. - 🧭 VoIP Stack now exposes Home Assistant's native Reconfigure flow. You
can change ports, debug mode, Assist intents, local SIP registrar support and
the optional trunk without deleting and recreating the integration. - 📴 The trunk setup is truly optional. A fresh setup starts with trunk disabled,
and an empty trunk page is treated as "no trunk" instead of failing validation. - 🔌 HA local SIP transport toggles were removed from setup. The user configures
ports and optional features; only provider/PBX trunk transport remains
configurable. - 🛡️ Browser audio WebSocket sessions remain server-authoritative: if the socket
is truly gone, the server ends the call. - 🔄 Browser/card reload during an active HA softphone call can explicitly rebind
to the existing server session within a short grace window, avoiding the old
"dashboard refresh killed or desynced the softphone call" behavior. - 📞 ESP -> HA browser-answer calls now initialize the browser audio pipeline
only after the HA SIP answer/control reply carries the negotiated TX/RX formats.
This keeps the ESP-caller / HA-responder path aligned with HA-originated
calls and avoids deep/slow audio caused by a browser worklet using stale
16 kHz framing against a negotiated 48 kHz leg. - 🧭 ESP caller devices now also apply the selected RX speaker format before
activation and again when the SIP answer arrives. ESP-originated calls answered
by HA therefore use the negotiated speaker rate instead of a stale/default
playback format. - 🚫 Duplicate terminal/bridge events were tightened so cards should see one
final reason instead of compensating for repeateddisconnectedevents. - 🧪 SIP negotiated-format testing now expects a standard external softphone or
private local debug helper, rather than a distributed project softphone.
🎛️ Lovelace Card
-
🧠 The card keeps the page-level audio engine model from the previous dev
cycle: one browser audio engine per page, cards as views. -
🔌 Audio still uses the dedicated binary WebSocket, not the shared HA frontend
WebSocket. -
📦 The card receives the negotiated effective formats and configures capture
and playback from that contract. -
🎧 Capture and playback worklets support dynamic PCM settings instead of
assuming 16 kHzInt16Arrayeverywhere. -
🧯 Browser playback now uses an adaptive jitter buffer with lightweight packet
loss concealment instead of fixed startup/drop thresholds. Remote HA app and
browser sessions tolerate normal websocket/RTP jitter without periodic
audio-gap artifacts. -
🔄 Softphone calls survive normal Lovelace reload/reconnect flows when the
browser rebinds inside the server grace window. -
🧭 Device identity remains
device_idbased in the frontend. Legacy
name/esphome-id matching is resolved server-side, once. -
🔔 HA softphone has an idle-only Options panel for Auto Answer, DND and
browser ringtone. Ringtone is a per-browser preference; HA softphone DND is
stored in HA state. -
⌨️ HA softphone also has a keypad/manual target view for calling a phonebook
name, extension, SIP URI or external number directly from the card. -
🪞 Hybrid cards keep the original mirror semantics: the card represents the
selected ESP, mirrors ESP ringing/in_call state, and becomes the HA
softphone leg only when that ESP is calling Home Assistant. A separate
ha_softphonecard represents HA itself and shows Answer/Decline only for
calls addressed to HA. -
🧹 Ringing/In-call screens hide runtime options, so only the call actions
relevant to the current state are visible. -
🧾 Terminal text uses the active call peer. A card or display no longer reports
the currently selected phonebook contact as the call...
2026.6.2: HA softphone and audio runtime stabilization
2026.6.2: HA softphone, runtime audio stability and full UI state cleanup
🏠 Intercom Native / Home Assistant
- 🧩 Home Assistant can now be represented as its own PBX-lite softphone endpoint.
- 📞 Home Assistant can receive valid PBX-lite calls addressed to HA itself, not only bridge calls toward ESP devices.
- 🚫 Added independent HA softphone Do Not Disturb state.
- ✅ Added independent HA softphone Auto Answer state.
- 🔁 Improved HA softphone session tracking for inbound, outbound and terminal call events.
- 🧭 Preserved intercom runtime state across HA reconnects more reliably.
- 🌐 Improved endpoint republish/reconnect behavior so ESP endpoints and HA routing state recover more cleanly after API/Wi-Fi churn.
- 🧪 Expanded
tools/intercom_softphone_probe.pyfor diagnostic calls, synthetic caller testing, tone/WAV injection and received-audio WAV capture. - 📡 External PBX-lite callers can call HA directly when they send valid protocol messages.
🎛️ Lovelace Card
- 🏠 Added the new Home Assistant softphone card mode requested in #46.
- 🔀 The existing Hybrid card mode remains the default and keeps the previous behavior: the card is attached to one ESP endpoint and mirrors/controls that ESP.
- 📞 In Hybrid mode, if the selected destination is Home Assistant, the browser/card acts as the Home Assistant audio leg and starts a call toward the attached ESP. The ESP will ring, or answer automatically if its Auto Answer is enabled.
- 🏠 In Home Assistant softphone mode, the card represents Home Assistant as an independent intercom endpoint instead of mirroring an ESP.
- 📲 The HA softphone card can call discovered ESP endpoints from its selector.
- 🔔 The HA softphone card rings only for calls addressed to Home Assistant itself, useful for clean dashboards where HA behaves like one standalone desk phone.
- 🚫 HA softphone mode has its own Do Not Disturb control.
- ✅ HA softphone mode has its own Auto Answer control.
- 🎯 HA softphone mode does not use
device_id; Hybrid mode still requires an ESPdevice_id. - 🎨 Card controls, selectors and buttons were polished to match the Home Assistant card surface better.
🔊 ESP Audio Runtime
- 🧱 AFE output now uses frame-atomic buffering, so consumers receive complete processed frames or silence, not short partial reads.
- 🧠 AFE reconfigure/fetch paths were hardened for cleaner runtime mode switching and shutdown/restart behavior.
- 🧩 Dual-mic AFE was realigned with the Espressif GMF element output path.
- 🧼 Ring buffer helpers now expose explicit type/placement logging and NOSPLIT frame-safe behavior where needed.
- 🔊 Added the project-local
speakermedia-player fork withpause_releases_pipeline. - ⏸️ Paused media now releases the playback pipeline before TTS, timer alarms or intercom audio need the same speaker graph.
- 📦
esp_audio_stackgained cleaner generated-code gating for TDM paths and codec backend setup. - 📡 Intercom TX/audio paths were tightened for cleaner buffering and transport lifecycle behavior.
⏲️ Timers and Full UI State
- ⏲️ Added optional Voice Assistant timer package.
- 🖥️ Timer-finished UI now owns the display while the alarm sound plays.
- 🧭 Full display YAMLs now use a centralized reducer-style UI priority model instead of duplicating screen priority logic per callback.
- 🧼 Media playback, timer alarms, Voice Assistant states and intercom pages no longer overwrite each other opportunistically.
- 💡 LED/UI state handling is cleaner across full-experience YAMLs.
🧠 P4 Touch Stabilization
- 🚀 P4 full AFE profiles now use a cleaner SDK baseline.
- ✅ PSRAM XIP remains enabled on P4.
- 🧹 Removed aggressive Wi-Fi/LWIP IRAM overrides from the P4 baseline.
- 🎧 Kept hot AFE/intercom bridge buffers internal where contiguous heap allows it.
- 📈 Improved largest-free-block headroom observed on P4 during media, TTS, intercom and repeated runtime use.
- 🖥️ Reduced P4 display flicker/audio glitches seen with the previous tuning set.
- 🧪 P4 landscape/portrait TCP/UDP full AFE YAMLs were kept aligned with the new state model.
📦 YAML Presets
- 🧩 Full-experience YAMLs that use media playback now include the local
speakercomponent where needed. - 🎚️ Full AFE profiles boot with aligned runtime defaults for AEC/VAD/FD mode.
- 📡 TCP/UDP variants were kept paired across maintained presets where supported.
- 🧪 Generic S3 full AEC UDP was added/aligned for the dual-bus/full-experience test matrix.
⚠️ Generic S3 full AFE presets are documented as untested and live underyamls/untested/.
📚 Documentation
- 📖 README updated for HA softphone mode, card behavior, timer UI ownership, speaker fork and P4 runtime tuning.
- 📘 Added documentation for the project-local
speakercomponent. - 🧭 Updated architecture/reference docs around the current audio stack and UI state model.
- 🧰 Updated troubleshooting docs for current HA/card/intercom routing behavior.
- 🖼️ Prepared wiki content with visual pages for getting started, YAML selection, HA integration, Lovelace card, phonebook/routing, audio stack, devices, P4 notes and troubleshooting.
⚠️ Breaking / Upgrade Notes from 2026.6.1
- HA softphone cards should use
mode: ha_softphoneand should not setdevice_id. - Hybrid cards still require an ESP
device_idand remain the ESP-mirroring mode. - Copied Lovelace card YAML should use
show_extended_info; the previous extended-info key is not kept as a compatibility alias. - Custom full-experience YAMLs using
platform: speakermedia playback should include the project-localspeakercomponent and setpause_releases_pipeline: true. - Custom display YAMLs with timers should treat
timer_ringingas the authoritative timer UI owner. - Custom YAMLs copied from older full-experience profiles should be refreshed if they duplicate the old per-callback UI priority logic.
v2026.6.1 - Native intercom modes, routing and phonebook cleanup
🧭 v2026.6.1 - Native intercom modes, stronger routing and phonebook cleanup
This is a focused follow-up to v2026.6.0.
2026.6.0 shipped the Espressif GMF / esp_audio_stack migration. 2026.6.1 tightens the intercom layer above it: Home Assistant routing, standalone ESPHome-native audio, one-way endpoint modes and safer reconnect behavior.
📞 Intercom Native / Home Assistant
🗂️ Phonebook moved to an attribute
The full roster is now stored in:
sensor.intercom_phonebook.attributes.phonebook
The entity state is now only a short summary such as 5 entries.
This removes the old 255-character state limit, which was too small for larger installs such as apartments, offices or multi-room deployments.
Maintained YAMLs already subscribe to the new attribute. Custom YAMLs must update their Home Assistant text sensor subscription accordingly.
🌐 Better HA recognition across subnets and NAT
The Home Assistant integration now handles routed networks more reliably.
Test coverage for this release included:
- ESP and HA on the same subnet
- ESP on a routed secondary subnet
- TCP calls across subnets
- UDP calls with NAT return paths
- mixed TCP/UDP endpoints
- HA PBX mode enabled and disabled
The goal is that HA is recognized as HA, not as a generic ESP peer, even when the ESP is on another routed network.
🔀 Stronger UDP return-path handling
UDP peers can now be matched by the observed packet source when NAT changes the apparent return address.
The phonebook still keeps the real endpoint identity; the transport layer only uses the observed source as the return path when needed.
🧩 Card config rename
The Lovelace card option was renamed:
show_protocol: truebecomes:
show_extended_info: trueThe old key is intentionally not kept. The option now shows more than the protocol, so the YAML name was updated to match what the UI does.
🎙️ Intercom API
🎧 ESPHome-native audio is supported again
intercom_api can now run directly on standard ESPHome microphone and speaker components.
This is important for hardware that already performs audio processing itself, for example XMOS-style front-ends or other DSP/codec paths that provide echo-cancelled microphone audio before ESPHome sees it.
In that case you do not need esp_audio_stack just to use Intercom Native.
↔️ Full-duplex, mic-only and speaker-only endpoints
intercom_api now supports three endpoint capabilities:
full_duplex: microphone + speakermic_only: microphone only, useful for listening/monitoring endpointsspeaker_only: speaker only, useful for announcement endpoints
The capability is derived from the declared audio components. Users do not need to set a separate mode flag.
🧹 Removed legacy intercom-side AEC plumbing
The old intercom fallback AEC/audio-processor path has been removed.
Current model:
- use native ESPHome audio when the hardware already gives you processed audio
- use
esp_audio_stackfull profiles when you need software AEC/AFE
This keeps intercom_api focused on call signaling and audio transport instead of duplicating audio processing.
🧪 New YAML Profiles
New ESPHome-native YAMLs are provided as starting points for native audio hardware:
yamls/full-experience/esphome-native/
yamls/intercom-only/esphome-native/
The tested generic native example uses INMP441 microphone + MAX98357-style I2S amplifier on separate I2S buses through ESPHome native microphone and speaker components.
Important: plain native INMP441 + MAX98357 does not provide echo cancellation by itself. For AEC you need either:
- hardware/DSP AEC, such as an XMOS-style front-end, or
- the maintained full
esp_audio_stackprofiles with software AEC/AFE.
The native profiles are not a ready-made Voice PE profile yet, but they are the base for that class of hardware. Feedback from users with native processed audio hardware is welcome.
🛡️ Runtime Stability
Full audio/LVGL profiles no longer run HA disconnect cleanup actions that could destabilize devices during a Home Assistant restart.
This avoids devices getting stuck or rebooting simply because HA was restarted during normal operation.
🧰 Upgrade Notes
After updating the Home Assistant integration:
- Restart Home Assistant.
- Hard-refresh dashboards using
intercom-card. - Update card YAML from
show_protocoltoshow_extended_infoif you used that option. - Recompile maintained ESP YAMLs.
- For custom YAMLs, update the phonebook subscription to read the
phonebookattribute.
Minimum versions remain:
- ESPHome:
2026.5.xor newer - Home Assistant Core:
2026.5.0or newer
v2026.6.0 - Intercom Native polish and Espressif GMF audio stack
🚀 v2026.6.0 - Intercom Native polish and Espressif GMF audio stack
Hotfix after initial 2026.6.0 publication
Published on May 30, 2026 after field testing the first 2026.6.0 build.
esp_afenow uses a compile-time split between single-mic and dual-mic targets.- Single-mic AFE profiles use the official ESP-SR direct feed/fetch path instead of the GMF AFE element path.
- Dual-mic AFE profiles keep the GMF manager/element path and raw-output selection behavior.
- Spotpear full AFE TCP/UDP profiles keep AFE VAD restore disabled by default to avoid restoring an unstable VAD state at boot.
- The full MWW safe-start logic now uses the Intercom API idle condition instead of string-matching the state name.
This hotfix keeps the public YAMLs pointed at main and removes local debug/telemetry from production profiles.
This release is the next major step after the 2026.5.0 PBX-lite migration.
2026.5.x introduced the new call model: ESP devices as independent extensions, Home Assistant as a peer/bridge, unified phonebook, TCP/UDP routing and browser softphone support.
2026.6.0 keeps that model and rebuilds the audio foundation under it.
🏠 Home Assistant / Intercom Native
The Home Assistant side has been cleaned up around the unified PBX-lite event model.
🔁 Unified call event model
The integration and Lovelace card now use the unified:
intercom_native.call_event
event shape for session, bridge and forward updates.
This gives automations and the card a more consistent view of:
- call scope
- event type
- call state
- hangup / decline / failure reason
- bridge and forward lifecycle
The older split event behavior is no longer the preferred model.
📵 Better unavailable-device handling
The card now handles unavailable ESP devices more explicitly instead of showing stale call controls as if the device were still reachable.
This should make dashboard state clearer when an ESP is offline, rebooting, being flashed, or temporarily disconnected from Home Assistant.
⚡ Safer fast hangup / redial behavior
The browser softphone path has been hardened for fast user actions.
If a call is ended and another call starts immediately after, browser audio cleanup no longer tears down the new call's microphone/audio path by mistake.
This fixes a class of "second call has no browser audio" style problems.
📱 Mobile notification answer flow
The documented mobile flow now supports real Answer / Decline actions:
- Answer opens the dashboard view containing
intercom-cardwith?intercom_answer=1 - the card requests microphone permission and starts the full-duplex browser/app audio path
- Decline stays in Home Assistant automation logic and calls
intercom_native.decline
This is the supported way to answer an ESP-originated call from the Home Assistant Companion app.
🧹 Versioned card cache behavior
The card is registered with a versioned frontend URL derived from the installed integration version.
After upgrading, hard-refresh the dashboard page or clear the Companion app cache if the card still shows an old version.
✅ Minimum Versions
This release requires:
- ESPHome:
2026.5.xor newer - Home Assistant Core:
2026.5.0or newer
HACS metadata now declares the Home Assistant minimum version accordingly.
⚠️ Breaking Changes
Custom YAMLs that still use the old audio component/package layout need to be migrated.
Main migration points:
- maintained YAMLs now use
esp_audio_stack - old
i2s_audio_duplexpackages are no longer the supported path - some YAML options were renamed:
speaker_volume->master_volumemic_attenuation->input_gainframe_buffers_in_psram->buffers_in_psramaudio_stack_in_psram->audio_task_stack_in_psram
- Generic full profiles are split into AEC and AFE variants
- full audio/LVGL profiles include OTA maintenance handling
- old copied Lovelace card files should be replaced by the bundled card
After upgrading, clear ESPHome build caches once before compiling.
find . -type d -name .esphome -prune -exec rm -rf {} +🎧 Audio Stack Migration
The biggest internal change in 2026.6.0 is the migration from the old custom duplex audio path to the new:
esp_audio_stack
backend.
This replaces the maintained i2s_audio_duplex path.
The goal is not just a component rename. The new backend is built around Espressif / ESP-IDF audio components that are designed to work together:
esp_driver_i2sfor official I2S channel ownershipesp_codec_devfor codec-backed devicesgmf_io/io_codec_devfor codec IOesp_audio_effectsfor rate, bit-depth and layout conversionesp-srfor Acoustic Echo Cancellationgmf_ai_audio/esp_gmf_afe_managerfor the full Audio Front-End pipeline
This means the project now carries less custom audio infrastructure and relies more directly on the Espressif audio ecosystem.
💡 Why This Matters
Earlier versions had custom code for a lot of low-level audio work:
- I2S lifecycle
- speaker/microphone glue
- AEC reference routing
- rate conversion
- bit-depth conversion
- channel layout conversion
- ring buffers
- processor feed/fetch timing
- codec-specific assumptions
That worked, but it created too much maintenance pressure and too many board-specific edge cases.
With esp_audio_stack, the project is closer to the native ESP-IDF audio model while still exposing normal ESPHome surfaces above it:
- microphone
- speaker
- media player
- mixer
- Voice Assistant
- Micro Wake Word
- intercom API
- Home Assistant entities
🧩 Supported Audio Shapes
The maintained profiles now cover these layouts through the new stack:
- single-bus codec boards
- single-bus no-codec boards
- dual-bus MEMS mic + I2S amplifier boards
- ES8311 stereo playback-reference boards
- ES7210 + ES8311 TDM reference boards
- dual-mic AFE boards
- lightweight AEC-only Generic S3 profiles
- full AFE profiles for larger flash/RAM layouts
Codec-backed devices use esp_codec_dev.
No-codec devices use official esp_driver_i2s channels directly, avoiding unnecessary codec/GMF IO dependencies on smaller builds.
🎙️ AEC and AFE Profiles
Profiles are now split more clearly.
🪶 esp_aec
Use this for lightweight echo cancellation.
It is the default direction for:
- intercom-only devices
- Generic S3 full-experience profiles that need to fit smaller flash layouts
- users who want Acoustic Echo Cancellation without the full Audio Front-End cost
🧠 esp_afe
Use this for the full Espressif Audio Front-End path.
It adds:
- Acoustic Echo Cancellation
- Noise Suppression
- Automatic Gain Control
- Voice Activity Detection
- Speech Enhancement / Blind Source Separation on supported dual-mic boards
It is heavier, but it is the right direction for boards with enough flash/RAM and for full voice-device profiles.
📦 Generic Profile Split
Generic S3 full-experience YAMLs are now split by intended target:
-
generic-s3-full-aec-*- lightweight path
- intended for 4 MB-friendly builds
- uses standalone
esp_aec - uses the lighter
previous_framereference
-
generic-s3-full-afe-*- full Audio Front-End path
- intended for larger flash layouts
- uses
esp_afe - uses TYPE2-style software reference
This avoids pretending one Generic YAML can fit every board and every flash layout.
🔊 Better AEC Reference Handling
Echo cancellation quality depends heavily on the playback reference.
The new stack handles reference routing per topology:
- ES8311 boards can use stereo digital feedback
- ES7210 TDM boards can use a hardware TDM reference slot
- no-codec Generic AEC profiles can use
previous_frame - Generic AFE profiles can use TYPE2-style software reference
This is one of the main reasons for the audio migration. AEC quality depends on reference timing, channel layout and conversion path, not only on enabling a library.
🧠 Runtime and Memory Improvements
The migration also cleaned up runtime behavior:
- large buffers and task stacks are allocated earlier
- repeated heap churn during call/media transitions has been reduced
- microphone and speaker wrapper loops wake on real events instead of spinning
intercom_apiparks its loop when idle- intercom TX uses lower-copy reads where possible
- full profiles place selected buffers/stacks in PSRAM
- full LVGL/audio profiles enter OTA maintenance mode before flashing
This helps demanding full-experience devices where media playback, Piper TTS, Micro Wake Word, Voice Assistant, AFE/AEC and intercom all coexist.
🧭 Maintained Board Direction
Current maintained baseline:
- Waveshare ESP32-S3 Audio Board: full AFE, dual mic, TDM reference
- Spotpear Ball v2: codec-backed AFE/intercom profiles
- Generic S3 AEC: lightweight 4 MB-friendly full-experience profiles
- Generic S3 AFE: larger flash full AFE profiles
- Generic dual-bus: maintained intercom profiles
- Waveshare P4 Touch: present and improving, still board-specific/experimental
🧪 Validation
Before this release, the public YAMLs were switched to remote release mode so users can download only the YAML and let ESPHome fetch packages, assets and external components from main.
Validation performed:
- HACS validation passes
- hassfest validation passes
generic-s3-full-afe-tcp.yamlcompiles successfully with ESPHome2026.5.1- ESPHome fetches this repository from
main - Espressif managed components resolve and build correctly
Generic full AFE firmware size from the validation build is about 2.1 MB.
⬆️ Upgrade Notes
Recommended upgrade path:
- Update the Home Assistant integration through HACS.
- Restart Home Assistant.
- Hard-refresh the dashboard page containing
intercom-card. - Clear ESPHome build cache once.
- Recompile from the updated YAMLs.
- Flash the ESP firmware.
If you maintain custom YAMLs, start from the closest maintained profile and reapply only your board-specif...
2026.5.0 - From PBX-like to PBX-lite
Release notes 2026.5.0
From PBX-like to PBX-lite
This release changes the shape of ESPHome Intercom.
It is no longer only an ESPHome audio experiment that can place a full-duplex
call. It now has a small but real PBX-lite model: every ESP is treated as an
independent extension, Home Assistant can join as another extension, the
browser card can act as a softphone, and HA can bridge calls when the two sides
cannot talk directly.
The important part: the simple doorbell and intercom use case is still simple.
If you want one ESP button that rings Home Assistant, start from
yamls/intercom-only/, adapt the board pins, install intercom_native, add the
ESP through the ESPHome integration, and call the HA destination. PBX-lite is
the model underneath, not a requirement to design a phone system.
What PBX-lite means here
- ESPs own their own call state: idle, ringing, outgoing, in call, declined,
busy and ended. - Devices dial by phonebook name, not by hardcoded IP glue.
- Same-transport ESPs can call each other directly.
- Home Assistant can act as a peer, a phonebook publisher and a bridge.
- TCP and UDP devices can live in the same setup, with HA bridging incompatible
legs. - The Lovelace card can ring, answer, decline and hang up as a browser
softphone. - Call reasons and errors are propagated through ESP sensors, HA state and the
card UI.
This is why the release moves from PBX-like behavior to PBX-lite architecture.
The protocol is still intentionally small, but the pieces that matter for a
home intercom are now there.
What this unlocks
You can now build:
- a one-button full-duplex doorbell that rings Home Assistant;
- room-to-room ESP intercoms;
- direct ESP-to-ESP calls on the same transport;
- HA-bridged calls across TCP, UDP and browser audio;
- a browser or mobile dashboard softphone;
- full voice devices with intercom, media player, Piper TTS, Micro Wake Word,
Voice Assistant, AFE/AEC and ducking on the same ESP; - ESP-only discovery setups with optional mDNS packages when HA is not meant to
be the phonebook authority.
The same firmware model scales from "press a GPIO and talk to Home Assistant"
to a multi-room intercom where ESPs call each other, Home Assistant bridges
mixed transports, and the dashboard can participate as a call endpoint.
Transport model
TCP and UDP are both first-class presets.
TCP is the safer default for routed networks, VLANs, containers, filtered Wi-Fi
and setups where packet delivery matters more than the lowest possible latency.
UDP is useful on simple LANs where latency is the priority and packet loss is
under control. It is lighter, but routing and firewall mistakes show up faster
as audible glitches.
Both transports expose the same PBX-lite behavior. You do not need to choose a
different product mode, only the transport that fits the network.
mode: raw_udp remains available as an explicit audio-only path for go2rtc and
raw PCM links. It bypasses PBX-lite signaling on purpose.
Home Assistant integration
intercom_native has been reworked into the PBX-lite hub:
- TCP listener;
- UDP socket manager;
- HA endpoint advertisement;
- phonebook publisher;
- bridge sessions;
- WebSocket state for the Lovelace card;
- validated services and clearer failure handling.
Home Assistant is no longer just a helper around ESPHome. In PBX mode it
becomes an active call participant and bridge, while direct ESP-to-ESP calls can
still avoid HA in the media path.
The HA peer name is hass.config.location_name, so the firmware does not need a
hardcoded "Home Assistant" label. Standard HA-managed YAMLs use HA as the
phonebook authority; ESP-side mDNS announce and discovery remain opt-in for
setups that deliberately run without HA managing the roster.
Lovelace card and frontend
The dashboard card now follows the PBX-lite state model instead of only
mirroring a narrow direct-call path.
It can show ringing, outgoing, in-call and ended states, surface hangup reasons,
call ESP contacts by name, and participate in HA-bridged calls. The current
README media was refreshed around this model so the visible demo matches the
runtime behavior of the release.
Audio engine rebuild
The audio side was almost completely rebuilt.
The full-experience devices now keep media playback, Piper TTS, Voice
Assistant, wake word, AFE/AEC and intercom in the same runtime instead of
treating them as isolated demos.
Main changes:
- early allocation of large stacks and buffers to preserve contiguous internal
RAM; - retained working buffers instead of repeated allocation during audio
transitions; - cleaner I2S lifecycle with hot-path enable and disable;
- native ESPHome speaker and microphone semantics wherever possible;
- improved mixer, ducking and AEC reference behavior;
- better socket accounting for overlapping HA API, logging, media HTTP, TTS
HTTP and intercom sockets; - media playback can be paused from Home Assistant and resumed later;
- barge-in, TTS, media pause and assistant responses were validated together
instead of as separate happy paths.
Board audio improvements
- Codec-backed boards keep their hardware codec path.
- Codec-less Generic S3 boards use the duplex software volume path.
- Media volume and Master Volume now behave as a cascade.
- Software attenuation is real across the range, not just mute versus full
scale. - AEC reference capture, FIR decimation and single-bus duplex handling were
tightened across supported boards. - ES7210 TDM reference-slot behavior is documented and guarded with runtime
warnings.
i2s_audio_duplex is not only for intercom. If you are building your own
ESPHome Voice Assistant and need a shared microphone and speaker I2S path,
speaker reference handling, or a cleaner audio lifecycle, the component can be
used as part of that build too.
YAML and release packaging
The public YAMLs are intended to be downloadable presets. A user should be able
to download one YAML, adjust the hardware options that are meant to be changed,
and let ESPHome fetch packages and external components from this repository.
For release, the managed YAMLs point to main. Local development can still use
the path-switching script to compile against the working tree.
Validated release targets
Validated targets for this release:
- Waveshare S3 Audio full AFE TCP and UDP;
- Spotpear Ball v2 full AFE TCP and UDP;
- Generic ESP32-S3 full AEC UDP as the codec-less reference target;
- Home Assistant OS with the native integration and Lovelace card.
Waveshare P4 Touch remains experimental in this release. The UI and audio stack
are present, but the ESP32-P4 runtime profile still deserves a separate
validation pass.
Upgrade note
This is a breaking release for 4.x users. Read
docs/BREAKING_CHANGES.md before upgrading firmware or
Home Assistant integration files.












