[RFC] Android Audio Sink Binding — Play TTS/Audio on Android Devices
Category: Development
Summary
I’d like to propose a new binding that registers Android devices (phones, tablets, Wear OS watches) as audio sinks in openHAB. This would allow TTS messages and audio streams to be played on any paired Android device running the openHAB app, similar to how the Chromecast binding exposes Chromecast devices as audio sinks.
Usage would look like:
say("Dinner is ready", "voicerss:default", "androidaudio:device:robert_pixel")
say("Alarm triggered", "voicerss:default", "androidaudio:broadcast:all")
Motivation
There are many situations where you want openHAB to speak to you on a device that’s always in your pocket — door opened alerts, temperature warnings, arrival announcements, etc. Currently this requires a dedicated speaker (Chromecast, Sonos) or relies on text-only notifications. Android phones already have speakers and are always nearby.
Proposed Architecture
The design leverages the existing openHAB Cloud notification infrastructure (FCM) with zero changes to the cloud service itself.
Flow
openHAB TTS Engine → AudioStream
↓
Android Audio Sink Binding (server-side, new)
↓ serves audio via AudioHTTPServer
↓ sends notification with extra fields: audioUrl, audioFormat, targetDeviceId
↓
openHAB Cloud (unchanged — passes payload through transparently)
↓ FCM data message to device(s)
↓
Android App (modified)
↓ detects audioUrl in FCM payload
↓ resolves URL (local connection or cloud proxy)
↓ plays audio via MediaPlayer
Key Design Decisions
-
No new FCM message type — The
audioUrlandtargetDeviceIdfields are added to the existing notification payload. The cloud already passes arbitrary fields through to FCM as data messages. The Android app just checks for the presence ofaudioUrland routes to audio playback instead of showing a notification. -
Targeting behavior:
targetDeviceIdpresent → only that device playstargetDeviceIdabsent → all capable devices play (broadcast)
-
URL resolution on the client side — The Android app already knows whether it has a local or remote connection. If the audio URL is relative (
/audio/stream123.mp3), the app resolves it against whichever connection is available (direct LAN preferred, cloud proxy as fallback). -
Extended TTL for audio streams — Chromecast uses 10s TTL. Because of FCM delivery latency (0.5-3s) + app wake time + download time, we’d use 30s TTL in AudioHTTPServer.
-
AudioSinkAsync base class — Same pattern as the Chromecast binding.
Components Affected
| Component | Changes | Scope |
|---|---|---|
| openhab-addons | New org.openhab.binding.androidaudio binding |
New add-on |
| openhab-cloud | None | — |
| openhab-android | FCM handler for audioUrl + new AudioPlaybackService |
Moderate |
| openhab-android-wear | FCM support + audio playback (Phase 2) | Future |
Thing Types
androidaudio:device— a specific Android device (configured by deviceId from cloud registration)androidaudio:broadcast— virtual thing that targets all capable devices
Questions for the Community
-
Standalone binding vs Cloud Connector extension? Should this be its own binding (
org.openhab.binding.androidaudio) or integrated into the existing openHAB Cloud Connector add-on? A standalone binding is architecturally cleaner but means an additional add-on to install. -
Payload convention — Is piggybacking on the existing notification payload (adding
audioUrl/targetDeviceIdfields) acceptable? Or should this use a distincttype(e.g."playAudio") to keep concerns separated? -
Device discovery — Phase 1 would use manual configuration (user enters deviceId). Phase 2 could auto-discover devices from cloud registration data. Is there an existing pattern for this in other bindings?
-
FOSS flavor — The Android app’s FOSS build uses polling instead of FCM. Audio sink would not work there. Is this acceptable, or should we design an alternative path (SSE/WebSocket)?
-
Concurrent playback — If two audio messages arrive close together, should the second interrupt the first, queue behind it, or be discarded?
-
Is anyone already working on something similar?
Implementation Plan
- Phase 1 (MVP): Server binding with manual device config, AudioSink impl (MP3/WAV), Android app FCM handler + MediaPlayer playback, broadcast sink, 30s audio TTL
- Phase 2: Auto-discovery, volume control, playback feedback, Wear OS
- Phase 3: Streaming, queue management, priority levels, DND awareness
I have a more detailed technical spec available if anyone is interested in the implementation details (AudioSink class structure, FCM payload format, URL resolution logic, etc.).
Looking forward to feedback on the approach before I start coding. Happy to adjust the design based on community input.