Public Access
Add ESPHome display component; ring-buffer driver shared with PlatformIO
Driver (components/el512/el512_panel.*, ESP-IDF only): - DMA ring of 8 x 4 lines (17 KB) refilled from the I2S EOF interrupt (IRAM) instead of a 142 KB full-frame stream; ~107 KB heap freed. - 256-entry lookup table encodes 8 pixels per step. - Back buffer + displayed buffer, swapped at the start of a frame. - PlatformIO builds it via lib_extra_dirs (EL512Core); lib/EL512 keeps the Adafruit GFX front end. ESPHome external component (platform: el512): - DisplayBuffer with optional ENABLE / _LOWPWR pins, set_enabled(), set_low_power(), helper functions for page lambdas. - still_alive_page: plays the Portal end-credits show, then returns to the previous page. Still Alive engine moved to components/el512/still_alive.* with a built-in 5x7 font (from Adafruit GFX, BSD); the PlatformIO demo uses the same code. The importer writes components/el512/still_alive_data.h and stops at 200 s (--until-ms), matching the hand-trimmed data. esphome/el512-panel.yaml: 8 pages (clock, forecast, agenda, dashboard, now playing, Plex, solar, Still Alive), page select, rotation, display and low-power switches, message text. home_assistant/packages/el512_panel.yaml: helper template sensors for forecast, agenda, now playing and Plex streams. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
410885de24
commit
963590cdfe
27 files changed
+2110
-828
No files matched your search
@@ -0,0 +1,133 @@
|
||||
# Helper sensors for the EL512.256-H ESPHome display (esphome/el512-panel.yaml).
|
||||
#
|
||||
# Install: put this file in /config/packages/ and make sure configuration.yaml has
|
||||
#
|
||||
# homeassistant:
|
||||
# packages: !include_dir_named packages
|
||||
#
|
||||
# then restart Home Assistant. All lists use the same compact text format so the
|
||||
# ESP32 can split them cheaply: fields separated by "|", entries by ";".
|
||||
|
||||
template:
|
||||
# Forecast and agenda come from actions, so they refresh on a timer.
|
||||
- triggers:
|
||||
- trigger: time_pattern
|
||||
minutes: "/15"
|
||||
- trigger: homeassistant
|
||||
event: start
|
||||
- trigger: event
|
||||
event_type: event_template_reloaded
|
||||
actions:
|
||||
- action: weather.get_forecasts
|
||||
target:
|
||||
entity_id: weather.knmi_home
|
||||
data:
|
||||
type: daily
|
||||
response_variable: fc
|
||||
- action: calendar.get_events
|
||||
target:
|
||||
entity_id:
|
||||
- calendar.mangavalk_gmail_com
|
||||
- calendar.li_s_happy_home
|
||||
- calendar.feestdagen_in_nederland
|
||||
data:
|
||||
duration:
|
||||
days: 14
|
||||
response_variable: cal
|
||||
sensor:
|
||||
# days: "Sun|cloudy|12|11|0;Mon|partlycloudy|14|10|20;..."
|
||||
# weekday|condition|high|low|rain chance %
|
||||
- name: EL512 forecast
|
||||
unique_id: el512_forecast
|
||||
icon: mdi:weather-partly-cloudy
|
||||
state: "{{ fc['weather.knmi_home'].forecast | count }}"
|
||||
attributes:
|
||||
days: >-
|
||||
{%- set ns = namespace(out=[]) -%}
|
||||
{%- for d in fc['weather.knmi_home'].forecast[:7] -%}
|
||||
{%- set ns.out = ns.out + [
|
||||
(as_datetime(d.datetime) | as_local).strftime('%a') ~ '|' ~
|
||||
d.condition ~ '|' ~
|
||||
(d.temperature | round(0) | int) ~ '|' ~
|
||||
(d.templow | default(d.temperature) | round(0) | int) ~ '|' ~
|
||||
(d.precipitation_probability | default(0) | int)] -%}
|
||||
{%- endfor -%}
|
||||
{{ ns.out | join(';') }}
|
||||
|
||||
# events: "Mon 19|11:00|Work;Tue 20||Holiday;..."
|
||||
# day|start time (empty = all day)|title
|
||||
- name: EL512 agenda
|
||||
unique_id: el512_agenda
|
||||
icon: mdi:calendar
|
||||
state: >-
|
||||
{%- set ns = namespace(n=0) -%}
|
||||
{%- for c in cal.values() -%}{%- set ns.n = ns.n + c.events | count -%}{%- endfor -%}
|
||||
{{ ns.n }}
|
||||
attributes:
|
||||
events: >-
|
||||
{%- set ns = namespace(ev=[]) -%}
|
||||
{%- for c in cal.values() -%}
|
||||
{%- for e in c.events -%}{%- set ns.ev = ns.ev + [e] -%}{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
{%- set out = namespace(items=[]) -%}
|
||||
{%- for e in (ns.ev | sort(attribute='start'))[:10] -%}
|
||||
{%- if e.start | length == 10 -%}
|
||||
{%- set day = strptime(e.start, '%Y-%m-%d').strftime('%a %d') -%}
|
||||
{%- set time = '' -%}
|
||||
{%- else -%}
|
||||
{%- set t = as_datetime(e.start) | as_local -%}
|
||||
{%- set day = t.strftime('%a %d') -%}
|
||||
{%- set time = t.strftime('%H:%M') -%}
|
||||
{%- endif -%}
|
||||
{%- set title = (e.summary | default('')) | replace('|', '/') | replace(';', ',') -%}
|
||||
{%- set out.items = out.items + [day ~ '|' ~ time ~ '|' ~ title[:40]] -%}
|
||||
{%- endfor -%}
|
||||
{{ out.items | join(';') }}
|
||||
|
||||
# Whatever media player is playing right now; position advances every 5 s.
|
||||
- triggers:
|
||||
- trigger: time_pattern
|
||||
seconds: "/5"
|
||||
sensor:
|
||||
- name: EL512 now playing
|
||||
unique_id: el512_now_playing
|
||||
icon: mdi:play-circle
|
||||
state: >-
|
||||
{%- set p = states.media_player | selectattr('state', 'eq', 'playing') | list -%}
|
||||
{{ p[0].entity_id if p else 'idle' }}
|
||||
attributes:
|
||||
# "title|artist|album|player|position s|duration s"
|
||||
info: >-
|
||||
{%- set p = states.media_player | selectattr('state', 'eq', 'playing') | list -%}
|
||||
{%- if p -%}
|
||||
{%- set a = p[0].attributes -%}
|
||||
{%- set pos = a.media_position | default(0) -%}
|
||||
{%- if a.media_position_updated_at is defined and a.media_position_updated_at -%}
|
||||
{%- set pos = pos + (now() - as_datetime(a.media_position_updated_at)).total_seconds() -%}
|
||||
{%- endif -%}
|
||||
{%- set dur = a.media_duration | default(0) -%}
|
||||
{%- set pos = [pos, dur] | min if dur else pos -%}
|
||||
{{ [a.media_title | default(a.app_name | default('')), a.media_artist | default(a.media_series_title | default('')),
|
||||
a.media_album_name | default(''), a.friendly_name | default(''), pos | int, dur | int]
|
||||
| map('string') | map('replace', '|', '/') | join('|') }}
|
||||
{%- endif -%}
|
||||
|
||||
# Plex: the server sensor has one attribute per stream ("user - client": "title").
|
||||
- sensor:
|
||||
- name: EL512 Plex streams
|
||||
unique_id: el512_plex_streams
|
||||
icon: mdi:plex
|
||||
state: "{{ states('sensor.mangavalkserver') }}"
|
||||
attributes:
|
||||
# "user - client|title;..."
|
||||
streams: >-
|
||||
{%- set s = states.sensor.mangavalkserver -%}
|
||||
{%- set ns = namespace(out=[]) -%}
|
||||
{%- if s -%}
|
||||
{%- for k, v in s.attributes.items()
|
||||
if k not in ['unit_of_measurement', 'friendly_name', 'icon', 'device_class', 'state_class'] -%}
|
||||
{%- set ns.out = ns.out + [(k | replace('|', '/') | replace(';', ',')) ~ '|' ~
|
||||
(v | string | replace('|', '/') | replace(';', ','))] -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{{ ns.out | join(';') }}
|
||||
Reference in new issue
Block a user