Follow-up to my earlier thread about turning an old iPad into a photo frame. Quite a lot has landed since then, so here is where SnapFrame actually stands now.

It lives inside Home Assistant now
The add-on offers ingress, so there is a proper panel in the HA sidebar / Open Web UI, while the mapped port stays available for the tablets themselves — every request the page makes is relative, so both routes work unchanged.
With Mosquitto running it also does MQTT discovery: the next waste collection (with days_until and the waste types as attributes), the photo count, the last scan time and whether weather mode is active show up as entities on their own. No hand-written REST sensors in configuration.yaml.
Weather screen, and now a badge
A motion sensor in the hallway triggers an automation, SnapFrame flips into weather mode for a couple of hours, and every 8th photo is replaced by a weather screen: current conditions, today’s high/low, and the next 12 hours as a row of large forecast cards.
(Those two shots are from the previous release — the 3.1 screen additionally prints how old the data is underneath.)
Weather mode is short-lived by design, so 3.1 adds a badge in the top-right corner (weather_display_mode: slide / badge / both) that can stay up over the photos all day. weather_badge_hours bounds when it may appear, and weather_badge_alerts_only turns it into a warning instead of a readout — hidden until the forecast shows rain, snow or a storm within 3 hours, or frost or heat within 6, with the accent colour matching what it is warning about.
3.1 also fixed something I had wrong: the big temperature was whatever HA pushed, however long ago, with nothing on screen to say so. Now a push younger than 20 minutes is used as-is, an older one has the current temperature interpolated from the hourly forecast that arrived with it, and the screen prints how old the data is (updated 12 min ago). Once the forecast runs out it shows --° rather than a number it can no longer stand behind.
The HA link is still one-directional — HA pushes over plain REST, no homeassistant_api permission and no HA token living on the frame:
rest_command:
snapframe_weather_mode_on:
url: "http://192.168.1.x:8099/weather-mode/on"
method: POST
snapframe_weather_update:
url: "http://192.168.1.x:8099/weather-update"
method: POST
content_type: "application/json"
payload: >
{
"temperature": {{ state_attr('weather.home', 'temperature') if state_attr('weather.home', 'temperature') is not none else 'null' }},
"condition": "{{ states('weather.home') }}",
"humidity": {{ state_attr('weather.home', 'humidity') if state_attr('weather.home', 'humidity') is not none else 'null' }},
"hourly": {{ hourly | default([], true) | tojson }}
}
automation:
- alias: "SnapFrame – enable weather mode on morning motion"
trigger:
- platform: state
entity_id: binary_sensor.hallway_motion
to: "on"
condition:
- condition: time
after: "06:00:00"
before: "10:00:00"
action:
- service: rest_command.snapframe_weather_mode_on
- alias: "SnapFrame – push weather update"
trigger:
- platform: time_pattern
minutes: "/30"
- platform: state
entity_id: weather.home
action:
- service: weather.get_forecasts
target:
entity_id: weather.home
data:
type: hourly
response_variable: fc
- service: rest_command.snapframe_weather_update
data:
hourly: "{{ fc['weather.home'].forecast[:12] }}"
(The weather.get_forecasts step is needed because recent HA versions removed the forecast attribute from the weather entity’s state. type: daily works too if that is all your integration offers.)
Waste-collection reminders, imported from the municipal leaflet
The frame can show when the bins go out, and rather than typing a year of dates in by hand you can upload the municipality’s PDF schedule and it reads the dates out of it.
Vector PDFs are parsed locally — no OCR, no network, no API key. These leaflets are grid calendars where the meaning is in the colour of the cell, not in text, which is exactly what OCR cannot give you. A vector PDF contains both the day numbers and the coloured rectangles with their coordinates, so row = ISO week and column = weekday gives the date directly, and the printed day number is then used as a checksum — a cell that disagrees is discarded rather than guessed.
It deliberately does not hard-code a palette, because one village marks plastic yellow and the next marks it blue, and one leaflet often carries several rounds at once. The import lists every colour series it found with a swatch, how often it recurs and its date range, and you tick the ones that apply to your household and name the waste type.
Security, in case you already run it
3.0.0 fixed a path-traversal hole: Flask’s <path:> converter hands ../.. straight through, so /delete/<path>, /thumb/<path> and /exif/<path> could step outside the photo library. All paths now resolve through one helper that rejects anything outside it. The same release added an api_token option — before it, uploading, deleting, triggering a scan and pushing weather data needed no credentials at all unless Basic Auth was on, and Basic Auth is off by default. Set it and those calls need an X-SnapFrame-Token header; reading stays open so the frame itself still needs no login.
Old Safari, which is most of the work
An iPad 2/3/Air is frozen on Safari 9–13, where flex gap (14.1) and clamp()/min() are silently dropped — a layout that looks fine on a laptop renders as a tiny broken mess. Fluid sizes all have plain-px fallbacks in front of them, the weather screen is laid out with display: table, and the emoji used for waste types were swapped for 2015-era glyphs after three of them turned out to render as nothing before iOS 11.1/13.2.
Performance got attention too: photo metadata is indexed in SQLite in /data instead of being re-read over CIFS on every request, thumbnails moved off the share, and listing a 400-photo album over a slow share went from 6.3 s to 0.04 s.
Install
Settings → Add-ons → Add-on Store → ⋮ → Repositories → add https://github.com/emo546/ha-snapframe
Photos stay on your own SMB share, nothing goes to a cloud photo service. aarch64 and amd64, with prebuilt images so an update is not minutes of compiling on a Pi.
Full setup guide, all 29 options and the endpoint docs: ha-snapframe/snapframe/README.md at main · emo546/ha-snapframe · GitHub
If you run it on a really old iPad and something renders wrong, tell me the iOS version — that is the hardware I am aiming at.

