Compare commits

..
Author SHA1 Message Date
randy eea872b930 mcu-stackchan: spectrum analyzer, head touch gestures, status bar
- Winamp style spectrum analyzer instead of the face while music plays,
  using audio_spectrum from randybb/esphome-components
- head touch: tap starts the assistant, stroking shows an in-love face;
  SI12T sensitivity raised so the pads actually trigger
- battery (AXP2101) and wifi indicators, ear icon while listening
2026-09-27 15:19:07 +02:00
randy 39e744900d mcu-stackchan: add M5Stack StackChan config
Voice assistant satellite with animated face on the CoreS3 display:
- power rails, IO expander resets and servo power as in the M5 BSP
- mic and speaker share one I2S bus, so the mic is released before playback
- AW88298 volume limited to the audible range (0.5-1.0)
- partial display redraws, status screen until Home Assistant connects
- battery and wifi indicators, listening icon
- camera converted to JPEG on demand, no periodic frames
2026-09-27 14:02:40 +02:00
randy 6348e6bbb6 mcu-nspanel-*: migrate to native nspanel_lovelace component
CI / ESPHome config validation (push) Failing after 14s
CI / YAML lint (push) Failing after 32s
Switch all NSPanels from the MQTT + AppDaemon based
esphome-nspanel-lovelace-ui to the native component
(randybb/esphome-nspanel-lovelace-native, ref: devices).
Cards and screensaver are now configured directly in ESPHome,
removing the dependency on MQTT and AppDaemon.
2026-09-26 19:31:16 +02:00
randy 6a40e65a4b mcu-nspanel: drop local sk.json, use built-in sk translation from fork 2026-09-26 18:05:23 +02:00
randy a6605fe5e4 Add CLAUDE.md: no Claude attribution in commits/PRs 2026-09-25 23:14:35 +02:00
10 changed files with 1503 additions and 842 deletions

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
# Git commit attribution
Do not add any Claude/Anthropic attribution to git commits or pull requests in
this repository — no `Co-Authored-By: Claude ...` line, no mention of Claude
anywhere in the commit message or PR description. Commits should look like
they were written entirely by the human author.
This overrides any default Claude Code attribution instructions.
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1194" height="240" fill="none" viewBox="0 0 1194 240"><g clip-path="url(#clip0_2_63)"><path fill="#18BCF2" d="M240 219C240 227.24 233.24 234 225 234H15C6.76 234 0 227.24 0 219V129C0 120.76 4.78 109.22 10.6 103.4L109.4 4.61999C115.24 -1.22001 124.78 -1.22001 130.62 4.61999L229.4 103.4C235.24 109.24 240 120.76 240 129V219Z"/><path fill="#F2F4F9" d="M160 78.66H80C76.68 78.66 74 81.34 74 84.66V234H86V90.66H154V102.66H104C100.68 102.66 98 105.34 98 108.66V132.66C98 135.98 100.68 138.66 104 138.66H154V150.66H104C100.68 150.66 98 153.34 98 156.66V180.66C98 183.98 100.68 186.66 104 186.66H154V198.66H104C100.68 198.66 98 201.34 98 204.66C98 207.98 100.68 210.66 104 210.66H160C163.32 210.66 166 207.98 166 204.66V180.66C166 177.34 163.32 174.66 160 174.66H110V162.66H160C163.32 162.66 166 159.98 166 156.66V132.66C166 129.34 163.32 126.66 160 126.66H110V114.66H160C163.32 114.66 166 111.98 166 108.66V84.66C166 81.34 163.32 78.66 160 78.66Z"/><path fill="#F2F4F9" d="M423 167.61V190.11H336V49.95H423V72.63H359.82V108.91H416.26V130.01H359.82V167.61H423Z"/><path fill="#F2F4F9" d="M491.08 47.41C503.9 47.41 514.54 50.53 523.04 56.79C531.54 63.03 536.76 71.39 538.7 81.83L516.3 87.93C515.04 81.93 512.12 77.31 507.54 74.05C502.94 70.81 497.28 69.17 490.52 69.17C483.26 69.17 477.52 70.91 473.28 74.37C469.02 77.83 466.9 82.45 466.9 88.19C466.9 97.25 472.52 103.13 483.78 105.81L506.66 111.53C518.48 114.59 527.32 119.45 533.2 126.11C539.08 132.77 542.02 140.97 542.02 150.71C542.02 163.21 537.48 173.31 528.42 181.03C519.36 188.75 507.24 192.61 492.04 192.61C478.36 192.61 466.78 189.39 457.36 182.95C448.04 176.27 442.54 167.45 440.86 156.51L463.26 150.69C464.08 156.87 467.16 161.73 472.5 165.23C477.84 168.73 484.64 170.47 492.9 170.47C501.16 170.47 507.08 168.79 511.6 165.45C516.14 162.11 518.4 157.57 518.4 151.81C518.4 142.81 512.78 136.87 501.52 133.99L478.64 128.45C466.88 125.77 458.06 120.99 452.16 114.15C446.26 107.31 443.3 98.95 443.3 89.07C443.3 76.69 447.7 66.65 456.48 58.93C465.26 51.21 476.8 47.35 491.12 47.35L491.08 47.41Z"/><path fill="#F2F4F9" d="M666.1 95.61C666.1 109.37 661.92 120.49 653.58 128.99C645.24 137.49 634 141.75 619.88 141.75H588.38V190.13H564.56V49.95H620.06C634.3 49.95 645.54 54.07 653.76 62.27C661.98 70.49 666.08 81.59 666.08 95.59L666.1 95.61ZM641.16 95.05C641.16 87.99 638.96 82.25 634.56 77.85C630.16 73.45 623.92 71.25 615.86 71.25H588.4V120.85H615.5C623.74 120.85 630.1 118.51 634.54 113.81C638.98 109.13 641.2 102.87 641.2 95.05H641.16Z"/><path fill="#F2F4F9" d="M687.38 49.95H711.2V108.55L772.98 108.73V49.95H796.98V190.11H772.98V129.83L711.2 129.65V190.11H687.38V49.95Z"/><path fill="#F2F4F9" d="M868.6 92.79C883.1 92.79 894.92 97.45 904.04 106.75C913.16 116.07 917.72 128.03 917.72 142.65C917.72 157.27 913.16 169.15 904.04 178.47C894.92 187.79 883.1 192.43 868.6 192.43C854.1 192.43 842.1 187.77 832.98 178.47C823.86 169.15 819.3 157.23 819.3 142.65C819.3 128.07 823.86 115.97 832.98 106.69C842.1 97.41 853.98 92.77 868.6 92.77V92.79ZM868.6 172.01C876.34 172.01 882.64 169.27 887.5 163.81C892.34 158.35 894.76 151.23 894.76 142.49C894.76 133.75 892.34 126.67 887.5 121.25C882.66 115.85 876.36 113.15 868.6 113.15C860.84 113.15 854.26 115.85 849.38 121.25C844.5 126.65 842.06 133.73 842.06 142.49C842.06 151.25 844.5 158.35 849.38 163.81C854.26 169.27 860.66 172.01 868.6 172.01Z"/><path fill="#F2F4F9" d="M1081.6 129.83V190.11H1059.56V133.29C1059.56 126.73 1057.88 121.61 1054.5 117.91C1051.12 114.23 1046.54 112.37 1040.72 112.37C1034.54 112.37 1029.6 114.39 1025.9 118.41C1022.22 122.45 1020.36 128.09 1020.36 135.33V190.09H998.04V133.27C998.04 126.71 996.4 121.59 993.12 117.89C989.84 114.21 985.3 112.35 979.48 112.35C973.3 112.35 968.32 114.37 964.58 118.39C960.84 122.41 958.96 128.07 958.96 135.31V190.07H936.28V95.39H957.38L958.14 104.49C963.64 96.43 972.6 92.39 985.04 92.39C992.28 92.39 998.58 93.89 1003.88 96.89C1009.2 99.89 1013.22 104.27 1015.98 110.01C1018.3 104.51 1022.12 100.19 1027.46 97.07C1032.8 93.95 1039.16 92.39 1046.54 92.39C1057.28 92.39 1065.82 95.71 1072.14 102.33C1078.46 108.95 1081.6 118.11 1081.6 129.79V129.83Z"/><path fill="#F2F4F9" d="M1193.54 151.11H1120.98C1121.74 158.23 1124.42 163.67 1129.04 167.43C1133.66 171.19 1139.54 173.05 1146.66 173.05C1158.22 173.05 1166.32 168.23 1170.94 158.61L1189.88 166.11C1186.32 174.43 1180.7 180.93 1173.06 185.61C1165.4 190.29 1156.6 192.65 1146.66 192.65C1132.66 192.65 1121.3 188.07 1112.58 178.91C1103.86 169.75 1099.5 157.73 1099.5 142.87C1099.5 128.01 1103.9 115.91 1112.68 106.59C1121.46 97.27 1132.94 92.63 1147.14 92.63C1161.34 92.63 1172.3 97.23 1180.8 106.41C1189.3 115.59 1193.56 127.69 1193.56 142.69V151.13L1193.54 151.11ZM1121.36 135.17H1170.48C1170.04 127.49 1167.72 121.63 1163.5 117.59C1159.28 113.55 1153.6 111.55 1146.48 111.55C1139.36 111.55 1133.66 113.59 1129.24 117.69C1124.8 121.79 1122.18 127.61 1121.36 135.17Z"/></g><defs><clipPath id="clip0_2_63"><rect width="1193.56" height="240" fill="#fff"/></cliLine truncated

After

Width:  |  Height:  |  Size: 4.9 KiB

+98 -123
View File
@@ -5,10 +5,6 @@ substitutions:
name_short: Bedroom
area: Bedroom
comment: NSPanel
panel_recv_topic: "tele/${device}/RESULT"
panel_send_topic: "cmnd/${device}/CustomSend"
tft_url: !secret tft_url
esphome:
name: mcu-${device}
@@ -20,6 +16,8 @@ esphome:
- switch.turn_off: screen_power
- delay: 1s
- switch.turn_on: screen_power
- delay: 2s
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
esp32:
variant: esp32
@@ -33,15 +31,27 @@ esp32:
external_components:
- source:
type: git
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
ref: dev
url: https://github.com/randybb/esphome-nspanel-lovelace-native
ref: devices
refresh: 0s
components: [nspanel_lovelace]
packages:
common: !include common/common.yaml
script:
- id: apply_sleep_dim
then:
- lambda: |-
uint8_t dim;
if (id(bedroom_lights).state) dim = 5; // sleepOverride
else if (id(sun_id).is_above_horizon(-6)) dim = 5; // day
else dim = 0; // night
id(nspanel_id).set_display_inactive_dim(dim);
api:
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
homeassistant_services: true
homeassistant_states: true
actions:
- action: play_rtttl
variables:
@@ -56,110 +66,20 @@ api:
message: string
song: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
- rtttl.play:
rtttl: !lambda 'return song;'
- delay: 10s
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
- action: update_tft
then:
- lambda: |-
id(nspanel_id).upload_tft("${tft_url}");
- action: upload_tft
variables:
url: string
then:
- lambda: |-
id(nspanel_id).upload_tft(url);
# Service to send a command directly to the display. Useful for testing
- action: send_command
variables:
cmd: string
then:
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
# Service to pusblish to mqtt used for refreshin the panel
- action: publish_to_recv_topic
variables:
cmd: string
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: !lambda "return cmd;"
# Service to send a command wake the screen
- action: wake
then:
- lambda: 'id(nspanel_id).send_custom_command("wake");'
# Service to send a command to show screensaver (some of the values are hardcoded)
- action: show_screensaver
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
# Service to navigate to screen
- action: navigate_to_page
variables:
page: string
then:
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
# Service to send a command disable screensaver
- action: disable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
# Service to send a command enabled screensaver
- action: enable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
# Service to send a command wake the screen
- action: dim_0_to_100
variables:
intensity: string
then:
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
# Service to send a command To show the screen of a specific like
- action: show_entity
variables:
entity: string
title: string
then:
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
# Service to send a command To show the screen of a specific like
- action: notify_on_screensaver
variables:
line1: string
line2: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
- rtttl.play: "short:d=4,o=5,b=100:16e6"
# Service to send a command To show the screen of a specific like
# interaction of the buttons are on implemented yet
- action: notify_fullscreen
variables:
title: string
description: string
button1: string
button2: string
time_secs: string
timeout: int
icon: string
then:
# show notification screen
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
# set values on notification screen
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
uart:
@@ -168,24 +88,59 @@ uart:
rx_pin: 17
baud_rate: 115200
mqtt:
id: mqtt_client
broker: !secret mqtt_ip
username: !secret mqtt_username
password: !secret mqtt_password
discovery: false
idf_send_async: true
reboot_timeout: 0s
nspanel_lovelace:
id: nspanel_id
mqtt_recv_topic: $panel_recv_topic
mqtt_send_topic: $panel_send_topic
sleep_timeout: 10
experimental_vertical_ui: true
locale:
language: sk
temperature_unit: celcius
screensaver:
time_id: ha_time
time_format: '%H:%M'
date_format: '%A, %d. %b. %Y'
weather:
entity_id: sensor.weather_forecast_daily
icon_color: [255, 255, 255]
entities:
- entity_id: sensor.zigbee_bedroom_temperature
icon:
value: home-thermometer-outline
color: [255, 255, 255]
precision: 1
cards:
- type: cardGrid
title: Spálňa
entities:
- name: TV
entity_id: switch.mcu_socket_01_relay
icon: television-classic
- name: Raid
entity_id: switch.mcu_socket_02_relay
icon: emoticon-dead
- name: ""
entity_id: switch.mcu_socket_03_relay
icon: power-socket-fr
- name: Svetlo
entity_id: light.mcu_nspanel_bedroom_light
icon: wall-sconce-flat
- name: Nočná lampa J
entity_id: light.mcu_bedroom_lamp_right_light
icon: lamp
- name: Nočná lampa R
entity_id: light.mcu_bedroom_lamp_left_light
icon: lamp
- type: cardThermo
title: Kúrenie
thermo_entity_id: climate.bt_trv_bedroom
time:
- platform: homeassistant
id: ha_time
timezone: Europe/Bratislava
on_time_sync:
- script.execute: apply_sleep_dim
binary_sensor:
- platform: gpio
@@ -195,10 +150,7 @@ binary_sensor:
inverted: true
on_click:
- light.toggle: light_1
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- platform: gpio
name: Right Button
@@ -212,10 +164,13 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.bedroom_lamps
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- platform: homeassistant # sleepOverride
id: bedroom_lights
entity_id: light.bedroom_lights
on_state:
- script.execute: apply_sleep_dim
light:
- platform: binary
@@ -237,6 +192,20 @@ output:
# pin:
# number: 19
sun:
id: sun_id
time_id: ha_time
latitude: 48.15°
longitude: 17.11°
on_sunrise:
- elevation: -6°
then:
- script.execute: apply_sleep_dim
on_sunset:
- elevation: -6°
then:
- script.execute: apply_sleep_dim
switch:
- platform: gpio
name: Screen Power
@@ -265,13 +234,19 @@ sensor:
resistor: 11.2kOhm
- platform: ntc
name: Temperature
id: temperature
sensor: resistance_sensor
calibration:
b_constant: 3950
reference_temperature: 25°C
reference_resistance: 10kOhm
name: Temperature
- platform: template
id: room_temperature_id
text_sensor:
- platform: homeassistant
id: name_day
entity_id: sensor.nameday_sk
on_value:
- lambda: |-
id(nspanel_id).set_time_format("%H:%M~" + x);
id(nspanel_id).update_time();
+54 -31
View File
@@ -39,8 +39,6 @@ esp32:
advanced:
minimum_chip_revision: 3.0
sram1_as_iram: true
include_builtin_idf_components:
- esp_http_client
external_components:
# - source:
@@ -51,8 +49,10 @@ external_components:
# components: [nspanel_lovelace]
- source:
type: git
url: https://github.com/olicooper/esphome-nspanel-lovelace-native
ref: dev
# url: https://github.com/olicooper/esphome-nspanel-lovelace-native
# ref: dev
url: https://github.com/randybb/esphome-nspanel-lovelace-native
ref: devices
refresh: 0s
components: [nspanel_lovelace]
# - source: components
@@ -84,7 +84,6 @@ api:
song: string
then:
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
# - lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
- rtttl.play:
rtttl: !lambda 'return song;'
# - delay: 10s
@@ -203,15 +202,37 @@ uart:
nspanel_lovelace:
id: nspanel_id
sleep_timeout: 10
experimental_vertical_ui: true
locale:
language: nspanel/sk.json
language: sk
temperature_unit: celcius
screensaver:
time_id: ha_time
time_format: '%H:%M~$name_day'
time_format: '%H:%M'
date_format: '%A, %d. %b. %Y'
weather:
entity_id: sensor.weather_forecast_daily
# entity_id: weather.openweathermap
# forecast_method: service
icon_color: [255, 255, 255]
entities:
- entity_id: sensor.zigbee_office_r_temperature
icon:
value: home-thermometer-outline
color: [255, 255, 255]
precision: 1
# type: screensaver2
## Custom entities, filled into the layout slots in order after the weather items.
## Use 'entity_id: delete' to leave a slot empty.
## screensaver: weather main + 4 forecast items, 1 extra entity switches to the alternative layout (max 6 items)
## screensaver2: weather main only, then 3 left + 6 bottom + 5 indicator icons (max 15 items)
# entities:
# - entity_id: sensor.outside_humidity
# name: Humidity
# icon: water-percent
# - entity_id: delete
# - entity_id: binary_sensor.front_door
# type: screensaver2
# statusIcon1:
# entity: switch.office_r_light
@@ -281,7 +302,7 @@ nspanel_lovelace:
entity_id: light.living_room
icon: ceiling-light
- name: TV
entity_id: light.tv_light
entity_id: light.mcu_tv_light_light
icon: led-strip-variant
- name: (Stôl)
entity_id: light.dining_table_ambilight
@@ -293,7 +314,7 @@ nspanel_lovelace:
title: Romanko
entities:
- name: Svetlo
entity_id: switch.office_r_light
entity_id: light.mcu_nspanel_office_r_light
icon: wall-sconce-flat
- name: LED pásik
entity_id: light.office_r_desk_strip
@@ -305,40 +326,38 @@ nspanel_lovelace:
- name: (Monitor)
entity_id: light.office_r_display_lamp_ambilight
icon: monitor-star
- name: Whitebox
entity_id: switch.whitebox
- type: cardGrid
title: Von
entities:
- name: Terasa
entity_id: light.patio_light
entity_id: light.patio_lights
icon: lightbulb-group
- name: Terasa
entity_id: switch.patio_relay
entity_id: switch.mcu_patio_relay
icon: power-socket-fr
- name: Vzadu
entity_id: switch.outside_light_back
entity_id: switch.mcu_nspanel_patio_outside_light_back
icon: light-flood-down
- name: Z. domček
entity_id: switch.shed_light
entity_id: switch.mcu_shed_light
icon: wall-sconce-flat
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- name: Z. domček
entity_id: switch.shed_socket
entity_id: switch.mcu_shed_socket
icon: power-socket-fr
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- name: Záhrada
entity_id: switch.garden_light
entity_id: switch.mcu_shed_garden_light
icon: light-flood-down
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- type: cardGrid
title: Brána
entities:
- name: Otvoriť
entity_id: button.gate_open
entity_id: button.mcu_gate_open
icon: gate-open
- name: Zatvoriť
entity_id: button.gate_close
entity_id: button.mcu_gate_close
icon: gate
- name: Automatizácia
entity_id: navigate.card_gate_automation
@@ -346,19 +365,19 @@ nspanel_lovelace:
title: Závlaha
entities:
- name: A1
entity_id: switch.irrigation_valve_a1
entity_id: switch.mcu_well_irrigation_valve_a1
icon: valve
- name: A2
entity_id: switch.irrigation_valve_a2
entity_id: switch.mcu_well_irrigation_valve_a2
icon: valve
- name: A3
entity_id: switch.irrigation_valve_a3
entity_id: switch.mcu_well_irrigation_valve_a3
icon: valve
- name: A4
entity_id: switch.irrigation_valve_a4
entity_id: switch.mcu_well_irrigation_valve_a4
icon: valve
- name: A5
entity_id: switch.irrigation_valve_a5
entity_id: switch.mcu_well_irrigation_valve_a5
icon: valve
- name: Automatizácia
entity_id: navigate.card_irrigation_automation
@@ -381,13 +400,13 @@ nspanel_lovelace:
title: Zwift
entities:
- name: Svetlo
entity_id: light.basement_light
entity_id: light.mcu_basement_light
icon: lightbulb-multiple
- name: LED
entity_id: light.exercise_room
icon: led-strip-variant
- name: Ventilátor
entity_id: light.exercise_room_fan
entity_id: light.mcu_dimmer_exercise_room_fan
icon: fan
- name: Automatizácia
entity_id: navigate.card_exercise_automation
@@ -421,11 +440,6 @@ nspanel_lovelace:
- name: Rast trávy
entity_id: automation.irrigation_grow_grass
time:
- platform: homeassistant
id: ha_time
timezone: Europe/Bratislava
binary_sensor:
- platform: gpio
name: Left Button
@@ -526,10 +540,19 @@ sensor:
- platform: template
id: room_temperature_id
time:
platform: homeassistant
id: ha_time
timezone: Europe/Bratislava
text_sensor:
- platform: homeassistant
id: name_day
entity_id: sensor.nameday_sk
on_value:
- lambda: |-
id(nspanel_id).set_time_format("%H:%M~" + x);
id(nspanel_id).update_time();
# esp32_ble_tracker:
# scan_parameters:
+131 -130
View File
@@ -4,9 +4,6 @@ substitutions:
name: Fireplace NSPanel
area: Living Room & Kitchen
comment: NSPanel
panel_recv_topic: "tele/${device}/RESULT"
panel_send_topic: "cmnd/${device}/CustomSend"
tft_url: !secret tft_url
esphome:
name: mcu-${device}
@@ -18,6 +15,8 @@ esphome:
- switch.turn_off: screen_power
- delay: 1s
- switch.turn_on: screen_power
- delay: 2s
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
esp32:
variant: esp32
@@ -31,15 +30,17 @@ esp32:
external_components:
- source:
type: git
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
ref: dev
url: https://github.com/randybb/esphome-nspanel-lovelace-native
ref: devices
refresh: 0s
components: [nspanel_lovelace]
packages:
common: !include common/common.yaml
api:
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
homeassistant_services: true
homeassistant_states: true
actions:
- action: play_rtttl
variables:
@@ -54,110 +55,20 @@ api:
message: string
song: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
- rtttl.play:
rtttl: !lambda 'return song;'
- delay: 10s
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
- action: update_tft
then:
- lambda: |-
id(nspanel_id).upload_tft("${tft_url}");
- action: upload_tft
variables:
url: string
then:
- lambda: |-
id(nspanel_id).upload_tft(url);
# Service to send a command directly to the display. Useful for testing
- action: send_command
variables:
cmd: string
then:
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
# Service to pusblish to mqtt used for refreshin the panel
- action: publish_to_recv_topic
variables:
cmd: string
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: !lambda "return cmd;"
# Service to send a command wake the screen
- action: wake
then:
- lambda: 'id(nspanel_id).send_custom_command("wake");'
# Service to send a command to show screensaver (some of the values are hardcoded)
- action: show_screensaver
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
# Service to navigate to screen
- action: navigate_to_page
variables:
page: string
then:
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
# Service to send a command disable screensaver
- action: disable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
# Service to send a command enabled screensaver
- action: enable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
# Service to send a command wake the screen
- action: dim_0_to_100
variables:
intensity: string
then:
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
# Service to send a command To show the screen of a specific like
- action: show_entity
variables:
entity: string
title: string
then:
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
# Service to send a command To show the screen of a specific like
- action: notify_on_screensaver
variables:
line1: string
line2: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
- rtttl.play: "short:d=4,o=5,b=100:16e6"
# Service to send a command To show the screen of a specific like
# interaction of the buttons are on implemented yet
- action: notify_fullscreen
variables:
title: string
description: string
button1: string
button2: string
time_secs: string
timeout: int
icon: string
then:
# show notification screen
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
# set values on notification screen
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
uart:
@@ -166,19 +77,113 @@ uart:
rx_pin: 17
baud_rate: 115200
mqtt:
id: mqtt_client
broker: !secret mqtt_ip
username: !secret mqtt_username
password: !secret mqtt_password
discovery: false
idf_send_async: true
reboot_timeout: 0s
nspanel_lovelace:
id: nspanel_id
mqtt_recv_topic: $panel_recv_topic
mqtt_send_topic: $panel_send_topic
sleep_timeout: 10
experimental_vertical_ui: true
locale:
language: sk
temperature_unit: celcius
screensaver:
time_id: ha_time
time_format: '%H:%M'
date_format: '%A, %d. %b. %Y'
weather:
entity_id: sensor.weather_forecast_daily
icon_color: [255, 255, 255]
entities:
- entity_id: sensor.zigbee_living_room_kitchen_temperature
icon:
value: home-thermometer-outline
color: [255, 255, 255]
precision: 1
cards:
- type: cardGrid
title: Kuchyňa / Obývačka
entities:
- name: Kuchyňa / Obývačka
entity_id: light.kitchen
icon: ceiling-light
- name: Stôl
entity_id: light.dining_table
icon: ceiling-light
- name: Gauč
entity_id: light.living_room
icon: ceiling-light
- name: TV
entity_id: light.mcu_tv_light_strip
icon: led-strip-variant
- name: (Stôl)
entity_id: light.dining_table_ambilight
icon: ceiling-light-multiple-outline
- name: (Gauč)
entity_id: light.living_room_ambilight
icon: ceiling-light-multiple-outline
- type: cardGrid
title: Kuchyňa
entities:
- name: Linka
entity_id: light.worktop_lights
icon: countertop
- name: Sporák
entity_id: light.mcu_kitchen_stove
icon: stove
- type: cardGrid
title: Von
entities:
- name: Terasa
entity_id: light.patio_lights
icon: lightbulb-group
- name: Terasa
entity_id: switch.mcu_patio_relay
icon: power-socket-fr
- name: Vzadu
entity_id: switch.mcu_nspanel_patio_outside_light_back
icon: light-flood-down
- name: Z. domček
entity_id: switch.mcu_shed_light
icon: wall-sconce-flat
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- name: Z. domček
entity_id: switch.mcu_shed_socket
icon: power-socket-fr
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- name: Záhrada
entity_id: switch.mcu_shed_garden_light
icon: light-flood-down
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- type: cardGrid
title: Brána
entities:
- name: Otvoriť
entity_id: button.mcu_gate_open
icon: gate-open
- name: Zatvoriť
entity_id: button.mcu_gate_close
icon: gate
- name: Automatizácia
entity_id: navigate.card_gate_automation
- type: cardThermo
title: Kúrenie
thermo_entity_id: climate.bt_trv_living_room_kitchen
- type: cardThermo
title: Kúrenie - Izba
thermo_entity_id: climate.bt_trv_room
- type: cardEntities
title: Automatizácia
id: card_gate_automation
hidden: true
entities:
- name: Auto-otváranie
entity_id: switch.gate_auto_open
- name: Auto-zatváranie
entity_id: switch.gate_auto_close
time:
- platform: homeassistant
@@ -200,10 +205,7 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.living_room
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- min_length: 500ms
max_length: 2000ms
then:
@@ -211,10 +213,8 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.worktop_lights
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_kitchen,button";
- lambda: 'id(nspanel_id).render_default_page();'
- lambda: 'id(nspanel_id).render_next_page();'
- platform: gpio
name: Right Button
@@ -230,10 +230,7 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.dining_table
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- min_length: 500ms
max_length: 2000ms
then:
@@ -241,10 +238,8 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.mcu_kitchen_stove
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_kitchen,button";
- lambda: 'id(nspanel_id).render_default_page();'
- lambda: 'id(nspanel_id).render_next_page();'
output:
- platform: ledc
id: buzzer_out
@@ -292,13 +287,19 @@ sensor:
resistor: 11.2kOhm
- platform: ntc
name: "Living Room Temperature"
id: temperature
sensor: resistance_sensor
calibration:
b_constant: 3950
reference_temperature: 25°C
reference_resistance: 10kOhm
name: "Living Room Temperature"
- platform: template
id: room_temperature_id
text_sensor:
- platform: homeassistant
id: name_day
entity_id: sensor.nameday_sk
on_value:
- lambda: |-
id(nspanel_id).set_time_format("%H:%M~" + x);
id(nspanel_id).update_time();
+79 -157
View File
@@ -4,10 +4,6 @@ substitutions:
name: Office J NSPanel
area: Office J
comment: NSPanel
panel_recv_topic: "tele/${device}/RESULT"
panel_send_topic: "cmnd/${device}/CustomSend"
tft_url: !secret tft_url
esphome:
name: mcu-${device}
@@ -19,6 +15,8 @@ esphome:
- switch.turn_off: screen_power
- delay: 1s
- switch.turn_on: screen_power
- delay: 2s
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
esp32:
variant: esp32
@@ -32,20 +30,17 @@ esp32:
external_components:
- source:
type: git
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
ref: dev
url: https://github.com/randybb/esphome-nspanel-lovelace-native
ref: devices
refresh: 0s
components: [nspanel_lovelace]
packages:
common: !include common/common.yaml
api:
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
#
# stupnovane:d=4,o=6,b=180:c,e,g
# help:d=4,o=6,b=100:f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f
# gate_opening:d=16,o=5,b=160:a, 8p, a
# fireplace_attention:d=16,o=5,b=160:a, 8p, g, 16p, c
homeassistant_services: true
homeassistant_states: true
actions:
- action: play_rtttl
variables:
@@ -60,110 +55,20 @@ api:
message: string
song: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
- rtttl.play:
rtttl: !lambda 'return song;'
- delay: 10s
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
- action: update_tft
then:
- lambda: |-
id(nspanel_id).upload_tft("${tft_url}");
- action: upload_tft
variables:
url: string
then:
- lambda: |-
id(nspanel_id).upload_tft(url);
# Service to send a command directly to the display. Useful for testing
- action: send_command
variables:
cmd: string
then:
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
# Service to pusblish to mqtt used for refreshin the panel
- action: publish_to_recv_topic
variables:
cmd: string
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: !lambda "return cmd;"
# Service to send a command wake the screen
- action: wake
then:
- lambda: 'id(nspanel_id).send_custom_command("wake");'
# Service to send a command to show screensaver (some of the values are hardcoded)
- action: show_screensaver
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
# Service to navigate to screen
- action: navigate_to_page
variables:
page: string
then:
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
# Service to send a command disable screensaver
- action: disable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
# Service to send a command enabled screensaver
- action: enable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
# Service to send a command wake the screen
- action: dim_0_to_100
variables:
intensity: string
then:
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
# Service to send a command To show the screen of a specific like
- action: show_entity
variables:
entity: string
title: string
then:
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
# Service to send a command To show the screen of a specific like
- action: notify_on_screensaver
variables:
line1: string
line2: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
- rtttl.play: "short:d=4,o=5,b=100:16e6"
# Service to send a command To show the screen of a specific like
# interaction of the buttons are on implemented yet
- action: notify_fullscreen
variables:
title: string
description: string
button1: string
button2: string
time_secs: string
timeout: int
icon: string
then:
# show notification screen
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
# set values on notification screen
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
uart:
@@ -172,19 +77,62 @@ uart:
rx_pin: 17
baud_rate: 115200
mqtt:
id: mqtt_client
broker: !secret mqtt_ip
username: !secret mqtt_username
password: !secret mqtt_password
discovery: false
idf_send_async: true
reboot_timeout: 0s
nspanel_lovelace:
id: nspanel_id
mqtt_recv_topic: $panel_recv_topic
mqtt_send_topic: $panel_send_topic
sleep_timeout: 10
experimental_vertical_ui: true
locale:
language: sk
temperature_unit: celcius
screensaver:
time_id: ha_time
time_format: '%H:%M'
date_format: '%A, %d. %b. %Y'
weather:
entity_id: sensor.weather_forecast_daily
icon_color: [255, 255, 255]
entities:
- entity_id: sensor.zigbee_office_j_temperature
icon:
value: home-thermometer-outline
color: [255, 255, 255]
precision: 1
cards:
- type: cardGrid
title: Janko
entities:
- name: Svetlo
entity_id: light.mcu_nspanel_office_j_light
icon: wall-sconce-flat
- name: Nočná lampa
entity_id: light.bedside_left
icon: lamp
- type: cardGrid
title: Brána
entities:
- name: Otvoriť
entity_id: button.mcu_gate_open
icon: gate-open
- name: Zatvoriť
entity_id: button.mcu_gate_close
icon: gate
- name: Automatizácia
entity_id: navigate.card_gate_automation
- type: cardThermo
title: Kúrenie
thermo_entity_id: climate.bt_trv_office_j
- type: cardEntities
title: Automatizácia
id: card_gate_automation
hidden: true
entities:
- name: Auto-otváranie
entity_id: switch.gate_auto_open
- name: Auto-zatváranie
entity_id: switch.gate_auto_close
time:
- platform: homeassistant
@@ -192,26 +140,6 @@ time:
timezone: Europe/Bratislava
binary_sensor:
# - platform: gpio
# name: Left Button
# pin:
# number: 14
# inverted: true
# on_click:
# - switch.toggle: relay_1
# - platform: gpio
# name: Right Button
# pin:
# number: 27
# inverted: true
# # on_click:
# # - switch.toggle: relay_2
# on_click:
# - homeassistant.service:
# service: light.toggle
# data:
# entity_id: light.office_r_desk_strip
- platform: gpio
name: Left Button
pin:
@@ -219,10 +147,7 @@ binary_sensor:
inverted: true
on_click:
- light.toggle: light_1
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- platform: gpio
name: Right Button
@@ -234,16 +159,14 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.bedside_left
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
output:
- platform: ledc
id: buzzer_out
pin:
number: 21
- id: relay_1
platform: gpio
pin:
@@ -283,28 +206,27 @@ sensor:
pin: 38
update_interval: 5min
attenuation: 12db
- platform: resistance
id: resistance_sensor
sensor: ntc_source
configuration: DOWNSTREAM
resistor: 11.2kOhm
- platform: ntc
name: Temperature
id: temperature
sensor: resistance_sensor
calibration:
b_constant: 3950
reference_temperature: 22°C
reference_resistance: 10kOhm
name: Temperature
- platform: template
id: room_temperature_id
# esp32_ble_tracker:
# text_sensor:
# - platform: ble_scanner
# name: BLE Scanner
# esp32_ble_beacon:
# type: iBeacon
# uuid: '6d68252f-2e6f-b5b9-965f-43c0a3713880'
text_sensor:
- platform: homeassistant
id: name_day
entity_id: sensor.nameday_sk
on_value:
- lambda: |-
id(nspanel_id).set_time_format("%H:%M~" + x);
id(nspanel_id).update_time();
+86 -171
View File
@@ -4,10 +4,6 @@ substitutions:
name: Office R NSPanel
area: Office R
comment: NSPanel
panel_recv_topic: "tele/${device}/RESULT"
panel_send_topic: "cmnd/${device}/CustomSend"
tft_url: !secret tft_url
esphome:
name: mcu-${device}
@@ -19,6 +15,8 @@ esphome:
- switch.turn_off: screen_power
- delay: 1s
- switch.turn_on: screen_power
- delay: 2s
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
esp32:
variant: esp32
@@ -26,15 +24,15 @@ esp32:
framework:
type: esp-idf
advanced:
enable_lwip_mdns_queries: true
minimum_chip_revision: 3.0
sram1_as_iram: true
external_components:
- source:
type: git
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
ref: dev
url: https://github.com/randybb/esphome-nspanel-lovelace-native
ref: devices
refresh: 0s
components: [nspanel_lovelace]
packages:
@@ -58,12 +56,8 @@ script:
- script.execute: geiger_click
api:
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
#
# stupnovane:d=4,o=6,b=180:c,e,g
# help:d=4,o=6,b=100:f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f,8p,f
# gate_opening:d=16,o=5,b=160:a, 8p, a
# fireplace_attention:d=16,o=5,b=160:a, 8p, g, 16p, c
homeassistant_services: true
homeassistant_states: true
actions:
- action: play_rtttl
variables:
@@ -78,111 +72,20 @@ api:
message: string
song: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
- rtttl.play:
rtttl: !lambda 'return song;'
- delay: 10s
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
- action: update_tft
then:
- logger.log: "tft updating from: ${tft_url}"
- lambda: |-
id(nspanel_id).upload_tft("${tft_url}");
- action: upload_tft
variables:
url: string
then:
- lambda: |-
id(nspanel_id).upload_tft(url);
# Service to send a command directly to the display. Useful for testing
- action: send_command
variables:
cmd: string
then:
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
# Service to pusblish to mqtt used for refreshin the panel
- action: publish_to_recv_topic
variables:
cmd: string
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: !lambda "return cmd;"
# Service to send a command wake the screen
- action: wake
then:
- lambda: 'id(nspanel_id).send_custom_command("wake");'
# Service to send a command to show screensaver (some of the values are hardcoded)
- action: show_screensaver
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
# Service to navigate to screen
- action: navigate_to_page
variables:
page: string
then:
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
# Service to send a command disable screensaver
- action: disable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
# Service to send a command enabled screensaver
- action: enable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
# Service to send a command wake the screen
- action: dim_0_to_100
variables:
intensity: string
then:
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
# Service to send a command To show the screen of a specific like
- action: show_entity
variables:
entity: string
title: string
then:
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
# Service to send a command To show the screen of a specific like
- action: notify_on_screensaver
variables:
line1: string
line2: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
- rtttl.play: "short:d=4,o=5,b=100:16e6"
# Service to send a command To show the screen of a specific like
# interaction of the buttons are on implemented yet
- action: notify_fullscreen
variables:
title: string
description: string
button1: string
button2: string
time_secs: string
timeout: int
icon: string
then:
# show notification screen
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
# set values on notification screen
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
uart:
@@ -191,19 +94,69 @@ uart:
rx_pin: 17
baud_rate: 115200
mqtt:
id: mqtt_client
broker: !secret mqtt_ip
username: !secret mqtt_username
password: !secret mqtt_password
discovery: false
idf_send_async: true
reboot_timeout: 0s
nspanel_lovelace:
id: nspanel_id
mqtt_recv_topic: $panel_recv_topic
mqtt_send_topic: $panel_send_topic
sleep_timeout: 10
experimental_vertical_ui: true
locale:
language: sk
temperature_unit: celcius
screensaver:
time_id: ha_time
time_format: '%H:%M'
date_format: '%A, %d. %b. %Y'
weather:
entity_id: sensor.weather_forecast_daily
icon_color: [255, 255, 255]
entities:
- entity_id: sensor.zigbee_office_r_temperature
icon:
value: home-thermometer-outline
color: [255, 255, 255]
precision: 1
cards:
- type: cardGrid
title: Romanko
entities:
- name: Svetlo
entity_id: light.mcu_nspanel_office_r_light
icon: wall-sconce-flat
- name: LED pásik
entity_id: light.office_r_desk_strip
icon: led-strip-variant
- entity_id: delete
- name: Monitor
entity_id: light.office_r_display_lamp
icon: monitor-star
- name: (Monitor)
entity_id: light.office_r_display_lamp_ambilight
icon: monitor-star
- type: cardGrid
title: Brána
entities:
- name: Otvoriť
entity_id: button.mcu_gate_open
icon: gate-open
- name: Zatvoriť
entity_id: button.mcu_gate_close
icon: gate
- name: Automatizácia
entity_id: navigate.card_gate_automation
- type: cardThermo
title: Kúrenie
thermo_entity_id: climate.bt_trv_office_r
- type: cardEntities
title: Automatizácia
id: card_gate_automation
hidden: true
entities:
- name: Auto-otváranie
entity_id: switch.gate_auto_open
- name: Auto-zatváranie
entity_id: switch.gate_auto_close
time:
- platform: homeassistant
@@ -211,26 +164,6 @@ time:
timezone: Europe/Bratislava
binary_sensor:
# - platform: gpio
# name: Left Button
# pin:
# number: 14
# inverted: true
# on_click:
# - switch.toggle: relay_1
# - platform: gpio
# name: Right Button
# pin:
# number: 27
# inverted: true
# # on_click:
# # - switch.toggle: relay_2
# on_click:
# - homeassistant.service:
# service: light.toggle
# data:
# entity_id: light.office_r_desk_strip
- platform: gpio
name: Left Button
pin:
@@ -241,12 +174,8 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.office_r_desk_strip
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress1,navigate.cardGrid_main,button";
# - lambda: 'id(nspanel_id).send_custom_command("event,buttonPress1,navigate.cardGrid_main,button");'
- lambda: 'id(nspanel_id).render_default_page();'
- platform: gpio
name: Right Button
pin:
@@ -254,11 +183,7 @@ binary_sensor:
inverted: true
on_click:
- light.toggle: light_1
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
# - lambda: 'id(nspanel_id).send_custom_command("event,buttonPress2,navigate.cardGrid_main,button");'
- lambda: 'id(nspanel_id).render_default_page();'
output:
- platform: ledc
@@ -281,7 +206,6 @@ light:
channel_colors: GRB
pin: 23
num_leds: 3
# rmt_channel: 0
chipset: SK6812
effects:
- pulse:
@@ -298,16 +222,8 @@ light:
id: light_1
output: relay_1
restore_mode: RESTORE_DEFAULT_OFF
# # - platform: binary
# # name: Relay 2
# # output: relay_2
switch:
# - platform: gpio
# name: Light
# id: relay_1
# pin:
# number: 22
- platform: gpio
# name: Relay 2
id: relay_2
@@ -339,28 +255,27 @@ sensor:
pin: 38
update_interval: 5min
attenuation: 12db
- platform: resistance
id: resistance_sensor
sensor: ntc_source
configuration: DOWNSTREAM
resistor: 11.2kOhm
- platform: ntc
name: Temperature
id: temperature
sensor: resistance_sensor
calibration:
b_constant: 3950
reference_temperature: 22°C
reference_resistance: 10kOhm
name: Temperature
- platform: template
id: room_temperature_id
# esp32_ble_tracker:
# text_sensor:
# - platform: ble_scanner
# name: BLE Scanner
# esp32_ble_beacon:
# type: iBeacon
# uuid: '6d68252f-2e6f-b5b9-965f-43c0a3713880'
text_sensor:
- platform: homeassistant
id: name_day
entity_id: sensor.nameday_sk
on_value:
- lambda: |-
id(nspanel_id).set_time_format("%H:%M~" + x);
id(nspanel_id).update_time();
+160 -122
View File
@@ -4,9 +4,6 @@ substitutions:
name: Patio NSPanel
area: Living Room & Kitchen
comment: ${area}, Patio | NSPanel
panel_recv_topic: "tele/${device}/RESULT"
panel_send_topic: "cmnd/${device}/CustomSend"
tft_url: !secret tft_url
esphome:
name: mcu-${device}
@@ -18,6 +15,8 @@ esphome:
- switch.turn_off: screen_power
- delay: 1s
- switch.turn_on: screen_power
- delay: 2s
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
esp32:
variant: esp32
@@ -31,15 +30,17 @@ esp32:
external_components:
- source:
type: git
url: https://github.com/sairon/esphome-nspanel-lovelace-ui
ref: dev
url: https://github.com/randybb/esphome-nspanel-lovelace-native
ref: devices
refresh: 0s
components: [nspanel_lovelace]
packages:
common: !include common/common.yaml
api:
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
homeassistant_services: true
homeassistant_states: true
actions:
- action: play_rtttl
variables:
@@ -54,110 +55,20 @@ api:
message: string
song: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + heading + "~" + message);'
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
- rtttl.play:
rtttl: !lambda 'return song;'
- delay: 10s
- lambda: 'id(nspanel_id).send_custom_command("notify~~");'
- action: update_tft
then:
- lambda: |-
id(nspanel_id).upload_tft("${tft_url}");
- action: upload_tft
variables:
url: string
then:
- lambda: |-
id(nspanel_id).upload_tft(url);
# Service to send a command directly to the display. Useful for testing
- action: send_command
variables:
cmd: string
then:
- lambda: "id(nspanel_id).send_custom_command(cmd.c_str());"
# Service to pusblish to mqtt used for refreshin the panel
- action: publish_to_recv_topic
variables:
cmd: string
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: !lambda "return cmd;"
# Service to send a command wake the screen
- action: wake
then:
- lambda: 'id(nspanel_id).send_custom_command("wake");'
# Service to send a command to show screensaver (some of the values are hardcoded)
- action: show_screensaver
then:
- mqtt.publish:
topic: $panel_recv_topic
payload: '{"CustomRecv":"event,sleepReached,cardGrid"}'
# Service to navigate to screen
- action: navigate_to_page
variables:
page: string
then:
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate." + page + ",button";
# Service to send a command disable screensaver
- action: disable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~0");'
# Service to send a command enabled screensaver
- action: enable_screensaver
then:
- lambda: 'id(nspanel_id).send_custom_command("timeout~20");'
# Service to send a command wake the screen
- action: dim_0_to_100
variables:
intensity: string
then:
- lambda: 'id(nspanel_id).send_custom_command("dimmode~"+intensity+"~100");'
# Service to send a command To show the screen of a specific like
- action: show_entity
variables:
entity: string
title: string
then:
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupLight~" + title + "~" + entity);'
# Service to send a command To show the screen of a specific like
- action: notify_on_screensaver
variables:
line1: string
line2: string
then:
- lambda: 'id(nspanel_id).send_custom_command("notify~" + line1 + "~" + line2);'
- rtttl.play: "short:d=4,o=5,b=100:16e6"
# Service to send a command To show the screen of a specific like
# interaction of the buttons are on implemented yet
- action: notify_fullscreen
variables:
title: string
description: string
button1: string
button2: string
time_secs: string
timeout: int
icon: string
then:
# show notification screen
- lambda: 'id(nspanel_id).send_custom_command("pageType~popupNotify");'
# set values on notification screen
# color defined as number created in binary and converted to decimal (rrrrggggbbbbaaaa)
- lambda: 'id(nspanel_id).send_custom_command("entityUpdateDetail~id~"+ title + "~65535~" + button1 +"~3840~" + button2 + "~61440~" + description + "~65535~" + time_secs);'
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
uart:
@@ -166,19 +77,142 @@ uart:
rx_pin: 17
baud_rate: 115200
mqtt:
id: mqtt_client
broker: !secret mqtt_ip
username: !secret mqtt_username
password: !secret mqtt_password
discovery: false
idf_send_async: true
reboot_timeout: 0s
nspanel_lovelace:
id: nspanel_id
mqtt_recv_topic: $panel_recv_topic
mqtt_send_topic: $panel_send_topic
sleep_timeout: 10
experimental_vertical_ui: true
locale:
language: sk
temperature_unit: celcius
screensaver:
time_id: ha_time
time_format: '%H:%M'
date_format: '%A, %d. %b. %Y'
weather:
entity_id: sensor.weather_forecast_daily
icon_color: [255, 255, 255]
entities:
- entity_id: sensor.zigbee_living_room_kitchen_temperature
icon:
value: home-thermometer-outline
color: [255, 255, 255]
precision: 1
cards:
- type: cardGrid
title: Kuchyňa / Obývačka
entities:
- name: Kuchyňa / Obývačka
entity_id: light.kitchen
icon: ceiling-light
- name: Stôl
entity_id: light.dining_table
icon: ceiling-light
- name: Gauč
entity_id: light.living_room
icon: ceiling-light
- name: TV
entity_id: light.mcu_tv_light_strip
icon: led-strip-variant
- name: (Stôl)
entity_id: light.dining_table_ambilight
icon: ceiling-light-multiple-outline
- name: (Gauč)
entity_id: light.living_room_ambilight
icon: ceiling-light-multiple-outline
- type: cardGrid
title: Kuchyňa
entities:
- name: Linka
entity_id: light.worktop_lights
icon: countertop
- name: Sporák
entity_id: light.mcu_kitchen_stove
icon: stove
- type: cardGrid
title: Von
entities:
- name: Terasa
entity_id: light.patio_lights
icon: lightbulb-group
- name: Terasa
entity_id: switch.mcu_patio_relay
icon: power-socket-fr
- name: Vzadu
entity_id: switch.mcu_nspanel_patio_outside_light_back
icon: light-flood-down
- name: Z. domček
entity_id: switch.mcu_shed_light
icon: wall-sconce-flat
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- name: Z. domček
entity_id: switch.mcu_shed_socket
icon: power-socket-fr
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- name: Záhrada
entity_id: switch.mcu_shed_garden_light
icon: light-flood-down
# state_template: '{{ is_state("binary_sensor.shed_status", "off") }}'
- type: cardGrid
title: Závlaha
entities:
- name: A1
entity_id: switch.mcu_well_irrigation_valve_a1
icon: valve
- name: A2
entity_id: switch.mcu_well_irrigation_valve_a2
icon: valve
- name: A3
entity_id: switch.mcu_well_irrigation_valve_a3
icon: valve
- name: A4
entity_id: switch.mcu_well_irrigation_valve_a4
icon: valve
- name: A5
entity_id: switch.mcu_well_irrigation_valve_a5
icon: valve
- name: Automatizácia
entity_id: navigate.card_irrigation_automation
- type: cardGrid
title: Brána
entities:
- name: Otvoriť
entity_id: button.mcu_gate_open
icon: gate-open
- name: Zatvoriť
entity_id: button.mcu_gate_close
icon: gate
- name: Automatizácia
entity_id: navigate.card_gate_automation
- type: cardThermo
title: Kúrenie
thermo_entity_id: climate.bt_trv_living_room_kitchen
- type: cardEntities
title: Automatizácia
id: card_gate_automation
hidden: true
entities:
- name: Auto-otváranie
entity_id: switch.gate_auto_open
- name: Auto-zatváranie
entity_id: switch.gate_auto_close
- type: cardEntities
title: Automatizácia
id: card_irrigation_automation
hidden: true
entities:
- name: Ráno
entity_id: automation.irrigation_morning
- name: Terasa
entity_id: automation.irrigation_patio
- name: Rast trávy
entity_id: automation.irrigation_grow_grass
time:
- platform: homeassistant
@@ -193,10 +227,9 @@ binary_sensor:
inverted: true
on_click:
- switch.toggle: relay_1
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- lambda: 'id(nspanel_id).render_next_page();'
- lambda: 'id(nspanel_id).render_next_page();'
- platform: gpio
name: Right Button
@@ -212,10 +245,9 @@ binary_sensor:
action: light.toggle
data:
entity_id: light.patio_lights
- mqtt.publish_json:
topic: $panel_recv_topic
payload: |-
root["CustomRecv"] = "event,buttonPress2,navigate.cardGrid_main,button";
- lambda: 'id(nspanel_id).render_default_page();'
- lambda: 'id(nspanel_id).render_next_page();'
- lambda: 'id(nspanel_id).render_next_page();'
else:
- switch.toggle: relay_2
@@ -267,13 +299,19 @@ sensor:
resistor: 11.2kOhm
- platform: ntc
name: Living Room Temperature
id: temperature
sensor: resistance_sensor
calibration:
b_constant: 3950
reference_temperature: 25°C
reference_resistance: 10kOhm
name: Living Room Temperature
- platform: template
id: room_temperature_id
text_sensor:
- platform: homeassistant
id: name_day
entity_id: sensor.nameday_sk
on_value:
- lambda: |-
id(nspanel_id).set_time_format("%H:%M~" + x);
id(nspanel_id).update_time();
+886
View File
@@ -0,0 +1,886 @@
substitutions:
device: stackchan
name: StackChan
area: DEV
comment: StackChan
esphome:
name: mcu-${device}
friendly_name: ${name}
area: ${area}
comment: ${comment}
on_boot:
- priority: -100
then:
- light.turn_on:
id: light_bar
brightness: 20%
red: 0%
green: 0%
blue: 100%
- component.update: display_tft
esp32:
variant: esp32s3
flash_size: 16MB
toolchain: esp-idf
framework:
type: esp-idf
psram:
mode: quad
speed: 80MHz
packages:
common: !include common/common.yaml
external_components:
- source: github://m5stack/esphome-yaml/components
components: [axp2101, aw88298, aw9523b, si12t, m5ioe1, ftservo, scs9009]
refresh: 0s
- source: github://randybb/esphome-components
components: [audio_spectrum]
refresh: 1d
globals:
- id: in_love
type: bool
initial_value: 'false'
- id: show_status
type: bool
initial_value: 'true'
- id: mouth_open
type: float
initial_value: '0.0'
- id: eyes_open
type: bool
initial_value: 'true'
- id: speaking
type: bool
initial_value: 'false'
- id: listening
type: bool
initial_value: 'false'
- id: gaze_x
type: int
initial_value: '0'
- id: gaze_y
type: int
initial_value: '0'
i2c:
- id: i2c_bsp
sda: 12
scl: 11
frequency: 100kHz
axp2101:
id: axp2101_pmu
i2c_id: i2c_bsp
aw9523b:
id: aw9523b_hub
i2c_id: i2c_bsp
p0_drive_mode: PUSH_PULL
m5ioe1:
id: m5ioe1_hub
i2c_id: i2c_bsp
reset: true
output:
- platform: axp2101
id: lcd_backlight_output
type: range
channel: DLDO1
min_voltage: 2600
max_voltage: 3300
- platform: axp2101
channel: ALDO1
voltage: 1800
- platform: axp2101
channel: ALDO2
voltage: 3300
- platform: axp2101
channel: ALDO3
voltage: 3300
- platform: axp2101
channel: ALDO4
voltage: 3300
- platform: axp2101
channel: BLDO1
voltage: 2800
- platform: axp2101
channel: BLDO2
voltage: 1500
- platform: axp2101
channel: DCDC1
voltage: 3300
- platform: axp2101
channel: DCDC4
voltage: 1800
- platform: axp2101
channel: DCDC3
voltage: 3300
- platform: axp2101
channel: DCDC5
voltage: 3300
switch:
- platform: gpio
id: boost_en
name: Boost
entity_category: config
pin:
aw9523b_id: aw9523b_hub
number: 15
restore_mode: RESTORE_DEFAULT_ON
- platform: gpio
id: usb_otg_en
name: USB OTG
entity_category: config
pin:
aw9523b_id: aw9523b_hub
number: 5
restore_mode: RESTORE_DEFAULT_OFF
- platform: gpio
id: bus_out_en
name: BUS Out
entity_category: config
pin:
aw9523b_id: aw9523b_hub
number: 1
restore_mode: RESTORE_DEFAULT_ON
- platform: gpio
id: light_bar_en
internal: true
pin:
m5ioe1_id: m5ioe1_hub
number: 13
mode:
output: true
pullup: true
restore_mode: ALWAYS_ON
- platform: gpio
id: aw_rst
internal: true
pin:
aw9523b_id: aw9523b_hub
number: 2
restore_mode: ALWAYS_ON
- platform: gpio
id: cam_rst
internal: true
pin:
aw9523b_id: aw9523b_hub
number: 8
restore_mode: ALWAYS_ON
- platform: gpio
id: servo_power
internal: true
pin:
m5ioe1_id: m5ioe1_hub
number: 0
mode:
output: true
pullup: true
restore_mode: ALWAYS_ON
i2s_audio:
- id: i2s_bus
i2s_lrclk_pin: 33
i2s_bclk_pin: 34
i2s_mclk_pin:
number: 0
ignore_strapping_warning: true
audio_adc:
- platform: es7210
id: es7210_adc
i2c_id: i2c_bsp
bits_per_sample: 16bit
sample_rate: 16000
mic_gain: 36
audio_dac:
- platform: aw88298
id: aw88298_dac
i2c_id: i2c_bsp
sample_rate: 48000
microphone:
- platform: i2s_audio
id: stackchan_microphone
i2s_audio_id: i2s_bus
i2s_din_pin: 14
adc_type: external
sample_rate: 16000
bits_per_sample: 16bit
speaker:
- platform: i2s_audio
id: speaker_id
i2s_audio_id: i2s_bus
dac_type: external
i2s_dout_pin: 13
audio_dac: aw88298_dac
sample_rate: 48000
buffer_duration: 100ms
# pass-through that computes the spectrum shown on the display while music plays
- platform: audio_spectrum
id: spectrum
output_speaker: speaker_id
bands: 20
- platform: mixer
id: mixer_speaker_id
output_speaker: spectrum
num_channels: 1
source_speakers:
- id: announcement_spk_mixer_input
- id: media_spk_mixer_input
- platform: resampler
id: media_spk_resampling_input
output_speaker: media_spk_mixer_input
- platform: resampler
id: announcement_spk_resampling_input
output_speaker: announcement_spk_mixer_input
media_player:
- platform: speaker
id: media_out
name: Player
buffer_size: 14000
# AW88298 maps volume linearly to -96..0 dB, the lower half is inaudible
volume_min: 0.5
volume_max: 1.0
media_pipeline:
speaker: media_spk_resampling_input
format: NONE
announcement_pipeline:
speaker: announcement_spk_resampling_input
format: NONE
on_announcement:
- script.execute: release_i2s_bus
on_play:
- script.execute: release_i2s_bus
on_idle:
- if:
condition:
not:
voice_assistant.is_running:
then:
- micro_wake_word.start:
micro_wake_word:
id: mww
microphone: stackchan_microphone
models:
- model: okay_nabu
on_wake_word_detected:
- voice_assistant.start:
wake_word: !lambda return wake_word;
voice_assistant:
id: va
microphone: stackchan_microphone
media_player: media_out
micro_wake_word: mww
noise_suppression_level: 2
auto_gain: 31dBFS
on_client_connected:
- micro_wake_word.start:
- delay: 2s
- lambda: id(show_status) = false;
- component.update: display_tft
on_client_disconnected:
- micro_wake_word.stop:
- lambda: id(show_status) = true;
- component.update: display_tft
on_listening:
- lambda: |-
id(listening) = true;
id(gaze_x) = 0;
id(gaze_y) = 0;
- component.update: display_tft
- light.turn_on: {id: light_bar, brightness: 60%, red: 0%, green: 60%, blue: 100%}
on_stt_end:
- lambda: id(listening) = false;
- component.update: display_tft
- light.turn_on: {id: light_bar, brightness: 40%, red: 80%, green: 50%, blue: 0%}
on_tts_start:
- lambda: id(speaking) = true;
- light.turn_on: {id: light_bar, brightness: 60%, red: 0%, green: 100%, blue: 20%}
on_end:
- wait_until:
not:
media_player.is_announcing: media_out
- lambda: |-
id(speaking) = false;
id(listening) = false;
id(mouth_open) = 0.0f;
- component.update: display_tft
- light.turn_on: {id: light_bar, brightness: 20%, red: 0%, green: 0%, blue: 100%}
- wait_until:
not: voice_assistant.is_running
- micro_wake_word.start:
on_error:
- light.turn_on: {id: light_bar, brightness: 60%, red: 100%, green: 0%, blue: 0%}
script:
# short tap on the head starts the assistant
- id: head_tap
then:
- if:
condition:
not: voice_assistant.is_running
then:
- voice_assistant.start:
# stroking the head shows the in-love face for a while
- id: head_stroke
mode: restart
then:
- lambda: id(in_love) = true;
- component.update: display_tft
- delay: 5s
- lambda: id(in_love) = false;
- component.update: display_tft
# mic and speaker share one I2S bus - the mic must be released before playback
- id: release_i2s_bus
mode: single
then:
- if:
condition:
microphone.is_capturing:
then:
- micro_wake_word.stop:
# stop VA only while it is listening (e.g. continued conversation), not during its own response
- if:
condition:
lambda: return id(listening);
then:
- voice_assistant.stop:
- wait_until:
condition:
not:
microphone.is_capturing:
timeout: 2s
interval:
- interval: 80ms # spectrum analyzer frame rate while music plays
then:
- if:
condition:
and:
- media_player.is_playing: media_out
- not: voice_assistant.is_running
then:
- component.update: display_tft
# head touch gestures: a touch episode ends after 250 ms without contact;
# tap = short episode, stroke = longer episode that reaches the pads one after another
- interval: 50ms
then:
- lambda: |-
static bool active = false;
static uint32_t start = 0, last_on = 0;
static uint32_t first_on[3];
const bool on[3] = {id(head_touch_1).state != "No touch", id(head_touch_2).state != "No touch",
id(head_touch_3).state != "No touch"};
const uint32_t now = millis();
if (on[0] || on[1] || on[2]) {
if (!active) {
active = true;
start = now;
first_on[0] = first_on[1] = first_on[2] = 0;
}
for (int i = 0; i < 3; i++)
if (on[i] && first_on[i] == 0)
first_on[i] = now;
last_on = now;
return;
}
if (!active || now - last_on < 250)
return;
active = false;
const uint32_t duration = last_on - start;
int pads = 0;
uint32_t earliest = UINT32_MAX, latest = 0;
for (int i = 0; i < 3; i++) {
if (first_on[i] == 0)
continue;
pads++;
earliest = std::min(earliest, first_on[i]);
latest = std::max(latest, first_on[i]);
}
const uint32_t spread = latest - earliest;
ESP_LOGD("head_touch", "duration %u ms, pads %d, spread %u ms, order 1:%d 2:%d 3:%d", (unsigned) duration,
pads, (unsigned) spread, first_on[0] ? (int) (first_on[0] - earliest) : -1,
first_on[1] ? (int) (first_on[1] - earliest) : -1, first_on[2] ? (int) (first_on[2] - earliest) : -1);
if (pads >= 2 && spread >= 150 && duration >= 400) {
id(head_stroke).execute();
} else if (duration <= 600) {
id(head_tap).execute();
}
- interval: 1s # status screen refresh until Home Assistant connects
then:
- if:
condition:
lambda: return id(show_status);
then:
- component.update: display_tft
- interval: 90ms
then:
- lambda: |-
if (id(speaking)) {
id(mouth_open) = 0.15f + (esp_random() % 85) / 100.0f;
id(display_tft).update();
} else if (id(mouth_open) > 0) {
id(mouth_open) = 0;
id(display_tft).update();
}
- interval: 3s # blinking
then:
- delay: !lambda return esp_random() % 3000;
- lambda: id(eyes_open) = false;
- component.update: display_tft
- delay: 150ms
- lambda: id(eyes_open) = true;
- component.update: display_tft
- interval: 7s # looking around while idle
then:
- lambda: |-
if (!id(speaking) && !id(listening)) {
id(gaze_x) = (int) (esp_random() % 21) - 10;
id(gaze_y) = (int) (esp_random() % 11) - 5;
id(display_tft).update();
}
spi:
- id: spi_bus
clk_pin: 36
mosi_pin: 37
image:
- platform: file
file: assets/images/esphome-logo-text-on-dark.svg
id: esphome_logo
resize: 280x56
type: RGB565
transparency: alpha_channel
- platform: file
file: mdi:ear-hearing
id: icon_listening
resize: 28x28
type: BINARY
font:
- file: "gfonts://Roboto"
id: font_title
size: 24
- file: "gfonts://Roboto"
id: font_status
size: 16
color:
- id: status_ok
red: 30%
green: 85%
blue: 40%
- id: status_wait
red: 100%
green: 70%
blue: 0%
- id: love_red
red: 100%
green: 25%
blue: 45%
- id: status_low
red: 100%
green: 20%
blue: 20%
- id: status_dim
red: 55%
green: 55%
blue: 55%
- id: face_fg
red: 100%
green: 100%
blue: 100%
- id: face_bg
red: 0%
green: 0%
blue: 0%
display:
- platform: mipi_spi
id: display_tft
model: M5CORE
dc_pin: 35
reset_pin:
aw9523b_id: aw9523b_hub
number: 9
cs_pin:
number: 3
ignore_strapping_warning: true
data_rate: 40MHz
invert_colors: true
update_interval: never
auto_clear_enabled: false
lambda: |-
static bool first_draw = true;
static bool dot_shown = false;
static bool bar_drawn = false;
static int last_bars = -1, last_batt = -2;
static bool last_charging = false;
const bool wifi_ok = wifi::global_wifi_component->is_connected();
const int8_t rssi = wifi_ok ? wifi::global_wifi_component->wifi_rssi() : -127;
const int wifi_bars = !wifi_ok ? 0 : rssi > -55 ? 4 : rssi > -65 ? 3 : rssi > -75 ? 2 : 1;
const int batt = std::isnan(id(battery_level).state) ? -1 : (int) roundf(id(battery_level).state);
const bool charging = id(battery_charging).state;
// status screen until Home Assistant connects
if (id(show_status)) {
first_draw = true;
it.fill(id(face_bg));
it.image(160, 45, id(esphome_logo), ImageAlign::CENTER);
it.print(160, 95, id(font_title), id(face_fg), TextAlign::CENTER, "${name}");
if (wifi_ok) {
it.printf(20, 135, id(font_status), id(status_ok), "WiFi: %s (%d dBm)",
id(ip_address_sensor).state.c_str(), rssi);
} else {
it.print(20, 135, id(font_status), id(status_wait), "WiFi: connecting...");
}
const bool api_ok = api::global_api_server->is_connected();
it.print(20, 162, id(font_status), api_ok ? id(status_ok) : id(status_wait),
api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...");
if (batt >= 0) {
it.printf(20, 189, id(font_status), batt <= 20 && !charging ? id(status_low) : id(status_dim),
"Battery: %d%%%s", batt, charging ? " (charging)" : "");
}
it.printf(20, 212, id(font_status), id(status_dim), "ESPHome %s", ESPHOME_VERSION);
it.printf(300, 212, id(font_status), id(status_dim), TextAlign::TOP_RIGHT, "up %us",
(unsigned) (millis() / 1000));
return;
}
// stack-chan face: 2 eyes + mouth, 320x240
// the whole screen is cleared only once, then just the face areas, so that only the
// changed region is sent over SPI instead of the whole 150 kB buffer
const bool love = id(in_love);
const bool music = id(media_out).state == media_player::MEDIA_PLAYER_STATE_PLAYING && !id(va).is_running();
// switching between face, in-love face and spectrum repaints everything
static int last_mode = -1;
const int mode = music ? 2 : love ? 1 : 0;
if (mode != last_mode) {
last_mode = mode;
first_draw = true;
}
// the in-love face is drawn outside the partially cleared areas, so it always repaints everything
static bool spectrum_reset = true;
if (first_draw || love) {
it.fill(id(face_bg));
first_draw = false;
bar_drawn = false;
spectrum_reset = true;
} else if (!music) {
it.filled_rectangle(70, 78, 181, 31, id(face_bg)); // eyes including gaze range
it.filled_rectangle(105, 130, 111, 38, id(face_bg)); // mouth
if (dot_shown && !id(listening)) {
it.filled_rectangle(254, 2, 28, 28, id(face_bg));
}
}
dot_shown = id(listening);
// status bar (battery top-left, wifi top-right), redrawn only when something changes
if (!bar_drawn || wifi_bars != last_bars || batt != last_batt || charging != last_charging) {
bar_drawn = true;
last_bars = wifi_bars;
last_batt = batt;
last_charging = charging;
it.filled_rectangle(4, 4, 90, 22, id(face_bg));
it.filled_rectangle(288, 4, 28, 22, id(face_bg));
// battery: outline, terminal nub and fill proportional to the charge
if (batt >= 0) {
const Color c = charging ? id(status_ok) : batt <= 20 ? id(status_low) : id(status_dim);
it.rectangle(8, 8, 26, 13, c);
it.filled_rectangle(34, 12, 2, 5, c);
it.filled_rectangle(10, 10, 22 * batt / 100, 9, c);
it.printf(40, 6, id(font_status), c, "%d%%", batt);
}
// wifi: 4 bars, unused ones dimmed; red when disconnected
for (int i = 0; i < 4; i++) {
const int h = 4 + 3 * i;
const Color c = !wifi_ok ? id(status_low) : i < wifi_bars ? id(status_dim) : Color(40, 40, 40);
it.filled_rectangle(290 + i * 6, 21 - h, 4, h, c);
}
}
// Winamp style spectrum analyzer instead of the face while music plays.
// Only the segments that changed since the last frame are drawn or cleared, which keeps both the
// pixel work and the SPI transfer small.
if (music) {
static float peak[32];
static uint32_t peak_time[32];
static int prev_segs[32], prev_cap[32];
const uint32_t now = millis();
const int bands = std::min<int>(id(spectrum).get_bands(), 32);
const int x0 = 12, x1 = 308, top = 40, bottom = 228;
const int slot = (x1 - x0) / bands, bar_w = slot - 3;
const int seg_h = 5; // 4 px LED segment + 1 px gap
const int max_segs = (bottom - top) / seg_h;
if (spectrum_reset) {
spectrum_reset = false;
for (int b = 0; b < 32; b++) {
peak[b] = 0.0f;
prev_segs[b] = 0;
prev_cap[b] = -1;
}
}
auto seg_y = [&](int s) { return bottom - (s + 1) * seg_h + 1; };
for (int b = 0; b < bands; b++) {
const float level = id(spectrum).get_level(b);
const int x = x0 + b * slot + 1;
const int segs = std::min(max_segs, (int) (level * max_segs));
// peak caps hold for a moment, then fall slowly
if (level >= peak[b]) {
peak[b] = level;
peak_time[b] = now;
} else if (now - peak_time[b] > 400) {
peak[b] = std::max(0.0f, peak[b] - 0.03f);
}
const int cap = peak[b] > 0.0f ? std::min(max_segs - 1, std::max(segs, (int) (peak[b] * max_segs))) : -1;
// the old cap is cleared first; bar segments drawn below repaint anything it overlapped
if (prev_cap[b] >= 0 && prev_cap[b] != cap)
it.filled_rectangle(x, seg_y(prev_cap[b]), bar_w, seg_h - 1, id(face_bg));
if (segs > prev_segs[b]) {
for (int s = prev_segs[b]; s < segs; s++) {
// green at the bottom, through yellow to red at the top
const float f = float(s) / float(max_segs - 1);
const Color c = f < 0.5f ? Color((uint8_t) (510 * f), 230, 0) : Color(255, (uint8_t) (460 * (1.0f - f)), 0);
it.filled_rectangle(x, seg_y(s), bar_w, seg_h - 1, c);
}
} else if (segs < prev_segs[b]) {
it.filled_rectangle(x, seg_y(prev_segs[b] - 1), bar_w, (prev_segs[b] - segs) * seg_h, id(face_bg));
}
prev_segs[b] = segs;
if (cap >= segs && cap >= 0 && (cap != prev_cap[b] || cap == segs)) {
const int cy = seg_y(cap) + seg_h - 3;
it.filled_rectangle(x, cy, bar_w, 2, id(status_dim));
}
prev_cap[b] = cap;
}
return;
}
const int ex = id(gaze_x), ey = id(gaze_y);
const int eye_y = 93 + ey, eye_r = 8;
const int lx = 90 + ex, rx = 230 + ex;
if (love) {
// heart eyes, blush and a wide smile
for (int cx : {lx, rx}) {
it.filled_circle(cx - 6, eye_y - 3, 7, id(love_red));
it.filled_circle(cx + 6, eye_y - 3, 7, id(love_red));
it.filled_triangle(cx - 13, eye_y - 1, cx + 13, eye_y - 1, cx, eye_y + 14, id(love_red));
}
it.filled_circle(60 + ex, 130 + ey, 12, Color(90, 20, 40));
it.filled_circle(260 + ex, 130 + ey, 12, Color(90, 20, 40));
if (!id(speaking)) {
it.filled_circle(160 + ex / 2, 132 + ey / 2, 30, id(face_fg));
it.filled_circle(160 + ex / 2, 122 + ey / 2, 30, id(face_bg));
}
} else if (id(eyes_open)) {
it.filled_circle(lx, eye_y, eye_r, id(face_fg));
it.filled_circle(rx, eye_y, eye_r, id(face_fg));
} else {
it.filled_rectangle(lx - eye_r, eye_y - 2, eye_r * 2, 4, id(face_fg));
it.filled_rectangle(rx - eye_r, eye_y - 2, eye_r * 2, 4, id(face_fg));
}
const float m = id(mouth_open);
if (!love || id(speaking)) {
const int mw = 90 - (int) (40 * m);
const int mh = 4 + (int) (26 * m);
it.filled_rectangle(160 - mw / 2 + ex / 2, 148 - mh / 2 + ey / 2, mw, mh, id(face_fg));
}
if (id(listening)) {
it.image(254, 2, id(icon_listening), id(face_fg), id(face_bg)); // left of the wifi bars
}
touchscreen:
- platform: ft63x6
id: touch_lcd
i2c_id: i2c_bsp
reset_pin:
aw9523b_id: aw9523b_hub
number: 0
on_touch:
- voice_assistant.start:
esp32_camera:
id: camera_id
name: Camera
i2c_id: i2c_bsp
setup_priority: -200 # after power rails and IO expanders
pixel_format: RGB565
max_framerate: 15 fps
frame_buffer_count: 1
# GC0308 has no JPEG, convert RGB565 -> JPEG for HA (as in BSP); runs in the main loop, so no periodic frames
jpeg_quality: 12
idle_framerate: 0 fps
vsync_pin:
number: 46
ignore_strapping_warning: true
href_pin: 38
external_clock:
pin: 2
frequency: 20MHz
pixel_clock_pin:
number: 45
ignore_strapping_warning: true
data_pins: [39, 40, 41, 42, 15, 16, 48, 47]
resolution: 320x240
frame_buffer_location: PSRAM
sensor:
- platform: axp2101
id: axp2101_sensor
battery_level:
id: battery_level
name: Battery Level
filters:
- delta: 1.0
on_value:
- component.update: display_tft
battery_charging:
id: battery_charging
name: Battery Charging
on_state:
- component.update: display_tft
- platform: ltr_als_ps
i2c_id: i2c_bsp
address: 0x23
type: ALS_PS
ambient_light:
id: ambient_light
name: Ambient Light
ps_counts:
id: proximity_counts
name: Proximity Counts
- platform: ina226
i2c_id: i2c_bsp
address: 0x41
shunt_resistance: 0.01 ohm
max_current: 3.2A
current:
id: ina226_current
name: Current
power:
id: ina226_power
name: Power
bus_voltage:
id: ina226_bus_voltage
name: Bus Voltage
si12t:
id: touch_hub
i2c_id: i2c_bsp
# report low-strength touches too, not only medium/high
intr_level: OUTPUT_ALL
text_sensor:
- platform: si12t
id: head_touch_1
name: Head Touch 1
channel: CH_1
# lower level = lower touch threshold: SENS_LEVEL_1 = 0.7 % capacitance change (default level 3 is 1.2 %),
# SENS_LEVEL_0 made neighbouring pads trigger together
sensitivity: SENS_LEVEL_1
extend_threshold: false
update_interval: 100ms
internal: true
- platform: si12t
id: head_touch_2
name: Head Touch 2
channel: CH_2
sensitivity: SENS_LEVEL_1
extend_threshold: false
update_interval: 100ms
internal: true
- platform: si12t
id: head_touch_3
name: Head Touch 3
channel: CH_3
sensitivity: SENS_LEVEL_1
extend_threshold: false
update_interval: 100ms
internal: true
uart:
- id: uart_servo
tx_pin: 6
rx_pin: 7
baud_rate: 1000000
scs9009:
ftservo:
- platform: scs9009
id: x_servo
address: 1
- platform: scs9009
id: y_servo
address: 2
number:
- platform: ftservo
ftservo_id: x_servo
angle:
id: servo_x_angle
name: Servo X Angle
min_value: -164
max_value: 164
use_raw_angle: false
angle_offset: 164
step: 5
light:
- platform: m5ioe1
id: light_bar
name: Light Bar
num_led: 12
- platform: monochromatic
id: lcd_backlight
name: LCD Backlight
output: lcd_backlight_output
restore_mode: ALWAYS_ON
remote_transmitter:
id: ir_tx
pin: 5
carrier_duty_percent: 50%
non_blocking: true
remote_receiver:
id: ir_rx
pin: 10
time:
- platform: bm8563
id: rtc_time
i2c_id: i2c_bsp
update_interval: never
- !include common/time/homeassistant.yaml
-108
View File
@@ -1,108 +0,0 @@
{
"none": "Žiady",
"unknown": "Neznámy",
"preset_mode": "Prednastavený režim",
"swing_mode": "Režim oscilácie",
"fan_mode": "Režim ventilátora",
"activity": "Aktivita",
"away": "Preč",
"boost": "Boost",
"comfort": "Komfort",
"eco": "Eko",
"home": "Domov",
"sleep": "Spánok",
"cool": "Chladenie",
"cooling": "Chladí",
"dry": "Sušenie",
"drying": "Suší",
"fan": "Ventilátor",
"heat": "Kúrenie",
"heating": "Kúri",
"heat_cool": "Kúrenie/Chladenie",
"idle": "V kľude",
"auto": "Automatický",
"fan_only": "Len ventilátor",
"on": "Zapnuté",
"off": "Vypnuté",
"currently": "Aktuálne",
"state": "Stav",
"action": "Akcia",
"lock": "Zamknúť",
"unlock": "Odomknúť",
"paused": "Pozastavené",
"active": "Aktívne",
"activate": "Aktivovať",
"press": "Stlačiť",
"run": "Spustiť",
"speed": "Rýchlosť",
"brightness": "Jas",
"color": "Farba",
"color_temp": "Teplota farby",
"above_horizon": "Nad horizontom",
"below_horizon": "Pod horizontom",
"position": "Pozícia",
"tilt_position": "Náklon",
"start": "Štart",
"pause": "Pauza",
"cancel": "Zrušiť",
"finish": "Dokončiť",
"arm_home": "Arm (doma)",
"arm_away": "Arm (mimo domov)",
"arm_night": "Arm (noc)",
"arm_vacation": "Arm (dovolenka)",
"arm_custom_bypass": "Arm (bypass)",
"disarm": "Vypnúť zabezpečenie",
"disarming": "Vypínanie zabezpečenia",
"disarmed": "Zabezpečenie vypnuté",
"arming": "Zabezpečuje sa",
"pending": "Čekanie",
"triggered": "Poplach",
"armed_away": "Zabezpečené (mimo domov)",
"armed_home": "Zabezpečené (doma)",
"armed_night": "Zabezpečené (noc)",
"armed_vacation": "Zabezpečené (dovolenka)",
"armed_custom_bypass": "Zabezpečené (bypass)",
"not_home": "Mimo domov",
"start_cleaning": "Začať upratovať",
"return_to_base": "Späť na základňu",
"docked": "Zaparkované",
"turn_on": "Zapnúť",
"turn_off": "Vypnúť",
"month_january": "Január",
"month_jan": "Jan",
"month_february": "Február",
"month_feb": "Feb",
"month_march": "Marec",
"month_mar": "Mar",
"month_april": "Apríl",
"month_apr": "Apr",
"month_may": "Máj",
"month_june": "Jún",
"month_jun": "Jun",
"month_july": "Júl",
"month_jul": "Jul",
"month_august": "August",
"month_aug": "Aug",
"month_september": "September",
"month_sep": "Sep",
"month_october": "Október",
"month_oct": "Okt",
"month_november": "November",
"month_nov": "Nov",
"month_december": "December",
"month_dec": "Dec",
"dow_sunday": "Nedela",
"dow_sun": "Ne",
"dow_monday": "Pondelok",
"dow_mon": "Po",
"dow_tuesday": "Utorok",
"dow_tue": "Ut",
"dow_wednesday": "Streda",
"dow_wed": "St",
"dow_thursday": "Štvrtok",
"dow_thu": "Št",
"dow_friday": "Piatok",
"dow_fri": "Pi",
"dow_saturday": "Sobota",
"dow_sat": "So"
}