This commit is contained in:
2026-03-26 12:10:21 +01:00
parent 1f4970c17c
commit d4d76db890
877 changed files with 631941 additions and 26195 deletions

View File

@@ -0,0 +1,292 @@
substitutions:
exit_icon: "\U0000e902"
globals:
- id: ${widget_name}_light_is_on
type: bool
initial_value: 'false'
- id: ${widget_name}_current_brightness
type: float
initial_value: '1.0'
- id: ${widget_name}_updating_from_ha
type: bool
initial_value: 'false'
- id: ${widget_name}_ha_data_received
type: bool
initial_value: 'false'
- id: ${widget_name}_sensors_ready_count
type: int
initial_value: '0'
sensor:
# Brightness sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_brightness
entity_id: "${light_entity}"
attribute: brightness
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_brightness_received = false;
if (!first_brightness_received) {
first_brightness_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_brightness_slider
value: !lambda return int(x);
else:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: '0.0'
- lvgl.slider.update:
id: ${widget_name}_light_rgb_brightness_slider
value: 0
- script.execute: ${widget_name}_check_all_data_ready
text_sensor:
- platform: homeassistant
id: ${widget_name}_light_rgb_state
entity_id: "${light_entity}"
on_value:
- lambda: |-
bool is_on = (x == "on");
bool was_on = id(${widget_name}_light_is_on);
id(${widget_name}_light_is_on) = is_on;
static bool first_state_received = false;
if (!first_state_received) {
first_state_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: color_amber
shadow_color: color_amber
shadow_opa: 100%
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_amber
else:
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: color_white
shadow_opa: TRANSP
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_steel_blue
- script.execute: ${widget_name}_check_all_data_ready
- platform: homeassistant
id: ${widget_name}_light_rgb_name
entity_id: "${light_entity}"
attribute: friendly_name
on_value:
then:
- lvgl.label.update:
id: ${widget_name}_light_rgb_lable_name
text: !lambda return x;
lvgl:
pages:
- id: ${widget_name}_light_rgb_page
bg_color: color_slate_blue_gray
widgets:
- button:
id: ${widget_name}_light_rgb_bg_name
y: 20
width: 440
height: 40
align: TOP_MID
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_lable_name
align: CENTER
text_font: nunito_16
text_color: color_misty_blue
text: "lightbulb"
- button:
id: ${widget_name}_light_rgb_bg_lightbulb
x: 20
y: 80
width: 200
height: 280
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- button:
id: ${widget_name}_light_rgb_light_on
width: 70
height: 70
y: 74
align: TOP_MID
bg_color: color_white
shadow_color: color_white
shadow_ofs_y: -3
shadow_spread: 3
shadow_width: 15
radius: 40
- image:
y: 30
align: CENTER
src: lightbulb_image
id: ${widget_name}_lightbulb_image
- button:
id: ${widget_name}_light_rgb_light_on_btn
width: 200
height: 280
align: CENTER
bg_opa: TRANSP
shadow_opa: TRANSP
on_press:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity}"
- obj:
id: ${widget_name}_light_rgb_bg_slider
x: -20
y: 80
width: 220
height: 380
align: TOP_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 20
widgets:
- button:
id: ${widget_name}_light_rgb_brightness_gradient
radius: 20
width: 100
height: 300
align: CENTER
bg_color: color_slate_blue_gray
bg_opa: 100%
shadow_opa: TRANSP
- slider:
id: ${widget_name}_light_rgb_brightness_slider
radius: 20
width: 100
height: 300
align: CENTER
bg_opa: TRANSP
min_value: 3
max_value: 255
indicator:
bg_color: color_amber
radius: 10
knob:
bg_opa: TRANSP
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- if:
condition:
lambda: 'return !id(${widget_name}_light_is_on) && x > 3;'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
brightness: !lambda return int(x);
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- if:
condition:
lambda: 'return x <= 3;'
then:
- homeassistant.service:
service: light.turn_off
data:
entity_id: "${light_entity}"
else:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
brightness: !lambda return int(x);
- button:
id: ${widget_name}_light_rgb_exit_on_btn
x: 20
y: -20
width: 200
height: 80
align: BOTTOM_LEFT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_exit_on_icon
align: CENTER
text_font: icons_28
text_color: color_misty_blue
text: "${exit_icon}"
on_press:
then:
- lvgl.widget.show: menu_controls_main
- lvgl.page.show:
id: lights_group_page
animation: OUT_RIGHT
time: 300ms
script:
- id: ${widget_name}_check_all_data_ready
mode: restart
then:
- delay: 500ms
- lambda: |-
if (id(${widget_name}_sensors_ready_count) >= 2) {
if (!id(${widget_name}_ha_data_received)) {
id(${widget_name}_ha_data_received) = true;
id(${widget_name}_updating_from_ha) = false;
}
}

View File

@@ -0,0 +1,798 @@
substitutions:
brightness_icon: "\U0000e900"
saturation_icon: "\U0000e901"
exit_icon: "\U0000e902"
globals:
- id: ${widget_name}_light_is_on
type: bool
initial_value: 'false'
- id: ${widget_name}_current_hue
type: float
initial_value: '0.0'
- id: ${widget_name}_current_saturation
type: float
initial_value: '0.0'
- id: ${widget_name}_current_brightness
type: float
initial_value: '1.0'
- id: ${widget_name}_current_color_temp
type: float
initial_value: '3000.0'
- id: ${widget_name}_updating_from_ha
type: bool
initial_value: 'false'
- id: ${widget_name}_ha_data_received
type: bool
initial_value: 'false'
- id: ${widget_name}_sensors_ready_count
type: int
initial_value: '0'
- id: ${widget_name}_is_temp_mode
type: bool
initial_value: 'true'
- id: ${widget_name}_mode_switching
type: bool
initial_value: 'false'
sensor:
# Brightness sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_brightness
entity_id: "${light_entity}"
attribute: brightness
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_brightness_received = false;
if (!first_brightness_received) {
first_brightness_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_brightness_slider
value: !lambda return int(x);
- lvgl.label.update:
id: ${widget_name}_light_rgb_brightness_state
text: !lambda |-
int percent = int((x / 255.0f) * 100 + 0.5f);
char buf[8];
snprintf(buf, sizeof(buf), "%d%%", percent);
return std::string(buf);
- script.execute: ${widget_name}_check_all_data_ready
# Color temp sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_color_temp
entity_id: "${light_entity}"
attribute: color_temp_kelvin
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_temp_received = false;
if (!first_temp_received) {
first_temp_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_color_temp
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_color_temp_slider
value: !lambda return int(x);
- if:
condition:
lambda: 'return id(${widget_name}_is_temp_mode) && !id(${widget_name}_mode_switching);'
then:
- script.execute:
id: ${widget_name}_update_color_temp_preview
kelvin: !lambda return x;
- script.execute: ${widget_name}_check_all_data_ready
text_sensor:
- platform: homeassistant
id: ${widget_name}_light_rgb_state
entity_id: "${light_entity}"
on_value:
- lambda: |-
bool is_on = (x == "on");
bool was_on = id(${widget_name}_light_is_on);
id(${widget_name}_light_is_on) = is_on;
static bool first_state_received = false;
if (!first_state_received) {
first_state_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_light_is_on);'
then:
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_steel_blue
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: color_white
shadow_opa: TRANSP
else:
- lambda: |-
if (id(${widget_name}_mode_switching)) return;
std::string rgb_str = id(${widget_name}_light_rgb_color).state;
size_t start = rgb_str.find('(') + 1;
size_t first_comma = rgb_str.find(',', start);
size_t second_comma = rgb_str.find(',', first_comma + 1);
size_t end = rgb_str.find(')', second_comma);
if (start < first_comma && first_comma < second_comma && second_comma < end) {
int red = atoi(rgb_str.substr(start, first_comma - start).c_str());
int green = atoi(rgb_str.substr(first_comma + 1, second_comma - first_comma - 1).c_str());
int blue = atoi(rgb_str.substr(second_comma + 1, end - second_comma - 1).c_str());
id(${widget_name}_update_light_button_color).execute(red, green, blue);
}
- script.execute: ${widget_name}_check_all_data_ready
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_hs
entity_id: "${light_entity}"
attribute: hs_color
on_value:
- if:
condition:
lambda: "return x != \"None\" && !x.empty();"
then:
- lambda: |-
static bool first_hs_received = false;
if (!first_hs_received) {
first_hs_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- lambda: |-
id(${widget_name}_updating_from_ha) = true;
std::string s = x;
size_t start = s.find('(') + 1;
size_t comma = s.find(',');
size_t end = s.find(')');
float hue = 0.0f, sat = 0.0f;
if (start < comma && comma < end) {
hue = atof(s.substr(start, comma - start).c_str());
sat = atof(s.substr(comma + 1, end - comma - 1).c_str());
}
id(${widget_name}_current_hue) = hue;
id(${widget_name}_current_saturation) = sat;
- lvgl.arc.update:
id: ${widget_name}_light_rgb_hue_arc
value: !lambda "return id(${widget_name}_current_hue);"
- lvgl.slider.update:
id: ${widget_name}_light_rgb_saturation_slider
value: !lambda "return id(${widget_name}_current_saturation) * 255.0f / 100.0f;"
- lvgl.label.update:
id: ${widget_name}_light_rgb_saturation_state
text: !lambda |-
return (std::to_string((int)id(${widget_name}_current_saturation)) + "%").c_str();
- if:
condition:
lambda: 'return !id(${widget_name}_is_temp_mode) && !id(${widget_name}_mode_switching);'
then:
- script.execute: ${widget_name}_update_light_color_from_current_values
- lambda: |-
id(${widget_name}_updating_from_ha) = false;
else:
- lambda: |-
static bool first_hs_received = false;
if (!first_hs_received) {
first_hs_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- script.execute: ${widget_name}_check_all_data_ready
- platform: homeassistant
id: ${widget_name}_light_rgb_color
entity_id: "${light_entity}"
attribute: rgb_color
- platform: homeassistant
id: ${widget_name}_light_rgb_name
entity_id: "${light_entity}"
attribute: friendly_name
on_value:
then:
- lvgl.label.update:
id: ${widget_name}_light_rgb_lable_name
text: !lambda return x;
lvgl:
gradients:
- id: ${widget_name}_color_temp_gradient
direction: ver
dither: none
stops:
- color: 0xA0C8FF
position: 0
- color: 0xE0F7FF
position: 80
- color: 0xFFFFFF
position: 128
- color: 0xFFFFE0
position: 175
- color: 0xFFD6A0
position: 255
pages:
- id: ${widget_name}_light_rgb_page
bg_color: color_slate_blue_gray
widgets:
- button:
id: ${widget_name}_light_rgb_bg_name
y: 20
width: 440
height: 40
align: TOP_MID
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_lable_name
align: CENTER
text_font: nunito_16
text_color: color_misty_blue
text: "lightbulb"
- button:
id: ${widget_name}_light_rgb_bg_lightbulb
x: 20
y: 80
width: 200
height: 180
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- button:
id: ${widget_name}_light_rgb_light_on
width: 70
height: 70
y: 24
align: TOP_MID
bg_color: color_white
shadow_color: color_white
shadow_ofs_y: -3
shadow_spread: 3
shadow_width: 15
radius: 40
- image:
y: 30
align: CENTER
src: lightbulb_image
id: ${widget_name}_lightbulb_image
- button:
id: ${widget_name}_light_rgb_light_on_btn
width: 200
height: 200
align: CENTER
bg_opa: TRANSP
shadow_opa: TRANSP
on_press:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity}"
- button:
id: ${widget_name}_light_rgb_mode_btn
x: 20
y: 60
width: 200
height: 40
align: LEFT_MID
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
checkable: true
state:
checked: false
checked:
bg_color: color_misty_blue
bg_opa: 30%
widgets:
- label:
id: ${widget_name}_light_rgb_mode_label
text_color: color_misty_blue
align: CENTER
text_font: nunito_16
text: "TEMP MODE"
on_press:
- lambda: |-
id(${widget_name}_is_temp_mode) = !id(${widget_name}_is_temp_mode);
- if:
condition:
lambda: 'return id(${widget_name}_is_temp_mode);'
then:
- lvgl.label.update:
id: ${widget_name}_light_rgb_mode_label
text: "TEMP MODE"
- script.execute: ${widget_name}_switch_to_temp_mode
else:
- lvgl.label.update:
id: ${widget_name}_light_rgb_mode_label
text: "RGB MODE"
- script.execute: ${widget_name}_switch_to_rgb_mode
- obj:
id: ${widget_name}_light_rgb_bg_slider
x: -20
y: 80
width: 220
height: 240
align: TOP_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- image:
id: ${widget_name}_light_rgb_hue_gradient
align: CENTER
src: rgb_img
hidden: true
- arc:
id: ${widget_name}_light_rgb_hue_arc
arc_opa: TRANSP
arc_width: 50
start_angle: 0
end_angle: 360
adjustable: true
adv_hittest: true
align: CENTER
width: 190
height: 190
min_value: 0
max_value: 360
hidden: true
indicator:
arc_opa: TRANSP
arc_width: 50
knob:
border_color: color_white
border_width: 3
bg_opa: TRANSP
pad_all: -7
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_hue
value: !lambda "return (float)x;"
- script.execute: ${widget_name}_update_light_color_from_current_values
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- script.execute: ${widget_name}_send_hs_color_to_ha
- button:
id: ${widget_name}_light_rgb_color_temp_gradient
radius: 20
width: 100
height: 200
align: CENTER
bg_grad: ${widget_name}_color_temp_gradient
bg_grad_dir: ver
shadow_opa: TRANSP
- slider:
id: ${widget_name}_light_rgb_color_temp_slider
radius: 20
width: 100
height: 200
align: CENTER
bg_opa: TRANSP
min_value: 2000
max_value: 6500
indicator:
bg_opa: TRANSP
knob:
bg_opa: TRANSP
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_color_temp
value: !lambda return x;
- script.execute:
id: ${widget_name}_update_color_temp_preview
kelvin: !lambda return x;
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
color_temp_kelvin: !lambda return int(x);
- obj:
id: ${widget_name}_light_rgb_bg_bs_sliders
x: -20
y: -20
width: 360
height: 120
align: BOTTOM_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- obj:
id: ${widget_name}_light_rgb_brightness_pos
pad_all: 0
width: 320
height: 40
align: CENTER
bg_opa: TRANSP
border_width: 0
widgets:
- label:
id: ${widget_name}_light_rgb_brightness_icon
x: 0
y: 0
align: LEFT_MID
text_font: icons_24
text_color: color_misty_blue
text: "${brightness_icon}"
- label:
id: ${widget_name}_light_rgb_brightness_state
x: 0
y: 0
align: RIGHT_MID
text_font: nunito_16
text_color: color_misty_blue
text: "100%"
- slider:
id: ${widget_name}_light_rgb_brightness_slider
bg_color: color_slate_blue_gray
bg_opa: 100%
align: CENTER
x: -10
y: 1
width: 220
height: 10
min_value: 3
max_value: 255
indicator:
bg_color: color_misty_blue
radius: 30
knob:
bg_color: color_misty_blue
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.label.update:
id: ${widget_name}_light_rgb_brightness_state
text: !lambda |-
int percent = int((x / 255.0f) * 100 + 0.5f);
char buf[8];
snprintf(buf, sizeof(buf), "%d%%", percent);
return std::string(buf);
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
brightness: !lambda return int(x);
- obj:
id: ${widget_name}_light_rgb_saturation_container
pad_all: 0
width: 320
height: 40
align: BOTTOM_MID
bg_opa: TRANSP
border_width: 0
hidden: true
widgets:
- label:
id: ${widget_name}_light_rgb_saturation_icon
x: 0
y: 0
align: LEFT_MID
text_font: icons_24
text_color: color_misty_blue
text: "${saturation_icon}"
- label:
id: ${widget_name}_light_rgb_saturation_state
x: 0
y: 0
align: RIGHT_MID
text_font: nunito_16
text_color: color_misty_blue
text: "100%"
- slider:
id: ${widget_name}_light_rgb_saturation_slider
bg_color: color_slate_blue_gray
bg_opa: 100%
align: CENTER
x: -10
y: 0
width: 220
height: 10
min_value: 1
max_value: 255
indicator:
bg_color: color_misty_blue
knob:
bg_color: color_misty_blue
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_saturation
value: !lambda "return (float)x * 100.0f / 255.0f;"
- lvgl.label.update:
id: ${widget_name}_light_rgb_saturation_state
text: !lambda |-
int percent = int((float)x * 100.0f / 255.0f + 0.5f);
return (std::to_string(percent) + "%").c_str();
- script.execute: ${widget_name}_update_light_color_from_current_values
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- script.execute: ${widget_name}_send_hs_color_to_ha
- button:
id: ${widget_name}_light_rgb_exit_on_btn
x: 20
y: -20
width: 60
height: 120
align: BOTTOM_LEFT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_exit_on_icon
align: CENTER
text_font: icons_28
text_color: color_misty_blue
text: "${exit_icon}"
on_press:
then:
- lvgl.widget.show: menu_controls_main
- lvgl.page.show:
id: lights_group_page
animation: OUT_RIGHT
time: 300ms
script:
- id: ${widget_name}_check_all_data_ready
mode: restart
then:
- delay: 500ms
- lambda: |-
if (id(${widget_name}_sensors_ready_count) >= 4) {
if (!id(${widget_name}_ha_data_received)) {
id(${widget_name}_ha_data_received) = true;
id(${widget_name}_updating_from_ha) = false;
id(${widget_name}_switch_to_temp_mode).execute();
}
}
- id: ${widget_name}_switch_to_temp_mode
then:
- globals.set:
id: ${widget_name}_mode_switching
value: 'true'
- lvgl.widget.hide: ${widget_name}_light_rgb_hue_arc
- lvgl.widget.hide: ${widget_name}_light_rgb_hue_gradient
- lvgl.widget.hide: ${widget_name}_light_rgb_saturation_container
- lvgl.widget.show: ${widget_name}_light_rgb_color_temp_slider
- lvgl.widget.show: ${widget_name}_light_rgb_color_temp_gradient
- lvgl.obj.update:
id: ${widget_name}_light_rgb_brightness_pos
align: CENTER
- delay: 500ms
- globals.set:
id: ${widget_name}_mode_switching
value: 'false'
- id: ${widget_name}_switch_to_rgb_mode
then:
- globals.set:
id: ${widget_name}_mode_switching
value: 'true'
- lvgl.widget.show: ${widget_name}_light_rgb_hue_arc
- lvgl.widget.show: ${widget_name}_light_rgb_hue_gradient
- lvgl.widget.show: ${widget_name}_light_rgb_saturation_container
- lvgl.widget.hide: ${widget_name}_light_rgb_color_temp_slider
- lvgl.widget.hide: ${widget_name}_light_rgb_color_temp_gradient
- lvgl.obj.update:
id: ${widget_name}_light_rgb_brightness_pos
align: TOP_MID
- delay: 500ms
- globals.set:
id: ${widget_name}_mode_switching
value: 'false'
- id: ${widget_name}_update_light_color_from_current_values
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on) && !id(${widget_name}_mode_switching);'
then:
- script.execute:
id: ${widget_name}_hsv_to_rgb_and_update
hue: !lambda "return id(${widget_name}_current_hue);"
saturation: !lambda "return id(${widget_name}_current_saturation);"
brightness: 1.0
- id: ${widget_name}_update_color_temp_preview
parameters:
kelvin: float
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on) && !id(${widget_name}_mode_switching);'
then:
- lambda: |-
float temp = kelvin / 100.0f;
float r, g, b;
if (temp <= 66.0f) {
r = 255.0f;
g = 99.4708025861f * logf(temp) - 161.1195681661f;
if (temp <= 19.0f) {
b = 0.0f;
} else {
b = 138.5177312231f * logf(temp - 10.0f) - 305.0447927307f;
}
} else {
r = 329.698727446f * powf(temp - 60.0f, -0.1332047592f);
g = 288.1221695283f * powf(temp - 60.0f, -0.0755148492f);
b = 255.0f;
}
r = std::clamp(r, 0.0f, 255.0f);
g = std::clamp(g, 0.0f, 255.0f);
b = std::clamp(b, 0.0f, 255.0f);
int red = static_cast<int>(r);
int green = static_cast<int>(g);
int blue = static_cast<int>(b);
id(${widget_name}_update_light_button_color).execute(red, green, blue);
- id: ${widget_name}_update_light_button_color
parameters:
red: int
green: int
blue: int
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: !lambda |-
return lv_color_make(red, green, blue);
shadow_color: !lambda |-
return lv_color_make(red, green, blue);
shadow_opa: 100%
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: !lambda |-
return lv_color_make(red, green, blue);
- id: ${widget_name}_hsv_to_rgb_and_update
parameters:
hue: float
saturation: float
brightness: float
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lambda: |-
float h = hue;
float s = saturation / 100.0f;
float v = brightness;
float c = v * s;
float x_val = c * (1.0f - abs(fmod(h / 60.0f, 2.0f) - 1.0f));
float m = v - c;
float r, g, b;
if (h >= 0 && h < 60) {
r = c; g = x_val; b = 0;
} else if (h >= 60 && h < 120) {
r = x_val; g = c; b = 0;
} else if (h >= 120 && h < 180) {
r = 0; g = c; b = x_val;
} else if (h >= 180 && h < 240) {
r = 0; g = x_val; b = c;
} else if (h >= 240 && h < 300) {
r = x_val; g = 0; b = c;
} else {
r = c; g = 0; b = x_val;
}
int red = (int)((r + m) * 255.0f);
int green = (int)((g + m) * 255.0f);
int blue = (int)((b + m) * 255.0f);
id(${widget_name}_update_light_button_color).execute(red, green, blue);
- id: ${widget_name}_send_hs_color_to_ha
then:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
data_template:
hs_color: "{{ (hue|float, sat|float)|list }}"
variables:
hue: !lambda "return id(${widget_name}_current_hue);"
sat: !lambda "return id(${widget_name}_current_saturation);"

View File

@@ -0,0 +1,815 @@
substitutions:
brightness_icon: "\U0000e900"
saturation_icon: "\U0000e901"
exit_icon: "\U0000e902"
globals:
- id: ${widget_name}_light_is_on
type: bool
initial_value: 'false'
- id: ${widget_name}_current_hue
type: float
initial_value: '0.0'
- id: ${widget_name}_current_saturation
type: float
initial_value: '0.0'
- id: ${widget_name}_current_brightness
type: float
initial_value: '1.0'
- id: ${widget_name}_current_color_temp
type: float
initial_value: '3000.0'
- id: ${widget_name}_updating_from_ha
type: bool
initial_value: 'false'
- id: ${widget_name}_ha_data_received
type: bool
initial_value: 'false'
- id: ${widget_name}_sensors_ready_count
type: int
initial_value: '0'
- id: ${widget_name}_is_temp_mode
type: bool
initial_value: 'true'
- id: ${widget_name}_mode_switching
type: bool
initial_value: 'false'
sensor:
# Brightness sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_brightness
entity_id: "${light_entity}"
attribute: brightness
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_brightness_received = false;
if (!first_brightness_received) {
first_brightness_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_brightness_slider
value: !lambda return int(x);
- lvgl.label.update:
id: ${widget_name}_light_rgb_brightness_state
text: !lambda |-
int percent = int((x / 255.0f) * 100 + 0.5f);
char buf[8];
snprintf(buf, sizeof(buf), "%d%%", percent);
return std::string(buf);
- script.execute: ${widget_name}_check_all_data_ready
# Color temp sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_color_temp
entity_id: "${light_entity}"
attribute: color_temp_kelvin
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_temp_received = false;
if (!first_temp_received) {
first_temp_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_color_temp
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_color_temp_slider
value: !lambda return int(x);
- if:
condition:
lambda: 'return id(${widget_name}_is_temp_mode) && !id(${widget_name}_mode_switching);'
then:
- script.execute:
id: ${widget_name}_update_color_temp_preview
kelvin: !lambda return x;
- script.execute: ${widget_name}_check_all_data_ready
text_sensor:
- platform: homeassistant
id: ${widget_name}_light_rgb_state
entity_id: "${light_entity}"
on_value:
- lambda: |-
bool is_on = (x == "on");
bool was_on = id(${widget_name}_light_is_on);
id(${widget_name}_light_is_on) = is_on;
static bool first_state_received = false;
if (!first_state_received) {
first_state_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_light_is_on);'
then:
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_steel_blue
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: color_white
shadow_opa: TRANSP
else:
- lambda: |-
if (id(${widget_name}_mode_switching)) return;
std::string rgb_str = id(${widget_name}_light_rgb_color).state;
size_t start = rgb_str.find('(') + 1;
size_t first_comma = rgb_str.find(',', start);
size_t second_comma = rgb_str.find(',', first_comma + 1);
size_t end = rgb_str.find(')', second_comma);
if (start < first_comma && first_comma < second_comma && second_comma < end) {
int red = atoi(rgb_str.substr(start, first_comma - start).c_str());
int green = atoi(rgb_str.substr(first_comma + 1, second_comma - first_comma - 1).c_str());
int blue = atoi(rgb_str.substr(second_comma + 1, end - second_comma - 1).c_str());
id(${widget_name}_update_light_button_color).execute(red, green, blue);
}
- script.execute: ${widget_name}_check_all_data_ready
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_hs
entity_id: "${light_entity}"
attribute: hs_color
on_value:
- if:
condition:
lambda: "return x != \"None\" && !x.empty();"
then:
- lambda: |-
static bool first_hs_received = false;
if (!first_hs_received) {
first_hs_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- lambda: |-
id(${widget_name}_updating_from_ha) = true;
std::string s = x;
size_t start = s.find('(') + 1;
size_t comma = s.find(',');
size_t end = s.find(')');
float hue = 0.0f, sat = 0.0f;
if (start < comma && comma < end) {
hue = atof(s.substr(start, comma - start).c_str());
sat = atof(s.substr(comma + 1, end - comma - 1).c_str());
}
id(${widget_name}_current_hue) = hue;
id(${widget_name}_current_saturation) = sat;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_hue_slider
value: !lambda "return id(${widget_name}_current_hue);"
- lvgl.slider.update:
id: ${widget_name}_light_rgb_saturation_slider
value: !lambda "return id(${widget_name}_current_saturation) * 255.0f / 100.0f;"
- lvgl.label.update:
id: ${widget_name}_light_rgb_saturation_state
text: !lambda |-
return (std::to_string((int)id(${widget_name}_current_saturation)) + "%").c_str();
- if:
condition:
lambda: 'return !id(${widget_name}_is_temp_mode) && !id(${widget_name}_mode_switching);'
then:
- script.execute: ${widget_name}_update_light_color_from_current_values
- lambda: |-
id(${widget_name}_updating_from_ha) = false;
else:
- lambda: |-
static bool first_hs_received = false;
if (!first_hs_received) {
first_hs_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- script.execute: ${widget_name}_check_all_data_ready
- platform: homeassistant
id: ${widget_name}_light_rgb_color
entity_id: "${light_entity}"
attribute: rgb_color
- platform: homeassistant
id: ${widget_name}_light_rgb_name
entity_id: "${light_entity}"
attribute: friendly_name
on_value:
then:
- lvgl.label.update:
id: ${widget_name}_light_rgb_lable_name
text: !lambda return x;
lvgl:
gradients:
- id: ${widget_name}_hue_gradient
direction: ver
dither: none
stops:
- color: 0xFF0000
position: 0
- color: 0xFF00FF
position: 42
- color: 0x0000FF
position: 84
- color: 0x00FFFF
position: 127
- color: 0x00FF00
position: 169
- color: 0xFFFF00
position: 212
- color: 0xFF0000
position: 255
- id: ${widget_name}_color_temp_gradient
direction: ver
dither: none
stops:
- color: 0xA0C8FF
position: 0
- color: 0xE0F7FF
position: 80
- color: 0xFFFFFF
position: 128
- color: 0xFFFFE0
position: 175
- color: 0xFFD6A0
position: 255
pages:
- id: ${widget_name}_light_rgb_page
bg_color: color_slate_blue_gray
widgets:
- button:
id: ${widget_name}_light_rgb_bg_name
y: 20
width: 440
height: 40
align: TOP_MID
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_lable_name
align: CENTER
text_font: nunito_16
text_color: color_misty_blue
text: "lightbulb"
- button:
id: ${widget_name}_light_rgb_bg_lightbulb
x: 20
y: 80
width: 200
height: 180
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- button:
id: ${widget_name}_light_rgb_light_on
width: 70
height: 70
y: 24
align: TOP_MID
bg_color: color_white
shadow_color: color_white
shadow_ofs_y: -3
shadow_spread: 3
shadow_width: 15
radius: 40
- image:
y: 30
align: CENTER
src: lightbulb_image
id: ${widget_name}_lightbulb_image
- button:
id: ${widget_name}_light_rgb_light_on_btn
width: 200
height: 200
align: CENTER
bg_opa: TRANSP
shadow_opa: TRANSP
on_press:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity}"
- button:
id: ${widget_name}_light_rgb_mode_btn
x: 20
y: 60
width: 200
height: 40
align: LEFT_MID
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
checkable: true
state:
checked: false
checked:
bg_color: color_misty_blue
bg_opa: 30%
widgets:
- label:
id: ${widget_name}_light_rgb_mode_label
text_color: color_misty_blue
align: CENTER
text_font: nunito_16
text: "TEMP MODE"
on_press:
- lambda: |-
id(${widget_name}_is_temp_mode) = !id(${widget_name}_is_temp_mode);
- if:
condition:
lambda: 'return id(${widget_name}_is_temp_mode);'
then:
- lvgl.label.update:
id: ${widget_name}_light_rgb_mode_label
text: "TEMP MODE"
- script.execute: ${widget_name}_switch_to_temp_mode
else:
- lvgl.label.update:
id: ${widget_name}_light_rgb_mode_label
text: "RGB MODE"
- script.execute: ${widget_name}_switch_to_rgb_mode
- obj:
id: ${widget_name}_light_rgb_bg_slider
x: -20
y: 80
width: 220
height: 240
align: TOP_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- button:
id: ${widget_name}_light_rgb_hue_gradient
radius: 20
width: 100
height: 200
align: CENTER
bg_grad: ${widget_name}_hue_gradient
bg_grad_dir: ver
shadow_opa: TRANSP
hidden: true
- slider:
id: ${widget_name}_light_rgb_hue_slider
radius: 20
align: CENTER
bg_opa: TRANSP
width: 100
height: 200
min_value: 0
max_value: 360
hidden: true
indicator:
bg_opa: TRANSP
knob:
bg_opa: TRANSP
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_hue
value: !lambda "return (float)x;"
- script.execute: ${widget_name}_update_light_color_from_current_values
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- script.execute: ${widget_name}_send_hs_color_to_ha
- button:
id: ${widget_name}_light_rgb_color_temp_gradient
radius: 20
width: 100
height: 200
align: CENTER
bg_grad: ${widget_name}_color_temp_gradient
bg_grad_dir: ver
shadow_opa: TRANSP
- slider:
id: ${widget_name}_light_rgb_color_temp_slider
radius: 20
width: 100
height: 200
align: CENTER
bg_opa: TRANSP
min_value: 2000
max_value: 6500
indicator:
bg_opa: TRANSP
knob:
bg_opa: TRANSP
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_color_temp
value: !lambda return x;
- script.execute:
id: ${widget_name}_update_color_temp_preview
kelvin: !lambda return x;
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
color_temp_kelvin: !lambda return int(x);
- obj:
id: ${widget_name}_light_rgb_bg_bs_sliders
x: -20
y: -20
width: 360
height: 120
align: BOTTOM_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- obj:
id: ${widget_name}_light_rgb_brightness_pos
pad_all: 0
width: 320
height: 40
align: CENTER
bg_opa: TRANSP
border_width: 0
widgets:
- label:
id: ${widget_name}_light_rgb_brightness_icon
x: 0
y: 0
align: LEFT_MID
text_font: icons_24
text_color: color_misty_blue
text: "${brightness_icon}"
- label:
id: ${widget_name}_light_rgb_brightness_state
x: 0
y: 0
align: RIGHT_MID
text_font: nunito_16
text_color: color_misty_blue
text: "100%"
- slider:
id: ${widget_name}_light_rgb_brightness_slider
bg_color: color_slate_blue_gray
bg_opa: 100%
align: CENTER
x: -10
y: 1
width: 220
height: 10
min_value: 3
max_value: 255
indicator:
bg_color: color_misty_blue
radius: 30
knob:
bg_color: color_misty_blue
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.label.update:
id: ${widget_name}_light_rgb_brightness_state
text: !lambda |-
int percent = int((x / 255.0f) * 100 + 0.5f);
char buf[8];
snprintf(buf, sizeof(buf), "%d%%", percent);
return std::string(buf);
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
brightness: !lambda return int(x);
- obj:
id: ${widget_name}_light_rgb_saturation_container
pad_all: 0
width: 320
height: 40
align: BOTTOM_MID
bg_opa: TRANSP
border_width: 0
hidden: true
widgets:
- label:
id: ${widget_name}_light_rgb_saturation_icon
x: 0
y: 0
align: LEFT_MID
text_font: icons_24
text_color: color_misty_blue
text: "${saturation_icon}"
- label:
id: ${widget_name}_light_rgb_saturation_state
x: 0
y: 0
align: RIGHT_MID
text_font: nunito_16
text_color: color_misty_blue
text: "100%"
- slider:
id: ${widget_name}_light_rgb_saturation_slider
bg_color: color_slate_blue_gray
bg_opa: 100%
align: CENTER
x: -10
y: 0
width: 220
height: 10
min_value: 1
max_value: 255
indicator:
bg_color: color_misty_blue
knob:
bg_color: color_misty_blue
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_saturation
value: !lambda "return (float)x * 100.0f / 255.0f;"
- lvgl.label.update:
id: ${widget_name}_light_rgb_saturation_state
text: !lambda |-
int percent = int((float)x * 100.0f / 255.0f + 0.5f);
return (std::to_string(percent) + "%").c_str();
- script.execute: ${widget_name}_update_light_color_from_current_values
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- script.execute: ${widget_name}_send_hs_color_to_ha
- button:
id: ${widget_name}_light_rgb_exit_on_btn
x: 20
y: -20
width: 60
height: 120
align: BOTTOM_LEFT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_exit_on_icon
align: CENTER
text_font: icons_28
text_color: color_misty_blue
text: "${exit_icon}"
on_press:
then:
- lvgl.widget.show: menu_controls_main
- lvgl.page.show:
id: lights_group_page
animation: OUT_RIGHT
time: 300ms
script:
- id: ${widget_name}_check_all_data_ready
mode: restart
then:
- delay: 500ms
- lambda: |-
if (id(${widget_name}_sensors_ready_count) >= 4) {
if (!id(${widget_name}_ha_data_received)) {
id(${widget_name}_ha_data_received) = true;
id(${widget_name}_updating_from_ha) = false;
id(${widget_name}_switch_to_temp_mode).execute();
}
}
- id: ${widget_name}_switch_to_temp_mode
then:
- globals.set:
id: ${widget_name}_mode_switching
value: 'true'
- lvgl.widget.hide: ${widget_name}_light_rgb_hue_slider
- lvgl.widget.hide: ${widget_name}_light_rgb_hue_gradient
- lvgl.widget.hide: ${widget_name}_light_rgb_saturation_container
- lvgl.widget.show: ${widget_name}_light_rgb_color_temp_slider
- lvgl.widget.show: ${widget_name}_light_rgb_color_temp_gradient
- lvgl.obj.update:
id: ${widget_name}_light_rgb_brightness_pos
align: CENTER
- delay: 500ms
- globals.set:
id: ${widget_name}_mode_switching
value: 'false'
- id: ${widget_name}_switch_to_rgb_mode
then:
- globals.set:
id: ${widget_name}_mode_switching
value: 'true'
- lvgl.widget.show: ${widget_name}_light_rgb_hue_slider
- lvgl.widget.show: ${widget_name}_light_rgb_hue_gradient
- lvgl.widget.show: ${widget_name}_light_rgb_saturation_container
- lvgl.widget.hide: ${widget_name}_light_rgb_color_temp_slider
- lvgl.widget.hide: ${widget_name}_light_rgb_color_temp_gradient
- lvgl.obj.update:
id: ${widget_name}_light_rgb_brightness_pos
align: TOP_MID
- delay: 500ms
- globals.set:
id: ${widget_name}_mode_switching
value: 'false'
- id: ${widget_name}_update_light_color_from_current_values
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on) && !id(${widget_name}_mode_switching);'
then:
- script.execute:
id: ${widget_name}_hsv_to_rgb_and_update
hue: !lambda "return id(${widget_name}_current_hue);"
saturation: !lambda "return id(${widget_name}_current_saturation);"
brightness: 1.0
- id: ${widget_name}_update_color_temp_preview
parameters:
kelvin: float
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on) && !id(${widget_name}_mode_switching);'
then:
- lambda: |-
float temp = kelvin / 100.0f;
float r, g, b;
if (temp <= 66.0f) {
r = 255.0f;
g = 99.4708025861f * logf(temp) - 161.1195681661f;
if (temp <= 19.0f) {
b = 0.0f;
} else {
b = 138.5177312231f * logf(temp - 10.0f) - 305.0447927307f;
}
} else {
r = 329.698727446f * powf(temp - 60.0f, -0.1332047592f);
g = 288.1221695283f * powf(temp - 60.0f, -0.0755148492f);
b = 255.0f;
}
r = std::clamp(r, 0.0f, 255.0f);
g = std::clamp(g, 0.0f, 255.0f);
b = std::clamp(b, 0.0f, 255.0f);
int red = static_cast<int>(r);
int green = static_cast<int>(g);
int blue = static_cast<int>(b);
id(${widget_name}_update_light_button_color).execute(red, green, blue);
- id: ${widget_name}_update_light_button_color
parameters:
red: int
green: int
blue: int
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: !lambda |-
return lv_color_make(red, green, blue);
shadow_color: !lambda |-
return lv_color_make(red, green, blue);
shadow_opa: 100%
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: !lambda |-
return lv_color_make(red, green, blue);
- id: ${widget_name}_hsv_to_rgb_and_update
parameters:
hue: float
saturation: float
brightness: float
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lambda: |-
float h = hue;
float s = saturation / 100.0f;
float v = brightness;
float c = v * s;
float x_val = c * (1.0f - abs(fmod(h / 60.0f, 2.0f) - 1.0f));
float m = v - c;
float r, g, b;
if (h >= 0 && h < 60) {
r = c; g = x_val; b = 0;
} else if (h >= 60 && h < 120) {
r = x_val; g = c; b = 0;
} else if (h >= 120 && h < 180) {
r = 0; g = c; b = x_val;
} else if (h >= 180 && h < 240) {
r = 0; g = x_val; b = c;
} else if (h >= 240 && h < 300) {
r = x_val; g = 0; b = c;
} else {
r = c; g = 0; b = x_val;
}
int red = (int)((r + m) * 255.0f);
int green = (int)((g + m) * 255.0f);
int blue = (int)((b + m) * 255.0f);
id(${widget_name}_update_light_button_color).execute(red, green, blue);
- id: ${widget_name}_send_hs_color_to_ha
then:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
data_template:
hs_color: "{{ (hue|float, sat|float)|list }}"
variables:
hue: !lambda "return id(${widget_name}_current_hue);"
sat: !lambda "return id(${widget_name}_current_saturation);"

View File

@@ -0,0 +1,18 @@
text_sensor:
- platform: homeassistant
id: ${widget_name}_light_rgb_state
entity_id: "${light_entity}"
on_value:
- if:
condition:
lambda: 'return x == "on";'
then:
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_amber
else:
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_steel_blue

View File

@@ -0,0 +1,470 @@
substitutions:
brightness_icon: "\U0000e900"
exit_icon: "\U0000e902"
globals:
- id: ${widget_name}_light_is_on
type: bool
initial_value: 'false'
- id: ${widget_name}_current_brightness
type: float
initial_value: '1.0'
- id: ${widget_name}_current_color_temp
type: float
initial_value: '3000.0'
- id: ${widget_name}_updating_from_ha
type: bool
initial_value: 'false'
- id: ${widget_name}_ha_data_received
type: bool
initial_value: 'false'
- id: ${widget_name}_sensors_ready_count
type: int
initial_value: '0'
sensor:
# Brightness sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_brightness
entity_id: "${light_entity}"
attribute: brightness
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_brightness_received = false;
if (!first_brightness_received) {
first_brightness_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_brightness_slider
value: !lambda return int(x);
- lvgl.label.update:
id: ${widget_name}_light_rgb_brightness_state
text: !lambda |-
int percent = int((x / 255.0f) * 100 + 0.5f);
char buf[8];
snprintf(buf, sizeof(buf), "%d%%", percent);
return std::string(buf);
- script.execute: ${widget_name}_check_all_data_ready
# Color temp sensor
- platform: homeassistant
id: ${widget_name}_light_rgb_sensor_color_temp
entity_id: "${light_entity}"
attribute: color_temp_kelvin
on_value:
- if:
condition:
lambda: 'return !std::isnan(x);'
then:
- lambda: |-
static bool first_temp_received = false;
if (!first_temp_received) {
first_temp_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha);'
then:
- globals.set:
id: ${widget_name}_current_color_temp
value: !lambda return x;
- lvgl.slider.update:
id: ${widget_name}_light_rgb_color_temp_slider
value: !lambda return int(x);
- script.execute:
id: ${widget_name}_update_color_temp_preview
kelvin: !lambda return x;
- script.execute: ${widget_name}_check_all_data_ready
text_sensor:
- platform: homeassistant
id: ${widget_name}_light_rgb_state
entity_id: "${light_entity}"
on_value:
- lambda: |-
bool is_on = (x == "on");
bool was_on = id(${widget_name}_light_is_on);
id(${widget_name}_light_is_on) = is_on;
static bool first_state_received = false;
if (!first_state_received) {
first_state_received = true;
id(${widget_name}_sensors_ready_count) += 1;
}
# Обновляем UI только при выключении
- if:
condition:
lambda: 'return !id(${widget_name}_light_is_on);'
then:
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: color_steel_blue
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: color_white
shadow_opa: TRANSP
else:
- lambda: |-
std::string rgb_str = id(${widget_name}_light_rgb_color).state;
size_t start = rgb_str.find('(') + 1;
size_t first_comma = rgb_str.find(',', start);
size_t second_comma = rgb_str.find(',', first_comma + 1);
size_t end = rgb_str.find(')', second_comma);
if (start < first_comma && first_comma < second_comma && second_comma < end) {
int red = atoi(rgb_str.substr(start, first_comma - start).c_str());
int green = atoi(rgb_str.substr(first_comma + 1, second_comma - first_comma - 1).c_str());
int blue = atoi(rgb_str.substr(second_comma + 1, end - second_comma - 1).c_str());
id(${widget_name}_update_light_button_color).execute(red, green, blue);
}
- script.execute: ${widget_name}_check_all_data_ready
- platform: homeassistant
id: ${widget_name}_light_rgb_color
entity_id: "${light_entity}"
attribute: rgb_color
- platform: homeassistant
id: ${widget_name}_light_rgb_name
entity_id: "${light_entity}"
attribute: friendly_name
on_value:
then:
- lvgl.label.update:
id: ${widget_name}_light_rgb_lable_name
text: !lambda return x;
lvgl:
gradients:
- id: ${widget_name}_color_temp_gradient
direction: ver
dither: none
stops:
- color: 0xA0C8FF
position: 0
- color: 0xE0F7FF
position: 80
- color: 0xFFFFFF
position: 128
- color: 0xFFFFE0
position: 175
- color: 0xFFD6A0
position: 255
pages:
- id: ${widget_name}_light_rgb_page
bg_color: color_slate_blue_gray
widgets:
- button:
id: ${widget_name}_light_rgb_bg_name
y: 20
width: 440
height: 40
align: TOP_MID
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_lable_name
align: CENTER
text_font: nunito_16
text_color: color_misty_blue
text: "lightbulb"
- button:
id: ${widget_name}_light_rgb_bg_lightbulb
x: 20
y: 80
width: 200
height: 280
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- button:
id: ${widget_name}_light_rgb_light_on
width: 70
height: 70
y: 74
align: TOP_MID
bg_color: color_white
shadow_color: color_white
shadow_ofs_y: -3
shadow_spread: 3
shadow_width: 15
radius: 40
- image:
y: 30
align: CENTER
src: lightbulb_image
id: ${widget_name}_lightbulb_image
- button:
id: ${widget_name}_light_rgb_light_on_btn
width: 200
height: 280
align: CENTER
bg_opa: TRANSP
shadow_opa: TRANSP
on_press:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity}"
- obj:
id: ${widget_name}_light_rgb_bg_slider
x: -20
y: 80
width: 220
height: 280
align: TOP_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- button:
id: ${widget_name}_light_rgb_color_temp_gradient
radius: 20
width: 100
height: 200
align: CENTER
bg_grad: ${widget_name}_color_temp_gradient
bg_grad_dir: ver
shadow_opa: TRANSP
- slider:
id: ${widget_name}_light_rgb_color_temp_slider
radius: 20
width: 100
height: 200
align: CENTER
bg_opa: TRANSP
min_value: 2000
max_value: 6500
indicator:
bg_opa: TRANSP
knob:
bg_opa: TRANSP
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_color_temp
value: !lambda return x;
- script.execute:
id: ${widget_name}_update_color_temp_preview
kelvin: !lambda return x;
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
color_temp_kelvin: !lambda return int(x);
- obj:
id: ${widget_name}_light_rgb_bg_bs_sliders
x: -20
y: -20
width: 360
height: 80
align: BOTTOM_RIGHT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- obj:
id: ${widget_name}_light_rgb_brightness_pos
pad_all: 0
width: 320
height: 40
align: CENTER
bg_opa: TRANSP
border_width: 0
widgets:
- label:
id: ${widget_name}_light_rgb_brightness_icon
x: 0
y: 0
align: LEFT_MID
text_font: icons_24
text_color: color_misty_blue
text: "${brightness_icon}"
- label:
id: ${widget_name}_light_rgb_brightness_state
x: 0
y: 0
align: RIGHT_MID
text_font: nunito_16
text_color: color_misty_blue
text: "100%"
- slider:
id: ${widget_name}_light_rgb_brightness_slider
bg_color: color_slate_blue_gray
bg_opa: 100%
align: CENTER
x: -10
y: 1
width: 220
height: 10
min_value: 3
max_value: 255
indicator:
bg_color: color_misty_blue
radius: 30
knob:
bg_color: color_misty_blue
on_value:
- if:
condition:
lambda: 'return !id(${widget_name}_updating_from_ha) && id(${widget_name}_ha_data_received);'
then:
- globals.set:
id: ${widget_name}_current_brightness
value: !lambda return x;
- lvgl.label.update:
id: ${widget_name}_light_rgb_brightness_state
text: !lambda |-
int percent = int((x / 255.0f) * 100 + 0.5f);
char buf[8];
snprintf(buf, sizeof(buf), "%d%%", percent);
return std::string(buf);
on_release:
- if:
condition:
lambda: 'return id(${widget_name}_ha_data_received);'
then:
- homeassistant.service:
service: light.turn_on
data:
entity_id: "${light_entity}"
brightness: !lambda return int(x);
- button:
id: ${widget_name}_light_rgb_exit_on_btn
x: 20
y: -20
width: 60
height: 80
align: BOTTOM_LEFT
bg_color: color_steel_blue
bg_opa: 20%
border_opa: TRANSP
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${widget_name}_light_rgb_exit_on_icon
align: CENTER
text_font: icons_28
text_color: color_misty_blue
text: "${exit_icon}"
on_press:
then:
- lvgl.widget.show: menu_controls_main
- lvgl.page.show:
id: lights_group_page
animation: OUT_RIGHT
time: 300ms
script:
- id: ${widget_name}_check_all_data_ready
mode: restart
then:
- delay: 500ms
- lambda: |-
if (id(${widget_name}_sensors_ready_count) >= 3) {
if (!id(${widget_name}_ha_data_received)) {
id(${widget_name}_ha_data_received) = true;
id(${widget_name}_updating_from_ha) = false;
}
}
- id: ${widget_name}_update_color_temp_preview
parameters:
kelvin: float
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lambda: |-
float temp = kelvin / 100.0f;
float r, g, b;
if (temp <= 66.0f) {
r = 255.0f;
g = 99.4708025861f * logf(temp) - 161.1195681661f;
if (temp <= 19.0f) {
b = 0.0f;
} else {
b = 138.5177312231f * logf(temp - 10.0f) - 305.0447927307f;
}
} else {
r = 329.698727446f * powf(temp - 60.0f, -0.1332047592f);
g = 288.1221695283f * powf(temp - 60.0f, -0.0755148492f);
b = 255.0f;
}
r = std::clamp(r, 0.0f, 255.0f);
g = std::clamp(g, 0.0f, 255.0f);
b = std::clamp(b, 0.0f, 255.0f);
int red = static_cast<int>(r);
int green = static_cast<int>(g);
int blue = static_cast<int>(b);
id(${widget_name}_update_light_button_color).execute(red, green, blue);
- id: ${widget_name}_update_light_button_color
parameters:
red: int
green: int
blue: int
then:
- if:
condition:
lambda: 'return id(${widget_name}_light_is_on);'
then:
- lvgl.button.update:
id: ${widget_name}_light_rgb_light_on
bg_color: !lambda |-
return lv_color_make(red, green, blue);
shadow_color: !lambda |-
return lv_color_make(red, green, blue);
shadow_opa: 100%
- lvgl.label.update:
id: ${widget_name}_light_on
text_color: !lambda |-
return lv_color_make(red, green, blue);

View File

@@ -0,0 +1,46 @@
substitutions:
light_entity_1: "light.lamp_tafel"
light_entity_2: "light.ikea_of_sweden_tradfri_bulb_e27_ww_clear_250lm"
light_entity_3: "light.ikea_of_sweden_stoftmoln_ceiling_wall_lamp_ww37"
light_entity_4: "light.lamp_kast"
light_widget_name_1: "lamptafel"
light_widget_name_2: "lampbank"
light_widget_name_3: "lampzitkamer"
light_widget_name_4: "lampkast"
light_label_name_1: "Tafel"
light_label_name_2: "Bank"
light_label_name_3: "Zithoek"
light_label_name_4: "Kast"
packages:
light_panel: !include lights_panel.yaml
light_1: !include
file: light_rgb_widget.yaml
vars:
light_entity: "${light_entity_1}"
widget_name: "${light_widget_name_1}"
light_2: !include
file: light_rgb_widget_slider.yaml
vars:
light_entity: "${light_entity_2}"
widget_name: "${light_widget_name_2}"
light_3: !include
file: light_temp_color_widget.yaml
vars:
light_entity: "${light_entity_3}"
widget_name: "${light_widget_name_3}"
light_4: !include
file: light_brightness_widget.yaml
vars:
light_entity: "${light_entity_4}"
widget_name: "${light_widget_name_4}"

View File

@@ -0,0 +1,208 @@
substitutions:
exit_icon: "\U0000e902"
settings_icon: "\U0000e903"
info_icon: "\U0000e904"
devices_icon: "\U0000e905"
home_icon: "\U0000e906"
ceiling_icon: "\U0000e907"
lightbulb_icon: "\U0000e908"
spotlight_group_icon: "\U0000e915"
desk_lamp_icon: "\U0000e916"
pendant_lamp_icon: "\U0000e917"
ceiling_lamp_icon: "\U0000e918"
ceiling_lamp_variant_icon: "\U0000e921"
night_lamp_icon: "\U0000e919"
swipe_icon: "\U0000e91a"
lvgl:
pages:
- id: lights_group_page
bg_color: color_slate_blue_gray
widgets:
- button:
id: ${light_widget_name_1}_btn
x: 20
y: 20
width: 210
height: 160
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${light_widget_name_1}_light_on
align: CENTER
text_color: color_steel_blue
text_font: icons_90
text: "${spotlight_group_icon}"
- label:
id: ${light_widget_name_1}_lable_name
align: TOP_MID
text_font: nunito_16
text_color: color_misty_blue
text: "${light_label_name_1}"
- button:
id: ${light_widget_name_2}_btn
x: 250
y: 20
width: 210
height: 160
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${light_widget_name_2}_light_on
align: CENTER
text_color: color_steel_blue
text_font: icons_90
text: "${pendant_lamp_icon}"
- label:
id: ${light_widget_name_2}_lable_name
align: TOP_MID
text_font: nunito_16
text_color: color_misty_blue
text: "${light_label_name_2}"
- button:
id: ${light_widget_name_3}_btn
x: 20
y: 200
width: 210
height: 160
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${light_widget_name_3}_light_on
align: CENTER
text_color: color_steel_blue
text_font: icons_90
text: "${ceiling_lamp_variant_icon}"
- label:
id: ${light_widget_name_3}_lable_name
align: TOP_MID
text_font: nunito_16
text_color: color_misty_blue
text: "${light_label_name_3}"
- button:
id: ${light_widget_name_4}_btn
x: 250
y: 200
width: 210
height: 160
align: TOP_LEFT
bg_color: color_steel_blue
bg_opa: 20%
shadow_opa: TRANSP
radius: 10
widgets:
- label:
id: ${light_widget_name_4}_light_on
align: CENTER
text_color: color_steel_blue
text_font: icons_90
text: "${night_lamp_icon}"
- label:
id: ${light_widget_name_4}_lable_name
align: TOP_MID
text_font: nunito_16
text_color: color_misty_blue
text: "${light_label_name_4}"
binary_sensor:
- platform: lvgl
id: ${light_widget_name_1}_btn_long_sensor
widget: ${light_widget_name_1}_btn
on_click:
- min_length: 50ms
max_length: 500ms
then:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity_1}"
- min_length: 800ms
max_length: 3000ms
then:
- lvgl.widget.hide: menu_controls_main
- lvgl.page.show:
id: ${light_widget_name_1}_light_rgb_page
animation: OUT_RIGHT
time: 300ms
- platform: lvgl
id: ${light_widget_name_2}_btn_long_sensor
widget: ${light_widget_name_2}_btn
on_click:
- min_length: 50ms
max_length: 500ms
then:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity_2}"
- min_length: 800ms
max_length: 3000ms
then:
- lvgl.widget.hide: menu_controls_main
- lvgl.page.show:
id: ${light_widget_name_2}_light_rgb_page
animation: OUT_RIGHT
time: 300ms
- platform: lvgl
id: ${light_widget_name_3}_btn_long_sensor
widget: ${light_widget_name_3}_btn
on_click:
- min_length: 50ms
max_length: 500ms
then:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity_3}"
- min_length: 800ms
max_length: 3000ms
then:
- lvgl.widget.hide: menu_controls_main
- lvgl.page.show:
id: ${light_widget_name_3}_light_rgb_page
animation: OUT_RIGHT
time: 300ms
- platform: lvgl
id: ${light_widget_name_4}_btn_long_sensor
widget: ${light_widget_name_4}_btn
on_click:
- min_length: 50ms
max_length: 500ms
then:
- homeassistant.service:
service: light.toggle
data:
entity_id: "${light_entity_4}"
- min_length: 800ms
max_length: 3000ms
then:
- lvgl.widget.hide: menu_controls_main
- lvgl.page.show:
id: ${light_widget_name_4}_light_rgb_page
animation: OUT_RIGHT
time: 300ms