Compare commits
5
Commits
c469aa4aae
...
17633d5909
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17633d5909 | ||
|
|
0bd960dbf7 | ||
|
|
5a0ccf6b36 | ||
|
|
bdd3d4b0ae | ||
|
|
91c3996f93 |
@@ -1,275 +0,0 @@
|
||||
substitutions:
|
||||
device_name: esp32-h2c-camera
|
||||
friendly_name: "H2C Camera"
|
||||
|
||||
esphome:
|
||||
name: ${device_name}
|
||||
friendly_name: ${friendly_name}
|
||||
|
||||
esp32:
|
||||
board: esp32-s3-devkitc-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
# Required for ESP32 camera buffering.
|
||||
psram:
|
||||
mode: octal
|
||||
|
||||
logger:
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_iot_ssid
|
||||
password: !secret wifi_password
|
||||
manual_ip:
|
||||
static_ip: 192.168.69.90
|
||||
gateway: 192.168.69.1
|
||||
subnet: 255.255.255.0
|
||||
ap:
|
||||
ssid: "${friendly_name} Fallback"
|
||||
password: !secret fallback_password
|
||||
# Prevents aggressive reconnection attempts
|
||||
power_save_mode: none # Use 'light' for battery devices
|
||||
|
||||
# Slower but more reliable connection
|
||||
fast_connect: true
|
||||
|
||||
# Handle connection failures gracefully
|
||||
on_connect:
|
||||
- logger.log: "Wi-Fi connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Wi-Fi disconnected!"
|
||||
|
||||
# Reduce mDNS traffic
|
||||
mdns:
|
||||
disabled: true # Set to true if you use static IPs and don't need discovery
|
||||
|
||||
# captive_portal:
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
|
||||
esp32_camera:
|
||||
name: "${friendly_name}"
|
||||
external_clock:
|
||||
pin: GPIO5
|
||||
# 10MHz (half the usual 20MHz) slows the sensor's line rate, so each of the
|
||||
# 1200 exposure lines below collects roughly twice the light. Costs
|
||||
# framerate, which does not matter for chamber monitoring.
|
||||
frequency: 10MHz
|
||||
i2c_pins:
|
||||
sda: GPIO8
|
||||
scl: GPIO9
|
||||
data_pins: [GPIO16, GPIO18, GPIO21, GPIO17, GPIO14, GPIO7, GPIO6, GPIO4]
|
||||
vsync_pin: GPIO1
|
||||
href_pin: GPIO2
|
||||
pixel_clock_pin: GPIO15
|
||||
|
||||
# Resolution & quality (ESP32-S3 with Octal PSRAM handles XGA easily)
|
||||
resolution: 1024X768
|
||||
jpeg_quality: 10
|
||||
max_framerate: 20 fps
|
||||
idle_framerate: 0.2 fps
|
||||
frame_buffer_count: 2
|
||||
|
||||
# Orientation
|
||||
vertical_flip: true
|
||||
horizontal_mirror: false
|
||||
|
||||
# Image tuning for chamber monitoring through tinted door.
|
||||
# Low contrast + high brightness lifts the shadows; the LED strips are
|
||||
# allowed to blow out since the chamber is what matters.
|
||||
brightness: 2
|
||||
contrast: -2
|
||||
saturation: 0
|
||||
|
||||
# Exposure & gain are MANUAL on purpose. The two LED strips in frame kept the
|
||||
# auto-exposure meter satisfied, so AEC never lengthened the exposure and the
|
||||
# tinted-door scene stayed black regardless of gain ceiling or AE level.
|
||||
# Pinning both to max forces a bright chamber; the strips clip out entirely.
|
||||
aec_mode: manual
|
||||
aec_value: 1200 # max integration time (0-1200 lines)
|
||||
aec2: true
|
||||
ae_level: 2 # only consulted if AEC is flipped back to auto
|
||||
agc_mode: manual
|
||||
agc_value: 30 # max sensor gain (0-30)
|
||||
agc_gain_ceiling: 128X # only consulted if AGC is flipped back to auto
|
||||
wb_mode: auto
|
||||
|
||||
# Camera endpoints:
|
||||
# Stream: http://<ip>:8080
|
||||
# Snapshot: http://<ip>:8081
|
||||
esp32_camera_web_server:
|
||||
- port: 8080
|
||||
mode: stream
|
||||
- port: 8081
|
||||
mode: snapshot
|
||||
|
||||
switch:
|
||||
# DFRobot board IR illumination control pin.
|
||||
- platform: gpio
|
||||
name: "${friendly_name} IR LED"
|
||||
pin: GPIO47
|
||||
restore_mode: ALWAYS_OFF
|
||||
|
||||
# Flip exposure/gain back to automatic for A/B comparison without a reflash.
|
||||
# Off (the default) = manual exposure per the esp32_camera block above.
|
||||
- platform: template
|
||||
name: "${friendly_name} Auto Exposure"
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
turn_on_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (!s) return;
|
||||
s->set_exposure_ctrl(s, 1);
|
||||
s->set_gain_ctrl(s, 1);
|
||||
turn_off_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (!s) return;
|
||||
s->set_exposure_ctrl(s, 0);
|
||||
s->set_gain_ctrl(s, 0);
|
||||
|
||||
sensor:
|
||||
- platform: wifi_signal
|
||||
name: "${friendly_name} WiFi Signal"
|
||||
update_interval: 60s
|
||||
|
||||
- platform: uptime
|
||||
name: "${friendly_name} Uptime"
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "${device_name} WiFi Signal"
|
||||
update_interval: 60s
|
||||
|
||||
- platform: uptime
|
||||
name: "${device_name} Uptime"
|
||||
update_interval: 60s
|
||||
|
||||
button:
|
||||
- platform: restart
|
||||
name: "${friendly_name} Restart"
|
||||
|
||||
text_sensor:
|
||||
- platform: wifi_info
|
||||
ip_address:
|
||||
name: "${device_name} IP Address"
|
||||
ssid:
|
||||
name: "${device_name} Connected SSID"
|
||||
bssid:
|
||||
name: "${device_name} BSSID"
|
||||
|
||||
# Watchdog to auto-reboot if things go wrong
|
||||
interval:
|
||||
- interval: 5min
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- lambda: 'return millis() > 300000;' # >5 min uptime
|
||||
- not:
|
||||
wifi.connected:
|
||||
then:
|
||||
- logger.log: "Wi-Fi not connected for 5 min, rebooting..."
|
||||
- delay: 10s
|
||||
- lambda: 'App.safe_reboot();'
|
||||
|
||||
# Dynamic controls exposed to Home Assistant for real-time camera tuning
|
||||
number:
|
||||
# The two that actually drive image brightness in manual mode.
|
||||
- platform: template
|
||||
name: "${friendly_name} Exposure"
|
||||
min_value: 0
|
||||
max_value: 1200
|
||||
step: 25
|
||||
initial_value: 1200
|
||||
optimistic: true
|
||||
set_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s) s->set_aec_value(s, (int)x);
|
||||
|
||||
- platform: template
|
||||
name: "${friendly_name} Gain"
|
||||
min_value: 0
|
||||
max_value: 30
|
||||
step: 1
|
||||
initial_value: 30
|
||||
optimistic: true
|
||||
set_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s) s->set_agc_gain(s, (int)x);
|
||||
|
||||
- platform: template
|
||||
name: "${friendly_name} Brightness"
|
||||
min_value: -2
|
||||
max_value: 2
|
||||
step: 1
|
||||
initial_value: 2
|
||||
optimistic: true
|
||||
set_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s) s->set_brightness(s, (int)x);
|
||||
|
||||
- platform: template
|
||||
name: "${friendly_name} Contrast"
|
||||
min_value: -2
|
||||
max_value: 2
|
||||
step: 1
|
||||
initial_value: -2
|
||||
optimistic: true
|
||||
set_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s) s->set_contrast(s, (int)x);
|
||||
|
||||
- platform: template
|
||||
name: "${friendly_name} AE Level"
|
||||
min_value: -2
|
||||
max_value: 2
|
||||
step: 1
|
||||
initial_value: 2
|
||||
optimistic: true
|
||||
set_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s) s->set_ae_level(s, (int)x);
|
||||
|
||||
select:
|
||||
# Caps how far AGC may amplify. Higher = brighter but noisier; back this off
|
||||
# if the chamber image gets too grainy at 128X.
|
||||
- platform: template
|
||||
name: "${friendly_name} Gain Ceiling"
|
||||
optimistic: true
|
||||
initial_option: "128X"
|
||||
options:
|
||||
- "2X"
|
||||
- "4X"
|
||||
- "8X"
|
||||
- "16X"
|
||||
- "32X"
|
||||
- "64X"
|
||||
- "128X"
|
||||
set_action:
|
||||
- lambda: |-
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (!s) return;
|
||||
int idx = 6;
|
||||
if (x == "2X") idx = 0;
|
||||
else if (x == "4X") idx = 1;
|
||||
else if (x == "8X") idx = 2;
|
||||
else if (x == "16X") idx = 3;
|
||||
else if (x == "32X") idx = 4;
|
||||
else if (x == "64X") idx = 5;
|
||||
s->set_gainceiling(s, (gainceiling_t)idx);
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
# Lights-only first version for the Family Room remote.
|
||||
# Entity IDs came from the project's HA snapshot; verify them in live HA.
|
||||
|
||||
substitutions:
|
||||
device_name: family-room-remote
|
||||
friendly_name: Family Room Remote
|
||||
|
||||
esphome:
|
||||
name: ${device_name}
|
||||
friendly_name: ${friendly_name}
|
||||
|
||||
packages:
|
||||
hardware: !include panel-remote-common.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: homeassistant
|
||||
id: standing_lamp_state
|
||||
entity_id: light.living_room_lamp_1
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: standing_lamp_button
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
- platform: homeassistant
|
||||
id: small_lamp_state
|
||||
entity_id: light.small_family_room_lamp
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: small_lamp_button
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
- platform: homeassistant
|
||||
id: tv_stand_state
|
||||
entity_id: light.family_room_tv_stand
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: tv_stand_button
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
|
||||
lvgl:
|
||||
rotation: 90
|
||||
on_idle:
|
||||
- timeout: 45s
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: panel_backlight
|
||||
brightness: 15%
|
||||
pages:
|
||||
- id: room_page
|
||||
widgets:
|
||||
- label:
|
||||
x: 12
|
||||
y: 8
|
||||
text: Family Room
|
||||
- button:
|
||||
id: standing_lamp_button
|
||||
x: 10
|
||||
y: 45
|
||||
width: 145
|
||||
height: 75
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: Standing Lamp
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.living_room_lamp_1
|
||||
- button:
|
||||
id: small_lamp_button
|
||||
x: 165
|
||||
y: 45
|
||||
width: 145
|
||||
height: 75
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: Small Lamp
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.small_family_room_lamp
|
||||
- button:
|
||||
id: tv_stand_button
|
||||
x: 10
|
||||
y: 135
|
||||
width: 145
|
||||
height: 75
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: TV Stand
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.family_room_tv_stand
|
||||
- button:
|
||||
x: 165
|
||||
y: 135
|
||||
width: 145
|
||||
height: 75
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: All Off
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: light.living_room_lamp_1
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: light.small_family_room_lamp
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: light.family_room_tv_stand
|
||||
@@ -1,198 +0,0 @@
|
||||
substitutions:
|
||||
device_name: led-controller-polly-a1m
|
||||
friendly_name: "Polly's LED Controller"
|
||||
|
||||
esphome:
|
||||
name: ${device_name}
|
||||
friendly_name: ${friendly_name}
|
||||
|
||||
esp32:
|
||||
board: esp32-c3-devkitm-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
|
||||
# Enable Home Assistant API
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
|
||||
# Enable over-the-air updates
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_iot_ssid
|
||||
password: !secret wifi_password
|
||||
manual_ip:
|
||||
static_ip: 192.168.69.181
|
||||
gateway: 192.168.69.1
|
||||
subnet: 255.255.255.0
|
||||
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||
ap:
|
||||
ssid: "${friendly_name} Fallback"
|
||||
password: !secret fallback_password
|
||||
|
||||
# Prevents aggressive reconnection attempts
|
||||
power_save_mode: none # Use 'light' for battery devices
|
||||
|
||||
# Slower but more reliable connection
|
||||
fast_connect: true
|
||||
|
||||
# Handle connection failures gracefully
|
||||
on_connect:
|
||||
- logger.log: "Wi-Fi connected!"
|
||||
on_disconnect:
|
||||
- logger.log: "Wi-Fi disconnected!"
|
||||
|
||||
# Reduce mDNS traffic
|
||||
mdns:
|
||||
disabled: false # Set to true if you use static IPs and don't need discovery
|
||||
|
||||
#captive_portal:
|
||||
web_server:
|
||||
port: 80
|
||||
|
||||
# LED Light configuration
|
||||
light:
|
||||
- platform: esp32_rmt_led_strip
|
||||
rgb_order: GRB
|
||||
chipset: WS2812
|
||||
pin: GPIO7
|
||||
num_leds: 10
|
||||
name: "${friendly_name}"
|
||||
id: led_strip
|
||||
|
||||
# Default color on boot
|
||||
restore_mode: RESTORE_DEFAULT_OFF
|
||||
|
||||
|
||||
# Effects - you can enable these for more features!
|
||||
effects:
|
||||
# Basic effects
|
||||
- random:
|
||||
name: "Random"
|
||||
transition_length: 5s
|
||||
update_interval: 7s
|
||||
|
||||
- strobe:
|
||||
name: "Strobe"
|
||||
colors:
|
||||
- state: true
|
||||
brightness: 100%
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
duration: 500ms
|
||||
- state: false
|
||||
duration: 250ms
|
||||
|
||||
- flicker:
|
||||
name: "Flicker"
|
||||
alpha: 95%
|
||||
intensity: 1.5%
|
||||
|
||||
# Rainbow effects
|
||||
- addressable_rainbow:
|
||||
name: "Rainbow"
|
||||
speed: 10
|
||||
width: 50
|
||||
|
||||
- addressable_color_wipe:
|
||||
name: "Color Wipe"
|
||||
colors:
|
||||
- red: 100%
|
||||
green: 0%
|
||||
blue: 0%
|
||||
num_leds: 1
|
||||
- red: 0%
|
||||
green: 100%
|
||||
blue: 0%
|
||||
num_leds: 1
|
||||
- red: 0%
|
||||
green: 0%
|
||||
blue: 100%
|
||||
num_leds: 1
|
||||
add_led_interval: 100ms
|
||||
reverse: false
|
||||
|
||||
- addressable_scan:
|
||||
name: "Scan"
|
||||
move_interval: 100ms
|
||||
scan_width: 3
|
||||
|
||||
- addressable_twinkle:
|
||||
name: "Twinkle"
|
||||
twinkle_probability: 5%
|
||||
progress_interval: 4ms
|
||||
|
||||
- addressable_fireworks:
|
||||
name: "Fireworks"
|
||||
update_interval: 32ms
|
||||
spark_probability: 10%
|
||||
use_random_color: true
|
||||
fade_out_rate: 120
|
||||
|
||||
# Pulse effect
|
||||
- pulse:
|
||||
name: "Pulse"
|
||||
transition_length: 1s
|
||||
update_interval: 1s
|
||||
|
||||
# Sensors for monitoring
|
||||
sensor:
|
||||
# WiFi Signal Strength
|
||||
- platform: wifi_signal
|
||||
name: "${friendly_name} WiFi Signal"
|
||||
update_interval: 60s
|
||||
|
||||
# Uptime
|
||||
- platform: uptime
|
||||
name: "${friendly_name} Uptime"
|
||||
|
||||
# Diagnostic sensors
|
||||
- platform: wifi_signal
|
||||
name: "${device_name} WiFi Signal"
|
||||
update_interval: 60s
|
||||
|
||||
- platform: uptime
|
||||
name: "${device_name} Uptime"
|
||||
update_interval: 60s
|
||||
|
||||
# Text sensors
|
||||
text_sensor:
|
||||
# ESPHome version
|
||||
- platform: version
|
||||
name: "${friendly_name} ESPHome Version"
|
||||
|
||||
# WiFi info
|
||||
- platform: wifi_info
|
||||
ip_address:
|
||||
name: "${device_name} IP Address"
|
||||
ssid:
|
||||
name: "${device_name} Connected SSID"
|
||||
bssid:
|
||||
name: "${device_name} BSSID"
|
||||
|
||||
# Button to restart ESP32
|
||||
button:
|
||||
- platform: restart
|
||||
name: "${friendly_name} Restart"
|
||||
|
||||
# Watchdog to auto-reboot if things go wrong
|
||||
interval:
|
||||
- interval: 5min
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
and:
|
||||
- lambda: 'return millis() > 300000;' # >5 min uptime
|
||||
- not:
|
||||
wifi.connected:
|
||||
then:
|
||||
- logger.log: "Wi-Fi not connected for 5 min, rebooting..."
|
||||
- delay: 10s
|
||||
- lambda: 'App.safe_reboot();'
|
||||
@@ -1,128 +0,0 @@
|
||||
# Lights-only first version for the Living Room remote.
|
||||
# Entity IDs came from the project's HA snapshot; verify them in live HA.
|
||||
|
||||
substitutions:
|
||||
device_name: living-room-remote
|
||||
friendly_name: Living Room Remote
|
||||
|
||||
esphome:
|
||||
name: ${device_name}
|
||||
friendly_name: ${friendly_name}
|
||||
|
||||
packages:
|
||||
hardware: !include panel-remote-common.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: homeassistant
|
||||
id: ceiling_light_state
|
||||
entity_id: light.living_room_light_2
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: ceiling_light_button
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
- platform: homeassistant
|
||||
id: small_lamp_state
|
||||
entity_id: light.small_living_room_lamp
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: small_lamp_button
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
- platform: homeassistant
|
||||
id: lampstand_state
|
||||
entity_id: light.studio_bulb_1
|
||||
trigger_on_initial_state: true
|
||||
on_state:
|
||||
- lvgl.widget.update:
|
||||
id: lampstand_button
|
||||
state:
|
||||
checked: !lambda return x;
|
||||
|
||||
lvgl:
|
||||
rotation: 90
|
||||
on_idle:
|
||||
- timeout: 45s
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: panel_backlight
|
||||
brightness: 15%
|
||||
pages:
|
||||
- id: room_page
|
||||
widgets:
|
||||
- label:
|
||||
x: 12
|
||||
y: 8
|
||||
text: Living Room
|
||||
- button:
|
||||
id: ceiling_light_button
|
||||
x: 10
|
||||
y: 45
|
||||
width: 145
|
||||
height: 75
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: Main Light
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.living_room_light_2
|
||||
- button:
|
||||
id: small_lamp_button
|
||||
x: 165
|
||||
y: 45
|
||||
width: 145
|
||||
height: 75
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: Small Lamp
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.small_living_room_lamp
|
||||
- button:
|
||||
id: lampstand_button
|
||||
x: 10
|
||||
y: 135
|
||||
width: 145
|
||||
height: 75
|
||||
checkable: true
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: Lampstand
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.toggle
|
||||
data:
|
||||
entity_id: light.studio_bulb_1
|
||||
- button:
|
||||
x: 165
|
||||
y: 135
|
||||
width: 145
|
||||
height: 75
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
text: All Lights Off
|
||||
on_click:
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: light.living_room_light_2
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: light.small_living_room_lamp
|
||||
- homeassistant.action:
|
||||
action: light.turn_off
|
||||
data:
|
||||
entity_id: light.studio_bulb_1
|
||||
Reference in New Issue
Block a user