supersensor2/supersensor.yaml
Joshua M. Boniface 8114d765f0 Adjust ideal humidity levels
I don't want to detract with 30-40 or 60-70% humidity.
2025-05-26 11:57:51 -04:00

1139 lines
32 KiB
YAML

---
###############################################################################
# SuperSensor v2.0 ESPHome configuration
###############################################################################
#
# Copyright (C) 2025 Joshua M. Boniface <joshua@boniface.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###############################################################################
esphome:
name: supersensor
name_add_mac_suffix: true
friendly_name: "Supersensor"
project:
name: joshuaboniface.supersensor
version: "2.0"
min_version: 2025.5.0
on_boot:
- priority: 600
then:
- lambda: |-
id(supersensor_occupancy).publish_state(false);
id(pir_presence).publish_state(false);
id(light_presence).publish_state(false);
id(radar_presence).publish_state(false);
- priority: -100
then:
- if:
condition:
- switch.is_on: enable_voice_support
then:
- micro_wake_word.start:
preferences:
flash_write_interval: 15sec
dashboard_import:
package_import_url: github://joshuaboniface/supersensor2/supersensor.yaml
esp32:
board: esp32dev
framework:
type: esp-idf
sdkconfig_options:
CONFIG_ESP32_DEFAULT_CPU_FREQ_240: "y"
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ: "240"
CONFIG_ESP32_DATA_CACHE_64KB: "y"
CONFIG_ESP32_DATA_CACHE_LINE_64B: "y"
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ: "240"
CONFIG_ESP32S3_DATA_CACHE_64KB: "y"
CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y"
CONFIG_SPIRAM_CACHE_WORKAROUND: "y"
CONFIG_OPTIMIZATION_LEVEL_RELEASE: "y"
globals:
# Defaults to -5 due to heating from the ESP
- id: temperature_offset
type: float
restore_value: true
initial_value: "-5.0"
- id: humidity_offset
type: float
restore_value: true
initial_value: "0.0"
- id: pir_hold_time
type: int
restore_value: true
initial_value: "15"
- id: light_presence_threshold
type: int
restore_value: true
initial_value: "30"
- id: occupancy_detect_mode
type: int
restore_value: true
initial_value: "0"
- id: occupancy_clear_mode
type: int
restore_value: true
initial_value: "0"
- id: last_api_connected_time
type: uint32_t
restore_value: no
initial_value: "0"
script:
- id: light_off
then:
if:
condition:
- binary_sensor.is_on: supersensor_occupancy
- switch.is_on: enable_presence_led
then:
- light.turn_on:
id: output_led
brightness: 15%
red: 1
green: 1
blue: 1
transition_length: 1s
else:
- light.turn_off:
id: output_led
transition_length: 1s
- id: pir_handler
then:
- lambda: |-
id(pir_presence).publish_state(true);
- while:
condition:
binary_sensor.is_on: pir_gpio
then:
- delay: !lambda 'return(id(pir_hold_time) * 1000);'
- lambda: |-
id(pir_presence).publish_state(false);
- id: light_handler
then:
- lambda: |-
if (id(tsl2591_lux).state >= id(light_presence_threshold)) {
id(light_presence).publish_state(true);
} else {
id(light_presence).publish_state(false);
}
- id: occupancy_detect_handler
then:
- lambda: |-
ESP_LOGD("occupancy_detect_handler", "Occupancy detect handler triggered");
// Get the current values of our presence sensors
bool pir = id(pir_presence).state;
bool radar = id(radar_presence).state;
bool light = id(light_presence).state;
// Determine if PIR counts (2nd bit of presence_type)
int pir_counts = (id(occupancy_detect_mode) & ( 1 << 2 )) >> 2;
// Determine if Radar counts (1st bit of presence_type)
int radar_counts = (id(occupancy_detect_mode) & ( 1 << 1 )) >> 1;
// Determine if Light counts (0th bit of presence_type)
int light_counts = (id(occupancy_detect_mode) & ( 1 << 0 )) >> 0;
// Determine our results
bool new_state = false;
if (pir_counts & radar_counts & light_counts) {
// Logical AND of pir & radar & light
new_state = pir & radar & light;
} else if (pir_counts & radar_counts) {
// Logical AND of pir & radar
new_state = pir & radar;
} else if (pir_counts & light_counts) {
// Logical AND of pir & light
new_state = pir & light;
} else if (radar_counts & light_counts) {
// Logical AND of radar & light
new_state = radar & light;
} else if (pir_counts) {
// Only pir
new_state = pir;
} else if (radar_counts) {
// Only radar
new_state = radar;
} else if (light_counts) {
// Only light
new_state = light;
}
ESP_LOGD("occupancy_detect_handler", "New state: %s", new_state ? "true" : "false");
// Force update even if state hasn't changed
id(supersensor_occupancy).publish_state(new_state);
// Add a delayed re-publish to ensure state propagation
if (new_state) {
id(supersensor_occupancy).publish_state(new_state);
}
- id: occupancy_clear_handler
then:
- lambda: |-
ESP_LOGD("occupancy_clear_handler", "Occupancy clear handler triggered");
// Get the current values of our presence sensors
bool pir = id(pir_presence).state;
bool radar = id(radar_presence).state;
bool light = id(light_presence).state;
// Determine if PIR counts (2nd bit of presence_type)
int pir_counts = (id(occupancy_clear_mode) & ( 1 << 2 )) >> 2;
// Determine if Radar counts (1st bit of presence_type)
int radar_counts = (id(occupancy_clear_mode) & ( 1 << 1 )) >> 1;
// Determine if Light counts (0th bit of presence_type)
int light_counts = (id(occupancy_clear_mode) & ( 1 << 0 )) >> 0;
// Determine our results
bool new_state = false;
if (pir_counts & radar_counts & light_counts) {
// Logical AND of pir & radar & light
new_state = pir & radar & light;
} else if (pir_counts & radar_counts) {
// Logical AND of pir & radar
new_state = pir & radar;
} else if (pir_counts & light_counts) {
// Logical AND of pir & light
new_state = pir & light;
} else if (radar_counts & light_counts) {
// Logical AND of radar & light
new_state = radar & light;
} else if (pir_counts) {
// Only pir
new_state = pir;
} else if (radar_counts) {
// Only radar
new_state = radar;
} else if (light_counts) {
// Only light
new_state = light;
}
ESP_LOGD("occupancy_clear_handler", "New state: %s", new_state ? "true" : "false");
// Force update even if state hasn't changed
id(supersensor_occupancy).publish_state(new_state);
// Add a delayed re-publish to ensure state propagation
if (!new_state) {
id(supersensor_occupancy).publish_state(new_state);
}
interval:
# Regular occupancy state reporting to HASS every 30s
- interval: 30s
then:
- lambda: |-
bool current_state = id(supersensor_occupancy).state;
ESP_LOGD("state_reporter", "Republishing occupancy state: %s", current_state ? "true" : "false");
id(supersensor_occupancy).publish_state(current_state);
# API watchdog every 5 minutes
- interval: 300s
then:
- lambda: |-
if (api::global_api_server->is_connected()) {
id(last_api_connected_time) = millis();
} else if (millis() - id(last_api_connected_time) > 300000) {
ESP_LOGE("api_watchdog", "API disconnected for too long, rebooting...");
App.safe_reboot();
}
logger:
level: WARN
baud_rate: 115200
api:
reboot_timeout: 15min
# on_client_connected:
# - logger.log:
# format: "Client %s (IP %s) connected to API"
# args: ["client_info.c_str()", "client_address.c_str()"]
# - script.execute: light_off
# - if:
# condition:
# lambda: |-
# return id(enable_voice_support).state &&
# !id(mww).is_running();
# then:
# - micro_wake_word.start:
# on_client_disconnected:
# - logger.log:
# format: "Client %s (IP %s) disconnected from API"
# args: ["client_info.c_str()", "client_address.c_str()"]
# - if:
# condition:
# lambda: |-
# return id(enable_voice_support).state &&
# id(mww).is_running();
# then:
# - micro_wake_word.stop:
# - light.turn_on:
# id: output_led
# effect: flash_white
ota:
platform: esphome
web_server:
port: 80
captive_portal:
mdns:
disabled: false
wifi:
ap: {}
domain: ""
output_power: 8.5dB
reboot_timeout: 15min
power_save_mode: none
time:
- platform: homeassistant
id: homeassistant_time
on_time_sync:
then:
- logger.log: "Time synchronized with Home Assistant"
uart:
id: ld2410_uart
rx_pin: GPIO19
tx_pin: GPIO18
baud_rate: 256000
data_bits: 8
stop_bits: 1
parity: NONE
i2c:
sda: GPIO27
scl: GPIO26
scan: true
i2s_audio:
- id: i2s_input
i2s_lrclk_pin:
number: GPIO17 # WS
i2s_bclk_pin:
number: GPIO16 # SCK
microphone:
- platform: i2s_audio
id: mic
i2s_audio_id: i2s_input
i2s_din_pin: GPIO4 # SD
adc_type: external
pdm: false
channel: left
micro_wake_word:
id: mww
microphone:
microphone: mic
gain_factor: 4
stop_after_detection: false
models:
- model: github://esphome/micro-wake-word-models/models/v2/hey_jarvis.json
id: mww_hey_jarvis
- model: github://esphome/micro-wake-word-models/models/v2/hey_mycroft.json
id: mww_hey_mycroft
- model: github://esphome/micro-wake-word-models/models/v2/okay_nabu.json
id: mww_okay_nabu
- model: github://esphome/micro-wake-word-models/models/v2/alexa.json
id: mww_alexa
vad:
on_wake_word_detected:
- logger.log: "A wake word was detected!"
- if:
condition:
voice_assistant.is_running:
then:
voice_assistant.stop:
- voice_assistant.start:
wake_word: !lambda return wake_word;
voice_assistant:
id: va
microphone: mic
micro_wake_word: mww
use_wake_word: false
noise_suppression_level: 3
auto_gain: 31 dbfs
volume_multiplier: 4
on_wake_word_detected:
- logger.log: "Wake word detected in VA pipeline"
- light.turn_on:
id: output_led
brightness: 100%
red: 0
green: 0
blue: 1
on_listening:
- logger.log: "Listening for commands"
- light.turn_on:
id: output_led
brightness: 100%
red: 0
green: 0
blue: 1
on_stt_vad_end:
- logger.log: "Processing STT result"
- light.turn_on:
id: output_led
brightness: 75%
red: 0
green: 1
blue: 1
on_tts_start:
- if:
condition:
- lambda: |-
ESP_LOGI("tts_response", "%s", x.c_str());
return x.rfind("Sorry", 0) == 0;
then:
- logger.log: "Command failed!"
- light.turn_on:
id: output_led
effect: hold
brightness: 100%
red: 1
green: 0
blue: 0
else:
- logger.log: "Command successful!"
- light.turn_on:
id: output_led
effect: hold
brightness: 100%
red: 0
green: 1
blue: 0
light:
- platform: rgb
id: output_led
red: rgb_r
green: rgb_g
blue: rgb_b
default_transition_length: 0.15s
flash_transition_length: 0.15s
effects:
- strobe:
name: flash_white
colors:
- state: true
brightness: 15%
red: 100%
green: 90%
blue: 90%
duration: 0.5s
- state: false
duration: 0.5s
- automation:
name: hold
sequence:
- delay: 5s
- script.execute: light_off
output:
- platform: ledc
id: rgb_r
pin: GPIO23
- platform: ledc
id: rgb_g
pin: GPIO22
- platform: ledc
id: rgb_b
pin: GPIO21
ld2410:
id: ld2410_radar
uart_id: ld2410_uart
sensor:
- platform: uptime
name: "ESP32 Uptime"
icon: mdi:clock-alert
update_interval: 5s
entity_category: diagnostic
- platform: wifi_signal
name: "WiFi RSSI"
icon: mdi:wifi-strength-2
update_interval: 5s
entity_category: diagnostic
- platform: internal_temperature
name: "ESP32 Temperature"
icon: mdi:thermometer
unit_of_measurement: °C
device_class: TEMPERATURE
update_interval: 5s
entity_category: diagnostic
- platform: template
name: "ESP32 Free Memory"
icon: mdi:memory
unit_of_measurement: 'kB'
state_class: measurement
update_interval: 5s
lambda: |-
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
entity_category: diagnostic
- platform: sgp30
eco2:
name: "SGP30 eCO2"
id: sgp30_eco2
accuracy_decimals: 1
filters:
- sliding_window_moving_average:
window_size: 20
send_every: 1
tvoc:
name: "SGP30 TVOC"
id: sgp30_tvoc
accuracy_decimals: 1
filters:
- sliding_window_moving_average:
window_size: 20
send_every: 1
eco2_baseline:
name: "SGP30 Baseline eCO2"
id: sgp30_baseline_ec02
tvoc_baseline:
name: "SGP30 Baseline TVOC"
id: sgp30_baseline_tvoc
compensation:
temperature_source: sht45_temperature
humidity_source: sht45_humidity
store_baseline: yes
update_interval: 15s
- platform: sht4x
temperature:
name: "SHT45 Temperature"
id: sht45_temperature
accuracy_decimals: 1
filters:
- offset: !lambda return id(temperature_offset);
- sliding_window_moving_average:
window_size: 20
send_every: 1
humidity:
name: "SHT45 Relative Humidity"
id: sht45_humidity
accuracy_decimals: 1
filters:
- offset: !lambda return id(humidity_offset);
- sliding_window_moving_average:
window_size: 20
send_every: 1
heater_max_duty: 0.0
update_interval: 15s
- platform: absolute_humidity
name: "SHT45 Absolute Humidity"
temperature: sht45_temperature
humidity: sht45_humidity
id: sht45_absolute_humidity
# Dew Point
- platform: template
name: "SHT45 Dew Point"
icon: mdi:thermometer-water
id: sht45_dew_point
unit_of_measurement: "°C"
lambda: |-
float temp = id(sht45_temperature).state;
float rh = id(sht45_humidity).state;
if (isnan(temp) || isnan(rh)) return NAN;
float a = 17.27, b = 237.7;
float alpha = ((a * temp) / (b + temp)) + log(rh / 100.0);
return (b * alpha) / (a - alpha);
update_interval: 15s
# IAQ Index (1-5, 5=Great))
- platform: template
name: "IAQ Index"
icon: mdi:air-purifier
id: iaq_index
lambda: |-
int tvoc = id(sgp30_tvoc).state;
int eco2 = id(sgp30_eco2).state;
if (tvoc > 2200 || eco2 > 2000) return 1; // Bad
if (tvoc > 660 || eco2 > 1200) return 2; // Poor
if (tvoc > 220 || eco2 > 800) return 3; // Fair
if (tvoc > 65 || eco2 > 500) return 4; // Good
return 5; // Great
update_interval: 15s
# Room Health Score (1-4, 4=Optimal)
- platform: template
name: "Room Health Score"
icon: mdi:home-thermometer
id: room_health
lambda: |-
float temp = id(sht45_temperature).state;
float rh = id(sht45_humidity).state;
int iaq = id(iaq_index).state;
bool temp_ok = (temp >= 18 && temp <= 24);
bool hum_ok = (rh >= 30 && rh <= 70);
bool iaq_ok = (iaq >= 4);
int conditions_met = 0;
if (temp_ok) conditions_met++;
if (hum_ok) conditions_met++;
if (iaq_ok) conditions_met++;
if (iaq_ok && temp_ok && hum_ok) {
return 4; // Optimal: All conditions met and IAQ is excellent/good
} else if (iaq >= 3 && conditions_met >= 2) {
return 3; // Fair: IAQ is moderate and at least 2 conditions met
} else if (iaq >= 2 && conditions_met >= 1) {
return 2; // Poor: IAQ is poor and at least 1 condition met
} else {
return 1; // Bad: All conditions failed or IAQ is unhealthy
}
update_interval: 15s
- platform: tsl2591
address: 0x29
update_interval: 1s
integration_time: 200ms
power_save_mode: no
gain: auto
device_factor: 53
glass_attenuation_factor: 7.7
visible:
name: "TSL2591 Raw Visible"
infrared:
name: "TSL2591 Raw Infrared"
full_spectrum:
name: "TSL2591 Raw Full Spectrum"
calculated_lux:
id: tsl2591_lux
name: "TSL2591 Illumination"
unit_of_measurement: lx
accuracy_decimals: 1
on_value:
- script.execute: light_handler
actual_gain:
id: "actual_gain"
name: "TSL2591 Gain"
- platform: ld2410
ld2410_id: ld2410_radar
moving_distance:
name: "LD2410C Moving Distance"
id: moving_distance
icon: mdi:signal-distance-variant
still_distance:
name: "LD2410C Still Distance"
id: still_distance
icon: mdi:signal-distance-variant
moving_energy:
name: "LD2410C Move Energy"
icon: mdi:flash
still_energy:
name: "LD2410C Still Energy"
icon: mdi:flash
detection_distance:
name: "LD2410C Presence Distance"
icon: mdi:signal-distance-variant
binary_sensor:
- platform: template
name: "SuperSensor Occupancy"
id: supersensor_occupancy
device_class: occupancy
on_press:
- script.execute: light_off
on_release:
- script.execute: light_off
- platform: gpio
name: "PIR GPIO"
id: pir_gpio
pin:
number: GPIO32
mode: INPUT_PULLUP
internal: false
device_class: motion
on_press:
- script.stop: pir_handler
- script.execute: pir_handler
- platform: template
name: "PIR Presence"
id: pir_presence
device_class: motion
on_press:
- script.execute: occupancy_detect_handler
on_release:
- script.execute: occupancy_clear_handler
- platform: template
name: "Light Presence"
id: light_presence
device_class: motion
on_press:
- script.execute: occupancy_detect_handler
on_release:
- script.execute: occupancy_clear_handler
- platform: ld2410
ld2410_id: ld2410_radar
has_target:
name: "LD2410C Presence"
id: radar_presence
icon: mdi:motion-sensor
device_class: motion
on_press:
- script.execute: occupancy_detect_handler
on_release:
- script.execute: occupancy_clear_handler
has_moving_target:
name: "LD2410C Moving Target"
has_still_target:
name: "LD2410C Still Target"
text_sensor:
- platform: wifi_info
ip_address:
name: "WiFi IP Address"
ssid:
name: "WiFi SSID"
bssid:
name: "WiFi BSSID"
mac_address:
name: "WiFi MAC Address"
- platform: ld2410
version:
name: "LD2410C Firmware Version"
mac_address:
name: "LD2410C MAC Address"
# VOC Level
- platform: template
name: "VOC Level"
icon: mdi:radiator
lambda: |-
int tvoc = id(sgp30_tvoc).state;
if (tvoc < 65) return {"Great"};
if (tvoc < 220) return {"Good"};
if (tvoc < 660) return {"Fair"};
if (tvoc < 2200) return {"Poor"};
return {"Bad"};
update_interval: 15s
# CO2 Level
- platform: template
name: "CO2 Level"
icon: mdi:molecule-co2
lambda: |-
int eco2 = id(sgp30_eco2).state;
if (eco2 < 500) return {"Great"};
if (eco2 < 800) return {"Good"};
if (eco2 < 1200) return {"Fair"};
if (eco2 < 2000) return {"Poor"};
return {"Bad"};
update_interval: 15s
# IAQ Classification
- platform: template
name: "IAQ Classification"
icon: mdi:air-purifier
lambda: |-
int iaq = id(iaq_index).state;
if (iaq == 5) return {"Great"};
if (iaq == 4) return {"Good"};
if (iaq == 3) return {"Fair"};
if (iaq == 2) return {"Poor"};
return {"Bad"};
update_interval: 15s
# Room Health
- platform: template
name: "Room Health"
icon: mdi:home-thermometer
lambda: |-
int score = id(room_health).state;
if (score == 4) return {"Optimal"};
if (score == 3) return {"Fair"};
if (score == 2) return {"Poor"};
return {"Bad"};
update_interval: 15s
button:
- platform: ld2410
restart:
name: "LD2410C Restart"
icon: mdi:power-cycle
entity_category: diagnostic
factory_reset:
name: "LD2410C Factory Reset"
icon: mdi:restart-alert
entity_category: diagnostic
- platform: restart
name: "ESP32 Restart"
icon: mdi:power-cycle
entity_category: diagnostic
- platform: factory_reset
name: "ESP32 Factory Reset"
icon: mdi:restart-alert
entity_category: diagnostic
switch:
# Global enable/disable for voice support
- platform: template
name: "Enable Voice Support"
icon: mdi:account-voice
id: enable_voice_support
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- micro_wake_word.start:
on_turn_off:
- micro_wake_word.stop:
# Global enable/disable for presence LED
- platform: template
name: "Enable Presence LED"
icon: mdi:lightbulb-alert
id: enable_presence_led
optimistic: true
restore_mode: RESTORE_DEFAULT_ON
on_turn_on:
- script.execute: light_off
on_turn_off:
- script.execute: light_off
- platform: ld2410
engineering_mode:
name: "LD2410C Engineering Mode"
entity_category: diagnostic
bluetooth:
name: "LD2410C Bluetooth"
entity_category: diagnostic
number:
# Temperature offset:
# A calibration from -30 to +5 for the temperature sensor
- platform: template
name: "Temperature Offset"
id: temperature_offset_setter
min_value: -30
max_value: 10
step: 0.1
lambda: |-
return id(temperature_offset);
set_action:
then:
- globals.set:
id: temperature_offset
value: !lambda 'return float(x);'
# Humidity offset:
# A calibration from -20 to +20 for the humidity sensor
- platform: template
name: "Humidity Offset"
id: humidity_offset_setter
min_value: -20
max_value: 20
step: 0.1
lambda: |-
return id(humidity_offset);
set_action:
then:
- globals.set:
id: humidity_offset
value: !lambda 'return float(x);'
# PIR Hold Time:
# The number of seconds after motion detection for the PIR sensor to remain held on
- platform: template
name: "PIR Hold Time"
id: pir_hold_time_setter
min_value: 0
max_value: 60
step: 5
lambda: |-
return id(pir_hold_time);
set_action:
then:
- globals.set:
id: pir_hold_time
value: !lambda 'return int(x);'
# Light Presence Threshold
# The minimum Lux value to consider presence based on the ambient light level
- platform: template
name: "Light Presence Threshold"
id: light_presence_threshold_setter
min_value: 0
max_value: 200
step: 5
lambda: |-
return id(light_presence_threshold);
set_action:
then:
- globals.set:
id: light_presence_threshold
value: !lambda 'return int(x);'
- platform: ld2410
timeout:
name: "LD2410C Timeout"
light_threshold:
name: "LD2410C Light Threshold"
max_move_distance_gate:
name: "LD2410C Max Move Distance Gate"
max_still_distance_gate:
name: "LD2410C Max Still Distance Gate"
g0:
move_threshold:
name: "LD2410C Gate0 Move Threshold"
still_threshold:
name: "LD2410C Gate0 Still Threshold"
g1:
move_threshold:
name: "LD2410C Gate1 Move Threshold"
still_threshold:
name: "LD2410C Gate1 Still Threshold"
g2:
move_threshold:
name: "LD2410C Gate2 Move Threshold"
still_threshold:
name: "LD2410C Gate2 Still Threshold"
g3:
move_threshold:
name: "LD2410C Gate3 Move Threshold"
still_threshold:
name: "LD2410C Gate3 Still Threshold"
g4:
move_threshold:
name: "LD2410C Gate4 Move Threshold"
still_threshold:
name: "LD2410C Gate4 Still Threshold"
g5:
move_threshold:
name: "LD2410C Gate5 Move Threshold"
still_threshold:
name: "LD2410C Gate5 Still Threshold"
g6:
move_threshold:
name: "LD2410C Gate6 Move Threshold"
still_threshold:
name: "LD2410C Gate6 Still Threshold"
g7:
move_threshold:
name: "LD2410C Gate7 Move Threshold"
still_threshold:
name: "LD2410C Gate7 Still Threshold"
g8:
move_threshold:
name: "LD2410C Gate8 Move Threshold"
still_threshold:
name: "LD2410C Gate8 Still Threshold"
select:
# Occupancy Detect Mode:
# This selector defines the detection mode for the integrated occupancy sensor. Depending on the
# selected option, only the given sensor(s) will be used to judge when occupancy begins (i.e.
# when the given sensor(s) detect, occupancy detects).
# * PIR + Radar + Light:
# All 3 sensors reporting detection simultaneously will begin occupancy
# * PIR + Radar
# Both PIR and Radar sensors reporting detection simultaneously will begin occupancy
# * PIR + Light
# Both PIR and Light sensors reporting detection simultaneously will begin occupancy
# * Radar + Light
# Both Radar and Light sensors reporting detection simultaneously will begin occupancy
# * PIR Only
# PIR sensor reporting detection will begin occupancy
# * Radar Only
# Radar sensor reporting detection will begin occupancy
# * Light Only
# Light sensor reporting detection will begin occupancy
# * None
# No sensors will begin occupancy and the integrated occupancy functionality is disabled
# Values are reported as integers using bitwise logic:
# Bit 2: PIR
# Bit 1: Radar
# Bit 0: Light
- platform: template
name: "Occupancy Detect Mode"
id: occupancy_detect_mode_setter
options:
- "PIR + Radar + Light" # 111 = 7
- "PIR + Radar" # 110 = 6
- "PIR + Light" # 101 = 5
- "Radar + Light" # 011 = 3
- "PIR Only" # 100 = 4
- "Radar Only" # 010 = 2
- "Light Only" # 001 = 1
- "None" # 000 = 0
initial_option: "None"
optimistic: true
restore_value: true
set_action:
- globals.set:
id: occupancy_detect_mode
value: !lambda |-
ESP_LOGD("occupancy_detect_mode_setter", x.c_str());
if (x == "PIR + Radar + Light") {
return 7;
} else if (x == "PIR + Radar") {
return 6;
} else if (x == "PIR + Light") {
return 5;
} else if (x == "Radar + Light") {
return 3;
} else if (x == "PIR Only") {
return 4;
} else if (x == "Radar Only") {
return 2;
} else if (x == "Light Only") {
return 1;
} else {
return 0;
}
# Occupancy Clear Mode:
# This selector defines the clear mode for the integrated occupancy sensor. Depending on the
# selected option, only the given sensor(s) will be used to judge when occupancy ends (i.e.
# when the given sensor(s) clear, occupancy clears).
# * PIR + Radar + Light:
# Any of the 3 sensors clearing will end occupancy
# * PIR + Radar:
# Either of the PIR or Radar sensors clearing will end occupancy
# * PIR + Light:
# Either of the PIR or Light sensors clearing will end occupancy
# * Radar + Light:
# Either of the Radar or Light sensors clearing will end occupancy
# * PIR Only
# PIR sensor clearing will end occupancy
# * Radar Only
# Radar sensor clearing will end occupancy
# * Light Only
# Light sensor clearing will end occupancy
# * None
# No sensors will end occupancy; state will persist indefinitely once triggered
# Values are reported as integers using bitwise logic:
# Bit 0: PIR
# Bit 1: Radar
# Bit 2: Light
- platform: template
name: "Occupancy Clear Mode"
id: occupancy_clear_mode_setter
options:
- "PIR + Radar + Light" # 111 = 7
- "PIR + Radar" # 110 = 6
- "PIR + Light" # 101 = 5
- "Radar + Light" # 011 = 3
- "PIR Only" # 100 = 4
- "Radar Only" # 010 = 2
- "Light Only" # 001 = 1
- "None" # 000 = 0
initial_option: "None"
optimistic: true
restore_value: true
set_action:
- globals.set:
id: occupancy_clear_mode
value: !lambda |-
ESP_LOGD("occupancy_detect_mode_setter", x.c_str());
if (x == "PIR + Radar + Light") {
return 7;
} else if (x == "PIR + Radar") {
return 6;
} else if (x == "PIR + Light") {
return 5;
} else if (x == "Radar + Light") {
return 3;
} else if (x == "PIR Only") {
return 4;
} else if (x == "Radar Only") {
return 2;
} else if (x == "Light Only") {
return 1;
} else {
return 0;
}
- platform: ld2410
distance_resolution:
name: "LD2410C Distance Resolution"
- platform: template
name: "Wake word sensitivity"
optimistic: true
initial_option: Moderately sensitive
restore_value: true
entity_category: config
options:
- Slightly sensitive
- Moderately sensitive
- Very sensitive
on_value:
# Sets specific wake word probabilities computed for each particular model
# Note probability cutoffs are set as a quantized uint8 value, each comment has the corresponding floating point cutoff
# False Accepts per Hour values are tested against all units and channels from the Dinner Party Corpus.
# These cutoffs apply only to the specific models included in the firmware: okay_nabu@20241226.3, hey_jarvis@v2, hey_mycroft@v2
lambda: |-
if (x == "Slightly sensitive") {
id(mww_hey_jarvis).set_probability_cutoff(247); // 0.97 -> 0.563 FAPH on DipCo (Manifest's default)
id(mww_hey_mycroft).set_probability_cutoff(253); // 0.99 -> 0.567 FAPH on DipCo
id(mww_okay_nabu).set_probability_cutoff(217); // 0.85 -> 0.000 FAPH on DipCo (Manifest's default)
id(mww_alexa).set_probability_cutoff(217); // 0.85 -> 0.000 FAPH on DipCo (Manifest's default)
} else if (x == "Moderately sensitive") {
id(mww_hey_jarvis).set_probability_cutoff(235); // 0.92 -> 0.939 FAPH on DipCo
id(mww_hey_mycroft).set_probability_cutoff(242); // 0.95 -> 1.502 FAPH on DipCo (Manifest's default)
id(mww_okay_nabu).set_probability_cutoff(176); // 0.69 -> 0.376 FAPH on DipCo
id(mww_alexa).set_probability_cutoff(176); // 0.69 -> 0.376 FAPH on DipCo
} else if (x == "Very sensitive") {
id(mww_hey_jarvis).set_probability_cutoff(212); // 0.83 -> 1.502 FAPH on DipCo
id(mww_hey_mycroft).set_probability_cutoff(237); // 0.93 -> 1.878 FAPH on DipCo
id(mww_okay_nabu).set_probability_cutoff(143); // 0.56 -> 0.751 FAPH on DipCo
id(mww_alexa).set_probability_cutoff(143); // 0.56 -> 0.751 FAPH on DipCo
}