Edit esp-bedside-panel.yaml
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml
|
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# VERSIONS:
|
# VERSIONS:
|
||||||
|
# V1.3 2026-07-16 Added Security page with 640x360 Doorbell camera snapshot
|
||||||
# V1.2 2026-07-16 Removed Climate title and aligned Heat Pump heading above Off
|
# V1.2 2026-07-16 Removed Climate title and aligned Heat Pump heading above Off
|
||||||
# V1.1 2026-07-16 Increased Clock to 280px and date to 64px
|
# V1.1 2026-07-16 Increased Clock to 280px and date to 64px
|
||||||
# V1.0 2026-07-16 First full Bedroom/Climate/Clock release; added 2-hour Sleep timer
|
# V1.0 2026-07-16 First full Bedroom/Climate/Clock release; added 2-hour Sleep timer
|
||||||
@@ -96,12 +97,12 @@ substitutions:
|
|||||||
# Device Naming
|
# Device Naming
|
||||||
device_name: "esp-bedside-panel"
|
device_name: "esp-bedside-panel"
|
||||||
friendly_name: "ESP Bedside Panel"
|
friendly_name: "ESP Bedside Panel"
|
||||||
description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Simplified Climate heading layout with aligned Heat Pump label. (Layout V1.1)"
|
description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Security page with Home Assistant Doorbell camera snapshots. (Layout V1.1)"
|
||||||
device_area: "Bedroom"
|
device_area: "Bedroom"
|
||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Guition.JC1060P470C_I_W_Y"
|
project_name: "Guition.JC1060P470C_I_W_Y"
|
||||||
project_version: "v1.2"
|
project_version: "v1.3"
|
||||||
|
|
||||||
# Passwords & Secrets
|
# Passwords & Secrets
|
||||||
api_key: !secret esp-api_key
|
api_key: !secret esp-api_key
|
||||||
@@ -126,6 +127,12 @@ substitutions:
|
|||||||
control_brightness: "80%"
|
control_brightness: "80%"
|
||||||
clock_brightness: "4%"
|
clock_brightness: "4%"
|
||||||
|
|
||||||
|
# Security Camera Settings
|
||||||
|
# Change this only if homeassistant.local is not reachable from the panel.
|
||||||
|
home_assistant_internal_url: "http://homeassistant.local:8123"
|
||||||
|
security_camera_refresh_interval: "5s"
|
||||||
|
security_doorbell_camera_entity: "camera.view_road_front_door_fluent"
|
||||||
|
|
||||||
# Home Assistant Entities - Status / Environment
|
# Home Assistant Entities - Status / Environment
|
||||||
moon_phase_entity: "sensor.astroweather_moon_phase"
|
moon_phase_entity: "sensor.astroweather_moon_phase"
|
||||||
outdoor_temperature_entity: "sensor.lounge_outside_temperature"
|
outdoor_temperature_entity: "sensor.lounge_outside_temperature"
|
||||||
@@ -796,6 +803,12 @@ globals:
|
|||||||
restore_value: true
|
restore_value: true
|
||||||
initial_value: "0"
|
initial_value: "0"
|
||||||
|
|
||||||
|
# True only while the Security page is visible.
|
||||||
|
- id: security_page_active
|
||||||
|
type: bool
|
||||||
|
restore_value: false
|
||||||
|
initial_value: "false"
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# SCRIPTS:
|
# SCRIPTS:
|
||||||
# Refresh all state-dependent LVGL button colours
|
# Refresh all state-dependent LVGL button colours
|
||||||
@@ -1237,6 +1250,75 @@ script:
|
|||||||
? lv_color_hex(0x6B4A86)
|
? lv_color_hex(0x6B4A86)
|
||||||
: lv_color_hex(0x28313D);
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
|
# Download a current Doorbell image through the authenticated proxy path
|
||||||
|
# supplied by the Home Assistant camera entity.
|
||||||
|
- id: refresh_security_camera
|
||||||
|
mode: restart
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
return (
|
||||||
|
id(lvgl_ready) &&
|
||||||
|
id(security_page_active)
|
||||||
|
);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
const auto path =
|
||||||
|
id(ha_doorbell_entity_picture).state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
!path.empty() &&
|
||||||
|
path != "unknown" &&
|
||||||
|
path != "unavailable"
|
||||||
|
);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: security_camera_status_label
|
||||||
|
text: "Loading Doorbell..."
|
||||||
|
|
||||||
|
- online_image.set_url:
|
||||||
|
id: security_camera_image
|
||||||
|
url: !lambda |-
|
||||||
|
const std::string path =
|
||||||
|
id(ha_doorbell_entity_picture).state;
|
||||||
|
|
||||||
|
std::string url;
|
||||||
|
|
||||||
|
if (
|
||||||
|
path.rfind("http://", 0) == 0 ||
|
||||||
|
path.rfind("https://", 0) == 0
|
||||||
|
) {
|
||||||
|
url = path;
|
||||||
|
} else {
|
||||||
|
url =
|
||||||
|
std::string(
|
||||||
|
"${home_assistant_internal_url}"
|
||||||
|
) + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
char suffix[32];
|
||||||
|
snprintf(
|
||||||
|
suffix,
|
||||||
|
sizeof(suffix),
|
||||||
|
"%ctime=%lu",
|
||||||
|
url.find('?') == std::string::npos
|
||||||
|
? '?'
|
||||||
|
: '&',
|
||||||
|
static_cast<unsigned long>(millis())
|
||||||
|
);
|
||||||
|
|
||||||
|
return url + suffix;
|
||||||
|
|
||||||
|
else:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: security_camera_status_label
|
||||||
|
text: "Waiting for Doorbell camera"
|
||||||
|
|
||||||
- id: release_climate_profile_optimistic
|
- id: release_climate_profile_optimistic
|
||||||
mode: restart
|
mode: restart
|
||||||
then:
|
then:
|
||||||
@@ -1340,6 +1422,16 @@ interval:
|
|||||||
else:
|
else:
|
||||||
- script.execute: refresh_sleep_timer
|
- script.execute: refresh_sleep_timer
|
||||||
|
|
||||||
|
- interval: "${security_camera_refresh_interval}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
return id(security_page_active);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- script.execute: refresh_security_camera
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# TEXT SENSOR COMPONENT:
|
# TEXT SENSOR COMPONENT:
|
||||||
# https://esphome.io/components/text_sensor/
|
# https://esphome.io/components/text_sensor/
|
||||||
@@ -1383,6 +1475,23 @@ text_sensor:
|
|||||||
then:
|
then:
|
||||||
- script.execute: refresh_climate_controls
|
- script.execute: refresh_climate_controls
|
||||||
|
|
||||||
|
# Home Assistant supplies a short-lived authenticated camera proxy URL in
|
||||||
|
# the entity_picture attribute. This avoids storing an HA token in firmware.
|
||||||
|
- platform: homeassistant
|
||||||
|
id: ha_doorbell_entity_picture
|
||||||
|
entity_id: "${security_doorbell_camera_entity}"
|
||||||
|
attribute: entity_picture
|
||||||
|
internal: true
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
return id(security_page_active);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- script.execute: refresh_security_camera
|
||||||
|
|
||||||
- platform: version
|
- platform: version
|
||||||
name: "${friendly_name} ESPHome Version"
|
name: "${friendly_name} ESPHome Version"
|
||||||
hide_timestamp: true
|
hide_timestamp: true
|
||||||
@@ -1400,6 +1509,64 @@ text_sensor:
|
|||||||
mac_address:
|
mac_address:
|
||||||
name: "${friendly_name} MAC Address"
|
name: "${friendly_name} MAC Address"
|
||||||
|
|
||||||
|
#:########################################################################################:#
|
||||||
|
# HTTP REQUEST:
|
||||||
|
# Used by the online Doorbell image
|
||||||
|
# https://esphome.io/components/http_request/
|
||||||
|
#:########################################################################################:#
|
||||||
|
http_request:
|
||||||
|
timeout: 10s
|
||||||
|
follow_redirects: true
|
||||||
|
|
||||||
|
#:########################################################################################:#
|
||||||
|
# ONLINE SECURITY CAMERA IMAGE:
|
||||||
|
# Home Assistant camera proxy image, resized for the Security page
|
||||||
|
# https://esphome.io/components/image/online_image/
|
||||||
|
#:########################################################################################:#
|
||||||
|
image:
|
||||||
|
- platform: online_image
|
||||||
|
id: security_camera_image
|
||||||
|
url: "${home_assistant_internal_url}/"
|
||||||
|
format: JPEG
|
||||||
|
type: RGB565
|
||||||
|
resize: 640x360
|
||||||
|
update_interval: never
|
||||||
|
buffer_size: 32768
|
||||||
|
|
||||||
|
on_download_finished:
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
return (
|
||||||
|
id(lvgl_ready) &&
|
||||||
|
id(security_page_active)
|
||||||
|
);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- lvgl.image.update:
|
||||||
|
id: security_camera_widget
|
||||||
|
src: security_camera_image
|
||||||
|
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: security_camera_status_label
|
||||||
|
text: ""
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
return (
|
||||||
|
id(lvgl_ready) &&
|
||||||
|
id(security_page_active)
|
||||||
|
);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: security_camera_status_label
|
||||||
|
text: "Camera image unavailable"
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# LVGL:
|
# LVGL:
|
||||||
# https://esphome.io/components/lvgl/
|
# https://esphome.io/components/lvgl/
|
||||||
@@ -1427,6 +1594,11 @@ lvgl:
|
|||||||
on_idle:
|
on_idle:
|
||||||
- timeout: "${clock_idle_timeout}"
|
- timeout: "${clock_idle_timeout}"
|
||||||
then:
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
id(security_page_active) = false;
|
||||||
|
|
||||||
|
- online_image.release: security_camera_image
|
||||||
|
|
||||||
- lvgl.page.show:
|
- lvgl.page.show:
|
||||||
id: clock_page
|
id: clock_page
|
||||||
animation: FADE_IN
|
animation: FADE_IN
|
||||||
@@ -1863,25 +2035,41 @@ lvgl:
|
|||||||
bg_color: 0x34404F
|
bg_color: 0x34404F
|
||||||
|
|
||||||
- button:
|
- button:
|
||||||
id: future_2_navigation_button
|
id: security_navigation_button
|
||||||
x: 612
|
x: 612
|
||||||
y: 475
|
y: 475
|
||||||
width: 190
|
width: 190
|
||||||
height: 92
|
height: 92
|
||||||
widgets:
|
widgets:
|
||||||
- label:
|
- label:
|
||||||
id: future_2_navigation_button_label
|
id: security_navigation_button_label
|
||||||
align: CENTER
|
align: CENTER
|
||||||
text: "Future2"
|
text: "Security"
|
||||||
text_font: font_button
|
text_font: font_button
|
||||||
text_color: 0xAEB8C4
|
text_color: 0xFFFFFF
|
||||||
bg_color: 0x28313D
|
bg_color: 0x493535
|
||||||
bg_opa: COVER
|
bg_opa: COVER
|
||||||
border_width: 2
|
border_width: 2
|
||||||
border_color: 0x445365
|
border_color: 0x805858
|
||||||
radius: 16
|
radius: 16
|
||||||
pressed:
|
pressed:
|
||||||
bg_color: 0x34404F
|
bg_color: 0x654949
|
||||||
|
|
||||||
|
on_click:
|
||||||
|
- lambda: |-
|
||||||
|
id(security_page_active) = true;
|
||||||
|
|
||||||
|
- lvgl.page.show:
|
||||||
|
id: security_page
|
||||||
|
animation: MOVE_LEFT
|
||||||
|
time: 220ms
|
||||||
|
|
||||||
|
- light.turn_on:
|
||||||
|
id: display_backlight
|
||||||
|
brightness: "${control_brightness}"
|
||||||
|
transition_length: 250ms
|
||||||
|
|
||||||
|
- script.execute: refresh_security_camera
|
||||||
|
|
||||||
- button:
|
- button:
|
||||||
id: clock_now_button
|
id: clock_now_button
|
||||||
@@ -2579,6 +2767,200 @@ lvgl:
|
|||||||
brightness: "${clock_brightness}"
|
brightness: "${clock_brightness}"
|
||||||
transition_length: 1500ms
|
transition_length: 1500ms
|
||||||
|
|
||||||
|
#:####################################################################################:#
|
||||||
|
# SECURITY PAGE
|
||||||
|
#:####################################################################################:#
|
||||||
|
- id: security_page
|
||||||
|
skip: true
|
||||||
|
bg_color: 0x0D1218
|
||||||
|
bg_opa: COVER
|
||||||
|
|
||||||
|
widgets:
|
||||||
|
#:################################################################################:#
|
||||||
|
# CAMERA SELECTION
|
||||||
|
#:################################################################################:#
|
||||||
|
- button:
|
||||||
|
id: security_doorbell_button
|
||||||
|
x: 25
|
||||||
|
y: 18
|
||||||
|
width: 220
|
||||||
|
height: 70
|
||||||
|
bg_color: 0x493535
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x9A6666
|
||||||
|
radius: 16
|
||||||
|
pressed:
|
||||||
|
bg_color: 0x654949
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Doorbell"
|
||||||
|
text_font: font_button
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
on_click:
|
||||||
|
- script.execute: refresh_security_camera
|
||||||
|
|
||||||
|
- button:
|
||||||
|
id: security_camera_2_button
|
||||||
|
x: 272
|
||||||
|
y: 18
|
||||||
|
width: 220
|
||||||
|
height: 70
|
||||||
|
bg_color: 0x28313D
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x445365
|
||||||
|
radius: 16
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Camera2"
|
||||||
|
text_font: font_button
|
||||||
|
text_color: 0x8D98A6
|
||||||
|
|
||||||
|
- button:
|
||||||
|
id: security_camera_3_button
|
||||||
|
x: 519
|
||||||
|
y: 18
|
||||||
|
width: 220
|
||||||
|
height: 70
|
||||||
|
bg_color: 0x28313D
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x445365
|
||||||
|
radius: 16
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Camera3"
|
||||||
|
text_font: font_button
|
||||||
|
text_color: 0x8D98A6
|
||||||
|
|
||||||
|
- button:
|
||||||
|
id: security_camera_4_button
|
||||||
|
x: 766
|
||||||
|
y: 18
|
||||||
|
width: 220
|
||||||
|
height: 70
|
||||||
|
bg_color: 0x28313D
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x445365
|
||||||
|
radius: 16
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Camera4"
|
||||||
|
text_font: font_button
|
||||||
|
text_color: 0x8D98A6
|
||||||
|
|
||||||
|
#:################################################################################:#
|
||||||
|
# 640x360 CAMERA IMAGE
|
||||||
|
#:################################################################################:#
|
||||||
|
- obj:
|
||||||
|
x: 190
|
||||||
|
y: 98
|
||||||
|
width: 644
|
||||||
|
height: 364
|
||||||
|
bg_color: 0x020304
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x445365
|
||||||
|
radius: 13
|
||||||
|
shadow_width: 0
|
||||||
|
clickable: false
|
||||||
|
|
||||||
|
- image:
|
||||||
|
id: security_camera_widget
|
||||||
|
x: 192
|
||||||
|
y: 100
|
||||||
|
width: 640
|
||||||
|
height: 360
|
||||||
|
src: security_camera_image
|
||||||
|
radius: 11
|
||||||
|
clip_corner: true
|
||||||
|
clickable: false
|
||||||
|
|
||||||
|
- label:
|
||||||
|
id: security_camera_status_label
|
||||||
|
x: 192
|
||||||
|
y: 260
|
||||||
|
width: 640
|
||||||
|
text: "Waiting for Doorbell camera"
|
||||||
|
text_font: font_button
|
||||||
|
text_align: CENTER
|
||||||
|
text_color: 0xD4D8DE
|
||||||
|
clickable: false
|
||||||
|
|
||||||
|
#:################################################################################:#
|
||||||
|
# BOTTOM NAVIGATION
|
||||||
|
#:################################################################################:#
|
||||||
|
- button:
|
||||||
|
id: security_back_button
|
||||||
|
x: 612
|
||||||
|
y: 475
|
||||||
|
width: 190
|
||||||
|
height: 92
|
||||||
|
bg_color: 0x315843
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x548C6C
|
||||||
|
radius: 16
|
||||||
|
pressed:
|
||||||
|
bg_color: 0x416F55
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Back"
|
||||||
|
text_font: font_button
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
on_click:
|
||||||
|
- lambda: |-
|
||||||
|
id(security_page_active) = false;
|
||||||
|
|
||||||
|
- online_image.release: security_camera_image
|
||||||
|
|
||||||
|
- lvgl.page.show:
|
||||||
|
id: bedroom_page
|
||||||
|
animation: MOVE_RIGHT
|
||||||
|
time: 220ms
|
||||||
|
|
||||||
|
- button:
|
||||||
|
id: security_clock_button
|
||||||
|
x: 810
|
||||||
|
y: 475
|
||||||
|
width: 190
|
||||||
|
height: 92
|
||||||
|
bg_color: 0x214F72
|
||||||
|
bg_opa: COVER
|
||||||
|
border_width: 2
|
||||||
|
border_color: 0x3E7FA7
|
||||||
|
radius: 16
|
||||||
|
pressed:
|
||||||
|
bg_color: 0x2E6A96
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Clock"
|
||||||
|
text_font: font_button
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
on_click:
|
||||||
|
- lambda: |-
|
||||||
|
id(security_page_active) = false;
|
||||||
|
|
||||||
|
- online_image.release: security_camera_image
|
||||||
|
|
||||||
|
- lvgl.page.show:
|
||||||
|
id: clock_page
|
||||||
|
animation: FADE_IN
|
||||||
|
time: 250ms
|
||||||
|
|
||||||
|
- light.turn_on:
|
||||||
|
id: display_backlight
|
||||||
|
brightness: "${clock_brightness}"
|
||||||
|
transition_length: 1500ms
|
||||||
|
|
||||||
#:####################################################################################:#
|
#:####################################################################################:#
|
||||||
# CLOCK PAGE
|
# CLOCK PAGE
|
||||||
#:####################################################################################:#
|
#:####################################################################################:#
|
||||||
|
|||||||
Reference in New Issue
Block a user