esphome RGB Bulbs and Fly Sprayer
This commit is contained in:
@@ -1,35 +0,0 @@
|
|||||||
- id: '1693027703299'
|
|
||||||
alias: Downstairs Kitchen turn ON LEDs on with main lights
|
|
||||||
description: ''
|
|
||||||
triggers:
|
|
||||||
- type: turned_on
|
|
||||||
device_id: 1a7e2bfdfaa08e2b78bcbd5d5d12d2a7
|
|
||||||
entity_id: ef02e93de5fdda30bea18dac1316927a
|
|
||||||
domain: switch
|
|
||||||
trigger: device
|
|
||||||
conditions: []
|
|
||||||
actions:
|
|
||||||
- data:
|
|
||||||
transition: 20
|
|
||||||
brightness_pct: 95
|
|
||||||
target:
|
|
||||||
entity_id: light.controller_dimmable_3c317c
|
|
||||||
action: light.turn_on
|
|
||||||
mode: single
|
|
||||||
- id: '1693027792035'
|
|
||||||
alias: Downstairs Kitchen turn OFF LEDs with main lights
|
|
||||||
description: ''
|
|
||||||
triggers:
|
|
||||||
- type: turned_off
|
|
||||||
device_id: 1a7e2bfdfaa08e2b78bcbd5d5d12d2a7
|
|
||||||
entity_id: ef02e93de5fdda30bea18dac1316927a
|
|
||||||
domain: switch
|
|
||||||
trigger: device
|
|
||||||
conditions: []
|
|
||||||
actions:
|
|
||||||
- data:
|
|
||||||
transition: 20
|
|
||||||
target:
|
|
||||||
entity_id: light.controller_dimmable_3c317c
|
|
||||||
action: light.turn_off
|
|
||||||
mode: single
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
sensor.dryer_finished:
|
|
||||||
icon: mdi:tumble-dryer-alert
|
|
||||||
sensor.washing_machine_finished:
|
|
||||||
icon: mdi:washing-machine-alert
|
|
||||||
sensor.dishwasher_finished:
|
|
||||||
icon: mdi:dishwasher-alert
|
|
||||||
sensor.pets_fed:
|
|
||||||
friendly_name: Pets Fed
|
|
||||||
icon: mdi:paw
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
##########################################################################################
|
##########################################################################################
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
# 4 Channel DMX Mains Dimmer
|
# 4 Channel DMX Mains Dimmer
|
||||||
# This is using an esp8366 (D1 Mini) sensing DMX512 Serial to a 2 channel
|
# This is using an esp8366 (D1 Mini) sending DMX512 Serial to a pair of 2 channel
|
||||||
# mains lighting contoller (1.2A/Channel)
|
# mains lighting contollers (1.2A/Channel)
|
||||||
#
|
#
|
||||||
# Contoller: https://www.aliexpress.com/item/32838426377.html
|
# Contoller: https://www.aliexpress.com/item/32838426377.html
|
||||||
# RS-485 TTL converter: https://www.aliexpress.com/item/1005005737922222.html
|
# RS-485 TTL converter: https://www.aliexpress.com/item/1005005737922222.html
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
# - The dimmer needs to be set to DMX mode, and given an ID of 1-999 (not standalone or dynamic)
|
# - The dimmer needs to be set to DMX mode, and given an ID of 1-999 (not standalone or dynamic)
|
||||||
# - there is no reason you can't run many dimmers off one ESP (limit may be in data speed lags?)
|
# - there is no reason you can't run many dimmers off one ESP (limit may be in data speed lags?)
|
||||||
# - The dimmer is pretty smooth, and it remembers values even if no data being transmitted.
|
# - The dimmer is pretty smooth, and it remembers values even if no data being transmitted.
|
||||||
|
# - On my system, I am restoring all channels to ON. Power is controlled by a (smart) wallswitch
|
||||||
|
# so if all networks fail, switching the switch will power these on and restore to full brightness.
|
||||||
#
|
#
|
||||||
# System Settings on Dimmer: (onboard buttons M and < held for 2 secs)
|
# System Settings on Dimmer: (onboard buttons M and < held for 2 secs)
|
||||||
# ------
|
# ------
|
||||||
@@ -56,7 +58,7 @@ substitutions:
|
|||||||
# Device Naming
|
# Device Naming
|
||||||
device_name: "esp-4chdmxdimmer1"
|
device_name: "esp-4chdmxdimmer1"
|
||||||
friendly_name: "4 Channel DMX Dimmer 1"
|
friendly_name: "4 Channel DMX Dimmer 1"
|
||||||
description_comment: "4 Channel Mains Lighting Dimmer using DMX512 protocol"
|
description_comment: "Lounge 4 Channel Mains Lighting Dimmer using DMX512 protocol"
|
||||||
device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
@@ -186,28 +188,28 @@ light:
|
|||||||
id: light_test_1
|
id: light_test_1
|
||||||
default_transition_length: 5s
|
default_transition_length: 5s
|
||||||
gamma_correct: 1
|
gamma_correct: 1
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
restore_mode: ALWAYS_ON
|
||||||
- platform: monochromatic
|
- platform: monochromatic
|
||||||
name: "Test Lights Chan 2"
|
name: "Test Lights Chan 2"
|
||||||
output: dmx_2
|
output: dmx_2
|
||||||
id: light_test_2
|
id: light_test_2
|
||||||
default_transition_length: 5s
|
default_transition_length: 5s
|
||||||
gamma_correct: 1
|
gamma_correct: 1
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
restore_mode: ALWAYS_ON
|
||||||
- platform: monochromatic
|
- platform: monochromatic
|
||||||
name: "Test Lights Chan 3"
|
name: "Test Lights Chan 3"
|
||||||
output: dmx_3
|
output: dmx_3
|
||||||
id: light_test_3
|
id: light_test_3
|
||||||
default_transition_length: 5s
|
default_transition_length: 5s
|
||||||
gamma_correct: 1
|
gamma_correct: 1
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
restore_mode: ALWAYS_ON
|
||||||
- platform: monochromatic
|
- platform: monochromatic
|
||||||
name: "Test Lights Chan 4"
|
name: "Test Lights Chan 4"
|
||||||
output: dmx_4
|
output: dmx_4
|
||||||
id: light_test_4
|
id: light_test_4
|
||||||
default_transition_length: 5s
|
default_transition_length: 5s
|
||||||
gamma_correct: 1
|
gamma_correct: 1
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
restore_mode: ALWAYS_ON
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
# SENSOR COMPONENT
|
# SENSOR COMPONENT
|
||||||
|
|||||||
@@ -0,0 +1,696 @@
|
|||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
# FLY SPRAYER – Sinilink XY-WFUSB (ESP8266/ESP8285) – ESPHome
|
||||||
|
#
|
||||||
|
# V1.0 2025-05-30 Initial Version
|
||||||
|
#
|
||||||
|
# Device: Sinilink XY-WFUSB module https://www.aliexpress.com/item/1005005815472269.html
|
||||||
|
# GPIO: Tasmota template: Button=GPIO4, Relay=GPIO5, Led_i=GPIO14, LedLink=GPIO16
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# - Pulse a MOSFET/relay with 5 V to trigger a fly spray can at selectable intervals.
|
||||||
|
# - Sprayer has its own timers, but it fires whenever it is started up, which we utilise here.
|
||||||
|
# - Enforce a cooldown so it never double-fires too quickly (Sprayer has some memory)
|
||||||
|
# - Operation window select: Night only / Day only / 24 Hours / On / Off.
|
||||||
|
# - Randomize each interval by ± a percentage to avoid a perfectly regular pattern.
|
||||||
|
# - Red LED (GPIO14, inverted) pulses slowly when idle; goes solid ON during spray.
|
||||||
|
# - Green LED (GPIO16) as standard ESPHome status LED.
|
||||||
|
# - Has a resettable total spray count, and a daily spray count
|
||||||
|
# - has a countdown until next spray
|
||||||
|
#
|
||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS
|
||||||
|
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
||||||
|
##########################################################################################
|
||||||
|
substitutions:
|
||||||
|
# Device Naming
|
||||||
|
device_name: "esp-flysprayer1"
|
||||||
|
friendly_name: "Fly Sprayer"
|
||||||
|
description_comment: "Control of generic automatic fly sprayer on a configurable interval"
|
||||||
|
device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
||||||
|
|
||||||
|
# Project Naming
|
||||||
|
project_name: "Generic ESP8266.XY-WFUSB" # Project Details
|
||||||
|
project_version: "v1.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
||||||
|
|
||||||
|
# Passwords & Secrets
|
||||||
|
api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
|
||||||
|
ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names
|
||||||
|
static_ip_address: !secret esp-flysprayer1_ip
|
||||||
|
#mqtt_local_command_main_topic: !secret mqtt_local_command_main_topic
|
||||||
|
#mqtt_local_status_main_topic: !secret mqtt_local_status_main_topic
|
||||||
|
|
||||||
|
# Device Settings
|
||||||
|
log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE
|
||||||
|
update_interval: "60s" # update time for for general sensors etc
|
||||||
|
|
||||||
|
# MQTT Controls
|
||||||
|
#mqtt_device_name: "lounge-flyspray"
|
||||||
|
#mqtt_local_command_topic: "${mqtt_local_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA
|
||||||
|
#mqtt_local_status_topic: "${mqtt_local_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA
|
||||||
|
|
||||||
|
# Hardware pins (XY-WFUSB per your Tasmota screen)
|
||||||
|
pin_button: 4 # GPIO4 -> momentary button (optional local trigger)
|
||||||
|
pin_relay: 5 # GPIO5 -> MOSFET/relay output
|
||||||
|
pin_led_red: 14 # GPIO14 -> Red LED (inverted)
|
||||||
|
pin_led_green: 16 # GPIO16 -> Green status LED (WiFi/status)
|
||||||
|
relay_inverted: false # Set to "true" if your MOSFET stage is active LOW
|
||||||
|
|
||||||
|
# Timing configuration
|
||||||
|
relay_activation_ms: 5000 # Relay Activation Time (ms). Nominally 5 seconds.
|
||||||
|
relay_cooldown_ms: 30000 # Relay Cool Down Period (ms). Minimum gap between sprays.
|
||||||
|
|
||||||
|
# Operation windows (local 24h). Night is from night_start_h to night_end_h across midnight.
|
||||||
|
night_start_h: 21 # 21:00 (9pm)
|
||||||
|
night_end_h: 7 # 07:00 (7am)
|
||||||
|
|
||||||
|
# Interval jitter (± percent of selected interval)
|
||||||
|
interval_jitter_pct: 10 # Default ±10%
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# PACKAGES: Included Common Packages
|
||||||
|
# https://esphome.io/components/packages.html
|
||||||
|
##########################################################################################
|
||||||
|
packages:
|
||||||
|
common_wifi: !include
|
||||||
|
file: common/network_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
local_static_ip_address: "${static_ip_address}"
|
||||||
|
local_ota_pass: "${ota_pass}"
|
||||||
|
common_api: !include
|
||||||
|
file: common/api_common.yaml
|
||||||
|
vars:
|
||||||
|
local_api_key: "${api_key}"
|
||||||
|
#common_webportal: !include
|
||||||
|
# file: common/webportal_common.yaml
|
||||||
|
common_mqtt: !include
|
||||||
|
file: common/mqtt_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
common_sntp: !include
|
||||||
|
file: common/sntp_common.yaml
|
||||||
|
common_general_sensors: !include
|
||||||
|
file: common/sensors_common.yaml
|
||||||
|
vars:
|
||||||
|
local_friendly_name: "${friendly_name}"
|
||||||
|
local_update_interval: "${update_interval}"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# ESPHome CORE CONFIGURATION
|
||||||
|
# https://esphome.io/components/esphome.html
|
||||||
|
##########################################################################################
|
||||||
|
esphome:
|
||||||
|
name: "${device_name}"
|
||||||
|
friendly_name: "${friendly_name}"
|
||||||
|
comment: "${description_comment}"
|
||||||
|
area: "${device_area}"
|
||||||
|
project:
|
||||||
|
name: "${project_name}"
|
||||||
|
version: "${project_version}"
|
||||||
|
on_boot:
|
||||||
|
# Early: make hardware safe/pretty
|
||||||
|
- priority: -10
|
||||||
|
then:
|
||||||
|
- switch.turn_off: relay_out
|
||||||
|
- light.turn_on:
|
||||||
|
id: red_led
|
||||||
|
brightness: 50%
|
||||||
|
effect: red_idle_pulse
|
||||||
|
|
||||||
|
# Late: after components & restores are ready, publish real values and start loop
|
||||||
|
- priority: 800
|
||||||
|
then:
|
||||||
|
- delay: 200ms
|
||||||
|
- lambda: |-
|
||||||
|
// Publish restored counts
|
||||||
|
id(spray_count_sensor).publish_state((float) id(spray_count));
|
||||||
|
id(spray_count_today_sensor).publish_state(id(spray_count_today));
|
||||||
|
// Reset countdown display
|
||||||
|
id(next_spray_seconds).publish_state(0);
|
||||||
|
id(next_spray_in_text).publish_state("Ready");
|
||||||
|
- script.execute: spray_loop
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# ESP PLATFORM AND FRAMEWORK
|
||||||
|
# https://esphome.io/components/esp8266.html
|
||||||
|
# https://esphome.io/components/esp32.html
|
||||||
|
##########################################################################################
|
||||||
|
esp8266:
|
||||||
|
board: esp8285
|
||||||
|
early_pin_init: false # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True.
|
||||||
|
board_flash_mode: dout # Default is dout
|
||||||
|
|
||||||
|
preferences:
|
||||||
|
flash_write_interval: 20s
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# GLOBAL VARIABLES
|
||||||
|
# https://esphome.io/components/globals.html
|
||||||
|
##########################################################################################
|
||||||
|
globals:
|
||||||
|
# millisecond tick when we last fired successfully
|
||||||
|
- id: last_fire_ms
|
||||||
|
type: uint32_t
|
||||||
|
restore_value: no
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
|
- id: last_wait_ms
|
||||||
|
type: uint32_t
|
||||||
|
restore_value: no
|
||||||
|
initial_value: '60000'
|
||||||
|
|
||||||
|
- id: window_allowed
|
||||||
|
type: bool
|
||||||
|
restore_value: no
|
||||||
|
initial_value: 'false'
|
||||||
|
|
||||||
|
# absolute millis() when the next spray countdown ends
|
||||||
|
- id: next_target_ms
|
||||||
|
type: uint32_t
|
||||||
|
restore_value: no
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
|
# total successful sprays (persists across reboots)
|
||||||
|
- id: spray_count
|
||||||
|
type: uint32_t
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
|
# total sprays since local midnight
|
||||||
|
- id: spray_count_today
|
||||||
|
type: uint32_t
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
|
# day-of-month snapshot to detect midnight rollover
|
||||||
|
- id: last_day
|
||||||
|
type: int
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: '-1'
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# LOGGER COMPONENT
|
||||||
|
# https://esphome.io/components/logger.html
|
||||||
|
# Logs all log messages through the serial port and through MQTT topics.
|
||||||
|
##########################################################################################
|
||||||
|
logger:
|
||||||
|
level: "${log_level}" #INFO Level suggested, or DEBUG for testing
|
||||||
|
#baud_rate: 0 #set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
|
||||||
|
#esp8266_store_log_strings_in_flash: false
|
||||||
|
#tx_buffer_size: 64
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# MQTT COMMANDS
|
||||||
|
# This adds device-specific MQTT command triggers to the common MQTT configuration.
|
||||||
|
##########################################################################################
|
||||||
|
#mqtt:
|
||||||
|
# on_message:
|
||||||
|
# # Light control to ramp up
|
||||||
|
# - topic: "${mqtt_local_command_topic}/light/set"
|
||||||
|
# payload: "${mqtt_local_device_command_ON}"
|
||||||
|
# then:
|
||||||
|
# - switch.turn_on: mosfet_ramp_switch
|
||||||
|
# # Light control to ramp down
|
||||||
|
# - topic: "${mqtt_local_command_topic}/light/set"
|
||||||
|
# payload: "${mqtt_local_device_command_OFF}"
|
||||||
|
# then:
|
||||||
|
# - switch.turn_off: mosfet_ramp_switch
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# STATUS LED
|
||||||
|
# https://esphome.io/components/status_led.html
|
||||||
|
#########################################################################################
|
||||||
|
status_led:
|
||||||
|
pin:
|
||||||
|
number: ${pin_led_green}
|
||||||
|
inverted: false
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SWITCH COMPONENT
|
||||||
|
# https://esphome.io/components/switch/
|
||||||
|
##########################################################################################
|
||||||
|
switch:
|
||||||
|
- platform: gpio
|
||||||
|
id: relay_out
|
||||||
|
name: "Spray Relay"
|
||||||
|
icon: mdi:relay
|
||||||
|
pin:
|
||||||
|
number: ${pin_relay}
|
||||||
|
inverted: ${relay_inverted}
|
||||||
|
restore_mode: RESTORE_DEFAULT_OFF
|
||||||
|
internal: true
|
||||||
|
on_turn_on:
|
||||||
|
# Ensure red LED goes solid during activation (script also enforces)
|
||||||
|
- light.turn_on:
|
||||||
|
id: red_led
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
on_turn_off:
|
||||||
|
# Return to idle pulsing when relay is off
|
||||||
|
- light.turn_on:
|
||||||
|
id: red_led
|
||||||
|
brightness: 50%
|
||||||
|
effect: red_idle_pulse
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
# BUTTON COMPONENT
|
||||||
|
# https://esphome.io/components/button/index.html
|
||||||
|
#################################################################################################
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
id: spray_now
|
||||||
|
name: "Spray Now"
|
||||||
|
icon: mdi:spray
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- script.execute: try_spray_now
|
||||||
|
- platform: template
|
||||||
|
id: reset_spray_count
|
||||||
|
name: "Reset Spray Count"
|
||||||
|
icon: mdi:backup-restore
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
id(spray_count) = 0;
|
||||||
|
id(spray_count_sensor).publish_state(0);
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# SELECT COMPONENT
|
||||||
|
# https://esphome.io/components/select/index.html
|
||||||
|
#########################################################################################
|
||||||
|
select:
|
||||||
|
- platform: template
|
||||||
|
id: spray_interval
|
||||||
|
name: "${friendly_name} Spray Interval"
|
||||||
|
icon: mdi:timer
|
||||||
|
optimistic: true
|
||||||
|
restore_value: true
|
||||||
|
initial_option: "20 minutes"
|
||||||
|
options:
|
||||||
|
- "5 minutes"
|
||||||
|
- "10 minutes"
|
||||||
|
- "20 minutes"
|
||||||
|
- "30 minutes"
|
||||||
|
- "1 hour"
|
||||||
|
- "2 hours"
|
||||||
|
- "4 hours"
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "Spray Interval changed to %s — rescheduling"
|
||||||
|
args: [ 'id(spray_interval).state.c_str()' ]
|
||||||
|
- script.execute: spray_loop
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
id: spray_operation
|
||||||
|
name: "${friendly_name} Operation"
|
||||||
|
icon: mdi:toggle-switch
|
||||||
|
optimistic: true
|
||||||
|
restore_value: true
|
||||||
|
initial_option: "On"
|
||||||
|
options:
|
||||||
|
- "Off"
|
||||||
|
- "Night Only"
|
||||||
|
- "Day Only"
|
||||||
|
- "24 Hours"
|
||||||
|
- "On"
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- logger.log:
|
||||||
|
format: "Operation changed to %s — rescheduling"
|
||||||
|
args: [ 'id(spray_operation).state.c_str()' ]
|
||||||
|
- script.execute: spray_loop
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# BINARY SENSORS
|
||||||
|
# https://esphome.io/components/binary_sensor/
|
||||||
|
##########################################################################################
|
||||||
|
binary_sensor:
|
||||||
|
# Front panel/local button (GPIO4) – single press triggers a Spray Now (honors cooldown)
|
||||||
|
- platform: gpio
|
||||||
|
id: btn_local
|
||||||
|
pin:
|
||||||
|
number: ${pin_button}
|
||||||
|
mode: INPUT_PULLUP
|
||||||
|
inverted: true
|
||||||
|
name: "Local Button Press"
|
||||||
|
icon: mdi:spray
|
||||||
|
internal: true
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- button.press: spray_now
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SENSOR COMPONENT
|
||||||
|
# https://esphome.io/components/sensor/
|
||||||
|
##########################################################################################
|
||||||
|
sensor:
|
||||||
|
# Next spray remaining time in seconds (updated by the scheduler)
|
||||||
|
- platform: template
|
||||||
|
id: next_spray_seconds
|
||||||
|
name: "Next Spray Seconds to go"
|
||||||
|
icon: mdi:timer-sand
|
||||||
|
unit_of_measurement: "s"
|
||||||
|
accuracy_decimals: 0
|
||||||
|
#entity_category: diagnostic
|
||||||
|
update_interval: never
|
||||||
|
|
||||||
|
# Total sprays counter (persisted; updated on each spray and when reset)
|
||||||
|
- platform: template
|
||||||
|
id: spray_count_sensor
|
||||||
|
name: "Spray Count Total"
|
||||||
|
icon: mdi:counter
|
||||||
|
unit_of_measurement: ""
|
||||||
|
accuracy_decimals: 0
|
||||||
|
state_class: total_increasing
|
||||||
|
update_interval: never
|
||||||
|
filters:
|
||||||
|
- round: 0
|
||||||
|
|
||||||
|
# Sprays since local midnight (auto-resets at midnight)
|
||||||
|
- platform: template
|
||||||
|
id: spray_count_today_sensor
|
||||||
|
name: "${friendly_name} Spray Count Today"
|
||||||
|
icon: mdi:counter
|
||||||
|
unit_of_measurement: ""
|
||||||
|
accuracy_decimals: 0
|
||||||
|
state_class: measurement
|
||||||
|
update_interval: never
|
||||||
|
filters:
|
||||||
|
- round: 0
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# TEXT SENSOR COMPONENT
|
||||||
|
# https://esphome.io/components/text_sensor/
|
||||||
|
##########################################################################################
|
||||||
|
text_sensor:
|
||||||
|
- platform: template
|
||||||
|
id: next_spray_in_text
|
||||||
|
name: "Next Spray In"
|
||||||
|
icon: mdi:clock-outline
|
||||||
|
#entity_category: diagnostic
|
||||||
|
update_interval: never
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# OUTPUT COMPONENT
|
||||||
|
# https://esphome.io/components/light/index.html
|
||||||
|
##########################################################################################
|
||||||
|
# An OUTPUT can be binary (0,1) or float, which is any value between 0 and 1.
|
||||||
|
# PWM Outputs such as "ledc" are float. https://esphome.io/components/output/ledc.html
|
||||||
|
##########################################################################################
|
||||||
|
output:
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: red_led_pwm
|
||||||
|
pin:
|
||||||
|
number: ${pin_led_red}
|
||||||
|
inverted: true # Tasmota showed Led_i on GPIO14
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# LIGHT COMPONENT
|
||||||
|
# https://esphome.io/components/light/
|
||||||
|
##########################################################################################
|
||||||
|
light:
|
||||||
|
- platform: monochromatic
|
||||||
|
id: red_led
|
||||||
|
name: "${friendly_name} Red LED"
|
||||||
|
icon: mdi:led-on
|
||||||
|
output: red_led_pwm
|
||||||
|
default_transition_length: 300ms
|
||||||
|
internal: true
|
||||||
|
effects:
|
||||||
|
- pulse:
|
||||||
|
name: red_idle_pulse
|
||||||
|
transition_length: 1500ms
|
||||||
|
update_interval: 1500ms
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# NUMBER COMPONENT
|
||||||
|
# https://esphome.io/components/number/
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SCRIPT COMPONENT
|
||||||
|
# https://esphome.io/components/script.html
|
||||||
|
# Scripts can be executed nearly anywhere in your device configuration with a single call.
|
||||||
|
##########################################################################################
|
||||||
|
# SCRIPTS – main loop, cooldown-safe trigger, and actual spray action
|
||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
# SCRIPT COMPONENT
|
||||||
|
# https://esphome.io/components/script.html
|
||||||
|
# Scripts can be executed nearly anywhere in your device configuration with a single call.
|
||||||
|
##########################################################################################
|
||||||
|
# SCRIPTS – main loop, cooldown-safe trigger, and actual spray action
|
||||||
|
##########################################################################################
|
||||||
|
script:
|
||||||
|
# Cooldown-checked "Spray Now"
|
||||||
|
- id: try_spray_now
|
||||||
|
mode: restart
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
const uint32_t now = millis();
|
||||||
|
return (now - id(last_fire_ms)) >= (uint32_t) ${relay_cooldown_ms};
|
||||||
|
then:
|
||||||
|
- logger.log: "Spray: activating relay (within allowed period)."
|
||||||
|
- lambda: |-
|
||||||
|
id(last_fire_ms) = millis();
|
||||||
|
- script.execute: do_spray
|
||||||
|
else:
|
||||||
|
- logger.log: "Spray request ignored: Fired during cool down period."
|
||||||
|
|
||||||
|
# Actual relay action for the configured activation time
|
||||||
|
- id: do_spray
|
||||||
|
mode: restart
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
id(spray_count)++;
|
||||||
|
id(spray_count_sensor).publish_state((float) id(spray_count));
|
||||||
|
id(spray_count_today)++;
|
||||||
|
id(spray_count_today_sensor).publish_state(id(spray_count_today)); // publish as int
|
||||||
|
- switch.turn_on: relay_out
|
||||||
|
- light.turn_on:
|
||||||
|
id: red_led
|
||||||
|
brightness: 100%
|
||||||
|
effect: none
|
||||||
|
- delay: !lambda "return (uint32_t) ${relay_activation_ms};"
|
||||||
|
- switch.turn_off: relay_out
|
||||||
|
- light.turn_on:
|
||||||
|
id: red_led
|
||||||
|
brightness: 50%
|
||||||
|
effect: red_idle_pulse
|
||||||
|
|
||||||
|
# Scheduler loop – waits a randomized interval, updates countdown text, checks window, then tries to spray
|
||||||
|
- id: spray_loop
|
||||||
|
mode: restart
|
||||||
|
then:
|
||||||
|
- while:
|
||||||
|
condition:
|
||||||
|
lambda: "return true;"
|
||||||
|
then:
|
||||||
|
# 1) Compute the base interval and randomized wait
|
||||||
|
- lambda: |-
|
||||||
|
auto s = id(spray_interval).state;
|
||||||
|
uint32_t base_sec = 3600; // default 1 hour
|
||||||
|
if (s == "5 minutes") base_sec = 5 * 60;
|
||||||
|
else if (s == "10 minutes") base_sec = 10 * 60;
|
||||||
|
else if (s == "20 minutes") base_sec = 20 * 60;
|
||||||
|
else if (s == "30 minutes") base_sec = 30 * 60;
|
||||||
|
else if (s == "1 hour") base_sec = 60 * 60;
|
||||||
|
else if (s == "2 hours") base_sec = 2 * 60 * 60;
|
||||||
|
else if (s == "4 hours") base_sec = 4 * 60 * 60;
|
||||||
|
|
||||||
|
// Jitter ±pct
|
||||||
|
const uint32_t pct = ${interval_jitter_pct};
|
||||||
|
const uint32_t jitter = (base_sec * pct) / 100;
|
||||||
|
const uint32_t span = (jitter * 2u) + 1u;
|
||||||
|
const uint32_t rnd = random_uint32() % span; // 0..(2*jitter)
|
||||||
|
const int32_t delta = (int32_t)rnd - (int32_t)jitter; // -jitter..+jitter
|
||||||
|
int32_t next_sec = (int32_t)base_sec + delta;
|
||||||
|
if (next_sec < 1) next_sec = 1;
|
||||||
|
|
||||||
|
ESP_LOGI("spray", "Next interval: base=%u s, jitter=%u%%, wait=%ld s",
|
||||||
|
base_sec, pct, (long) next_sec);
|
||||||
|
|
||||||
|
id(last_wait_ms) = (uint32_t) next_sec * 1000u;
|
||||||
|
id(next_target_ms) = millis() + id(last_wait_ms);
|
||||||
|
|
||||||
|
// Publish an initial countdown snapshot (minutes only for text; seconds kept for numeric sensor)
|
||||||
|
uint32_t rem_ms = id(next_target_ms) > millis() ? (id(next_target_ms) - millis()) : 0;
|
||||||
|
uint32_t sec = (rem_ms + 999u) / 1000u; // seconds, rounded up
|
||||||
|
id(next_spray_seconds).publish_state(sec);
|
||||||
|
uint32_t min = (sec + 59u) / 60u; // minutes, ceiling
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%u min", (unsigned)min);
|
||||||
|
std::string countdown_txt(buf);
|
||||||
|
|
||||||
|
// Override text for specific modes
|
||||||
|
auto op = id(spray_operation).state;
|
||||||
|
char await_buf[16];
|
||||||
|
|
||||||
|
if (op == "Off") {
|
||||||
|
id(next_spray_in_text).publish_state("Disabled");
|
||||||
|
} else if (op == "Night Only") {
|
||||||
|
bool in_window = false;
|
||||||
|
if (id(sntp_time).now().is_valid()) {
|
||||||
|
auto n = id(sntp_time).now();
|
||||||
|
int hour = n.hour;
|
||||||
|
int nh = ${night_start_h};
|
||||||
|
int eh = ${night_end_h};
|
||||||
|
if (nh > eh) in_window = (hour >= nh) || (hour < eh);
|
||||||
|
else in_window = (hour >= nh) && (hour < eh);
|
||||||
|
}
|
||||||
|
if (!in_window) {
|
||||||
|
snprintf(await_buf, sizeof(await_buf), "%02d:00", ${night_start_h});
|
||||||
|
id(next_spray_in_text).publish_state(std::string("Awaiting ") + await_buf);
|
||||||
|
} else {
|
||||||
|
id(next_spray_in_text).publish_state(countdown_txt);
|
||||||
|
}
|
||||||
|
} else if (op == "Day Only") {
|
||||||
|
bool in_window = false;
|
||||||
|
if (id(sntp_time).now().is_valid()) {
|
||||||
|
auto n = id(sntp_time).now();
|
||||||
|
int hour = n.hour;
|
||||||
|
int nh = ${night_start_h};
|
||||||
|
int eh = ${night_end_h};
|
||||||
|
bool is_night;
|
||||||
|
if (nh > eh) is_night = (hour >= nh) || (hour < eh);
|
||||||
|
else is_night = (hour >= nh) && (hour < eh);
|
||||||
|
in_window = !is_night; // day is the opposite of night window
|
||||||
|
}
|
||||||
|
if (!in_window) {
|
||||||
|
snprintf(await_buf, sizeof(await_buf), "%02d:00", ${night_end_h});
|
||||||
|
id(next_spray_in_text).publish_state(std::string("Awaiting ") + await_buf);
|
||||||
|
} else {
|
||||||
|
id(next_spray_in_text).publish_state(countdown_txt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id(next_spray_in_text).publish_state(countdown_txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2) Tick the countdown once per second until the target time
|
||||||
|
- while:
|
||||||
|
condition:
|
||||||
|
lambda: "return millis() < id(next_target_ms);"
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
uint32_t rem_ms = id(next_target_ms) > millis() ? (id(next_target_ms) - millis()) : 0;
|
||||||
|
uint32_t sec = (rem_ms + 999u) / 1000u; // seconds, rounded up
|
||||||
|
id(next_spray_seconds).publish_state(sec);
|
||||||
|
uint32_t min = (sec + 59u) / 60u; // minutes, ceiling
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%u min", (unsigned)min);
|
||||||
|
std::string countdown_txt(buf);
|
||||||
|
|
||||||
|
// Override text for specific modes
|
||||||
|
auto op = id(spray_operation).state;
|
||||||
|
char await_buf[16];
|
||||||
|
|
||||||
|
if (op == "Off") {
|
||||||
|
id(next_spray_in_text).publish_state("Disabled");
|
||||||
|
} else if (op == "Night Only") {
|
||||||
|
bool in_window = false;
|
||||||
|
if (id(sntp_time).now().is_valid()) {
|
||||||
|
auto n = id(sntp_time).now();
|
||||||
|
int hour = n.hour;
|
||||||
|
int nh = ${night_start_h};
|
||||||
|
int eh = ${night_end_h};
|
||||||
|
if (nh > eh) in_window = (hour >= nh) || (hour < eh);
|
||||||
|
else in_window = (hour >= nh) && (hour < eh);
|
||||||
|
}
|
||||||
|
if (!in_window) {
|
||||||
|
snprintf(await_buf, sizeof(await_buf), "%02d:00", ${night_start_h});
|
||||||
|
id(next_spray_in_text).publish_state(std::string("Awaiting ") + await_buf);
|
||||||
|
} else {
|
||||||
|
id(next_spray_in_text).publish_state(countdown_txt);
|
||||||
|
}
|
||||||
|
} else if (op == "Day Only") {
|
||||||
|
bool in_window = false;
|
||||||
|
if (id(sntp_time).now().is_valid()) {
|
||||||
|
auto n = id(sntp_time).now();
|
||||||
|
int hour = n.hour;
|
||||||
|
int nh = ${night_start_h};
|
||||||
|
int eh = ${night_end_h};
|
||||||
|
bool is_night;
|
||||||
|
if (nh > eh) is_night = (hour >= nh) || (hour < eh);
|
||||||
|
else is_night = (hour >= nh) && (hour < eh);
|
||||||
|
in_window = !is_night;
|
||||||
|
}
|
||||||
|
if (!in_window) {
|
||||||
|
snprintf(await_buf, sizeof(await_buf), "%02d:00", ${night_end_h});
|
||||||
|
id(next_spray_in_text).publish_state(std::string("Awaiting ") + await_buf);
|
||||||
|
} else {
|
||||||
|
id(next_spray_in_text).publish_state(countdown_txt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
id(next_spray_in_text).publish_state(countdown_txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Midnight rollover check (via sntp_time)
|
||||||
|
if (id(sntp_time).now().is_valid()) {
|
||||||
|
auto n = id(sntp_time).now();
|
||||||
|
int d = n.day_of_month;
|
||||||
|
if (id(last_day) == -1) {
|
||||||
|
// First valid time after boot: initialise without reset
|
||||||
|
id(last_day) = d;
|
||||||
|
} else if (id(last_day) != d) {
|
||||||
|
// New day -> reset today's counter
|
||||||
|
id(last_day) = d;
|
||||||
|
id(spray_count_today) = 0;
|
||||||
|
id(spray_count_today_sensor).publish_state(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- delay: 1s
|
||||||
|
|
||||||
|
# 3) Check operation window; if allowed, attempt spray (cooldown enforced inside)
|
||||||
|
- lambda: |-
|
||||||
|
auto op = id(spray_operation).state;
|
||||||
|
bool allowed = false;
|
||||||
|
|
||||||
|
if (op == "Off") {
|
||||||
|
allowed = false;
|
||||||
|
} else if (op == "24 Hours" || op == "On") {
|
||||||
|
allowed = true;
|
||||||
|
} else {
|
||||||
|
if (!id(sntp_time).now().is_valid()) {
|
||||||
|
allowed = false; // no time yet; be conservative
|
||||||
|
} else {
|
||||||
|
auto n = id(sntp_time).now();
|
||||||
|
int hour = n.hour; // 0..23
|
||||||
|
int nh = ${night_start_h};
|
||||||
|
int eh = ${night_end_h};
|
||||||
|
|
||||||
|
bool is_night;
|
||||||
|
if (nh > eh) {
|
||||||
|
// window crosses midnight (e.g., 21:00..07:00)
|
||||||
|
is_night = (hour >= nh) || (hour < eh);
|
||||||
|
} else {
|
||||||
|
is_night = (hour >= nh) && (hour < eh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == "Night Only") allowed = is_night;
|
||||||
|
else if (op == "Day Only") allowed = !is_night;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowed) ESP_LOGI("spray", "Window OK: attempting spray.");
|
||||||
|
else ESP_LOGI("spray", "Window blocked: skipping this cycle.");
|
||||||
|
|
||||||
|
id(window_allowed) = allowed;
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: "return id(window_allowed);"
|
||||||
|
then:
|
||||||
|
- script.execute: try_spray_now
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
# XCRhom.tech E27 9W RGBWW Smart Bulb
|
||||||
|
#
|
||||||
|
# # V1.0 2025-09-09 Initial Version
|
||||||
|
#
|
||||||
|
# Device https://www.aliexpress.com/item/1005007226730234.html
|
||||||
|
# Tasmota name AiYaTo-RGBCW
|
||||||
|
#
|
||||||
|
# GPIO Config
|
||||||
|
# -----
|
||||||
|
# - Pin map mirrors original Tasmota config:
|
||||||
|
# PWM1=GPIO12 (R), PWM2=GPIO14 (G), PWM3=GPIO5 (B),
|
||||||
|
# PWM4=GPIO4 (CW), PWM5=GPIO13 (WW, inverted).
|
||||||
|
#
|
||||||
|
# NOTES
|
||||||
|
#
|
||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS
|
||||||
|
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
||||||
|
##########################################################################################
|
||||||
|
substitutions:
|
||||||
|
# Device Naming
|
||||||
|
device_name: esp-rgblampbulb1
|
||||||
|
friendly_name: "RGBWW 9W E27 Smart Bulb"
|
||||||
|
description_comment: "RGBWW light bulb in bedroom 3 lamp"
|
||||||
|
device_area: "Bedroom 3" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
||||||
|
|
||||||
|
# Passwords If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
||||||
|
api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
|
||||||
|
ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names
|
||||||
|
static_ip_address: !secret esp-rgblampbulb1_ip
|
||||||
|
|
||||||
|
# Adjust the color temperature here (200 mired is approx 5000k)
|
||||||
|
white_temp: "3000K"
|
||||||
|
|
||||||
|
# Device Settings
|
||||||
|
log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE
|
||||||
|
update_interval: "60s" # update time for for general sensors etc
|
||||||
|
|
||||||
|
# Adjust the ON/OFF times for flashing.
|
||||||
|
# 600ms on, 400ms off => 60% ON, 40% OFF duty cycle
|
||||||
|
on_time_ms: "500ms"
|
||||||
|
off_time_ms: "500ms"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# PACKAGES: Included Common Packages
|
||||||
|
# https://esphome.io/components/packages.html
|
||||||
|
##########################################################################################
|
||||||
|
# Comment some of these out where not needed, eg MQTT, Web portal, SNTP, General Sensors
|
||||||
|
##########################################################################################
|
||||||
|
packages:
|
||||||
|
common_wifi: !include
|
||||||
|
file: common/network_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
local_static_ip_address: "${static_ip_address}"
|
||||||
|
local_ota_pass: "${ota_pass}"
|
||||||
|
common_api: !include
|
||||||
|
file: common/api_common.yaml
|
||||||
|
vars:
|
||||||
|
local_api_key: "${api_key}"
|
||||||
|
#common_webportal: !include
|
||||||
|
# file: common/webportal_common.yaml
|
||||||
|
common_mqtt: !include
|
||||||
|
file: common/mqtt_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
common_sntp: !include
|
||||||
|
file: common/sntp_common.yaml
|
||||||
|
common_general_sensors: !include
|
||||||
|
file: common/sensors_common.yaml
|
||||||
|
vars:
|
||||||
|
local_friendly_name: "${friendly_name}"
|
||||||
|
local_update_interval: "${update_interval}"
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# ESPHome
|
||||||
|
# https://esphome.io/components/esphome.html
|
||||||
|
#########################################################################################
|
||||||
|
esphome:
|
||||||
|
name: ${device_name}
|
||||||
|
friendly_name: ${friendly_name}
|
||||||
|
comment: ${description_comment} # appears on the esphome page in HA
|
||||||
|
on_boot:
|
||||||
|
priority: 800
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: my_light
|
||||||
|
brightness: 80%
|
||||||
|
color_temperature: ${white_temp} # e.g. "3000K" or "370 mireds"
|
||||||
|
transition_length: 3s
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# ESP Platform and Framework
|
||||||
|
# https://esphome.io/components/esp32.html
|
||||||
|
#########################################################################################
|
||||||
|
esp8266:
|
||||||
|
board: esp01_1m
|
||||||
|
#early_pin_init: False # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True.
|
||||||
|
board_flash_mode: dout # Default is dout
|
||||||
|
#restore_from_flash: true # restore some values on reboot
|
||||||
|
|
||||||
|
mdns:
|
||||||
|
disabled: false # Disabling will make the build file smaller (and it is still available via static IP)
|
||||||
|
|
||||||
|
preferences:
|
||||||
|
flash_write_interval: 20s
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# ESPHome Logging Enable
|
||||||
|
# https://esphome.io/components/logger.html
|
||||||
|
#########################################################################################
|
||||||
|
logger:
|
||||||
|
level: INFO # INFO Level suggested, or DEBUG for testing
|
||||||
|
#baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
|
||||||
|
#esp8266_store_log_strings_in_flash: false
|
||||||
|
#tx_buffer_size: 64
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# OUTPUT COMPONENT
|
||||||
|
# https://esphome.io/components/output/esp8266_pwm.html
|
||||||
|
##########################################################################################
|
||||||
|
output:
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_red # PWM1 → GPIO12
|
||||||
|
pin: GPIO12
|
||||||
|
frequency: 1500 Hz
|
||||||
|
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_green # PWM2 → GPIO14
|
||||||
|
pin: GPIO14
|
||||||
|
frequency: 1500 Hz
|
||||||
|
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_blue # PWM3 → GPIO05
|
||||||
|
pin: GPIO05
|
||||||
|
frequency: 1500 Hz
|
||||||
|
|
||||||
|
# Typically one channel is cold white...
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_cold_white # PWM4 → GPIO04
|
||||||
|
pin: GPIO04
|
||||||
|
frequency: 1500 Hz
|
||||||
|
|
||||||
|
# ...and one channel is warm white (inverted on GPIO13).
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_warm_white # PWM5 → GPIO13
|
||||||
|
pin: GPIO13
|
||||||
|
frequency: 1500 Hz
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# LIGHT COMPONENT
|
||||||
|
# https://esphome.io/components/light/
|
||||||
|
##########################################################################################
|
||||||
|
light:
|
||||||
|
- platform: rgbww
|
||||||
|
name: ${friendly_name}
|
||||||
|
id: my_light
|
||||||
|
red: output_red
|
||||||
|
green: output_green
|
||||||
|
blue: output_blue
|
||||||
|
cold_white: output_cold_white
|
||||||
|
warm_white: output_warm_white
|
||||||
|
|
||||||
|
# Keep total output reasonable on 9W bulbs and avoid mixing RGB with WW simultaneously.
|
||||||
|
constant_brightness: true
|
||||||
|
color_interlock: true
|
||||||
|
|
||||||
|
# Typical white temperature mapping for RGBWW
|
||||||
|
cold_white_color_temperature: 6500 K
|
||||||
|
warm_white_color_temperature: 2700 K
|
||||||
|
|
||||||
|
# Ensure it always powers on (and with our desired effect).
|
||||||
|
restore_mode: ALWAYS_ON
|
||||||
|
|
||||||
|
# Optional: small gamma tweak for nicer dimming curves
|
||||||
|
gamma_correct: 2.4
|
||||||
|
|
||||||
|
# You can omit this or set it to zero for no fade on effect changes.
|
||||||
|
default_transition_length: 0s
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
# CALM EFFECTS
|
||||||
|
# - Night Light: steady warm, ~3% brightness
|
||||||
|
# - Breathe (Soft): slow, gentle brightness wave on current color
|
||||||
|
# - Amber Glow: very gentle warm amber wave
|
||||||
|
# - Blue Calm: subtle cool blue drift
|
||||||
|
# - Warm Dim: static warm preset around 15%
|
||||||
|
######################################################################################
|
||||||
|
effects:
|
||||||
|
# Night Light (steady warm ~3%)
|
||||||
|
- lambda:
|
||||||
|
name: "Night Light"
|
||||||
|
update_interval: 2s
|
||||||
|
lambda: |-
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_brightness(0.03f);
|
||||||
|
// 2700K ≈ 370 mireds
|
||||||
|
call.set_color_temperature(370.0f);
|
||||||
|
call.set_transition_length(1000);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Breathe (Soft) — gentle pulse on the *current* color
|
||||||
|
- pulse:
|
||||||
|
name: "Breathe (Soft)"
|
||||||
|
transition_length: 3s
|
||||||
|
update_interval: 3s
|
||||||
|
min_brightness: 5%
|
||||||
|
max_brightness: 35%
|
||||||
|
|
||||||
|
# Amber Glow — very gentle warm wave (8–20%)
|
||||||
|
- lambda:
|
||||||
|
name: "Amber Glow"
|
||||||
|
update_interval: 500ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 0.5f; // seconds step (approx)
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/16.0f)) + 1.0f) / 2.0f; // 16s cycle
|
||||||
|
float b = 0.08f + phase * 0.12f; // 8%..20%
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_brightness(b);
|
||||||
|
// Warm amber tone via WW channel (keep RGB off)
|
||||||
|
call.set_color_temperature(400.0f); // ≈ 2500K-2800K feel
|
||||||
|
call.set_transition_length(800);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Blue Calm — soft cool tint, tiny drift (6–14%)
|
||||||
|
- lambda:
|
||||||
|
name: "Blue Calm"
|
||||||
|
update_interval: 1000ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.0f;
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/20.0f)) + 1.0f) / 2.0f; // 20s cycle
|
||||||
|
float b = 0.06f + phase * 0.08f; // 6%..14%
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_brightness(b);
|
||||||
|
// Gentle cool tint using RGB to keep it soft
|
||||||
|
call.set_rgb(0.45f, 0.55f, 1.00f);
|
||||||
|
call.set_transition_length(1200);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Warm Dim — static warm preset (~15%)
|
||||||
|
- lambda:
|
||||||
|
name: "Warm Dim"
|
||||||
|
update_interval: 5s
|
||||||
|
lambda: |-
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_brightness(0.15f);
|
||||||
|
call.set_color_temperature(360.0f); // ~2777K
|
||||||
|
call.set_transition_length(800);
|
||||||
|
call.perform();
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
# LB01 Athom 15W RGBCCT Bulb (ESP8266) - ESPHome
|
||||||
|
#
|
||||||
|
# V1.0 2025-09-09 Initial Version (Tasmota pinout import)
|
||||||
|
#
|
||||||
|
# Based on Tasmota template in screenshot:
|
||||||
|
# GPIO4 -> PWM 1
|
||||||
|
# GPIO5 -> PWM 4
|
||||||
|
# GPIO12 -> PWM 2
|
||||||
|
# GPIO13 -> PWM_i 5 (inverted)
|
||||||
|
# GPIO14 -> PWM 3
|
||||||
|
#
|
||||||
|
# Assumed channel roles (adjust if colors are swapped on test):
|
||||||
|
# PWM1 = Red, PWM2 = Green, PWM3 = Blue,
|
||||||
|
# PWM4 = Cold White, PWM5 = Warm White (inverted)
|
||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS
|
||||||
|
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
||||||
|
##########################################################################################
|
||||||
|
substitutions:
|
||||||
|
# Device Naming
|
||||||
|
device_name: esp-rgblampbulb2
|
||||||
|
friendly_name: "Athom LB01 15W RGBCCT Bulb"
|
||||||
|
description_comment: "Athom RGBCCT bulb using Tasmota pin mapping"
|
||||||
|
device_area: "Bedroom 3" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
||||||
|
|
||||||
|
# Passwords If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
||||||
|
api_key: !secret esp-api_key
|
||||||
|
ota_pass: !secret esp-ota_pass
|
||||||
|
static_ip_address: !secret esp-rgblampbulb2_ip
|
||||||
|
|
||||||
|
# Preferred warm color temperature (use K or mireds, e.g. "2700K" or "370 mireds")
|
||||||
|
white_temp: "3000K"
|
||||||
|
|
||||||
|
# Device Settings
|
||||||
|
log_level: "INFO" # NONE, ERROR, WARN, INFO, DEBUG, VERBOSE, VERY_VERBOSE
|
||||||
|
update_interval: "60s" # general sensors update
|
||||||
|
|
||||||
|
# Effect timing defaults
|
||||||
|
on_time_ms: "500ms"
|
||||||
|
off_time_ms: "500ms"
|
||||||
|
|
||||||
|
# >>> PWM frequency for all channels (easy to adjust in one place) <<<
|
||||||
|
pwm_freq: "2000 Hz"
|
||||||
|
|
||||||
|
# >>> ESPHome equivalent of Tasmota DimmerRange 24,100 <<<
|
||||||
|
# 0.24 = 24% minimum PWM when the light is ON. OFF still goes to 0.
|
||||||
|
dimmer_min_power: "0.24"
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# PACKAGES: Included Common Packages
|
||||||
|
# https://esphome.io/components/packages.html
|
||||||
|
##########################################################################################
|
||||||
|
# Comment some of these out where not needed, eg MQTT, Web portal, SNTP, General Sensors
|
||||||
|
##########################################################################################
|
||||||
|
packages:
|
||||||
|
common_wifi: !include
|
||||||
|
file: common/network_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
local_static_ip_address: "${static_ip_address}"
|
||||||
|
local_ota_pass: "${ota_pass}"
|
||||||
|
common_api: !include
|
||||||
|
file: common/api_common.yaml
|
||||||
|
vars:
|
||||||
|
local_api_key: "${api_key}"
|
||||||
|
#common_webportal: !include
|
||||||
|
# file: common/webportal_common.yaml
|
||||||
|
common_mqtt: !include
|
||||||
|
file: common/mqtt_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
common_sntp: !include
|
||||||
|
file: common/sntp_common.yaml
|
||||||
|
common_general_sensors: !include
|
||||||
|
file: common/sensors_common.yaml
|
||||||
|
vars:
|
||||||
|
local_friendly_name: "${friendly_name}"
|
||||||
|
local_update_interval: "${update_interval}"
|
||||||
|
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# ESPHome
|
||||||
|
# https://esphome.io/components/esphome.html
|
||||||
|
#########################################################################################
|
||||||
|
esphome:
|
||||||
|
name: ${device_name}
|
||||||
|
friendly_name: ${friendly_name}
|
||||||
|
comment: ${description_comment} # appears on the esphome page in HA
|
||||||
|
on_boot:
|
||||||
|
# Run late so it wins over everything (Wi-Fi, light init, restore, etc.)
|
||||||
|
priority: -10
|
||||||
|
then:
|
||||||
|
- delay: 100ms
|
||||||
|
- lambda: |-
|
||||||
|
// Force WHITE mode @ 60% and 3000K (333 mireds) with no fade
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::WHITE);
|
||||||
|
call.set_brightness(0.60f);
|
||||||
|
call.set_color_temperature(333.0f); // 3000 K
|
||||||
|
call.set_transition_length(0);
|
||||||
|
call.perform();
|
||||||
|
# Optional project metadata and 2MB flash map (uncomment if you confirm ESP8285 2MB)
|
||||||
|
project:
|
||||||
|
name: "China Athom Technology.Athom RGBCCT Bulb"
|
||||||
|
version: "v1.1.4"
|
||||||
|
platformio_options:
|
||||||
|
board_upload.flash_size: 2MB
|
||||||
|
board_upload.maximum_size: 2097152
|
||||||
|
board_build.ldscript: eagle.flash.2m.ld
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# ESP Platform and Framework
|
||||||
|
# https://esphome.io/components/esp8266.html
|
||||||
|
#########################################################################################
|
||||||
|
esp8266:
|
||||||
|
board: esp8285
|
||||||
|
early_pin_init: False # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True.
|
||||||
|
board_flash_mode: dout # Default is dout
|
||||||
|
#restore_from_flash: true # restore some values on reboot
|
||||||
|
|
||||||
|
mdns:
|
||||||
|
disabled: false # Disabling will make the build file smaller (and it is still available via static IP)
|
||||||
|
|
||||||
|
#preferences:
|
||||||
|
#flash_write_interval: 2s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
# ESPHome Logging Enable
|
||||||
|
# https://esphome.io/components/logger.html
|
||||||
|
#########################################################################################
|
||||||
|
logger:
|
||||||
|
level: ${log_level}
|
||||||
|
#baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
|
||||||
|
#esp8266_store_log_strings_in_flash: false
|
||||||
|
#tx_buffer_size: 64
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# OUTPUT COMPONENT
|
||||||
|
##########################################################################################
|
||||||
|
output:
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_red # PWM1 -> GPIO4
|
||||||
|
pin: GPIO4
|
||||||
|
frequency: ${pwm_freq}
|
||||||
|
min_power: 0.000499
|
||||||
|
max_power: 1
|
||||||
|
zero_means_zero: true
|
||||||
|
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_green # PWM2 -> GPIO12
|
||||||
|
pin: GPIO12
|
||||||
|
frequency: ${pwm_freq}
|
||||||
|
min_power: 0.000499
|
||||||
|
max_power: 1
|
||||||
|
zero_means_zero: true
|
||||||
|
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_blue # PWM3 -> GPIO14
|
||||||
|
pin: GPIO14
|
||||||
|
frequency: ${pwm_freq}
|
||||||
|
min_power: 0.000499
|
||||||
|
max_power: 1
|
||||||
|
zero_means_zero: true
|
||||||
|
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_cold_white # PWM4 -> GPIO5
|
||||||
|
pin: GPIO5
|
||||||
|
frequency: ${pwm_freq}
|
||||||
|
min_power: 0.01
|
||||||
|
max_power: 0.9
|
||||||
|
zero_means_zero: true
|
||||||
|
|
||||||
|
- platform: esp8266_pwm
|
||||||
|
id: output_warm_white # PWM5 -> GPIO13 (inverted)
|
||||||
|
pin: GPIO13
|
||||||
|
frequency: ${pwm_freq}
|
||||||
|
inverted: true
|
||||||
|
min_power: 0.01
|
||||||
|
max_power: 0.9
|
||||||
|
zero_means_zero: true
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# DIAGNOSTIC BUTTONS: Force individual channels (bypass light logic)
|
||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
# DIAGNOSTIC BUTTONS (use light API, not raw PWMs)
|
||||||
|
##########################################################################################
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
name: "Test WW 100%"
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: my_light
|
||||||
|
brightness: 100%
|
||||||
|
color_temperature: 3000 K
|
||||||
|
transition_length: 0s
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Test CW 100%"
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- light.turn_on:
|
||||||
|
id: my_light
|
||||||
|
brightness: 100%
|
||||||
|
color_temperature: 6000 K
|
||||||
|
transition_length: 0s
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "All OFF (light)"
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
- light.turn_off:
|
||||||
|
id: my_light
|
||||||
|
transition_length: 0s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# LIGHT COMPONENT
|
||||||
|
# https://esphome.io/components/light/
|
||||||
|
##########################################################################################
|
||||||
|
light:
|
||||||
|
- platform: rgbct
|
||||||
|
id: my_light
|
||||||
|
name: ${friendly_name}
|
||||||
|
restore_mode: RESTORE_DEFAULT_OFF
|
||||||
|
red: output_red
|
||||||
|
green: output_green
|
||||||
|
blue: output_blue
|
||||||
|
white_brightness: output_cold_white # GPIO5
|
||||||
|
color_temperature: output_warm_white # GPIO13 (inverted)
|
||||||
|
|
||||||
|
# Keep total output reasonable on 15W bulbs and avoid mixing RGB with WW simultaneously.
|
||||||
|
color_interlock: true
|
||||||
|
|
||||||
|
# Typical white temperature mapping for RGBWW
|
||||||
|
cold_white_color_temperature: 6000 K
|
||||||
|
warm_white_color_temperature: 3000 K
|
||||||
|
|
||||||
|
# Do NOT restore last color at boot; on_boot shapes warm 60% only after power loss
|
||||||
|
# restore_mode: RESTORE_DEFAULT_OFF
|
||||||
|
|
||||||
|
# Optional: small gamma tweak for nicer dimming curves
|
||||||
|
# gamma_correct: 2.4
|
||||||
|
|
||||||
|
# Default fade for normal changes; boot uses its own 3s transition above.
|
||||||
|
default_transition_length: 1s
|
||||||
|
|
||||||
|
######################################################################################
|
||||||
|
# CALM EFFECTS
|
||||||
|
# - Night Light: steady warm, ~3% brightness
|
||||||
|
# - Breathe (Soft): slow, gentle brightness wave on current color
|
||||||
|
# - Amber Glow: very gentle warm amber wave
|
||||||
|
# - Blue Calm: subtle cool blue drift
|
||||||
|
# - Warm Dim: static warm preset around 15%
|
||||||
|
######################################################################################
|
||||||
|
effects:
|
||||||
|
# Night Light (steady warm ~3%)
|
||||||
|
- lambda:
|
||||||
|
name: "Night Light"
|
||||||
|
update_interval: 2s
|
||||||
|
lambda: |-
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::WHITE);
|
||||||
|
call.set_brightness(0.03f);
|
||||||
|
call.set_color_temperature(333.0f); // ~3000K
|
||||||
|
call.set_transition_length(1000);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Breathe (Soft) — gentle pulse on the current color
|
||||||
|
- pulse:
|
||||||
|
name: "Breathe (Soft)"
|
||||||
|
transition_length: 3s
|
||||||
|
update_interval: 3s
|
||||||
|
min_brightness: 5%
|
||||||
|
max_brightness: 35%
|
||||||
|
|
||||||
|
# Amber Glow — very gentle warm wave (8–20%)
|
||||||
|
- lambda:
|
||||||
|
name: "Amber Glow"
|
||||||
|
update_interval: 500ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 0.5f; // seconds step (approx)
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/16.0f)) + 1.0f) / 2.0f; // ~16s cycle
|
||||||
|
float b = 0.08f + phase * 0.12f; // 8%..20%
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::WHITE);
|
||||||
|
call.set_brightness(b);
|
||||||
|
call.set_color_temperature(333.0f); // 3000K
|
||||||
|
call.set_transition_length(800);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Pastel Cycle — very slow, soft rainbow (~12–20% brightness)
|
||||||
|
- lambda:
|
||||||
|
name: "Pastel Cycle"
|
||||||
|
update_interval: 1000ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.0f; // seconds
|
||||||
|
const float period = 120.0f; // 120s full cycle
|
||||||
|
float w = 2.0f * 3.14159f / period;
|
||||||
|
// Three sine waves 120° apart for a pastel rainbow
|
||||||
|
float r = 0.35f + 0.25f * sinf(w * t + 0.0f);
|
||||||
|
float g = 0.35f + 0.25f * sinf(w * t + 2.094f);
|
||||||
|
float b = 0.35f + 0.25f * sinf(w * t + 4.188f);
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::RGB);
|
||||||
|
call.set_brightness(0.18f);
|
||||||
|
call.set_rgb(r, g, b);
|
||||||
|
call.set_transition_length(1200);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Ocean Fade — teal↔blue drift (~10–18% brightness)
|
||||||
|
- lambda:
|
||||||
|
name: "Ocean Fade"
|
||||||
|
update_interval: 1200ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.2f;
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/36.0f)) + 1.0f) / 2.0f; // ~36s cycle
|
||||||
|
// Lerp between teal (0.0,0.6,0.6) and blue (0.25,0.45,1.0)
|
||||||
|
float r = (1.0f - phase) * 0.00f + phase * 0.25f;
|
||||||
|
float g = (1.0f - phase) * 0.60f + phase * 0.45f;
|
||||||
|
float b = (1.0f - phase) * 0.60f + phase * 1.00f;
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::RGB);
|
||||||
|
call.set_brightness(0.16f);
|
||||||
|
call.set_rgb(r, g, b);
|
||||||
|
call.set_transition_length(1400);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Sunset Drift — peach↔soft amber (~12–22% brightness)
|
||||||
|
- lambda:
|
||||||
|
name: "Sunset Drift"
|
||||||
|
update_interval: 1000ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.0f;
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/40.0f)) + 1.0f) / 2.0f; // ~40s
|
||||||
|
// Peach (1.0,0.65,0.50) to Amber (1.0,0.75,0.30)
|
||||||
|
float r = 1.00f;
|
||||||
|
float g = (1.0f - phase) * 0.65f + phase * 0.75f;
|
||||||
|
float b = (1.0f - phase) * 0.50f + phase * 0.30f;
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::RGB);
|
||||||
|
call.set_brightness(0.20f);
|
||||||
|
call.set_rgb(r, g, b);
|
||||||
|
call.set_transition_length(1200);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Lavender Breeze — lavender↔rose (~10–18% brightness)
|
||||||
|
- lambda:
|
||||||
|
name: "Lavender Breeze"
|
||||||
|
update_interval: 1200ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.2f;
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/48.0f)) + 1.0f) / 2.0f; // ~48s
|
||||||
|
// Lavender (0.70,0.55,1.00) to Rose (1.00,0.60,0.85)
|
||||||
|
float r = (1.0f - phase) * 0.70f + phase * 1.00f;
|
||||||
|
float g = (1.0f - phase) * 0.55f + phase * 0.60f;
|
||||||
|
float b = (1.0f - phase) * 1.00f + phase * 0.85f;
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::RGB);
|
||||||
|
call.set_brightness(0.15f);
|
||||||
|
call.set_rgb(r, g, b);
|
||||||
|
call.set_transition_length(1400);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Forest Calm — moss↔mint (~8–16% brightness)
|
||||||
|
- lambda:
|
||||||
|
name: "Forest Calm"
|
||||||
|
update_interval: 1000ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.0f;
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/44.0f)) + 1.0f) / 2.0f; // ~44s
|
||||||
|
// Moss (0.25,0.55,0.25) to Mint (0.50,0.95,0.65)
|
||||||
|
float r = (1.0f - phase) * 0.25f + phase * 0.50f;
|
||||||
|
float g = (1.0f - phase) * 0.55f + phase * 0.95f;
|
||||||
|
float b = (1.0f - phase) * 0.25f + phase * 0.65f;
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::RGB);
|
||||||
|
call.set_brightness(0.14f);
|
||||||
|
call.set_rgb(r, g, b);
|
||||||
|
call.set_transition_length(1200);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Blue Calm — soft cool tint, tiny drift (6–14%)
|
||||||
|
- lambda:
|
||||||
|
name: "Blue Calm"
|
||||||
|
update_interval: 1000ms
|
||||||
|
lambda: |-
|
||||||
|
static float t = 0.0f;
|
||||||
|
t += 1.0f;
|
||||||
|
float phase = (sinf(t * 2.0f * (3.14159f/20.0f)) + 1.0f) / 2.0f; // ~20s cycle
|
||||||
|
float b = 0.06f + phase * 0.08f; // 6%..14%
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::RGB);
|
||||||
|
call.set_brightness(b);
|
||||||
|
call.set_rgb(0.45f, 0.55f, 1.00f); // gentle cool bluish tint
|
||||||
|
call.set_transition_length(1200);
|
||||||
|
call.perform();
|
||||||
|
|
||||||
|
# Warm Dim — static warm preset (~15%)
|
||||||
|
- lambda:
|
||||||
|
name: "Warm Dim"
|
||||||
|
update_interval: 5s
|
||||||
|
lambda: |-
|
||||||
|
auto call = id(my_light).turn_on();
|
||||||
|
call.set_color_mode(ColorMode::WHITE);
|
||||||
|
call.set_brightness(0.15f);
|
||||||
|
call.set_color_temperature(333.0f); // ~3000K
|
||||||
|
call.set_transition_length(800);
|
||||||
|
call.perform();
|
||||||
@@ -1,37 +1,42 @@
|
|||||||
automation:
|
automation:
|
||||||
- alias: "Turn on the Wifi Guest Network"
|
- alias: "Turn on the Wifi Guest Network"
|
||||||
trigger:
|
mode: single
|
||||||
platform: state
|
triggers:
|
||||||
|
- trigger: state
|
||||||
entity_id: input_boolean.wifi_guest_network
|
entity_id: input_boolean.wifi_guest_network
|
||||||
to: "on"
|
to: "on"
|
||||||
action:
|
actions:
|
||||||
service: media_player.play_media
|
- action: media_player.play_media
|
||||||
target:
|
target:
|
||||||
entity_id: media_player.view_rd_kitchen_echo_show
|
entity_id: media_player.view_rd_kitchen_echo_show
|
||||||
data:
|
data:
|
||||||
media_content_type: "custom"
|
media_content_type: custom
|
||||||
media_content_id: "ask t.p.link to enable guest network"
|
media_content_id: ask t.p.link to enable guest network
|
||||||
|
|
||||||
- alias: "Turn off the Wifi Guest Network"
|
- alias: "Turn off the Wifi Guest Network"
|
||||||
trigger:
|
mode: single
|
||||||
platform: state
|
triggers:
|
||||||
|
- trigger: state
|
||||||
entity_id: input_boolean.wifi_guest_network
|
entity_id: input_boolean.wifi_guest_network
|
||||||
to: "off"
|
to: "off"
|
||||||
action:
|
actions:
|
||||||
service: media_player.play_media
|
- action: media_player.play_media
|
||||||
target:
|
target:
|
||||||
entity_id: media_player.view_rd_kitchen_echo_show
|
entity_id: media_player.view_rd_kitchen_echo_show
|
||||||
data:
|
data:
|
||||||
media_content_type: "custom"
|
media_content_type: custom
|
||||||
media_content_id: "ask t.p.link to disable guest network"
|
media_content_id: ask t.p.link to disable guest network
|
||||||
|
|
||||||
- alias: "Switch guest network off after time"
|
- alias: "Switch guest network off after time"
|
||||||
trigger:
|
mode: single
|
||||||
platform: state
|
triggers:
|
||||||
|
- trigger: state
|
||||||
entity_id: input_boolean.wifi_guest_network
|
entity_id: input_boolean.wifi_guest_network
|
||||||
from: "off"
|
from: "off"
|
||||||
to: "on"
|
to: "on"
|
||||||
for: "02:00:00"
|
for: "02:00:00"
|
||||||
condition: []
|
conditions: []
|
||||||
action:
|
actions:
|
||||||
service: input_boolean.turn_off
|
- action: input_boolean.turn_off
|
||||||
|
target:
|
||||||
entity_id: input_boolean.wifi_guest_network
|
entity_id: input_boolean.wifi_guest_network
|
||||||
mode: single
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
input_select:
|
|
||||||
pool_light_colour_select:
|
|
||||||
icon: mdi:swimming-pool
|
|
||||||
name: Pool Light Colour
|
|
||||||
initial: "Off"
|
|
||||||
options:
|
|
||||||
- "Off"
|
|
||||||
- "Dk Blue"
|
|
||||||
- "Red"
|
|
||||||
- "Green"
|
|
||||||
- "Lt Blue"
|
|
||||||
- "Disco"
|
|
||||||
- "9"
|
|
||||||
- "10"
|
|
||||||
- "11"
|
|
||||||
- "12"
|
|
||||||
- "13"
|
|
||||||
|
|
||||||
automation:
|
|
||||||
- id: set_the_pool_light_colour
|
|
||||||
alias: Set the Pool Light Colour
|
|
||||||
trigger:
|
|
||||||
entity_id: input_select.pool_light_colour_select
|
|
||||||
platform: state
|
|
||||||
action:
|
|
||||||
service: mqtt.publish
|
|
||||||
data:
|
|
||||||
topic: viewroad-commands/poolcontrols/poollightcolour
|
|
||||||
payload: '{% set mapping = { "Off":"0", "Dk Blue":"1",
|
|
||||||
"Red":"2", "Green":"3", "4":"4" ,"Lt Blue":"5", "6":"6", "7":"7", "Disco":"8", "9":"9", "10":"10", "11":"11", "12":"12", "13":"13" } %} {% set dta = trigger.to_state.state %}
|
|
||||||
{{ mapping[dta] }}
|
|
||||||
'
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
mqtt:
|
||||||
|
- light:
|
||||||
|
schema: json
|
||||||
|
name: "Lounge Dimmer"
|
||||||
|
unique_id: lounge_dimmer_1
|
||||||
|
state_topic: "livingroom/dimmer"
|
||||||
|
command_topic: "livingroom/dimmer/set"
|
||||||
|
availability:
|
||||||
|
- topic: "livingroom/availability"
|
||||||
|
brightness: true
|
||||||
|
supported_color_modes: ["brightness"]
|
||||||
|
retain: true
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#check_config:
|
|
||||||
# sequence: []
|
|
||||||
# alias: Check Configuration
|
|
||||||
Reference in New Issue
Block a user