various tidyups and esphome adds
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
|||||||
{"pid": 67, "version": 1, "ha_version": "2026.2.0", "start_ts": 1770279860.4199212}
|
{"pid": 67, "version": 1, "ha_version": "2026.2.2", "start_ts": 1771127640.2174048}
|
||||||
@@ -0,0 +1,737 @@
|
|||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
# LOUNGE ENVIRONMENT SENSOR
|
||||||
|
#
|
||||||
|
# ESP32-C3 SuperMini + BME280 (I2C)
|
||||||
|
#
|
||||||
|
# V1.2 2026-02-16 Pressure tendency now 5-state over 3 hours + rate/hour, 1h removed
|
||||||
|
# V1.1 2026-02-16 Added derived min/max sensors and pressure trend text sensors
|
||||||
|
# V1.0 2026-02-16 Initial Version
|
||||||
|
##########################################################################################
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
substitutions:
|
||||||
|
# Device Naming
|
||||||
|
device_name: "esp-loungeenvironment"
|
||||||
|
friendly_name: "Lounge Environment"
|
||||||
|
description_comment: "Lounge Environment :: ESP32-C3 SuperMini + BME280"
|
||||||
|
device_area: "Lounge"
|
||||||
|
|
||||||
|
# Project Naming
|
||||||
|
project_name: "ESP32-C3 SuperMini.BME280"
|
||||||
|
project_version: "v1.2"
|
||||||
|
|
||||||
|
# Passwords
|
||||||
|
api_key: !secret esp-api_key
|
||||||
|
ota_pass: !secret esp-ota_pass
|
||||||
|
static_ip_address: !secret esp-loungeenvironment_ip
|
||||||
|
|
||||||
|
# General Settings
|
||||||
|
log_level: "WARN"
|
||||||
|
update_interval: "60s"
|
||||||
|
|
||||||
|
# I2C Settings
|
||||||
|
i2c_sda_pin: "GPIO1"
|
||||||
|
i2c_scl_pin: "GPIO3"
|
||||||
|
i2c_frequency: "100kHz"
|
||||||
|
bmp_address: "0x76"
|
||||||
|
|
||||||
|
# Pressure tendency thresholds (hPa over 3 hours)
|
||||||
|
# - falling rapidly: <= -6.0 hPa / 3h
|
||||||
|
# - falling: <= -1.0 hPa / 3h
|
||||||
|
# - steady: between -1.0 and +1.0 hPa / 3h
|
||||||
|
# - rising: >= +1.0 hPa / 3h
|
||||||
|
# - rising rapidly: >= +6.0 hPa / 3h
|
||||||
|
pressure_3h_steady_band: "1.0"
|
||||||
|
pressure_3h_rapid_threshold: "6.0"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# PACKAGES
|
||||||
|
##########################################################################################
|
||||||
|
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_mqtt: !include
|
||||||
|
file: common/mqtt_common.yaml
|
||||||
|
vars:
|
||||||
|
local_device_name: "${device_name}"
|
||||||
|
|
||||||
|
# Provides:
|
||||||
|
# - time id: sntp_time
|
||||||
|
# - global: current_mins (minutes since midnight, fallback supported)
|
||||||
|
# - text sensors: time_text, time_sync, device_last_restart
|
||||||
|
common_sntp: !include common/sntp_common.yaml
|
||||||
|
|
||||||
|
diag_basic: !include common/include_basic_diag_sensors.yaml
|
||||||
|
diag_more: !include common/include_more_diag_sensors.yaml
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# ESPHome
|
||||||
|
##########################################################################################
|
||||||
|
esphome:
|
||||||
|
name: "${device_name}"
|
||||||
|
friendly_name: "${friendly_name}"
|
||||||
|
comment: "${description_comment}"
|
||||||
|
area: "${device_area}"
|
||||||
|
name_add_mac_suffix: false
|
||||||
|
min_version: 2026.1.0
|
||||||
|
project:
|
||||||
|
name: "${project_name}"
|
||||||
|
version: "${project_version}"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# PLATFORM
|
||||||
|
##########################################################################################
|
||||||
|
esp32:
|
||||||
|
board: esp32-c3-devkitm-1
|
||||||
|
variant: ESP32C3
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
cpu_frequency: 80MHz
|
||||||
|
|
||||||
|
preferences:
|
||||||
|
flash_write_interval: 5min
|
||||||
|
|
||||||
|
mdns:
|
||||||
|
disabled: false
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# LOGGING
|
||||||
|
##########################################################################################
|
||||||
|
logger:
|
||||||
|
level: "${log_level}"
|
||||||
|
baud_rate: 0
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# I2C
|
||||||
|
##########################################################################################
|
||||||
|
i2c:
|
||||||
|
sda: ${i2c_sda_pin}
|
||||||
|
scl: ${i2c_scl_pin}
|
||||||
|
scan: true
|
||||||
|
frequency: ${i2c_frequency}
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# GLOBALS (tracking min/max across time windows)
|
||||||
|
##########################################################################################
|
||||||
|
globals:
|
||||||
|
# Last completed overnight window (23:00 -> 07:00) min temperature
|
||||||
|
- id: g_min_temp_today_overnight
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Working overnight min temperature for the current 23:00 -> 07:00 window
|
||||||
|
- id: g_work_min_temp_overnight
|
||||||
|
type: float
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last completed daytime window (07:00 -> 23:00) max temperature
|
||||||
|
- id: g_max_temp_today_daytime
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Working daytime max temperature for the current 07:00 -> 23:00 window
|
||||||
|
- id: g_work_max_temp_daytime
|
||||||
|
type: float
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last completed overnight window (23:00 -> 07:00) min humidity
|
||||||
|
- id: g_min_humidity_today_overnight
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Working overnight min humidity for the current 23:00 -> 07:00 window
|
||||||
|
- id: g_work_min_humidity_overnight
|
||||||
|
type: float
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last completed daytime window (07:00 -> 23:00) max humidity
|
||||||
|
- id: g_max_humidity_today_daytime
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Working daytime max humidity for the current 07:00 -> 23:00 window
|
||||||
|
- id: g_work_max_humidity_daytime
|
||||||
|
type: float
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Current month max temperature
|
||||||
|
- id: g_max_temp_this_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last month max temperature
|
||||||
|
- id: g_max_temp_last_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Current month min temperature
|
||||||
|
- id: g_min_temp_this_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last month min temperature
|
||||||
|
- id: g_min_temp_last_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Current month max humidity
|
||||||
|
- id: g_max_humidity_this_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last month max humidity
|
||||||
|
- id: g_max_humidity_last_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Current month min humidity
|
||||||
|
- id: g_min_humidity_this_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Last month min humidity
|
||||||
|
- id: g_min_humidity_last_month
|
||||||
|
type: float
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "NAN"
|
||||||
|
|
||||||
|
# Track current calendar month/year so we can roll over month stats
|
||||||
|
- id: g_current_month
|
||||||
|
type: int
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "0"
|
||||||
|
|
||||||
|
- id: g_current_year
|
||||||
|
type: int
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: "0"
|
||||||
|
|
||||||
|
# Hour-bucket rolling 24h min/max temperature
|
||||||
|
- id: g_temp_hour_min
|
||||||
|
type: std::array<float, 24>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN}"
|
||||||
|
|
||||||
|
- id: g_temp_hour_max
|
||||||
|
type: std::array<float, 24>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN}"
|
||||||
|
|
||||||
|
- id: g_temp_hour_ts
|
||||||
|
type: std::array<uint32_t, 24>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}"
|
||||||
|
|
||||||
|
# Hour-bucket rolling 24h min/max humidity
|
||||||
|
- id: g_hum_hour_min
|
||||||
|
type: std::array<float, 24>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN}"
|
||||||
|
|
||||||
|
- id: g_hum_hour_max
|
||||||
|
type: std::array<float, 24>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN}"
|
||||||
|
|
||||||
|
- id: g_hum_hour_ts
|
||||||
|
type: std::array<uint32_t, 24>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}"
|
||||||
|
|
||||||
|
# Pressure sampling (every 5 minutes) for trend calculation
|
||||||
|
- id: g_pressure_samples
|
||||||
|
type: std::array<float, 40>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN}"
|
||||||
|
|
||||||
|
- id: g_pressure_ts
|
||||||
|
type: std::array<uint32_t, 40>
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}"
|
||||||
|
|
||||||
|
- id: g_pressure_idx
|
||||||
|
type: int
|
||||||
|
restore_value: no
|
||||||
|
initial_value: "0"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SENSORS
|
||||||
|
##########################################################################################
|
||||||
|
sensor:
|
||||||
|
- platform: bme280_i2c
|
||||||
|
address: ${bmp_address}
|
||||||
|
update_interval: 20s
|
||||||
|
|
||||||
|
temperature:
|
||||||
|
name: "${friendly_name} Temperature"
|
||||||
|
id: lounge_temperature
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
// We only need hour buckets and month stats if we have valid SNTP.
|
||||||
|
if (!now.is_valid()) return;
|
||||||
|
|
||||||
|
const int hour = now.hour;
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
|
||||||
|
// Update hourly bucket (rolling 24h)
|
||||||
|
{
|
||||||
|
int idx = hour;
|
||||||
|
if (idx < 0) idx = 0;
|
||||||
|
if (idx > 23) idx = 23;
|
||||||
|
id(g_temp_hour_ts)[idx] = now_ts;
|
||||||
|
|
||||||
|
float v = x;
|
||||||
|
if (isnan(id(g_temp_hour_min)[idx]) || v < id(g_temp_hour_min)[idx]) id(g_temp_hour_min)[idx] = v;
|
||||||
|
if (isnan(id(g_temp_hour_max)[idx]) || v > id(g_temp_hour_max)[idx]) id(g_temp_hour_max)[idx] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Daily window tracking uses current_mins so it still works during fallback.
|
||||||
|
// Overnight: 23:00 -> 07:00
|
||||||
|
// Daytime: 07:00 -> 23:00
|
||||||
|
const int mins = id(current_mins);
|
||||||
|
const bool in_overnight = (mins >= 1380) || (mins < 420);
|
||||||
|
|
||||||
|
if (in_overnight) {
|
||||||
|
if (isnan(id(g_work_min_temp_overnight)) || x < id(g_work_min_temp_overnight)) {
|
||||||
|
id(g_work_min_temp_overnight) = x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isnan(id(g_work_max_temp_daytime)) || x > id(g_work_max_temp_daytime)) {
|
||||||
|
id(g_work_max_temp_daytime) = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Month stats
|
||||||
|
if (isnan(id(g_min_temp_this_month)) || x < id(g_min_temp_this_month)) id(g_min_temp_this_month) = x;
|
||||||
|
if (isnan(id(g_max_temp_this_month)) || x > id(g_max_temp_this_month)) id(g_max_temp_this_month) = x;
|
||||||
|
|
||||||
|
pressure:
|
||||||
|
name: "${friendly_name} Pressure"
|
||||||
|
id: lounge_pressure
|
||||||
|
|
||||||
|
humidity:
|
||||||
|
name: "${friendly_name} Humidity"
|
||||||
|
id: lounge_humidity
|
||||||
|
on_value:
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return;
|
||||||
|
|
||||||
|
const int hour = now.hour;
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
|
||||||
|
// Update hourly bucket (rolling 24h)
|
||||||
|
{
|
||||||
|
int idx = hour;
|
||||||
|
if (idx < 0) idx = 0;
|
||||||
|
if (idx > 23) idx = 23;
|
||||||
|
id(g_hum_hour_ts)[idx] = now_ts;
|
||||||
|
|
||||||
|
float v = x;
|
||||||
|
if (isnan(id(g_hum_hour_min)[idx]) || v < id(g_hum_hour_min)[idx]) id(g_hum_hour_min)[idx] = v;
|
||||||
|
if (isnan(id(g_hum_hour_max)[idx]) || v > id(g_hum_hour_max)[idx]) id(g_hum_hour_max)[idx] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Daily window tracking uses current_mins so it still works during fallback.
|
||||||
|
const int mins = id(current_mins);
|
||||||
|
const bool in_overnight = (mins >= 1380) || (mins < 420);
|
||||||
|
|
||||||
|
if (in_overnight) {
|
||||||
|
if (isnan(id(g_work_min_humidity_overnight)) || x < id(g_work_min_humidity_overnight)) {
|
||||||
|
id(g_work_min_humidity_overnight) = x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isnan(id(g_work_max_humidity_daytime)) || x > id(g_work_max_humidity_daytime)) {
|
||||||
|
id(g_work_max_humidity_daytime) = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Month stats
|
||||||
|
if (isnan(id(g_min_humidity_this_month)) || x < id(g_min_humidity_this_month)) id(g_min_humidity_this_month) = x;
|
||||||
|
if (isnan(id(g_max_humidity_this_month)) || x > id(g_max_humidity_this_month)) id(g_max_humidity_this_month) = x;
|
||||||
|
|
||||||
|
# min_temp_today_overnight (this is the min temp, from 11pm to 7am for the last daily period)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Temp Overnight"
|
||||||
|
id: min_temp_today_overnight
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_min_temp_today_overnight);
|
||||||
|
|
||||||
|
# max_temp_today_daytime (this is the max temp, from 7am to 11pm for the last daily period)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Temp Today (Daytime)"
|
||||||
|
id: max_temp_today_daytime
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_max_temp_today_daytime);
|
||||||
|
|
||||||
|
# max_temp_this_month (max for this current calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Temp This Month"
|
||||||
|
id: max_temp_this_month
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_max_temp_this_month);
|
||||||
|
|
||||||
|
# max_temp_last_month (max for this last calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Temp Last Month"
|
||||||
|
id: max_temp_last_month
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_max_temp_last_month);
|
||||||
|
|
||||||
|
# min_temp_this_month (min for this current calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Temp This Month"
|
||||||
|
id: min_temp_this_month
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_min_temp_this_month);
|
||||||
|
|
||||||
|
# min_temp_last_month (min for this last calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Temp Last Month"
|
||||||
|
id: min_temp_last_month
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_min_temp_last_month);
|
||||||
|
|
||||||
|
# min_temp_today (min temp for last rolling 24 hours)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Temp Last 24 Hours"
|
||||||
|
id: min_temp_today
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return NAN;
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
|
||||||
|
float m = NAN;
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
if (id(g_temp_hour_ts)[i] == 0) continue;
|
||||||
|
if ((now_ts - id(g_temp_hour_ts)[i]) > 86400) continue;
|
||||||
|
float v = id(g_temp_hour_min)[i];
|
||||||
|
if (isnan(v)) continue;
|
||||||
|
if (isnan(m) || v < m) m = v;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
|
||||||
|
# max_temp_today (max temp for last rolling 24 hours)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Temp Last 24 Hours"
|
||||||
|
id: max_temp_today
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return NAN;
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
|
||||||
|
float m = NAN;
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
if (id(g_temp_hour_ts)[i] == 0) continue;
|
||||||
|
if ((now_ts - id(g_temp_hour_ts)[i]) > 86400) continue;
|
||||||
|
float v = id(g_temp_hour_max)[i];
|
||||||
|
if (isnan(v)) continue;
|
||||||
|
if (isnan(m) || v > m) m = v;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
|
||||||
|
# min_humidity_today_overnight (this is the min humidity, from 11pm to 7am for the last daily period)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Humidity Overnight"
|
||||||
|
id: min_humidity_today_overnight
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_min_humidity_today_overnight);
|
||||||
|
|
||||||
|
# max_humidity_today_daytime (this is the max humidity, from 7am to 11pm for the last daily period)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Humidity Today (Daytime)"
|
||||||
|
id: max_humidity_today_daytime
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_max_humidity_today_daytime);
|
||||||
|
|
||||||
|
# max_humidity_this_month (max for this current calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Humidity This Month"
|
||||||
|
id: max_humidity_this_month
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_max_humidity_this_month);
|
||||||
|
|
||||||
|
# max_humidity_last_month (max for this last calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Humidity Last Month"
|
||||||
|
id: max_humidity_last_month
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_max_humidity_last_month);
|
||||||
|
|
||||||
|
# min_humidity_this_month (min for this current calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Humidity This Month"
|
||||||
|
id: min_humidity_this_month
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_min_humidity_this_month);
|
||||||
|
|
||||||
|
# min_humidity_last_month (min for this last calendar month)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Humidity Last Month"
|
||||||
|
id: min_humidity_last_month
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
return id(g_min_humidity_last_month);
|
||||||
|
|
||||||
|
# min_humidity_today (min humidity for last rolling 24 hours)
|
||||||
|
- platform: template
|
||||||
|
name: "Lowest Humidity Last 24 Hours"
|
||||||
|
id: min_humidity_today
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return NAN;
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
|
||||||
|
float m = NAN;
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
if (id(g_hum_hour_ts)[i] == 0) continue;
|
||||||
|
if ((now_ts - id(g_hum_hour_ts)[i]) > 86400) continue;
|
||||||
|
float v = id(g_hum_hour_min)[i];
|
||||||
|
if (isnan(v)) continue;
|
||||||
|
if (isnan(m) || v < m) m = v;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
|
||||||
|
# max_humidity_today (max humidity for last rolling 24 hours)
|
||||||
|
- platform: template
|
||||||
|
name: "Highest Humidity Last 24 Hours"
|
||||||
|
id: max_humidity_today
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return NAN;
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
|
||||||
|
float m = NAN;
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
if (id(g_hum_hour_ts)[i] == 0) continue;
|
||||||
|
if ((now_ts - id(g_hum_hour_ts)[i]) > 86400) continue;
|
||||||
|
float v = id(g_hum_hour_max)[i];
|
||||||
|
if (isnan(v)) continue;
|
||||||
|
if (isnan(m) || v > m) m = v;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
|
||||||
|
# Pressure Rate Per Hour (3h) (hPa/hour based on delta over the last ~3 hours)
|
||||||
|
- platform: template
|
||||||
|
name: "Pressure Rate Per Hour (3h)"
|
||||||
|
id: pressure_rate_per_hour_3h
|
||||||
|
unit_of_measurement: "hPa/h"
|
||||||
|
accuracy_decimals: 2
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return NAN;
|
||||||
|
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
const uint32_t target_ts = now_ts - 10800; // 3h
|
||||||
|
|
||||||
|
float past = NAN;
|
||||||
|
uint32_t best_dt = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
for (int i = 0; i < 40; i++) {
|
||||||
|
uint32_t ts = id(g_pressure_ts)[i];
|
||||||
|
if (ts == 0) continue;
|
||||||
|
uint32_t dt = (ts > target_ts) ? (ts - target_ts) : (target_ts - ts);
|
||||||
|
if (dt < best_dt) {
|
||||||
|
best_dt = dt;
|
||||||
|
past = id(g_pressure_samples)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float cur = id(lounge_pressure).state;
|
||||||
|
if (isnan(cur) || isnan(past)) return NAN;
|
||||||
|
|
||||||
|
float delta_3h = cur - past;
|
||||||
|
return delta_3h / 3.0f;
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# TEXT SENSORS (pressure tendency)
|
||||||
|
##########################################################################################
|
||||||
|
text_sensor:
|
||||||
|
# Pressure Direction, Last 3 Hours (5-state tendency over the last ~3 hours)
|
||||||
|
# Outputs exactly one of:
|
||||||
|
# - rising rapidly
|
||||||
|
# - rising
|
||||||
|
# - steady
|
||||||
|
# - falling
|
||||||
|
# - falling rapidly
|
||||||
|
- platform: template
|
||||||
|
name: "Pressure Direction, Last 3 Hours"
|
||||||
|
id: pressure_rising_or_falling_3_hours
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return std::string("unknown");
|
||||||
|
|
||||||
|
const uint32_t now_ts = now.timestamp;
|
||||||
|
const uint32_t target_ts = now_ts - 10800; // 3h
|
||||||
|
|
||||||
|
float past = NAN;
|
||||||
|
uint32_t best_dt = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
for (int i = 0; i < 40; i++) {
|
||||||
|
uint32_t ts = id(g_pressure_ts)[i];
|
||||||
|
if (ts == 0) continue;
|
||||||
|
uint32_t dt = (ts > target_ts) ? (ts - target_ts) : (target_ts - ts);
|
||||||
|
if (dt < best_dt) {
|
||||||
|
best_dt = dt;
|
||||||
|
past = id(g_pressure_samples)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float cur = id(lounge_pressure).state;
|
||||||
|
if (isnan(cur) || isnan(past)) return std::string("unknown");
|
||||||
|
|
||||||
|
float delta_3h = cur - past;
|
||||||
|
|
||||||
|
float steady_band = atof("${pressure_3h_steady_band}");
|
||||||
|
float rapid_thr = atof("${pressure_3h_rapid_threshold}");
|
||||||
|
|
||||||
|
if (delta_3h >= rapid_thr) return std::string("rising rapidly");
|
||||||
|
if (delta_3h >= steady_band) return std::string("rising");
|
||||||
|
if (delta_3h <= -rapid_thr) return std::string("falling rapidly");
|
||||||
|
if (delta_3h <= -steady_band) return std::string("falling");
|
||||||
|
return std::string("steady");
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# INTERVALS (rollover overnight/daytime periods, month rollover, and pressure sampling)
|
||||||
|
##########################################################################################
|
||||||
|
interval:
|
||||||
|
# Sample pressure every 5 minutes for trend calculations
|
||||||
|
- interval: 5min
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return;
|
||||||
|
|
||||||
|
float p = id(lounge_pressure).state;
|
||||||
|
if (isnan(p)) return;
|
||||||
|
|
||||||
|
const uint32_t ts = now.timestamp;
|
||||||
|
|
||||||
|
int idx = id(g_pressure_idx);
|
||||||
|
if (idx < 0) idx = 0;
|
||||||
|
if (idx > 39) idx = 0;
|
||||||
|
|
||||||
|
id(g_pressure_samples)[idx] = p;
|
||||||
|
id(g_pressure_ts)[idx] = ts;
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
if (idx >= 40) idx = 0;
|
||||||
|
id(g_pressure_idx) = idx;
|
||||||
|
|
||||||
|
# Check period boundaries once a minute (uses current_mins, works during fallback)
|
||||||
|
- interval: 60s
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
const int mins = id(current_mins);
|
||||||
|
const bool at_0700 = (mins == 420);
|
||||||
|
const bool at_2300 = (mins == 1380);
|
||||||
|
|
||||||
|
// Roll overnight window at 07:00
|
||||||
|
if (at_0700) {
|
||||||
|
id(g_min_temp_today_overnight) = id(g_work_min_temp_overnight);
|
||||||
|
id(g_work_min_temp_overnight) = NAN;
|
||||||
|
|
||||||
|
id(g_min_humidity_today_overnight) = id(g_work_min_humidity_overnight);
|
||||||
|
id(g_work_min_humidity_overnight) = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll daytime window at 23:00
|
||||||
|
if (at_2300) {
|
||||||
|
id(g_max_temp_today_daytime) = id(g_work_max_temp_daytime);
|
||||||
|
id(g_work_max_temp_daytime) = NAN;
|
||||||
|
|
||||||
|
id(g_max_humidity_today_daytime) = id(g_work_max_humidity_daytime);
|
||||||
|
id(g_work_max_humidity_daytime) = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Month rollover requires real date/time (SNTP valid)
|
||||||
|
auto now = id(sntp_time).now();
|
||||||
|
if (!now.is_valid()) return;
|
||||||
|
|
||||||
|
const int mon = now.month;
|
||||||
|
const int yr = now.year;
|
||||||
|
|
||||||
|
if (id(g_current_month) == 0 || id(g_current_year) == 0) {
|
||||||
|
id(g_current_month) = mon;
|
||||||
|
id(g_current_year) = yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mon != id(g_current_month) || yr != id(g_current_year)) {
|
||||||
|
// Copy current month stats to last month
|
||||||
|
id(g_max_temp_last_month) = id(g_max_temp_this_month);
|
||||||
|
id(g_min_temp_last_month) = id(g_min_temp_this_month);
|
||||||
|
id(g_max_humidity_last_month) = id(g_max_humidity_this_month);
|
||||||
|
id(g_min_humidity_last_month) = id(g_min_humidity_this_month);
|
||||||
|
|
||||||
|
// Reset current month stats
|
||||||
|
id(g_max_temp_this_month) = NAN;
|
||||||
|
id(g_min_temp_this_month) = NAN;
|
||||||
|
id(g_max_humidity_this_month) = NAN;
|
||||||
|
id(g_min_humidity_this_month) = NAN;
|
||||||
|
|
||||||
|
id(g_current_month) = mon;
|
||||||
|
id(g_current_year) = yr;
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
substitutions:
|
substitutions:
|
||||||
# Device Naming
|
# Device Naming
|
||||||
device_name: "esp-mainbathroomlights"
|
device_name: "esp-mainbathroomlights"
|
||||||
friendly_name: "Main Bathroom Lightswitch (3) 2"
|
friendly_name: "Main Bathroom Lightswitch (3)"
|
||||||
description_comment: "BK7231N - Main Bathroom Lightswitch using a Zemismart KS-811 Triple Push Button. Main Lights (1), Shower Lights (2), Cabinet Lights (3)"
|
description_comment: "BK7231N - Main Bathroom Lightswitch using a Zemismart KS-811 Triple Push Button. Main Lights (1), Shower Lights (2), Cabinet Lights (3)"
|
||||||
device_area: "Main Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
device_area: "Main Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ binary_sensor:
|
|||||||
name: "Button 1: ${switch_1_name}"
|
name: "Button 1: ${switch_1_name}"
|
||||||
on_press:
|
on_press:
|
||||||
- delay: 50ms
|
- delay: 50ms
|
||||||
- switch.toggle: Relay_1
|
- switch.toggle: Relay_2 #(FIX shoud be relay 1)
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin:
|
pin:
|
||||||
@@ -183,7 +183,7 @@ binary_sensor:
|
|||||||
name: "Button 3: ${switch_3_name}"
|
name: "Button 3: ${switch_3_name}"
|
||||||
on_press:
|
on_press:
|
||||||
- delay: 50ms
|
- delay: 50ms
|
||||||
- switch.turn_off: Relay_1
|
#- switch.turn_off: Relay_1
|
||||||
- switch.turn_off: Relay_2
|
- switch.turn_off: Relay_2
|
||||||
- switch.turn_off: Relay_3
|
- switch.turn_off: Relay_3
|
||||||
|
|
||||||
@@ -212,3 +212,22 @@ switch:
|
|||||||
pin: P15 #GPIO15
|
pin: P15 #GPIO15
|
||||||
id: Relay_3
|
id: Relay_3
|
||||||
restore_mode: ${relay_restore_mode}
|
restore_mode: ${relay_restore_mode}
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# TEMPLATE COMPONENT
|
||||||
|
#
|
||||||
|
##########################################################################################
|
||||||
|
# Virtual group switch for Home Assistant
|
||||||
|
- platform: template
|
||||||
|
name: "All Main Bathroom Lights"
|
||||||
|
id: all_main_bathroom_lights
|
||||||
|
optimistic: false
|
||||||
|
restore_mode: RESTORE_DEFAULT_OFF
|
||||||
|
lambda: |-
|
||||||
|
return id(Relay_1).state || id(Relay_2).state || id(Relay_3).state;
|
||||||
|
turn_on_action:
|
||||||
|
- switch.turn_on: Relay_2
|
||||||
|
turn_off_action:
|
||||||
|
#- switch.turn_off: Relay_1
|
||||||
|
- switch.turn_off: Relay_2
|
||||||
|
- switch.turn_off: Relay_3
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
# input_select for dropdown menu to select the timer duration or cancel
|
|
||||||
input_select:
|
|
||||||
master_bedroom_climate_timer_duration:
|
|
||||||
name: Master Bedroom Aircon Sleep
|
|
||||||
options:
|
|
||||||
- "1 hour"
|
|
||||||
- "2 hours"
|
|
||||||
- "3 hours"
|
|
||||||
- "Cancel sleep timer"
|
|
||||||
initial: "Cancel sleep timer"
|
|
||||||
icon: mdi:timer-outline
|
|
||||||
|
|
||||||
# Single timer for managing the countdown
|
|
||||||
timer:
|
|
||||||
master_bedroom_climate_timer:
|
|
||||||
name: Master Bedroom Aircon Timer
|
|
||||||
duration: "00:00:00" # Initial duration; will be set dynamically
|
|
||||||
|
|
||||||
# Automation to start or cancel the timer based on the dropdown selection
|
|
||||||
automation:
|
|
||||||
- alias: "Start or Cancel Master Bedroom Climate Timer"
|
|
||||||
trigger:
|
|
||||||
- platform: state
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
action:
|
|
||||||
- service: timer.cancel
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
|
|
||||||
- choose:
|
|
||||||
- conditions:
|
|
||||||
- condition: state
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
state: "1 hour"
|
|
||||||
sequence:
|
|
||||||
- service: timer.start
|
|
||||||
data:
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
duration: "01:00:00"
|
|
||||||
|
|
||||||
- conditions:
|
|
||||||
- condition: state
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
state: "1 minute"
|
|
||||||
sequence:
|
|
||||||
- service: timer.start
|
|
||||||
data:
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
duration: "00:01:00"
|
|
||||||
|
|
||||||
- conditions:
|
|
||||||
- condition: state
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
state: "2 hours"
|
|
||||||
sequence:
|
|
||||||
- service: timer.start
|
|
||||||
data:
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
duration: "02:00:00"
|
|
||||||
|
|
||||||
- conditions:
|
|
||||||
- condition: state
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
state: "3 hours"
|
|
||||||
sequence:
|
|
||||||
- service: timer.start
|
|
||||||
data:
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
duration: "03:00:00"
|
|
||||||
|
|
||||||
- conditions:
|
|
||||||
- condition: state
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
state: "Cancel sleep timer"
|
|
||||||
sequence:
|
|
||||||
- service: timer.cancel
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
- service: climate.turn_off
|
|
||||||
target:
|
|
||||||
entity_id: climate.master_bedroom
|
|
||||||
|
|
||||||
# Automation to turn off the climate entity when the timer finishes
|
|
||||||
- alias: "Turn Off Master Bedroom Climate on Timer Completion"
|
|
||||||
trigger:
|
|
||||||
- platform: event
|
|
||||||
event_type: timer.finished
|
|
||||||
event_data:
|
|
||||||
entity_id: timer.master_bedroom_climate_timer
|
|
||||||
action:
|
|
||||||
- service: climate.turn_off
|
|
||||||
target:
|
|
||||||
entity_id: climate.master_bedroom
|
|
||||||
- service: input_select.select_option
|
|
||||||
data:
|
|
||||||
entity_id: input_select.master_bedroom_climate_timer_duration
|
|
||||||
option: "Cancel sleep timer" # Reset to the initial value
|
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# ------------------------------
|
||||||
|
# TIMER HELPER (define in YAML so it definitely exists)
|
||||||
|
# ------------------------------
|
||||||
|
timer:
|
||||||
|
master_bedroom_climate_timer:
|
||||||
|
name: Master Bedroom Climate Timer
|
||||||
|
duration: "00:00:00"
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# DROPDOWN (sleep timer)
|
||||||
|
# ------------------------------
|
||||||
|
input_select:
|
||||||
|
master_bedroom_climate_timer_duration:
|
||||||
|
name: Master Bedroom Aircon Sleep
|
||||||
|
options:
|
||||||
|
- "1 hour"
|
||||||
|
- "2 hours"
|
||||||
|
- "3 hours"
|
||||||
|
- "6 hours"
|
||||||
|
- "Cancel timer"
|
||||||
|
initial: "Cancel timer"
|
||||||
|
icon: mdi:timer-outline
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# LIVE REMAINING (updates immediately on selection, and every minute)
|
||||||
|
# ------------------------------
|
||||||
|
template:
|
||||||
|
- trigger:
|
||||||
|
# Update once per minute (suits hh:mm display)
|
||||||
|
- platform: time_pattern
|
||||||
|
minutes: "/1"
|
||||||
|
|
||||||
|
# Update when timer STATE changes (idle/active/paused)
|
||||||
|
- platform: state
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
|
||||||
|
# Update immediately when user changes the dropdown
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- name: "Master Bedroom Climate Timer Remaining"
|
||||||
|
unique_id: master_bedroom_climate_timer_remaining
|
||||||
|
icon: mdi:timer
|
||||||
|
state: >-
|
||||||
|
{% set st = states('timer.master_bedroom_climate_timer') | lower %}
|
||||||
|
{% if st == 'idle' %}
|
||||||
|
0:00
|
||||||
|
{% else %}
|
||||||
|
{% set fa = state_attr('timer.master_bedroom_climate_timer', 'finishes_at') %}
|
||||||
|
{% if fa %}
|
||||||
|
{% set end = as_datetime(fa) %}
|
||||||
|
{% set sec = (end - now()).total_seconds() | int(0) %}
|
||||||
|
{% if sec < 0 %}{% set sec = 0 %}{% endif %}
|
||||||
|
{% set hh = (sec // 3600) | int %}
|
||||||
|
{% set mm = ((sec % 3600) // 60) | int %}
|
||||||
|
{{ hh }}:{{ '%02d' | format(mm) }}
|
||||||
|
{% else %}
|
||||||
|
0:00
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# AUTOMATION: start/cancel timer when dropdown changes
|
||||||
|
# ------------------------------
|
||||||
|
automation:
|
||||||
|
- alias: "Start or Cancel Master Bedroom Climate Timer"
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
|
||||||
|
variables:
|
||||||
|
opt: "{{ states('input_select.master_bedroom_climate_timer_duration') }}"
|
||||||
|
dur_map:
|
||||||
|
"1 hour": "01:00:00"
|
||||||
|
"2 hours": "02:00:00"
|
||||||
|
"3 hours": "03:00:00"
|
||||||
|
"6 hours": "06:00:00"
|
||||||
|
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ opt in dur_map }}"
|
||||||
|
sequence:
|
||||||
|
- service: timer.cancel
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
- delay: "00:00:01"
|
||||||
|
- service: timer.start
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
data:
|
||||||
|
duration: "{{ dur_map[opt] }}"
|
||||||
|
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
state: "Cancel timer"
|
||||||
|
sequence:
|
||||||
|
- service: timer.cancel
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
|
||||||
|
- alias: "Turn Off Master Bedroom Climate on Timer Completion"
|
||||||
|
trigger:
|
||||||
|
- platform: event
|
||||||
|
event_type: timer.finished
|
||||||
|
event_data:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
action:
|
||||||
|
- service: climate.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
data:
|
||||||
|
option: "Cancel timer"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
input_boolean:
|
||||||
|
moon_phase_percent_overlay:
|
||||||
|
name: Moon phase percent overlay
|
||||||
|
icon: mdi:percent
|
||||||
|
|
||||||
|
script:
|
||||||
|
moon_phase_percent_overlay_10s:
|
||||||
|
alias: Moon phase percent overlay (10s)
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.moon_phase_percent_overlay
|
||||||
|
- delay: "00:00:10"
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.moon_phase_percent_overlay
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
input_boolean:
|
||||||
|
downstairs_flat_occupied:
|
||||||
|
name: Downstairs Guest Occupied
|
||||||
|
icon: mdi:briefcase-plus-outline
|
||||||
|
|
||||||
|
script:
|
||||||
|
downstairs_light_toggle_lounge_main:
|
||||||
|
alias: Downstairs Light Toggle - Lounge Main Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstloungemain_relay_1_main_lights
|
||||||
|
|
||||||
|
downstairs_light_toggle_lounge_back_wall:
|
||||||
|
alias: Downstairs Light Toggle - Lounge Back Wall Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstloungemain_relay_2_back_wall_lights
|
||||||
|
|
||||||
|
downstairs_light_toggle_main_bedroom_main:
|
||||||
|
alias: Downstairs Light Toggle - Main Bedroom Main Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstmasterbedrmlights_relay_1_main_lights
|
||||||
|
|
||||||
|
downstairs_light_toggle_small_bedroom_main:
|
||||||
|
alias: Downstairs Light Toggle - Small Bedroom Room Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstbedrm2lights_relay_1_main_lights
|
||||||
|
|
||||||
|
downstairs_light_toggle_kitchen:
|
||||||
|
alias: Downstairs Light Toggle - Kitchen Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstkitchlights_relay_2_kitchen_light
|
||||||
|
|
||||||
|
downstairs_light_toggle_dining:
|
||||||
|
alias: Downstairs Light Toggle - Dining Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstkitchlights_relay_1_dining_light
|
||||||
|
|
||||||
|
downstairs_light_toggle_bathroom_main:
|
||||||
|
alias: Downstairs Light Toggle - Bathroom Main Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstbathswitch_relay_1_main_lights
|
||||||
|
|
||||||
|
downstairs_light_toggle_bathroom_cabinet:
|
||||||
|
alias: Downstairs Light Toggle - Bathroom Cabinet Lights
|
||||||
|
mode: queued
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
- service: switch.toggle
|
||||||
|
target:
|
||||||
|
entity_id: switch.esp_downstbathswitch_relay_2_cabinet_light
|
||||||
|
|
||||||
|
downstairs_light_toggle_all_group:
|
||||||
|
alias: Downstairs Lights - All On/Off
|
||||||
|
mode: single
|
||||||
|
sequence:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.downstairs_flat_occupied
|
||||||
|
state: "off"
|
||||||
|
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: group.downstairs_flat_lights
|
||||||
|
state: "on"
|
||||||
|
sequence:
|
||||||
|
- service: homeassistant.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: group.downstairs_flat_lights
|
||||||
|
default:
|
||||||
|
- service: homeassistant.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: group.downstairs_flat_lights
|
||||||
@@ -62,3 +62,37 @@ automation:
|
|||||||
"1hr top-up now":"boost", "Charge now until full":"now-full", "Stop charging now":"stop" } %} {% set dta = trigger.to_state.state %}
|
"1hr top-up now":"boost", "Charge now until full":"now-full", "Stop charging now":"stop" } %} {% set dta = trigger.to_state.state %}
|
||||||
{{ mapping[dta] }}
|
{{ mapping[dta] }}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
template:
|
||||||
|
- trigger:
|
||||||
|
# Update at least every 10 seconds (and also on HA start)
|
||||||
|
- platform: time_pattern
|
||||||
|
seconds: "/10"
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
binary_sensor:
|
||||||
|
- name: "32A EV Charger Charging"
|
||||||
|
unique_id: ev_charger_32a_charging
|
||||||
|
icon: mdi:car-electric
|
||||||
|
state: >-
|
||||||
|
{{ (states('sensor.32a_ev_wallcharger_power') | float(0)) > 0.1 }}
|
||||||
|
delay_on:
|
||||||
|
seconds: 60
|
||||||
|
delay_off:
|
||||||
|
seconds: 60
|
||||||
|
|
||||||
|
- name: "16A EV Charger Charging"
|
||||||
|
unique_id: ev_charger_16a_charging
|
||||||
|
icon: mdi:car-electric
|
||||||
|
state: >-
|
||||||
|
{{ (states('sensor.16a_ev_wallcharger_power') | float(0)) > 0.1 }}
|
||||||
|
delay_on:
|
||||||
|
seconds: 60
|
||||||
|
delay_off:
|
||||||
|
seconds: 60
|
||||||
|
|
||||||
|
- binary_sensor:
|
||||||
|
- name: 32A EV Charger Charging UI
|
||||||
|
unique_id: ev_charger_charging_ui
|
||||||
|
state: "{{ is_state('sensor.32a_ev_charger_charging', 'on') }}"
|
||||||
|
icon: mdi:car-electric
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
input_boolean:
|
|
||||||
foxhole_occupied:
|
|
||||||
name: Foxhole Guest Occupied
|
|
||||||
icon: mdi:briefcase-plus-outline
|
|
||||||
@@ -0,0 +1,309 @@
|
|||||||
|
##########################################################################################
|
||||||
|
# heat_pump_helpers.yaml
|
||||||
|
#
|
||||||
|
# Panasonic Comfort Cloud (panasonic_cc) helpers for Heat/Cool control + presets.
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# - Provide "optimistic" UI feedback for cloud/slow mode changes
|
||||||
|
# - Provide one-tap scripts for Off/Heat/Cool/Dehumid
|
||||||
|
# - Provide a 3-step "Profile" cycler (Normal/Boost/Quiet) using preset_mode:
|
||||||
|
# * preset_mode: boost -> Powerful ON (we label this "Boost")
|
||||||
|
# * preset_mode: eco -> Quiet ON (we label this "Quiet")
|
||||||
|
# * preset_mode: none -> Powerful OFF + Quiet OFF (we label this "Normal")
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - Your climate entity supports preset_modes: [none, eco, boost]
|
||||||
|
# - The switch.lounge_ai_eco entity appears to be read-only (cannot be controlled),
|
||||||
|
# so "Eco" and "Eco/Quiet" profiles have been removed.
|
||||||
|
# - When turning the heat pump OFF, we also force preset_mode to "none" so that
|
||||||
|
# Boost/Quiet are cleared.
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
input_select:
|
||||||
|
# Used by your dashboard buttons to "light up" instantly (optimistic state).
|
||||||
|
# When set to something other than "none", the UI can treat that as "pending".
|
||||||
|
master_bedroom_hvac_requested_mode:
|
||||||
|
name: Master Bedroom HVAC Requested Mode
|
||||||
|
options:
|
||||||
|
- "none"
|
||||||
|
- "off"
|
||||||
|
- "heat"
|
||||||
|
- "cool"
|
||||||
|
- "dry"
|
||||||
|
initial: "none"
|
||||||
|
|
||||||
|
# Heat pump profile selector shown on the dashboard and cycled with one tap.
|
||||||
|
master_bedroom_heat_pump_profile:
|
||||||
|
name: Master Bedroom Heat Pump Profile
|
||||||
|
options:
|
||||||
|
- Normal
|
||||||
|
- Boost
|
||||||
|
- Quiet
|
||||||
|
initial: Normal
|
||||||
|
|
||||||
|
script:
|
||||||
|
########################################################################################
|
||||||
|
# One-tap HVAC mode scripts
|
||||||
|
# These set the optimistic helper first, then call the real climate services.
|
||||||
|
#
|
||||||
|
# IMPORTANT:
|
||||||
|
# - Off also clears Boost/Quiet by forcing preset_mode to "none".
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
master_bedroom_hvac_off:
|
||||||
|
alias: Master Bedroom HVAC - Off
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
# Optimistic UI: show "Off" immediately
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "off"
|
||||||
|
|
||||||
|
# Turn the unit off (more reliable than set_hvac_mode off on many cloud integrations)
|
||||||
|
- service: climate.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
# Clear Panasonic preset flags: none = Boost OFF + Quiet OFF
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "none"
|
||||||
|
|
||||||
|
master_bedroom_hvac_heat:
|
||||||
|
alias: Master Bedroom HVAC - Heat
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "heat"
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_hvac_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
hvac_mode: "heat"
|
||||||
|
|
||||||
|
master_bedroom_hvac_cool:
|
||||||
|
alias: Master Bedroom HVAC - Cool
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "cool"
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_hvac_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
hvac_mode: "cool"
|
||||||
|
|
||||||
|
master_bedroom_hvac_dry:
|
||||||
|
alias: Master Bedroom HVAC - Dehumid
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "dry"
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_hvac_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
hvac_mode: "dry"
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# Profile logic: Apply + Cycle
|
||||||
|
#
|
||||||
|
# Profile definitions (Panasonic CC / panasonic_cc):
|
||||||
|
# - Normal -> preset_mode none (Boost OFF, Quiet OFF)
|
||||||
|
# - Boost -> preset_mode boost (Powerful ON)
|
||||||
|
# - Quiet -> preset_mode eco (Quiet ON)
|
||||||
|
#
|
||||||
|
# The profile selector is kept in-sync with the actual preset_mode via automation.
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
master_bedroom_heat_pump_profile_apply:
|
||||||
|
alias: Master Bedroom Heat Pump - Apply Profile
|
||||||
|
mode: restart
|
||||||
|
fields:
|
||||||
|
profile:
|
||||||
|
description: Profile name (Normal, Boost, Quiet)
|
||||||
|
example: Normal
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
p: "{{ profile | default(states('input_select.master_bedroom_heat_pump_profile')) }}"
|
||||||
|
preset_for_profile: >-
|
||||||
|
{% if p == 'Normal' %}none
|
||||||
|
{% elif p == 'Boost' %}boost
|
||||||
|
{% elif p == 'Quiet' %}eco
|
||||||
|
{% else %}none
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Optional: ensure the unit is on when applying a profile.
|
||||||
|
# Comment out if you want profile selection while the unit is off.
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
# Set preset explicitly every time so Boost/Quiet are mutually exclusive
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "{{ preset_for_profile }}"
|
||||||
|
|
||||||
|
master_bedroom_heat_pump_profile_next:
|
||||||
|
alias: Master Bedroom Heat Pump - Next Profile
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
cur: "{{ states('input_select.master_bedroom_heat_pump_profile') }}"
|
||||||
|
nxt: >-
|
||||||
|
{% if cur == 'Normal' %}Boost
|
||||||
|
{% elif cur == 'Boost' %}Quiet
|
||||||
|
{% else %}Normal
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Store the new selection (UI)
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_heat_pump_profile
|
||||||
|
data:
|
||||||
|
option: "{{ nxt }}"
|
||||||
|
|
||||||
|
# Apply it to the heat pump
|
||||||
|
- service: script.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: script.master_bedroom_heat_pump_profile_apply
|
||||||
|
data:
|
||||||
|
variables:
|
||||||
|
profile: "{{ nxt }}"
|
||||||
|
|
||||||
|
automation:
|
||||||
|
########################################################################################
|
||||||
|
# Clear "requested mode" once the climate entity catches up, so the UI reflects reality.
|
||||||
|
# This prevents two mode buttons appearing lit due to stale cloud attributes.
|
||||||
|
########################################################################################
|
||||||
|
- id: master_bedroom_hvac_clear_requested_mode
|
||||||
|
alias: Master Bedroom HVAC - Clear Requested Mode
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
to:
|
||||||
|
- "off"
|
||||||
|
- "heat"
|
||||||
|
- "cool"
|
||||||
|
- "dry"
|
||||||
|
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ states('input_select.master_bedroom_hvac_requested_mode') != 'none' }}
|
||||||
|
|
||||||
|
action:
|
||||||
|
# Small delay to allow the cloud integration to reflect the change
|
||||||
|
- delay: "00:00:03"
|
||||||
|
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{% set req = states('input_select.master_bedroom_hvac_requested_mode') %}
|
||||||
|
{% set mode = states('climate.master_bedroom') %}
|
||||||
|
{% set action = (state_attr('climate.master_bedroom','hvac_action') or '') %}
|
||||||
|
{{ (req == 'off' and (mode == 'off' or action == 'off'))
|
||||||
|
or (req in ['heat','cool','dry'] and mode == req) }}
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "none"
|
||||||
|
|
||||||
|
default:
|
||||||
|
# Failsafe: if the cloud entity is slow or ambiguous, clear after a while anyway
|
||||||
|
- delay: "00:00:25"
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "none"
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# Sync the Profile selector from actual device preset_mode (including changes made in
|
||||||
|
# the Panasonic app). This keeps the UI truthful.
|
||||||
|
#
|
||||||
|
# Mapping:
|
||||||
|
# - preset boost -> Boost
|
||||||
|
# - preset eco -> Quiet
|
||||||
|
# - preset none -> Normal
|
||||||
|
########################################################################################
|
||||||
|
- id: master_bedroom_heat_pump_profile_sync_from_entities
|
||||||
|
alias: Master Bedroom Heat Pump - Sync Profile From Entities
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
action:
|
||||||
|
- delay: "00:00:02"
|
||||||
|
- variables:
|
||||||
|
preset: "{{ state_attr('climate.master_bedroom', 'preset_mode') or 'none' }}"
|
||||||
|
new_profile: >-
|
||||||
|
{% if preset == 'boost' %}
|
||||||
|
Boost
|
||||||
|
{% elif preset == 'eco' %}
|
||||||
|
Quiet
|
||||||
|
{% else %}
|
||||||
|
Normal
|
||||||
|
{% endif %}
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('input_select.master_bedroom_heat_pump_profile') != new_profile }}"
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_heat_pump_profile
|
||||||
|
data:
|
||||||
|
option: "{{ new_profile }}"
|
||||||
|
|
||||||
|
template:
|
||||||
|
- sensor:
|
||||||
|
- name: Master Bedroom Heat Pump Profile Tile
|
||||||
|
unique_id: master_bedroom_heat_pump_profile_tile
|
||||||
|
state: "{{ states('input_select.master_bedroom_heat_pump_profile') }}"
|
||||||
|
icon: >
|
||||||
|
{% set p = states('input_select.master_bedroom_heat_pump_profile') %}
|
||||||
|
{% if p == 'Boost' %}
|
||||||
|
mdi:rocket-launch
|
||||||
|
{% elif p == 'Quiet' %}
|
||||||
|
mdi:leaf
|
||||||
|
{% else %}
|
||||||
|
mdi:gauge
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,480 @@
|
|||||||
|
##########################################################################################
|
||||||
|
# heat_pump_master_bedroom_helpers.yaml
|
||||||
|
#
|
||||||
|
# Master Bedroom Panasonic Comfort Cloud Heat Pump Helpers
|
||||||
|
#
|
||||||
|
# Combines:
|
||||||
|
# - Setpoint helpers (optimistic cloud control)
|
||||||
|
# - HVAC mode helpers (Off / Heat / Cool / Dry)
|
||||||
|
# - Profile logic (Normal / Boost / Quiet)
|
||||||
|
# - Sleep timer system
|
||||||
|
# - Sync automations
|
||||||
|
#
|
||||||
|
# Designed for:
|
||||||
|
# climate.master_bedroom (Panasonic Comfort Cloud)
|
||||||
|
#
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# TIMER (Sleep Timer Entity)
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
timer:
|
||||||
|
master_bedroom_climate_timer:
|
||||||
|
name: Master Bedroom Climate Timer
|
||||||
|
duration: "00:00:00"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# INPUT HELPERS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
input_select:
|
||||||
|
# Sleep timer dropdown
|
||||||
|
master_bedroom_climate_timer_duration:
|
||||||
|
name: Master Bedroom Aircon Sleep
|
||||||
|
options:
|
||||||
|
- "1 hour"
|
||||||
|
- "2 hours"
|
||||||
|
- "3 hours"
|
||||||
|
- "6 hours"
|
||||||
|
- "Cancel timer"
|
||||||
|
initial: "Cancel timer"
|
||||||
|
icon: mdi:timer-outline
|
||||||
|
|
||||||
|
# Optimistic requested HVAC mode (used by dashboard buttons)
|
||||||
|
master_bedroom_hvac_requested_mode:
|
||||||
|
name: Master Bedroom HVAC Requested Mode
|
||||||
|
options:
|
||||||
|
- "none"
|
||||||
|
- "off"
|
||||||
|
- "heat"
|
||||||
|
- "cool"
|
||||||
|
- "dry"
|
||||||
|
initial: "none"
|
||||||
|
|
||||||
|
# Heat pump profile selector
|
||||||
|
master_bedroom_heat_pump_profile:
|
||||||
|
name: Master Bedroom Heat Pump Profile
|
||||||
|
options:
|
||||||
|
- Normal
|
||||||
|
- Boost
|
||||||
|
- Quiet
|
||||||
|
initial: Normal
|
||||||
|
|
||||||
|
input_number:
|
||||||
|
# Optimistic UI setpoint helper
|
||||||
|
master_bedroom_setpoint_ui:
|
||||||
|
name: Master Bedroom Setpoint UI
|
||||||
|
min: 10
|
||||||
|
max: 30
|
||||||
|
step: 0.5
|
||||||
|
mode: box
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# TEMPLATE SENSORS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
template:
|
||||||
|
############################################################################
|
||||||
|
# Sleep Timer Remaining (HH:MM display)
|
||||||
|
############################################################################
|
||||||
|
- trigger:
|
||||||
|
- platform: time_pattern
|
||||||
|
minutes: "/1"
|
||||||
|
|
||||||
|
- platform: state
|
||||||
|
entity_id:
|
||||||
|
- timer.master_bedroom_climate_timer
|
||||||
|
- input_select.master_bedroom_climate_timer_duration
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- name: "Master Bedroom Climate Timer Remaining"
|
||||||
|
unique_id: master_bedroom_climate_timer_remaining
|
||||||
|
icon: mdi:timer
|
||||||
|
state: >-
|
||||||
|
{% set st = states('timer.master_bedroom_climate_timer') | lower %}
|
||||||
|
{% if st == 'idle' %}
|
||||||
|
0:00
|
||||||
|
{% else %}
|
||||||
|
{% set fa = state_attr('timer.master_bedroom_climate_timer', 'finishes_at') %}
|
||||||
|
{% if fa %}
|
||||||
|
{% set end = as_datetime(fa) %}
|
||||||
|
{% set sec = (end - now()).total_seconds() | int(0) %}
|
||||||
|
{% if sec < 0 %}{% set sec = 0 %}{% endif %}
|
||||||
|
{% set hh = (sec // 3600) | int %}
|
||||||
|
{% set mm = ((sec % 3600) // 60) | int %}
|
||||||
|
{{ hh }}:{{ '%02d' | format(mm) }}
|
||||||
|
{% else %}
|
||||||
|
0:00
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# Profile tile sensor (for dashboard icon)
|
||||||
|
############################################################################
|
||||||
|
- sensor:
|
||||||
|
- name: Master Bedroom Heat Pump Profile Tile
|
||||||
|
unique_id: master_bedroom_heat_pump_profile_tile
|
||||||
|
state: "{{ states('input_select.master_bedroom_heat_pump_profile') }}"
|
||||||
|
icon: >
|
||||||
|
{% set p = states('input_select.master_bedroom_heat_pump_profile') %}
|
||||||
|
{% if p == 'Boost' %}
|
||||||
|
mdi:rocket-launch
|
||||||
|
{% elif p == 'Quiet' %}
|
||||||
|
mdi:leaf
|
||||||
|
{% else %}
|
||||||
|
mdi:gauge
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SCRIPTS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
script:
|
||||||
|
############################################################################
|
||||||
|
# SETPOINT SYNC (cloud → UI helper)
|
||||||
|
############################################################################
|
||||||
|
master_bedroom_setpoint_sync_from_climate:
|
||||||
|
alias: Master Bedroom Setpoint Sync From Climate
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
sp: "{{ state_attr('climate.master_bedroom', 'temperature') | float(0) }}"
|
||||||
|
- service: input_number.set_value
|
||||||
|
target:
|
||||||
|
entity_id: input_number.master_bedroom_setpoint_ui
|
||||||
|
data:
|
||||||
|
value: "{{ sp }}"
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# SETPOINT ADJUST (Chevron buttons)
|
||||||
|
############################################################################
|
||||||
|
master_bedroom_setpoint_adjust:
|
||||||
|
alias: Master Bedroom Setpoint Adjust
|
||||||
|
mode: queued
|
||||||
|
fields:
|
||||||
|
step:
|
||||||
|
description: "Step in degrees C, e.g. -0.5 or 0.5"
|
||||||
|
example: -0.5
|
||||||
|
sequence:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{% set s = states('climate.master_bedroom') | lower %}
|
||||||
|
{{ s not in ['off','unavailable','unknown','none',''] }}
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
cur_ui: "{{ states('input_number.master_bedroom_setpoint_ui') | float(0) }}"
|
||||||
|
cur_cl: "{{ state_attr('climate.master_bedroom', 'temperature') | float(0) }}"
|
||||||
|
cur: "{{ cur_ui if cur_ui > 0 else cur_cl }}"
|
||||||
|
raw: "{{ cur + (step | float(0)) }}"
|
||||||
|
new: "{{ [30, [10, raw] | max] | min | round(1) }}"
|
||||||
|
|
||||||
|
- service: input_number.set_value
|
||||||
|
target:
|
||||||
|
entity_id: input_number.master_bedroom_setpoint_ui
|
||||||
|
data:
|
||||||
|
value: "{{ new }}"
|
||||||
|
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
temperature: "{{ new }}"
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# HVAC MODE SCRIPTS
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
master_bedroom_hvac_off:
|
||||||
|
alias: Master Bedroom HVAC - Off
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "off"
|
||||||
|
|
||||||
|
- service: climate.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "none"
|
||||||
|
|
||||||
|
master_bedroom_hvac_heat:
|
||||||
|
alias: Master Bedroom HVAC - Heat
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "heat"
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_hvac_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
hvac_mode: "heat"
|
||||||
|
|
||||||
|
master_bedroom_hvac_cool:
|
||||||
|
alias: Master Bedroom HVAC - Cool
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "cool"
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_hvac_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
hvac_mode: "cool"
|
||||||
|
|
||||||
|
master_bedroom_hvac_dry:
|
||||||
|
alias: Master Bedroom HVAC - Dehumid
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "dry"
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_hvac_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
hvac_mode: "dry"
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# PROFILE SCRIPTS
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
master_bedroom_heat_pump_profile_apply:
|
||||||
|
alias: Master Bedroom Heat Pump - Apply Profile
|
||||||
|
mode: restart
|
||||||
|
fields:
|
||||||
|
profile:
|
||||||
|
description: Profile name (Normal, Boost, Quiet)
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
p: "{{ profile | default(states('input_select.master_bedroom_heat_pump_profile')) }}"
|
||||||
|
preset_for_profile: >-
|
||||||
|
{% if p == 'Normal' %}none
|
||||||
|
{% elif p == 'Boost' %}boost
|
||||||
|
{% elif p == 'Quiet' %}eco
|
||||||
|
{% else %}none
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "{{ preset_for_profile }}"
|
||||||
|
|
||||||
|
master_bedroom_heat_pump_profile_next:
|
||||||
|
alias: Master Bedroom Heat Pump - Next Profile
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
cur: "{{ states('input_select.master_bedroom_heat_pump_profile') }}"
|
||||||
|
nxt: >-
|
||||||
|
{% if cur == 'Normal' %}Boost
|
||||||
|
{% elif cur == 'Boost' %}Quiet
|
||||||
|
{% else %}Normal
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_heat_pump_profile
|
||||||
|
data:
|
||||||
|
option: "{{ nxt }}"
|
||||||
|
|
||||||
|
- service: script.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: script.master_bedroom_heat_pump_profile_apply
|
||||||
|
data:
|
||||||
|
variables:
|
||||||
|
profile: "{{ nxt }}"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# AUTOMATIONS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
automation:
|
||||||
|
############################################################################
|
||||||
|
# Sleep Timer Start / Cancel
|
||||||
|
############################################################################
|
||||||
|
- alias: "Start or Cancel Master Bedroom Climate Timer"
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
|
||||||
|
variables:
|
||||||
|
opt: "{{ states('input_select.master_bedroom_climate_timer_duration') }}"
|
||||||
|
dur_map:
|
||||||
|
"1 hour": "01:00:00"
|
||||||
|
"2 hours": "02:00:00"
|
||||||
|
"3 hours": "03:00:00"
|
||||||
|
"6 hours": "06:00:00"
|
||||||
|
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ opt in dur_map }}"
|
||||||
|
sequence:
|
||||||
|
- service: timer.cancel
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
- delay: "00:00:01"
|
||||||
|
- service: timer.start
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
data:
|
||||||
|
duration: "{{ dur_map[opt] }}"
|
||||||
|
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
state: "Cancel timer"
|
||||||
|
sequence:
|
||||||
|
- service: timer.cancel
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# Turn off heat pump when timer finishes
|
||||||
|
############################################################################
|
||||||
|
- alias: "Turn Off Master Bedroom Climate on Timer Completion"
|
||||||
|
trigger:
|
||||||
|
- platform: event
|
||||||
|
event_type: timer.finished
|
||||||
|
event_data:
|
||||||
|
entity_id: timer.master_bedroom_climate_timer
|
||||||
|
action:
|
||||||
|
- service: climate.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_climate_timer_duration
|
||||||
|
data:
|
||||||
|
option: "Cancel timer"
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# Sync setpoint from climate → UI helper
|
||||||
|
############################################################################
|
||||||
|
- id: master_bedroom_setpoint_sync
|
||||||
|
alias: Master Bedroom Setpoint Sync
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
action:
|
||||||
|
- service: script.master_bedroom_setpoint_sync_from_climate
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# Clear requested HVAC mode when cloud catches up
|
||||||
|
############################################################################
|
||||||
|
- id: master_bedroom_hvac_clear_requested_mode
|
||||||
|
alias: Master Bedroom HVAC - Clear Requested Mode
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
to: ["off", "heat", "cool", "dry"]
|
||||||
|
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{ states('input_select.master_bedroom_hvac_requested_mode') != 'none' }}
|
||||||
|
|
||||||
|
action:
|
||||||
|
- delay: "00:00:03"
|
||||||
|
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{% set req = states('input_select.master_bedroom_hvac_requested_mode') %}
|
||||||
|
{% set mode = states('climate.master_bedroom') %}
|
||||||
|
{% set action = (state_attr('climate.master_bedroom','hvac_action') or '') %}
|
||||||
|
{{ (req == 'off' and (mode == 'off' or action == 'off'))
|
||||||
|
or (req in ['heat','cool','dry'] and mode == req) }}
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "none"
|
||||||
|
|
||||||
|
default:
|
||||||
|
- delay: "00:00:25"
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_hvac_requested_mode
|
||||||
|
data:
|
||||||
|
option: "none"
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
# Sync Profile from actual preset_mode
|
||||||
|
############################################################################
|
||||||
|
- id: master_bedroom_heat_pump_profile_sync_from_entities
|
||||||
|
alias: Master Bedroom Heat Pump - Sync Profile From Entities
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
action:
|
||||||
|
- delay: "00:00:02"
|
||||||
|
- variables:
|
||||||
|
preset: "{{ state_attr('climate.master_bedroom', 'preset_mode') or 'none' }}"
|
||||||
|
new_profile: >-
|
||||||
|
{% if preset == 'boost' %}
|
||||||
|
Boost
|
||||||
|
{% elif preset == 'eco' %}
|
||||||
|
Quiet
|
||||||
|
{% else %}
|
||||||
|
Normal
|
||||||
|
{% endif %}
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('input_select.master_bedroom_heat_pump_profile') != new_profile }}"
|
||||||
|
sequence:
|
||||||
|
- service: input_select.select_option
|
||||||
|
target:
|
||||||
|
entity_id: input_select.master_bedroom_heat_pump_profile
|
||||||
|
data:
|
||||||
|
option: "{{ new_profile }}"
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
##########################################################################################
|
||||||
|
# heat_pump_master_bedroom_schedules.yaml
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# HELPERS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
input_boolean:
|
||||||
|
master_bedroom_night_power_mode:
|
||||||
|
name: Night Power Mode
|
||||||
|
icon: mdi:weather-night
|
||||||
|
|
||||||
|
master_bedroom_9pm_on:
|
||||||
|
name: 9pm On
|
||||||
|
icon: mdi:clock-start
|
||||||
|
|
||||||
|
master_bedroom_midnight_off:
|
||||||
|
name: Midnight Off
|
||||||
|
icon: mdi:clock-end
|
||||||
|
|
||||||
|
master_bedroom_boost_restore_off:
|
||||||
|
name: Master Bedroom Boost Restore Off
|
||||||
|
icon: mdi:power
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# TIMERS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
timer:
|
||||||
|
master_bedroom_boost_timer:
|
||||||
|
name: Master Bedroom Boost Timer
|
||||||
|
duration: "00:00:00"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# SCRIPTS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
script:
|
||||||
|
master_bedroom_boost_1hr:
|
||||||
|
alias: Master Bedroom Boost 1hr
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
# Remember if the heat pump was OFF when boost started
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.master_bedroom_boost_restore_off
|
||||||
|
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
state: "off"
|
||||||
|
sequence:
|
||||||
|
- service: input_boolean.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.master_bedroom_boost_restore_off
|
||||||
|
|
||||||
|
# Start boost (this will turn it on if it was off)
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "boost"
|
||||||
|
|
||||||
|
- service: timer.start
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_boost_timer
|
||||||
|
data:
|
||||||
|
duration: "01:00:00"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# AUTOMATIONS
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
automation:
|
||||||
|
##########################################################################################
|
||||||
|
# CANCEL LOGIC
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
- alias: MB Cancel Others When Night Power Enabled
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_boolean.master_bedroom_night_power_mode
|
||||||
|
to: "on"
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- input_boolean.master_bedroom_9pm_on
|
||||||
|
- input_boolean.master_bedroom_midnight_off
|
||||||
|
|
||||||
|
- alias: MB Cancel Night Power If 9pm On Enabled
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_boolean.master_bedroom_9pm_on
|
||||||
|
to: "on"
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.master_bedroom_night_power_mode
|
||||||
|
|
||||||
|
- alias: MB Cancel Night Power If Midnight Off Enabled
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: input_boolean.master_bedroom_midnight_off
|
||||||
|
to: "on"
|
||||||
|
action:
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.master_bedroom_night_power_mode
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# 9PM LOGIC
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
- alias: MB 9PM Start Logic
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: "21:00:00"
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.master_bedroom_night_power_mode
|
||||||
|
state: "on"
|
||||||
|
sequence:
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "boost"
|
||||||
|
|
||||||
|
- service: timer.start
|
||||||
|
target:
|
||||||
|
entity_id: timer.master_bedroom_boost_timer
|
||||||
|
data:
|
||||||
|
duration: "00:30:00"
|
||||||
|
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.master_bedroom_9pm_on
|
||||||
|
state: "on"
|
||||||
|
sequence:
|
||||||
|
- service: climate.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# MIDNIGHT LOGIC
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
- alias: MB Midnight Off Logic
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: "00:00:00"
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: or
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.master_bedroom_midnight_off
|
||||||
|
state: "on"
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.master_bedroom_night_power_mode
|
||||||
|
state: "on"
|
||||||
|
sequence:
|
||||||
|
- service: climate.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# BOOST TIMER FINISHED
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
- alias: MB Boost Timer Finished
|
||||||
|
trigger:
|
||||||
|
- platform: event
|
||||||
|
event_type: timer.finished
|
||||||
|
event_data:
|
||||||
|
entity_id: timer.master_bedroom_boost_timer
|
||||||
|
action:
|
||||||
|
- choose:
|
||||||
|
# If boost started while the heat pump was OFF, ensure it ends OFF
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.master_bedroom_boost_restore_off
|
||||||
|
state: "on"
|
||||||
|
sequence:
|
||||||
|
- service: climate.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
|
||||||
|
# Otherwise, revert to Normal (preset_mode none) and keep running
|
||||||
|
- conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.master_bedroom_boost_restore_off
|
||||||
|
state: "off"
|
||||||
|
sequence:
|
||||||
|
- service: climate.set_preset_mode
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
preset_mode: "none"
|
||||||
|
|
||||||
|
# Always clear the helper
|
||||||
|
- service: input_boolean.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: input_boolean.master_bedroom_boost_restore_off
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
input_number:
|
||||||
|
master_bedroom_setpoint_ui:
|
||||||
|
name: Master Bedroom Setpoint UI
|
||||||
|
min: 10
|
||||||
|
max: 30
|
||||||
|
step: 0.5
|
||||||
|
mode: box
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
icon: mdi:thermometer
|
||||||
|
|
||||||
|
script:
|
||||||
|
master_bedroom_setpoint_sync_from_climate:
|
||||||
|
alias: Master Bedroom Setpoint Sync From Climate
|
||||||
|
mode: restart
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
sp: "{{ state_attr('climate.master_bedroom', 'temperature') | float(0) }}"
|
||||||
|
- service: input_number.set_value
|
||||||
|
target:
|
||||||
|
entity_id: input_number.master_bedroom_setpoint_ui
|
||||||
|
data:
|
||||||
|
value: "{{ sp }}"
|
||||||
|
|
||||||
|
master_bedroom_setpoint_adjust:
|
||||||
|
alias: Master Bedroom Setpoint Adjust
|
||||||
|
mode: queued
|
||||||
|
fields:
|
||||||
|
step:
|
||||||
|
description: "Step in degrees C, e.g. -0.5 or 0.5"
|
||||||
|
example: -0.5
|
||||||
|
sequence:
|
||||||
|
# ─────────────────────────────
|
||||||
|
# GUARD: only block when climate is OFF/unavailable/unknown
|
||||||
|
# (Do NOT use hvac_action == idle - Panasonic cloud often reports idle)
|
||||||
|
# ─────────────────────────────
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{% set s = states('climate.master_bedroom') | lower %}
|
||||||
|
{{ s not in ['off', 'unavailable', 'unknown', 'none', ''] }}
|
||||||
|
|
||||||
|
# ─────────────────────────────
|
||||||
|
# Compute new setpoint
|
||||||
|
# Prefer UI helper if valid, otherwise use climate temp
|
||||||
|
# ─────────────────────────────
|
||||||
|
- variables:
|
||||||
|
cur_ui: "{{ states('input_number.master_bedroom_setpoint_ui') | float(0) }}"
|
||||||
|
cur_cl: "{{ state_attr('climate.master_bedroom', 'temperature') | float(0) }}"
|
||||||
|
cur: "{{ cur_ui if cur_ui > 0 else cur_cl }}"
|
||||||
|
raw: "{{ cur + (step | float(0)) }}"
|
||||||
|
new: "{{ [30, [10, raw] | max] | min | round(1) }}"
|
||||||
|
|
||||||
|
# Update UI immediately
|
||||||
|
- service: input_number.set_value
|
||||||
|
target:
|
||||||
|
entity_id: input_number.master_bedroom_setpoint_ui
|
||||||
|
data:
|
||||||
|
value: "{{ new }}"
|
||||||
|
|
||||||
|
# Send to climate (cloud may lag)
|
||||||
|
- service: climate.set_temperature
|
||||||
|
target:
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
data:
|
||||||
|
temperature: "{{ new }}"
|
||||||
|
|
||||||
|
automation:
|
||||||
|
- id: master_bedroom_setpoint_sync
|
||||||
|
alias: Master Bedroom Setpoint Sync
|
||||||
|
mode: restart
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: climate.master_bedroom
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
action:
|
||||||
|
- service: script.master_bedroom_setpoint_sync_from_climate
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
##########################################################################################
|
||||||
|
# MASTER BEDROOM FAN (Tasmota / Sonoff iFan02)
|
||||||
|
# Improvements:
|
||||||
|
# - Numeric speed entity (0-3) reflecting actual device state via MQTT
|
||||||
|
# - Button to cycle speed 1,2,3,0
|
||||||
|
# - Keep input_select in sync with actual FanSpeed (incl after reboots)
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
input_select:
|
input_select:
|
||||||
master_bedroom_fan_set:
|
master_bedroom_fan_set:
|
||||||
name: Master Bedroom Fan Speed
|
name: Master Bedroom Fan Speed
|
||||||
@@ -24,21 +32,121 @@ input_select:
|
|||||||
- "Medium"
|
- "Medium"
|
||||||
- "High"
|
- "High"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# MASTER BEDROOM FAN - Helpers
|
||||||
|
##########################################################################################
|
||||||
|
input_number:
|
||||||
|
master_bedroom_fan_speed_numeric:
|
||||||
|
name: Master Bedroom Fan Speed (Numeric)
|
||||||
|
min: 0
|
||||||
|
max: 3
|
||||||
|
step: 1
|
||||||
|
mode: box
|
||||||
|
|
||||||
|
template:
|
||||||
|
- button:
|
||||||
|
- name: Bedroom 1 Fan Speed Change
|
||||||
|
unique_id: bedroom_1_fan_speed_change
|
||||||
|
icon: mdi:fan
|
||||||
|
press:
|
||||||
|
- variables:
|
||||||
|
cur: "{{ states('input_number.master_bedroom_fan_speed_numeric') | int(0) }}"
|
||||||
|
nxt: "{{ (cur + 1) if (cur < 3) else 0 }}"
|
||||||
|
- service: mqtt.publish
|
||||||
|
data:
|
||||||
|
topic: cmnd/tasmo-ifan02-3793-bedrm1-1/FanSpeed
|
||||||
|
payload: "{{ nxt }}"
|
||||||
|
# Optimistic local update (MQTT will correct if needed)
|
||||||
|
- service: input_number.set_value
|
||||||
|
data:
|
||||||
|
entity_id: input_number.master_bedroom_fan_speed_numeric
|
||||||
|
value: "{{ nxt }}"
|
||||||
|
- service: input_select.select_option
|
||||||
|
data:
|
||||||
|
entity_id: input_select.master_bedroom_fan_set
|
||||||
|
option: >-
|
||||||
|
{% set m = {0:'Off',1:'Low',2:'Medium',3:'High'} %}
|
||||||
|
{{ m[nxt] }}
|
||||||
|
|
||||||
automation:
|
automation:
|
||||||
|
########################################################################################
|
||||||
|
# MASTER BEDROOM FAN - Query on HA start (fixes wrong value after reboots)
|
||||||
|
########################################################################################
|
||||||
|
- id: master_bedroom_fan_query_on_ha_start
|
||||||
|
initial_state: true
|
||||||
|
alias: Master Bedroom Fan - Query FanSpeed on HA Start
|
||||||
|
trigger:
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
action:
|
||||||
|
- service: mqtt.publish
|
||||||
|
data:
|
||||||
|
topic: cmnd/tasmo-ifan02-3793-bedrm1-1/FanSpeed
|
||||||
|
payload: ""
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# MASTER BEDROOM FAN - Sync helpers from Tasmota MQTT
|
||||||
|
########################################################################################
|
||||||
|
- id: master_bedroom_fan_sync_from_mqtt
|
||||||
|
initial_state: true
|
||||||
|
alias: Master Bedroom Fan - Sync Helpers From MQTT FanSpeed
|
||||||
|
trigger:
|
||||||
|
- platform: mqtt
|
||||||
|
topic: stat/tasmo-ifan02-3793-bedrm1-1/RESULT
|
||||||
|
- platform: mqtt
|
||||||
|
topic: tele/tasmo-ifan02-3793-bedrm1-1/STATE
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ trigger.payload_json is defined and (trigger.payload_json.FanSpeed is defined) }}"
|
||||||
|
action:
|
||||||
|
- variables:
|
||||||
|
spd: "{{ trigger.payload_json.FanSpeed | int(0) }}"
|
||||||
|
opt: >-
|
||||||
|
{% set m = {0:'Off',1:'Low',2:'Medium',3:'High'} %}
|
||||||
|
{{ m[spd] }}
|
||||||
|
- service: input_number.set_value
|
||||||
|
data:
|
||||||
|
entity_id: input_number.master_bedroom_fan_speed_numeric
|
||||||
|
value: "{{ spd }}"
|
||||||
|
# Only update dropdown if it differs (avoid loops)
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ states('input_select.master_bedroom_fan_set') != opt }}"
|
||||||
|
- service: input_select.select_option
|
||||||
|
data:
|
||||||
|
entity_id: input_select.master_bedroom_fan_set
|
||||||
|
option: "{{ opt }}"
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# MASTER BEDROOM FAN - Set speed from dropdown (improved)
|
||||||
|
########################################################################################
|
||||||
- id: set_the_master_bedroom_fan
|
- id: set_the_master_bedroom_fan
|
||||||
initial_state: true
|
initial_state: true
|
||||||
alias: Set the Master Bedroom Fan
|
alias: Set the Master Bedroom Fan
|
||||||
trigger:
|
trigger:
|
||||||
|
- platform: state
|
||||||
entity_id: input_select.master_bedroom_fan_set
|
entity_id: input_select.master_bedroom_fan_set
|
||||||
platform: state
|
variables:
|
||||||
|
desired: >-
|
||||||
|
{% set mapping = { 'Off':0, 'Low':1, 'Medium':2, 'High':3 } %}
|
||||||
|
{{ mapping.get(trigger.to_state.state, 0) }}
|
||||||
|
current: "{{ states('input_number.master_bedroom_fan_speed_numeric') | int(0) }}"
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ desired != current }}"
|
||||||
action:
|
action:
|
||||||
service: mqtt.publish
|
- service: mqtt.publish
|
||||||
data:
|
data:
|
||||||
topic: cmnd/tasmo-ifan02-3793-bedrm1-1/FanSpeed
|
topic: cmnd/tasmo-ifan02-3793-bedrm1-1/FanSpeed
|
||||||
payload: '{% set mapping = { "Off":"0", "Low":"1",
|
payload: "{{ desired }}"
|
||||||
"Medium":"2", "High":"3" } %} {% set dta = trigger.to_state.state %}
|
# Optimistic local update (MQTT will correct if needed)
|
||||||
{{ mapping[dta] }}
|
- service: input_number.set_value
|
||||||
'
|
data:
|
||||||
|
entity_id: input_number.master_bedroom_fan_speed_numeric
|
||||||
|
value: "{{ desired }}"
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# BEDROOM 2 FAN - Unchanged
|
||||||
|
########################################################################################
|
||||||
- id: set_bedroom_2_fan
|
- id: set_bedroom_2_fan
|
||||||
initial_state: true
|
initial_state: true
|
||||||
alias: Set Bedroom 2 Fan Speed
|
alias: Set Bedroom 2 Fan Speed
|
||||||
@@ -53,6 +161,10 @@ automation:
|
|||||||
"Medium":"2", "High":"3" } %} {% set dta = trigger.to_state.state %}
|
"Medium":"2", "High":"3" } %} {% set dta = trigger.to_state.state %}
|
||||||
{{ mapping[dta] }}
|
{{ mapping[dta] }}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# BEDROOM 3 FAN - Unchanged
|
||||||
|
########################################################################################
|
||||||
- id: set_bedroom_3_fan
|
- id: set_bedroom_3_fan
|
||||||
initial_state: true
|
initial_state: true
|
||||||
alias: Set Bedroom 3 Fan Speed
|
alias: Set Bedroom 3 Fan Speed
|
||||||
|
|||||||
Reference in New Issue
Block a user