Updates for HA integration

This commit is contained in:
iosullivan committed 2026-10-18 08:02:07 +10:00
1 parent 61c37de884
commit 0802e60876
5 files changed
+14 -666

No files matched your search

-71
View File
@@ -1,77 +1,6 @@
###############################################################################
# Guition JC3636K718C (1.8" 360x360 ROUND KNOB, ESP32-S3) - core config
# Pins and init_sequence from the OFFICIAL manufacturer demo (JC3636K718_knob_EN),
# files pincfg.h + scr_st77916.h. This is the correct pinout for THIS board
# (different from the JC3636W518!).
#
# Always-on core: display (ST77916/QSPI) + touch (CST816) + knob + audio + voice assistant +
# LED ring + the clock home screen, control tiles and settings. Optional carousel screens
# (player, timer, games, weather, demo) live as packages in base/screens/ - see base/screens/README.md.
###############################################################################
substitutions:
name: voice-knob
friendly_name: "Voice Knob"
# Clock timezone. HA does not push its zone to the device, so set yours here. Use a friendly IANA
# name (e.g. Europe/Warsaw, America/New_York) - core maps the common ones to the POSIX form the
# device needs (it has no IANA database). For a zone not in that table, paste its POSIX string
# instead (see https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv). DST automatic.
timezone: "Australia/Brisbane"
# For album art: base URL of your HA + the media_player entity (change if different)
ha_base_url: "http://ha.iosull.com"
player_entity: media_player.voice_knob_player
# On-device wake word. Built-in models: alexa, okay_nabu, hey_jarvis, hey_mycroft.
wake_word: "hey_jarvis"
# Entities for the home screen (toggle each in Settings -> Home). Change if different.
weather_entity: weather.home
room_temp_entity: sensor.room_temperature
room_hum_entity: sensor.room_humidity
climate_entity: climate.home # thermostat screen target (a climate.* entity)
# Control tiles (swipe up): 4 toggleable HA entities, each with an icon + a name. Any domain.
# tileN_icon = a name from the built-in set: bulb lamp led power plug switch fan ac heater
# thermometer lock unlock door blinds tv speaker bell wifi home flash. The same icon may
# repeat across tiles. (For an icon outside the set, paste its glyph or a \U000Fxxxx code.)
tile1_entity: light.desk_lamp
tile1_icon: "bulb"
tile1_name: "Lamp"
tile2_entity: switch.neon
tile2_icon: "flash"
tile2_name: "Neon"
tile3_entity: light.desk_lamp
tile3_icon: "power"
tile3_name: "Tile "
# Sensors screen (knob cycles): up to 6 read-only HA entities, one shown big at a time.
# Set as many glanceN_entity as you want (+ a name, optional icon name); leave the rest at
# their default (a non-existent entity) and they're skipped.
glance1_entity: sensor.power
glance1_name: "Power"
glance1_icon: "flash"
glance2_entity: sensor.va_glance_unused2
glance2_name: ""
glance2_icon: ""
glance3_entity: sensor.va_glance_unused3
glance3_name: ""
glance3_icon: ""
glance4_entity: sensor.va_glance_unused4
glance4_name: ""
glance4_icon: ""
glance5_entity: sensor.va_glance_unused5
glance5_name: ""
glance5_icon: ""
glance6_entity: sensor.va_glance_unused6
glance6_name: ""
glance6_icon: ""
forecast_entity: sensor.va_weekly_forecast # weather screen forecast (HA helper, see base/screens/weather.ha-helper.yaml)
# Carousel screen order (left-to-right). Screens not included in the build are skipped.
screen_order: "clock,player,timer,cars,space,snake,knobuss,weather,thermostat,sensors,demo"
esphome:
name: ${name}
friendly_name: ${friendly_name}
platformio_options:
board_build.flash_mode: dio
board_build.f_flash: 80000000L
board_build.f_cpu: 240000000L
on_boot:
- priority: 800
then:
+1 -2
View File
@@ -149,8 +149,7 @@ script:
sensor:
- platform: homeassistant
id: th_cur_s
entity_id: ${climate_entity}
attribute: current_temperature
entity_id: ${room_temp_entity}
on_value:
- lambda: 'id(g_th_cur) = x;'
- script.execute: th_render
-37
View File
@@ -1,37 +0,0 @@
# Home Assistant helper for the device's Weather screen (base/screens/weather.yaml).
#
# HA no longer exposes a weather entity's multi-day forecast as an attribute, so this
# template calls weather.get_forecasts and publishes a single compact sensor the device
# reads. State format: "condition,high,low,DayName|..." (one segment per day, up to 7).
#
# 1. Change weather.forecast_dom below to your weather entity (3 places).
# 2. Put this in HA configuration.yaml (or a packages/ file), then restart HA / reload
# template entities. It creates sensor.va_weekly_forecast.
# 3. On the device, ${forecast_entity} defaults to sensor.va_weekly_forecast - override
# the substitution if you name it differently.
template:
- trigger:
- platform: homeassistant
event: start
- platform: time_pattern
minutes: "/30"
action:
- service: weather.get_forecasts
target:
entity_id: weather.forecast_dom
data:
type: daily
response_variable: fc
sensor:
- name: "VA weekly forecast"
unique_id: va_weekly_forecast
state: >
{% set ns = namespace(parts=[]) %}
{% for d in fc['weather.forecast_dom'].forecast[:7] %}
{% set hi = (d.temperature | round(0)) if d.temperature is not none else 0 %}
{% set lo = (d.templow | round(0)) if (d.templow is defined and d.templow is not none) else hi %}
{% set nm = as_timestamp(d.datetime) | timestamp_custom('%a', true) %}
{% set ns.parts = ns.parts + [d.condition ~ ',' ~ hi ~ ',' ~ lo ~ ',' ~ nm] %}
{% endfor %}
{{ ns.parts | join('|') }}
-541
View File
@@ -1,541 +0,0 @@
# Weather - carousel screen (base id 5). GENERATED by scripts/gen_weather.py.
# A dial of forecast days around the rim (knob scrolls), animated condition icon + colour-coded
# temperature in the centre, a glow that slides to the selected day. Forecast comes from a Home
# Assistant helper sensor (see base/screens/weather.ha-helper.yaml). Remove: drop from `files:`.
esphome:
on_boot:
- priority: 655
then:
- lambda: 'id(g_present)[5] = true;'
- priority: 650
then:
- lambda: |-
int g = id(g_wgrp_n)++;
id(g_wgrp_labels) += (g ? "\n" : ""); id(g_wgrp_labels) += "Weather";
int o = id(g_wopt_n)++;
id(g_wopt_group)[o] = g; id(g_wopt_label)[o] = "Fahrenheit"; id(g_wopt_kind)[o] = 0; id(g_wopt_ptr)[o] = &id(g_wx_fahrenheit);
globals:
# --- forecast data ---
- id: g_wx_fahrenheit
type: bool
restore_value: yes
initial_value: 'false'
- id: g_wx_now
type: int
restore_value: no
initial_value: '-127'
- id: g_wx_state_cur
type: std::string
restore_value: no
- id: g_wx_cond
type: std::string[7]
restore_value: no
- id: g_wx_icon
type: std::string[7]
restore_value: no
- id: g_wx_name
type: std::string[7]
restore_value: no
- id: g_wx_hi
type: int[7]
restore_value: no
- id: g_wx_lo
type: int[7]
restore_value: no
- id: g_wx_n
type: int
restore_value: no
initial_value: '0'
- id: g_wx_day
type: int
restore_value: no
initial_value: '0'
# --- render cache ---
- id: g_wx_sel
type: std::string
restore_value: no
- id: g_wx_tc
type: int
restore_value: no
initial_value: '-127'
- id: g_wx_lastcond
type: std::string
restore_value: no
- id: g_wx_ikey
type: std::string
restore_value: no
- id: g_wx_gpos
type: float
restore_value: no
initial_value: '0.0'
font:
- file: "https://github.com/Templarian/MaterialDesign-Webfont/raw/v7.4.47/fonts/materialdesignicons-webfont.ttf"
id: icons_wx
size: 30
bpp: 4
glyphs: ["󰖙", "󰖔", "󰖕", "󰖑", "󰖝", "󰖓", "󰖖", "󰖗", "󰖘", "󰖐"]
image:
- id: img_wx_sun0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_sun7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/sun7.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_partly7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/partly7.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_moon7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/moon7.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_cloud7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/cloud7.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_rain7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/rain7.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_storm7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/storm7.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow0
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow0.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow1
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow1.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow2
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow2.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow3
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow3.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow4
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow4.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow5
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow5.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow6
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow6.png"
type: RGB565
transparency: alpha_channel
- id: img_wx_snow7
file: "https://raw.githubusercontent.com/MichalZaniewicz/esphome-guition-jc3636k718c-va/main/assets/sprites/weather/snow7.png"
type: RGB565
transparency: alpha_channel
script:
- id: wx_render
then:
- lambda: |-
int d = id(g_wx_day);
bool has = (id(g_wx_n) > 0 && d < id(g_wx_n));
id(g_wx_sel) = has ? id(g_wx_cond)[d] : id(g_wx_state_cur);
id(g_wx_tc) = has ? id(g_wx_hi)[d] : id(g_wx_now);
- lvgl.label.update:
id: wx_day
text: !lambda |-
int d = id(g_wx_day);
if (id(g_wx_n) <= 0 || d >= id(g_wx_n)) return std::string("TODAY");
std::string s = id(g_wx_name)[d];
if (s == "Mon") return std::string("MONDAY");
if (s == "Tue") return std::string("TUESDAY");
if (s == "Wed") return std::string("WEDNESDAY");
if (s == "Thu") return std::string("THURSDAY");
if (s == "Fri") return std::string("FRIDAY");
if (s == "Sat") return std::string("SATURDAY");
if (s == "Sun") return std::string("SUNDAY");
return s;
- lvgl.label.update:
id: wx_htemp
text: !lambda |-
int d = id(g_wx_day);
bool f = id(g_wx_fahrenheit);
char u = f ? 'F' : 'C';
if (id(g_wx_n) <= 0 || d >= id(g_wx_n)) {
if (id(g_wx_now) > -100) { int n = id(g_wx_now); if (f) n = n*9/5+32; char b[12]; snprintf(b,sizeof(b),"%d°%c",n,u); return std::string(b); }
return std::string("--");
}
int hi = id(g_wx_hi)[d], lo = id(g_wx_lo)[d];
if (f) { hi = hi*9/5+32; lo = lo*9/5+32; }
char b[24]; snprintf(b,sizeof(b),"%d° / %d°",hi,lo); return std::string(b);
- lvgl.label.update:
id: wx_cond
text: !lambda |-
if (id(g_wx_n) <= 0) return std::string("Add the HA forecast helper");
std::string s = id(g_wx_sel);
if (s == "sunny") return std::string("Sunny");
if (s == "clear-night") return std::string("Clear");
if (s == "partlycloudy") return std::string("Partly Cloudy");
if (s == "cloudy") return std::string("Cloudy");
if (s == "fog") return std::string("Fog");
if (s == "rainy") return std::string("Rainy");
if (s == "pouring") return std::string("Heavy Rain");
if (s == "lightning" || s == "lightning-rainy") return std::string("Storm");
if (s == "snowy" || s == "snowy-rainy") return std::string("Snow");
if (s == "hail") return std::string("Hail");
if (s == "windy" || s == "windy-variant") return std::string("Windy");
if (s == "exceptional") return std::string("Severe");
return s;
- if:
condition: { lambda: 'return id(g_wx_sel) != id(g_wx_lastcond);' }
then:
- lambda: |-
id(g_wx_lastcond) = id(g_wx_sel);
std::string s = id(g_wx_sel), k;
if (s == "sunny") k = "sun";
else if (s == "clear-night") k = "moon";
else if (s == "partlycloudy") k = "partly";
else if (s == "rainy" || s == "pouring") k = "rain";
else if (s == "lightning" || s == "lightning-rainy") k = "storm";
else if (s == "snowy" || s == "snowy-rainy" || s == "hail") k = "snow";
else k = "cloud";
id(g_wx_ikey) = k;
- lvgl.animimg.stop: wx_a_sun
- lvgl.animimg.stop: wx_a_partly
- lvgl.animimg.stop: wx_a_moon
- lvgl.animimg.stop: wx_a_cloud
- lvgl.animimg.stop: wx_a_rain
- lvgl.animimg.stop: wx_a_storm
- lvgl.animimg.stop: wx_a_snow
- lvgl.widget.hide: wx_a_sun
- lvgl.widget.hide: wx_a_partly
- lvgl.widget.hide: wx_a_moon
- lvgl.widget.hide: wx_a_cloud
- lvgl.widget.hide: wx_a_rain
- lvgl.widget.hide: wx_a_storm
- lvgl.widget.hide: wx_a_snow
- if: { condition: { lambda: 'return id(g_wx_ikey) == "sun";' }, then: [ lvgl.widget.show: wx_a_sun, lvgl.animimg.start: wx_a_sun ] }
- if: { condition: { lambda: 'return id(g_wx_ikey) == "partly";' }, then: [ lvgl.widget.show: wx_a_partly, lvgl.animimg.start: wx_a_partly ] }
- if: { condition: { lambda: 'return id(g_wx_ikey) == "moon";' }, then: [ lvgl.widget.show: wx_a_moon, lvgl.animimg.start: wx_a_moon ] }
- if: { condition: { lambda: 'return id(g_wx_ikey) == "cloud";' }, then: [ lvgl.widget.show: wx_a_cloud, lvgl.animimg.start: wx_a_cloud ] }
- if: { condition: { lambda: 'return id(g_wx_ikey) == "rain";' }, then: [ lvgl.widget.show: wx_a_rain, lvgl.animimg.start: wx_a_rain ] }
- if: { condition: { lambda: 'return id(g_wx_ikey) == "storm";' }, then: [ lvgl.widget.show: wx_a_storm, lvgl.animimg.start: wx_a_storm ] }
- if: { condition: { lambda: 'return id(g_wx_ikey) == "snow";' }, then: [ lvgl.widget.show: wx_a_snow, lvgl.animimg.start: wx_a_snow ] }
- lvgl.widget.update: { id: wx_glow, hidden: !lambda 'return id(g_wx_n) <= 0;' }
- lvgl.widget.update: { id: wx_d0, hidden: !lambda 'return 0 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(0 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(0 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d0, text: !lambda 'return id(g_wx_n) > 0 ? id(g_wx_icon)[0] : std::string("");' }
- lvgl.widget.update: { id: wx_d1, hidden: !lambda 'return 1 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(1 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(1 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d1, text: !lambda 'return id(g_wx_n) > 1 ? id(g_wx_icon)[1] : std::string("");' }
- lvgl.widget.update: { id: wx_d2, hidden: !lambda 'return 2 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(2 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(2 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d2, text: !lambda 'return id(g_wx_n) > 2 ? id(g_wx_icon)[2] : std::string("");' }
- lvgl.widget.update: { id: wx_d3, hidden: !lambda 'return 3 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(3 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(3 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d3, text: !lambda 'return id(g_wx_n) > 3 ? id(g_wx_icon)[3] : std::string("");' }
- lvgl.widget.update: { id: wx_d4, hidden: !lambda 'return 4 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(4 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(4 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d4, text: !lambda 'return id(g_wx_n) > 4 ? id(g_wx_icon)[4] : std::string("");' }
- lvgl.widget.update: { id: wx_d5, hidden: !lambda 'return 5 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(5 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(5 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d5, text: !lambda 'return id(g_wx_n) > 5 ? id(g_wx_icon)[5] : std::string("");' }
- lvgl.widget.update: { id: wx_d6, hidden: !lambda 'return 6 >= id(g_wx_n);', x: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(140.0f * sinf(6 * 6.2831853f / n));', y: !lambda 'int n=id(g_wx_n); if(n<1)n=1; return (int) lroundf(-140.0f * cosf(6 * 6.2831853f / n));' }
- lvgl.label.update: { id: wx_d6, text: !lambda 'return id(g_wx_n) > 6 ? id(g_wx_icon)[6] : std::string("");' }
- lvgl.label.update: { id: wx_htemp, text_color: 0xFFFFFF }
- if: { condition: { lambda: 'return id(g_wx_tc) > -100 && id(g_wx_tc) <= 0;' }, then: [ lvgl.label.update: { id: wx_htemp, text_color: 0x7FB2FF } ] }
- if: { condition: { lambda: 'return id(g_wx_tc) > 0 && id(g_wx_tc) < 10;' }, then: [ lvgl.label.update: { id: wx_htemp, text_color: 0x6FD0E8 } ] }
- if: { condition: { lambda: 'return id(g_wx_tc) >= 10 && id(g_wx_tc) < 18;' }, then: [ lvgl.label.update: { id: wx_htemp, text_color: 0x7FE6B0 } ] }
- if: { condition: { lambda: 'return id(g_wx_tc) >= 18 && id(g_wx_tc) < 25;' }, then: [ lvgl.label.update: { id: wx_htemp, text_color: 0xFFD061 } ] }
- if: { condition: { lambda: 'return id(g_wx_tc) >= 25;' }, then: [ lvgl.label.update: { id: wx_htemp, text_color: 0xFF7A4D } ] }
sensor:
- platform: homeassistant
id: wx_now_temp
entity_id: ${weather_entity}
attribute: temperature
on_value:
- lambda: 'id(g_wx_now) = (int) roundf(x);'
- script.execute: wx_render
text_sensor:
- platform: homeassistant
id: wx_now_state
entity_id: ${weather_entity}
on_value:
- lambda: 'id(g_wx_state_cur) = x;'
- script.execute: wx_render
- platform: homeassistant
id: wx_week
entity_id: ${forecast_entity}
on_value:
- lambda: |-
auto glyph = [](const std::string &s) -> std::string {
if (s == "sunny") return std::string("󰖙");
if (s == "clear-night") return std::string("󰖔");
if (s == "partlycloudy") return std::string("󰖕");
if (s == "fog") return std::string("󰖑");
if (s == "windy" || s == "windy-variant") return std::string("󰖝");
if (s == "lightning" || s == "lightning-rainy") return std::string("󰖓");
if (s == "pouring") return std::string("󰖖");
if (s == "rainy") return std::string("󰖗");
if (s == "snowy" || s == "snowy-rainy" || s == "hail") return std::string("󰖘");
return std::string("󰖐");
};
std::string s = x;
for (int i = 0; i < 7; i++) { id(g_wx_icon)[i] = ""; id(g_wx_cond)[i] = ""; }
int n = 0; size_t pos = 0;
while (n < 7) {
size_t bar = s.find('|', pos);
std::string seg = (bar == std::string::npos) ? s.substr(pos) : s.substr(pos, bar - pos);
size_t c1 = seg.find(',');
if (c1 != std::string::npos) {
size_t c2 = seg.find(',', c1 + 1);
size_t c3 = (c2 != std::string::npos) ? seg.find(',', c2 + 1) : std::string::npos;
if (c2 != std::string::npos && c3 != std::string::npos) {
std::string cond = seg.substr(0, c1);
id(g_wx_cond)[n] = cond;
id(g_wx_icon)[n] = glyph(cond);
id(g_wx_hi)[n] = atoi(seg.substr(c1 + 1, c2 - c1 - 1).c_str());
id(g_wx_lo)[n] = atoi(seg.substr(c2 + 1, c3 - c2 - 1).c_str());
id(g_wx_name)[n] = seg.substr(c3 + 1);
n++;
}
}
if (bar == std::string::npos) break;
pos = bar + 1;
}
id(g_wx_n) = n;
if (id(g_wx_day) >= n) id(g_wx_day) = 0;
- script.execute: wx_render
interval:
- interval: 50ms
then:
- if:
condition: { lambda: 'return id(g_nav_req) && id(g_base)==5;' }
then:
- lambda: 'id(g_nav_req) = false; id(g_knob_capture) = true; id(g_wx_day) = 0; id(g_wx_gpos) = 0.0f; id(g_wx_lastcond) = "";'
- if: { condition: { lambda: 'return id(g_nav_anim)==0;' }, then: [lvgl.page.show: { id: page_weather, animation: move_left, time: 250ms }] }
- if: { condition: { lambda: 'return id(g_nav_anim)==1;' }, then: [lvgl.page.show: { id: page_weather, animation: move_right, time: 250ms }] }
- if: { condition: { lambda: 'return id(g_nav_anim)==2;' }, then: [lvgl.page.show: { id: page_weather, animation: move_top, time: 250ms }] }
- if: { condition: { lambda: 'return id(g_nav_anim)==3;' }, then: [lvgl.page.show: { id: page_weather, animation: move_bottom, time: 250ms }] }
- script.execute: wx_render
- interval: 120ms
then:
- if:
condition: { lambda: 'return id(g_base)==5 && id(g_knob_delta) != 0;' }
then:
- lambda: |-
id(g_wx_day) += id(g_knob_delta); id(g_knob_delta) = 0;
int mx = (id(g_wx_n) > 0) ? id(g_wx_n) - 1 : 0;
if (id(g_wx_day) < 0) id(g_wx_day) = 0;
if (id(g_wx_day) > mx) id(g_wx_day) = mx;
- script.execute: wx_render
- interval: 30ms
then:
- if:
condition: { lambda: 'return id(g_base)==5;' }
then:
- lambda: |-
float p = id(g_wx_gpos), tgt = (float) id(g_wx_day);
if (fabsf(tgt - p) > 0.001f) { // only while actually sliding
int n = id(g_wx_n); if (n < 1) n = 1;
p += (tgt - p) * 0.35f;
if (fabsf(tgt - p) < 0.02f) p = tgt;
id(g_wx_gpos) = p;
float rx = 140.0f * sinf(p * 6.2831853f / n);
float ry = -140.0f * cosf(p * 6.2831853f / n);
lv_obj_set_pos(id(wx_glow), 180 + (int) lroundf(rx) - 28, 180 + (int) lroundf(ry) - 28);
}
lvgl:
pages:
- id: page_weather
bg_color: 0x0B1020
widgets:
- obj: { id: wx_bg, align: CENTER, x: 0, y: 0, width: 360, height: 360, radius: 0, bg_color: 0x1C2838, bg_opa: COVER, border_width: 0 }
- obj: { id: wx_ring_out, align: CENTER, width: 348, height: 348, radius: 174, bg_opa: TRANSP, border_width: 2, border_color: 0x33455C, border_opa: 55%, clickable: false, scrollable: false }
- obj: { id: wx_glow, x: 152, y: 12, width: 56, height: 56, radius: 28, bg_color: 0xCAD6E8, bg_opa: 30%, border_width: 0, hidden: true }
- label: { id: wx_d0, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- label: { id: wx_d1, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- label: { id: wx_d2, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- label: { id: wx_d3, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- label: { id: wx_d4, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- label: { id: wx_d5, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- label: { id: wx_d6, align: CENTER, x: 0, y: 0, text: "", text_font: icons_wx, text_color: 0xDCE6F2, hidden: true }
- animimg: { id: wx_a_sun, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_sun0, img_wx_sun1, img_wx_sun2, img_wx_sun3, img_wx_sun4, img_wx_sun5, img_wx_sun6, img_wx_sun7 ] }
- animimg: { id: wx_a_partly, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_partly0, img_wx_partly1, img_wx_partly2, img_wx_partly3, img_wx_partly4, img_wx_partly5, img_wx_partly6, img_wx_partly7 ] }
- animimg: { id: wx_a_moon, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_moon0, img_wx_moon1, img_wx_moon2, img_wx_moon3, img_wx_moon4, img_wx_moon5, img_wx_moon6, img_wx_moon7 ] }
- animimg: { id: wx_a_cloud, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_cloud0, img_wx_cloud1, img_wx_cloud2, img_wx_cloud3, img_wx_cloud4, img_wx_cloud5, img_wx_cloud6, img_wx_cloud7 ] }
- animimg: { id: wx_a_rain, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_rain0, img_wx_rain1, img_wx_rain2, img_wx_rain3, img_wx_rain4, img_wx_rain5, img_wx_rain6, img_wx_rain7 ] }
- animimg: { id: wx_a_storm, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_storm0, img_wx_storm1, img_wx_storm2, img_wx_storm3, img_wx_storm4, img_wx_storm5, img_wx_storm6, img_wx_storm7 ] }
- animimg: { id: wx_a_snow, align: CENTER, x: 0, y: -52, duration: 1400ms, repeat_count: 65535, auto_start: false, hidden: true, src: [ img_wx_snow0, img_wx_snow1, img_wx_snow2, img_wx_snow3, img_wx_snow4, img_wx_snow5, img_wx_snow6, img_wx_snow7 ] }
- label: { id: wx_day, align: CENTER, x: 0, y: -10, text: "", text_font: font_artist, text_color: 0x9AA6B2 }
- label: { id: wx_htemp, align: CENTER, x: 0, y: 24, text: "--", text_font: montserrat_34, text_color: 0xFFFFFF }
- label: { id: wx_cond, align: CENTER, x: 0, y: 62, text: "", text_font: font_med, text_color: 0xCFE0F2 }