--- ############################################################################### # SuperSensor v1.0 ESPHome configuration ############################################################################### # # Copyright (C) 2023 Joshua M. Boniface # # 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 . # ############################################################################### esphome: name: supersensor name_add_mac_suffix: true friendly_name: "Supersensor" project: name: joshuaboniface.supersensor version: "1.1" 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); - light.turn_on: id: output_led effect: flash_white - priority: -600 then: - wait_until: api.connected: - delay: 5s - if: condition: switch.is_on: enable_voice_support then: - logger.log: "Initializing voice assistant on boot" - switch.turn_off: voice_support_active - delay: 2s - switch.turn_on: voice_support_active dashboard_import: package_import_url: github://joshuaboniface/supersensor/supersensor.yaml esp32: board: esp32dev framework: type: esp-idf version: 4.4.8 platform_version: 5.4.0 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: - id: gas_resistance_ceiling type: int restore_value: true initial_value: "200" - id: temperature_offset type: float restore_value: true initial_value: "0.0" - id: humidity_offset type: float restore_value: true initial_value: "0.0" - id: pressure_offset type: float restore_value: true initial_value: "0.0" - id: elevation_meters 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); } preferences: flash_write_interval: 15sec logger: level: INFO baud_rate: 115200 api: reboot_timeout: 15min services: - service: restart_voice_assistant then: - logger.log: "Manually restarting voice assistant" - voice_assistant.stop: - delay: 2s - if: condition: switch.is_on: enable_voice_support then: - switch.turn_off: voice_support_active - delay: 1s - switch.turn_on: voice_support_active 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 uart: id: ld2410_uart rx_pin: GPIO19 tx_pin: GPIO18 baud_rate: 256000 data_bits: 8 stop_bits: 1 parity: NONE i2c: sda: GPIO16 scl: GPIO17 scan: true i2s_audio: i2s_lrclk_pin: GPIO26 i2s_bclk_pin: GPIO27 microphone: - platform: i2s_audio id: mic adc_type: external i2s_din_pin: GPIO14 pdm: false interval: - interval: 5s then: - if: condition: and: - switch.is_on: enable_voice_support - switch.is_on: voice_support_active - not: voice_assistant.is_running then: - logger.log: "Voice assistant not running but should be; restarting" - voice_assistant.start_continuous: # Regular state reporting to HASS - 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: 60s 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(); } # Add optional microWakeWord support (on-device wake word) # Doesn't work well as of 2024-07-04 so leave disabled #micro_wake_word: # model: hey_jarvis # on_wake_word_detected: # then: # - voice_assistant.start: # wake_word: !lambda return wake_word; # Include the Espressif Audio Development Framework for VAD support esp_adf: external_components: - source: github://pr#5230 components: - esp_adf refresh: 0s voice_assistant: microphone: mic use_wake_word: false noise_suppression_level: 3 auto_gain: 31dBFS volume_multiplier: 8.0 id: assist on_error: - logger.log: "voice error" - if: condition: and: - switch.is_on: voice_support_active - not: voice_assistant.is_running then: - voice_assistant.start_continuous: on_end: - logger.log: "voice ended" - if: condition: and: - switch.is_on: voice_support_active - not: voice_assistant.is_running then: - voice_assistant.start_continuous: on_client_connected: - light.turn_off: id: output_led transition_length: 2s - script.execute: light_off - lambda: |- id(voice_support_active).publish_state(true); on_client_disconnected: - light.turn_on: id: output_led effect: flash_white on_wake_word_detected: - light.turn_on: id: output_led brightness: 100% red: 0 green: 0 blue: 1 on_listening: - light.turn_on: id: output_led brightness: 100% red: 0 green: 0 blue: 1 on_stt_vad_end: - light.turn_on: id: output_led brightness: 75% red: 0 green: 1 blue: 1 on_stt_end: - script.execute: light_off 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: GPIO32 - platform: ledc id: rgb_g pin: GPIO33 - platform: ledc id: rgb_b pin: GPIO25 ld2410: id: ld2410_radar uart_id: ld2410_uart # These default values are captured here for # posterity. They are configured below. # max_move_distance : 6m # max_still_distance: 0.75m # g0_move_threshold: 10 # g0_still_threshold: 20 # g1_move_threshold: 10 # g1_still_threshold: 20 # g2_move_threshold: 20 # g2_still_threshold: 21 # g3_move_threshold: 30 # g3_still_threshold: 31 # g4_move_threshold: 40 # g4_still_threshold: 41 # g5_move_threshold: 50 # g5_still_threshold: 51 # g6_move_threshold: 60 # g6_still_threshold: 61 # g7_move_threshold: 70 # g7_still_threshold: 71 # g8_move_threshold: 80 # g8_still_threshold: 81 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: GPIO13 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" sensor: - platform: bme680 address: 0x77 update_interval: 15s iir_filter: 127x temperature: name: "BME680 Temperature" id: bme680_temperature oversampling: 16x filters: - offset: !lambda return id(temperature_offset); humidity: name: "BME680 Relative Humidity" id: bme680_humidity oversampling: 16x filters: - offset: !lambda return id(humidity_offset); pressure: name: "BME680 Pressure" id: bme680_pressure oversampling: 16x accuracy_decimals: 1 filters: - offset: !lambda return id(pressure_offset); gas_resistance: name: "BME680 Gas Resistance" id: bme680_gas_resistance - platform: absolute_humidity name: "BME680 Absolute Humidity" temperature: bme680_temperature humidity: bme680_humidity id: bme680_absolute_humidity - platform: template name: "BME680 AQ" id: bme680_aq icon: "mdi:gauge" unit_of_measurement: "%" accuracy_decimals: 0 update_interval: 15s # Calculation from https://github.com/thstielow/raspi-bme680-iaq lambda: |- float ph_slope = 0.03; float comp_gas = id(bme680_gas_resistance).state * pow(2.718281, (ph_slope * id(bme680_absolute_humidity).state)); float gas_ratio = pow((comp_gas / (id(gas_resistance_ceiling) * 1000)), 2); if (gas_ratio > 1) { gas_ratio = 1.0; } float air_quality = gas_ratio * 100; int normalized_air_quality = (int)air_quality; if (normalized_air_quality > 100) { normalized_air_quality = 100; } return normalized_air_quality; # Relative Pressure: # Calculates the relative pressure based on the elevation above sea level - platform: template name: "BME680 Relative Pressure" id: bme680_relative_pressure unit_of_measurement: hPa accuracy_decimals: 1 update_interval: 15s lambda: |- // Get the current absolute pressure in hPa float abs_pressure = id(bme680_pressure).state; // Get the current temperature in Celsius float temperature = id(bme680_temperature).state; // Get the elevation in meters float elevation = id(elevation_meters); // Convert temperature to Kelvin float temp_kelvin = temperature + 273.15; // Calculate relative pressure using the barometric formula // P0 = P * exp(g * M * h / (R * T)) // where: // P0 = sea level pressure // P = absolute pressure // g = gravitational acceleration (9.80665 m/s²) // M = molar mass of Earth's air (0.0289644 kg/mol) // h = height above sea level // R = universal gas constant (8.31432 N·m/(mol·K)) // T = temperature in Kelvin float relative_pressure = abs_pressure * exp(9.80665 * 0.0289644 * elevation / (8.31432 * temp_kelvin)); return relative_pressure; - 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 - 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 CPU Frequency" icon: mdi:cpu-32-bit accuracy_decimals: 1 unit_of_measurement: MHz update_interval: 5s lambda: |- return ets_get_cpu_frequency(); 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 text_sensor: - platform: template name: "BME680 AQ Classification" icon: "mdi:air-filter" update_interval: 5s lambda: |- int aq = int(id(bme680_aq).state); if (aq >= 90) { return {"Excellent"}; } else if (aq >= 80) { return {"Good"}; } else if (aq >= 70) { return {"Fair"}; } else if (aq >= 60) { return {"Moderate"}; } else if (aq >= 50) { return {"Bad"}; } else { return {"Terrible"}; } - 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" 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: - switch.turn_on: voice_support_active on_turn_off: - switch.turn_off: voice_support_active # Active voice support flag/switch - platform: template name: "Voice Support Active" icon: mdi:account-voice id: voice_support_active optimistic: true restore_mode: ALWAYS_OFF entity_category: config on_turn_on: - lambda: id(assist).set_use_wake_word(true); - voice_assistant.stop: - delay: 1s - voice_assistant.start_continuous: on_turn_off: - voice_assistant.stop: - lambda: id(assist).set_use_wake_word(false); # 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: - platform: template name: "Gas Resistance Ceiling (kΩ)" id: gas_resistance_ceiling_setter min_value: 10 max_value: 500 step: 1 entity_category: config lambda: |- return id(gas_resistance_ceiling); set_action: then: - globals.set: id: gas_resistance_ceiling value: !lambda 'return int(x);' # Temperature offset: # A calibration from -7 to +3 for the temperature sensor of the BME680 - platform: template name: "BME680 Temperature Offset" id: temperature_offset_setter min_value: -7 max_value: 3 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 -10 to +10 for the humidity sensor of the BME680 - platform: template name: "BME680 Humidity Offset" id: humidity_offset_setter min_value: -10 max_value: 10 step: 0.1 lambda: |- return id(humidity_offset); set_action: then: - globals.set: id: humidity_offset value: !lambda 'return float(x);' # Pressure offset: # A calibration from -25 to +25 for the barometric pressure sensor of the BME680 - platform: template name: "BME680 Pressure Offset" id: pressure_offset_setter min_value: -10 max_value: 10 step: 0.1 lambda: |- return id(pressure_offset); set_action: then: - globals.set: id: pressure_offset value: !lambda 'return float(x);' # Elevation in meters: # The elevation above sea level for relative pressure calculation - platform: template name: "Elevation (meters)" id: elevation_meters_setter min_value: -100 max_value: 10000 step: 1 lambda: |- return id(elevation_meters); set_action: then: - globals.set: id: elevation_meters 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"