I am fairly handy, I can do a lot in the house, with a hammer and with VI. I understand how Home Assistant works, what Frigate does, how Ring works. I installed all of them myself
Getting them to work together is a lot of Googling and testing. Things LLMs are great at. So instead of doing it myself, I asked Alfie, my LLM agent to do it.
Couple of messages back and forth, giving information as to what is working and what not, how I want layout, et voila! Everything works.
The summary below is LLM generated, in case people want to replay what I did.
Your mileage may vary, warranty ends when you copy-paste.
Teaching a picture frame to answer the door
My television spends most of its life pretending to be a painting. It’s a Samsung Frame, it hangs in the living room, and when nobody is watching (which is most of the time) it shows art. That’s the whole point of the thing.
What I wanted was for it to stop pretending when someone walks up to the house — to drop the artwork, show me the last twenty seconds from the driveway camera, and go back to being a painting. And I wanted to be able to ask for it out loud.
It works now. Say “Hey Siri, front door replay” and the wall plays back whoever just came up the path. Press the doorbell and it does it by itself.
Getting there took a lot longer than it should have, almost entirely because of one component that behaves nothing like its documentation implies. This is the writeup I wanted before I started: what the pieces are, which paths are dead ends, and the specific incantations that work.
The cast
Samsung The Frame Any recent Frame should behave the same.
Home Assistant, doing the orchestration.
Frigate, an open-source NVR, running on its own box with continuous recording.
A Reolink RLC-843A watching the front of the house over RTSP.
A Ring doorbell, used only as a button. More on that later — it’s the one piece that can’t do what you’d expect.
Apple’s HomeKit bridge, built into Home Assistant, to get Siri involved.
You don’t need all of it. The replay trick needs Frigate and the TV. Siri needs HomeKit. The doorbell trigger needs a doorbell that reports presses to Home Assistant, and almost any of them do.
The thing nobody tells you about The Frame
Here is where all the time went, so I’ll put it first.
The Frame exposes a DLNA renderer. Home Assistant discovers it and gives you a media_player entity that advertises PLAY_MEDIA. It looks, from the outside, like you can point it at a camera stream and be finished in an afternoon.
You cannot. The renderer is real, but it is fussy in ways that produce no useful error messages. Everything below I established by watching what the television actually did, not by reading anything:
It refuses https. Point it at an https:// URL and the renderer answers SetAVTransportURI with UPnP error 716, “Resource not found”. The resource is fine. It just won’t do TLS. Plain http only, which in practice means everything has to live on your LAN.
It accepts HLS and then ignores it. This is the cruel one. Hand it an .m3u8 and the call succeeds, Home Assistant cheerfully reports the URL as the current media, and the television never fetches a single byte. No error anywhere. It simply doesn’t play.
It probes with HEAD before it GETs. Your server has to answer a HEAD request properly. ffmpeg’s built-in HTTP server (-listen 1) does not — it autodetects a GET, receives a HEAD, and dies with a 400. That failure looks identical to the TV not connecting at all.
It wants bytes immediately. If the first data is slow to arrive, it hangs up and retries. Anything that takes a second or two to spin up needs to be started before you send the response headers.
But it will happily play MPEG-TS and plain MP4 over plain http. That sentence is the entire solution.
Two problems that look like one
“Show me the camera on the TV” is actually two jobs with very different difficulty:
Replay — who just walked up? — is a finite video clip. Easy, robust, no moving parts.
Live — what’s happening right now? — is a continuous stream. Needs a small piece of software running somewhere, permanently.
Start with replay. It’s most of the value for a fraction of the work, and if you only ever build that, you have a complete, dependency-free system.
Replay: nothing has to be running
This is my favourite part of the setup because there is no daemon, no relay, no transcoding. Frigate will cut you a clip on demand:
http://<frigate-host>:5000/api/<camera>/start/<epoch>/end/<epoch>/clip.mp4
Give it two Unix timestamps and it builds an MP4 and serves it. On my box a twenty-second clip comes back in under half a second, about 13 MB at 4K. It’s a finite MP4 over plain http, which is precisely the Frame’s happy path, and the television fetches it directly from Frigate — Home Assistant just makes the introduction.
The Home Assistant script:
alias: Front Door Replay
icon: mdi:history
mode: single
sequence:
- action: media_player.play_media
target:
entity_id: media_player.frame_dlna # your DLNA entity, not the TV's main one
data:
media_content_type: video/mp4
media_content_id: >-
{% set end = (now().timestamp() | int) - 8 %}http://<frigate-host>:5000/api/FrontCam/start/{{ end - 20 }}/end/{{ end }}/clip.mp4
Two details worth keeping:
The eight-second backoff. Frigate writes recordings in segments, and the newest one is still being written. Asking for video up to this instant gets you a truncated or empty clip. Backing off eight seconds costs you nothing — you’re watching something that already happened — and makes it reliable.
You’ll get roughly what you asked for. Request twenty seconds and expect sixteen to twenty-one, because Frigate trims to whole segments. Nobody watching notices.
The prerequisite is continuous recording. In your Frigate config:
record:
enabled: true
retain:
days: 1
mode: all # not "motion", not "active_objects"
mode: all is the bit that matters. With event-based retention, “the last twenty seconds” only exists if something already tripped a detection — which is exactly the moment you can’t rely on.
When the clip finishes, my Frame returns to art on its own. Yours probably will too.
Live: a small relay, and where to run it
For a live view something must convert the camera’s RTSP into MPEG-TS over plain http. There’s no way around it: the Frame takes neither RTSP nor HLS.
Don’t use Home Assistant’s HLS stream as the source. This was my first attempt and it half-works, which is worse than not working. HA can hand you an HLS URL for any camera, and ffmpeg will read it — for a few minutes. It’s a rolling playlist whose segments expire underneath the transcoder, and when they do ffmpeg exits with Invalid data found when processing input and the picture vanishes off the wall. I chased that failure for an hour before accepting that the middle layer was the problem.
Go straight to the camera. An RTSP session has nothing to expire, so one ffmpeg process runs for as long as somebody is watching.
The relay is about a hundred lines of Python with no dependencies: a threaded HTTP server that answers HEADimmediately, and on GET starts
ffmpeg -rtsp_transport tcp -i <rtsp-url> -c:v copy -an \
-flush_packets 1 -muxdelay 0 -muxpreload 0 -f mpegts -
and pipes stdout to the response. Three things make it work where naive versions don’t:
-c:v copy. No re-encoding. The television does the decoding, so the relay is nearly free — it’s just repackaging. My camera is 4K HEVC and the Frame decodes it without complaint.Read ffmpeg’s first chunk before sending the response headers. This is the fix for the TV hanging up on slow starts. By the time it sees a
200, video is already in hand.Answer HEAD, and survive clients that disappear. A DLNA renderer opening and immediately dropping a connection is normal behaviour, not an error, and shouldn’t take your server down or fill your logs with tracebacks.
Point Home Assistant at it exactly as before, with media_content_type: video/mp2t.
Run it on the machine that already has the camera feed — for me, the Frigate host. My first version ran on a spare Linux box, and it worked, but that put a general-purpose machine permanently in the path of my television for no reason.
And run it under systemd, not in a terminal. I lost twenty minutes to a bug that turned out to be “the process exited when I closed the window I started it in”. I had, in that time, constructed a detailed and entirely wrong theory involving firewall rules and Docker’s habit of bypassing ufw. The relay was simply not running. A systemd unit with Restart=alwaysremoves the entire category.
The trap that cost me a working button
The Frame has two states that both look like “off”: genuinely powered down, and showing art. In Home Assistant, with the integrations I have, both report off, and they are reached through different entities:
What you wantEntity to call media_player.turn_off onArt modethe samsungtv entityActually offthe SmartThings entity
My own setup notes, written months earlier, said the samsungtv entity “doesn’t work” for power control. That was true and it was misleading: it doesn’t power the TV off because it’s the art-mode control. I trusted the note, wired a button labelled “Art Mode”, watched the entity flip to off, and declared victory.
It was switching the television off. It took someone actually looking at the wall to catch it.
Two lessons, both obvious in hindsight. Don’t infer physical state from an entity state when the entity can’t distinguish the two things you care about — go and look at the screen. And when you build the “stop” control, guard it on the entity that reports reliably (SmartThings, in my case) while acting on the one that does the job.
alias: Art Mode
sequence:
# Stop the DLNA session first — this is also what lets the relay's
# ffmpeg exit instead of streaming to nobody.
- if:
- condition: state
entity_id: media_player.frame_dlna
state: playing
then:
- action: media_player.media_stop
target:
entity_id: media_player.frame_dlna
- delay:
seconds: 2
- if:
- condition: state
entity_id: media_player.frame # reliable "is it powered" signal
state: "on"
then:
- action: media_player.turn_off
target:
entity_id: media_player.samsung_frame # samsungtv entity -> art mode
Siri, without an Apple TV
Home Assistant’s HomeKit bridge exposes entities to Apple Home, and anything in Apple Home can be spoken to. There’s no Apple TV or HomePod required — an iPhone is enough.
The bridge filters by domain, and script is not included by default. Add it, and your scripts appear in the Home app as switches.
Two things worth knowing before you do:
Adding the domain exposes every script you have. Mine brought eleven, including “Dishwasher Rinse”. You can exclude individual entities in the same dialog if you’d rather keep it tidy — but decide deliberately, because it’s your whole household’s Home app.
Siri matches the accessory name, not the entity id. Name the script as the sentence you want to say. “Front Door Live” gives you “Hey Siri, turn on Front Door Live”. If the “turn on” grates, make a scene in the Home app containing just that switch and name the scene naturally — then it’s “Hey Siri, front door live”.
The alternative, if you’d rather not touch HomeKit, is the Home Assistant companion app: it publishes your scripts to Apple’s Shortcuts, and any shortcut is a Siri phrase. Less setup, but it only runs on a device that has the app.
Letting the doorbell do it
Ring exposes the button press to Home Assistant as an event entity — event.front_door_ding. Its state is the timestamp of the last press, so the trigger is a state change, with a condition to ignore the flaps that happen at startup:
alias: "Doorbell: replay who walked up"
mode: single
max_exceeded: silent
triggers:
- trigger: state
entity_id: event.front_door_ding
conditions:
- condition: template
value_template: >-
{{ trigger.from_state is not none
and trigger.to_state.state not in ['unknown', 'unavailable']
and trigger.from_state.state != trigger.to_state.state }}
actions:
- action: script.front_door_replay
max_exceeded: silent keeps an impatient visitor leaning on the button from stacking up clips or filling the log with warnings.
This works from art mode, which is the case that matters — the panel is still powered and the renderer still answers. I haven’t got it working from a fully powered-down television, and I suspect it can’t.
Worth thinking about before you enable it: it interrupts whatever is on screen. If someone is watching a film when the bell goes, the TV cuts to the driveway for twenty seconds. A condition restricting it to art mode, or to certain hours, is easy to add and probably wise.
What doesn’t work
The Ring’s own camera. This surprised me, and it’s worth knowing before you plan around it. Ring’s live view in Home Assistant is WebRTC, and Home Assistant will not hand it to anything else — asking for a stream returns, flatly, does not support play stream service. So the doorbell in my setup is a button, and the footage comes from a separate camera pointed at the same approach. If you want the actual doorbell’s view on a TV, you’re looking at a project like ring-mqtt to coax an RTSP feed out of it.
AirPlay. The natural instinct, and a dead end. The Frame is a perfectly good AirPlay 2 receiver, but something has to send, and there’s no practical AirPlay video sender on Linux or in Home Assistant. You’d be reaching for your phone every time, which defeats the point.
Anything over https. Covered above, but worth repeating because it quietly rules out most of the internet. Everything the television plays has to come from your own network, unencrypted.
The order I’d do it in
Get Frigate recording continuously with
mode: all. Nothing downstream works without it.Find your TV’s DLNA entity in Home Assistant. It’s a separate
media_playerfrom the main TV one, usually with the same name — check that its integration isdlna_dmr.Test with a clip before writing any automation. Build a Frigate clip URL by hand, call
media_player.play_mediafrom Developer Tools, and watch the screen. If that works, everything else is plumbing.Wrap it in a script, with the timestamp template.
Work out your TV’s art-mode gesture — and verify it by looking at the television, not at an entity state.
Add the
scriptdomain to your HomeKit bridge and try Siri.Only then build the live relay, if you still want it. You may find you don’t.
Step 3 is the one I’d emphasise. Every hard problem in this project was in the television’s DLNA renderer, and you can find out whether yours behaves like mine in about two minutes, before you’ve written a line of anything.
Was it worth it?
The live feed is the thing I thought I wanted and the thing I use least. It’s a 4K camera pointed at a driveway; there is rarely anything happening on it.
The replay is the one that earned its place. The doorbell goes, the painting on the wall turns into a twenty-second recording of somebody walking up the path, and then it’s a painting again. It answers the actual question — who was that? — without anybody reaching for a phone.
And the failure I’ll remember longest wasn’t technical. It was trusting a note I’d written to myself, months earlier, over the evidence on the screen in front of me.
