Compare commits
58 Commits
dcf195e161
...
master
Author | SHA1 | Date | |
---|---|---|---|
a03e12dfa7 | |||
1f7df559b1 | |||
d75a7d26a6 | |||
e4c1ab1ac2 | |||
efd9fc8bfe | |||
9aebae647a | |||
3300ca2d8e | |||
0ba3b855d0 | |||
4f6597d92f | |||
985e7e20e5 | |||
426684e9da | |||
7fe3829544 | |||
206691257d | |||
aca7e16ed0 | |||
556f8564c4 | |||
8114d765f0 | |||
d529f695a3 | |||
c0b5adecca | |||
5c94e56847 | |||
fd7e438c62 | |||
64cb43dec8 | |||
e224044de3 | |||
450ff085d2 | |||
f3d7b16aa9 | |||
e62b9a8b91 | |||
9555fd37dc | |||
b3b9fd7086 | |||
cec8d27a4a | |||
40f0ae215a | |||
90403f990b | |||
1eb8e2134f | |||
6c32edc9a9 | |||
7221213ac4 | |||
28b76d0508 | |||
647e6711b8 | |||
7d5d7da2e8 | |||
565df10fe0 | |||
21fc3436ee | |||
91d9ff2a43 | |||
1600417026 | |||
decf2ad244 | |||
6695f9eb6b | |||
3768589de2 | |||
713a693cef | |||
d680735be3 | |||
6cdf340c5b | |||
4157ee2e65 | |||
e1840a37aa | |||
a095936f3f | |||
547983173b | |||
2145d4eff1 | |||
9fc8ad7318 | |||
66573f2346 | |||
a2ff9491a3 | |||
1ef3ba2e57 | |||
97dd3a9a90 | |||
993c3407ea | |||
ae4713969d |
193
README.md
193
README.md
@ -1,7 +1,10 @@
|
||||
# SuperSensor v2.x
|
||||
|
||||
The SuperSensor is an all-in-one voice, motion, presence, temperature/humidity/
|
||||
air quality, and light sensor, built on an ESP32 with ESPHome, and inspired
|
||||
**NOTICE**: The Supersensor v2.x is still under development! Parts and configurations
|
||||
may change until the design is finalized.
|
||||
|
||||
The SuperSensor is an all-in-one voice, motion, presence, temperature/humidity/air
|
||||
quality, and light sensor, built on an ESP32 with ESPHome, and inspired
|
||||
heavily by the EverythingSmartHome Everything Presence One sensor and the
|
||||
HomeAssistant "$13 Voice Assistant" project.
|
||||
|
||||
@ -17,9 +20,8 @@ it bare if you like the "PCB on a wall" aesthetic.
|
||||
|
||||
To Use:
|
||||
|
||||
* Install the ESPHome configuration `supersensor-2.x.yaml` to a compatible ESP32 devkit (below).
|
||||
* Install the ESP32 and sensors into the custom PCB (if desired).
|
||||
* [Optional] 3D Print the custom case.
|
||||
* Install the ESPHome configuration `supersensor.yaml` to a compatible ESP32 devkit (below).
|
||||
* Install the ESP32 and sensors into the custom PCB.
|
||||
* Power up the SuperSensor, connect to the WiFi AP, and connect it to your network.
|
||||
* Install the SuperSensor somewhere that makes sense.
|
||||
* Add/adopt the SuperSensor to HomeAssistant using the automatic name.
|
||||
@ -35,21 +37,19 @@ Note: Once programmed, the output LED will flash continuously until connected
|
||||
For more details, please [see my first blog post on the SuperSensor project](https://www.boniface.me/the-supersensor/)
|
||||
and [my update post on version 2.0](https://www.boniface.me/the-supersensor-2.0).
|
||||
|
||||
**NOTE: For those with v1.x hardware, see [the branch for that code instead](https://github.com/joshuaboniface/supersensor/tree/v1.x).**
|
||||
**NOTE: For those with v1.x hardware, see [the repository for that code instead](https://github.com/joshuaboniface/supersensor).**
|
||||
|
||||
## Major Changes from 1.x
|
||||
|
||||
1. Replaced the Bosch BME680 with the Sensirion SHT45 and Sensirion SGP30.
|
||||
1. Replaced the Bosch BME680 with the Sensirion SHT45 and Sensirion SGP41.
|
||||
|
||||
The BME680 proved to be woefully unreliable in my testing. Temperature and humidity were wildly off of what other thermometers/hydrometers
|
||||
would report, including a known issue with self-heating rending the temperature anywhere from 2 to 5(!) degrees Celsius above the actual
|
||||
temperature.
|
||||
The BME680 proved to be woefully unreliable in my testing. Temperature was fairly accurate (internal heating and offset notwithstanding),
|
||||
but humidity was wildly off of what other thermometers/hydrometers would report. In addition, the AQ functionality of the sensor was a
|
||||
source of much frustration and I was never able to get it to work reliably, either with the official BSEC library or with my own attempts
|
||||
at self-configuration.
|
||||
|
||||
In addition, the AQ functionality of the sensor was a source of much frustration and I was never able to get it to work reliably, either
|
||||
with the official BSEC library or with my own attempts at self-configuration.
|
||||
|
||||
Thus, this sensor has been replaced with two Sensirion sensors which in my experience so far have been much more reliable and consistent,
|
||||
and the cost difference is negligible.
|
||||
Thus, this sensor has been replaced with two Sensirion sensors which in my experience so far have been much more reliable and consistent.
|
||||
There is a slight cost increase due to these sensors, but not signfigant enough to outweigh the benefit of reliable monitoring they confer.
|
||||
|
||||
2. Replaced the SR602 PIR sensor with the AM312 PIR sensor.
|
||||
|
||||
@ -63,16 +63,12 @@ and [my update post on version 2.0](https://www.boniface.me/the-supersensor-2.0)
|
||||
3. Completely redesigned the custom PCB around the above sensor changes, which is now more compact in a 50x55mm almost-square configuration.
|
||||
|
||||
4. Significantly cleaned up the ESPHome configuration, to support the above sensors and remove a lot of cruft that was caused by the BME680.
|
||||
This includes a new set of custom AQ calculations based on the SGP30 and SHT45 sensors that, while not necessarily following the full EPA
|
||||
IAQI spec, should still give a reasonable view of the air quality conditions of an interior room and not deviate wildly and nonsensically
|
||||
like the BME680 did. Details of the calculation are provided below.
|
||||
|
||||
## Parts List
|
||||
|
||||
|-------|--------------------|----------------------------------|-------|
|
||||
| Qty | Component | Cost (2025/05 CAD, ex. shipping) | Links |
|
||||
|-------|--------------------|----------------------------------|-------|
|
||||
| 1 | GY-SGP30 | $5.73 | [AliExpress](https://www.aliexpress.com/item/1005008473372972.html) |
|
||||
| 1 | GY-SGP41 | $11.08 | [AliExpress](https://www.aliexpress.com/item/1005006746827606.html) |
|
||||
| 1 | GY-SHT45 | $5.67 | [AliExpress](https://www.aliexpress.com/item/1005008175340220.html)* |
|
||||
| 1 | SR602 | $0.81 | [AliExpress](https://www.aliexpress.com/item/1005001572550300.html) |
|
||||
| 1 | TSL2591 | $4.59 | [AliExpress](https://www.aliexpress.com/item/1005008619462097.html) |
|
||||
@ -81,16 +77,59 @@ and [my update post on version 2.0](https://www.boniface.me/the-supersensor-2.0)
|
||||
| 1 | ESP32 HW-395 | $6.67 | [AliExpress](https://www.aliexpress.com/item/1005006019875837.html)* |
|
||||
| 2 | RBG LED | $0.09 ($9.12/100) | [Amazon](https://www.amazon.ca/dp/B09Y8M2PKS) |
|
||||
| 1 | 470Ω resistor | $0.08 ($7.99/100) | [Amazon](https://www.amazon.ca/dp/B08MKQX2XT) |
|
||||
| 1 | Female pin header† | $1.59 ($15.99/10) | [Amazon](https://www.amazon.ca/dp/B08CMNRXJ1) |
|
||||
| 2 | Female pin header† | $1.59 ($15.99/10) | [Amazon](https://www.amazon.ca/dp/B08CMNRXJ1) |
|
||||
| 1 | Custom PCB (JLC) | $0.69 ($6.89/10) | [GitHub](https://github.com/joshuaboniface/supersensor) |
|
||||
| 1 | 3D Printed case | $?.??‡ | [GitHub](https://github.com/joshuaboniface/supersensor) |
|
||||
|-------|--------------------|----------------------------------|-------|
|
||||
| TOTAL | | $33.64 | |
|
||||
|-------|--------------------|----------------------------------|-------|
|
||||
| **TOTAL** | | **$40.58** | |
|
||||
|
||||
`*` Ensure you select the correct device on the page as it shows multiple options.
|
||||
`†` This is optional and only required if you don't want to directly solder the ESP32 to the board, but I recommend it.
|
||||
`‡` Providing a price is impossible due to the wide range of possible fillament types and brands, but should be negligible.
|
||||
|
||||
`†` One of these sets is optional, and is useful if you do not want to solder the individual sensors directly to the board (see below).
|
||||
|
||||
### To Solder or Not To Solder
|
||||
|
||||
Personally, for my Supersensor 1.x's and the initial batch of Supersensor 2.x's, I directly soldered
|
||||
all the non-ESP components to the board. This proved to be a major mistake when I later decided
|
||||
to switch from SGP30's to SGP41's after some testing and I had to desolder all of them, ruining
|
||||
several PCBs in the process. It was also a hassle to desolder the existing sensors for reuse
|
||||
during the 1.x to 2.x conversion.
|
||||
|
||||
As a result, I actually strongly encourage anyone building one of these units to leverage sockets
|
||||
for all components, to allow for quick swapping if any turn out to be defective or if future changes
|
||||
are warranted.
|
||||
|
||||
Note that due to the PCB design, you *must* socket at least one set of components - either the ESP32
|
||||
or the sensors on the front. Due to the positioning and overlap, it would be impossible to solder
|
||||
everything directly to the board, as the ESP covers several of the solder points of the front
|
||||
sensors and vice versa.
|
||||
|
||||
You can use the provided 40-pin female headers exclusively if you wish, and cut them to length for
|
||||
the individual sensors as needed, or you can use individually-sized female headers in the following
|
||||
quantities should you wish for a slightly neater finish:
|
||||
|
||||
* 3x 3-pin (AM302, INMP441 x2)
|
||||
* 2x 4-pin (SGP41, SHT45)
|
||||
* 1x 5-pin (LD2410C)
|
||||
* 1x 6-pin (TSL2591)
|
||||
|
||||
I will leave it up to the reader to source these specific sizes if they desire (I found all except
|
||||
a 5-pin on Amazon, and just used a 6-pin with one pin removed).
|
||||
|
||||
I still directly solder the RGB LEDs and resistor to the board for simplicity as these very small
|
||||
leads are not easily socketed, and these components are so inexpensive as to be effectively
|
||||
disposable along with the PCB should that be required.
|
||||
|
||||
### Part Swaps
|
||||
|
||||
To save a little money, it is possible to swap out the two Sensirion sensors for their less-feature-
|
||||
rich peers, with no code changes:
|
||||
|
||||
* SGP41 -> SGP40 - removes the NOx functionality
|
||||
* SHT45 -> SHT40/41/43 - less accuracy
|
||||
|
||||
Personally, I do not find the minimal cost savings to be worth sacrificing the extra potential
|
||||
functionality, so I recommend using the provided models, but this is up to the builder to decide.
|
||||
|
||||
No other parts can be easily swapped without code or PCB design changes.
|
||||
|
||||
## Configurable Options
|
||||
|
||||
@ -112,15 +151,19 @@ SuperSensors in a single room and only want one to respond to voice commands.
|
||||
If enabled (the default), when overall presence is detected, the LEDs will
|
||||
glow "white" at 15% power to signal presence.
|
||||
|
||||
### Temperature Offset (selector, -7 to +3 @ 0.1)
|
||||
### Temperature Offset (selector, -30 to +10 @ 0.1, -5 default)
|
||||
|
||||
Allows calibration of the SHT45 temperature sensor with an offset from -7 to +3
|
||||
degrees C. Useful if the sensor is misreporting actual ambient tempreatures.
|
||||
Allows calibration of the SHT45 temperature sensor with an offset from -30 to +10
|
||||
degrees C. Useful if the sensor is misreporting actual ambient tempreatures. Due
|
||||
to internal heating of the SHT45 by the ESP32, this defaults to -5; further
|
||||
calibration may be needed for your sensors and environment based on an external
|
||||
reference.
|
||||
|
||||
### Humidity Offset (selector, -10 to +10 @ 0.1)
|
||||
### Humidity Offset (selector, -20 to +20 @ 0.1)
|
||||
|
||||
Allows calibration of the SHT45 humidity sensor with an offset from -10 to +10
|
||||
percent relative humidity. Useful if the sensor is misreporting actual humidity.
|
||||
percent relative humidity. Useful if the sensor is misreporting actual humidity
|
||||
based on an external reference.
|
||||
|
||||
### PIR Hold Time (selector, 0 to +60 @ 5, 0 default)
|
||||
|
||||
@ -228,70 +271,28 @@ is likely not useful.
|
||||
|
||||
## AQ Details
|
||||
|
||||
The SuperSensor 2.x provides 2 base air quality sensors (numeric), from which
|
||||
4 human-readable text sensors are derived.
|
||||
The SuperSensor 2.0 features an SGP41 air quality sensor by Sensirion. This is a powerful AQ
|
||||
sensor which powers several commercial devices including the AirGradient One, which gave
|
||||
us a lot of our configuration via their sharing of algorithms.
|
||||
|
||||
The goal of these sensors is to track general comfort and livability in a
|
||||
room, not specific contaminants or conditions. Because the SGP30 can only
|
||||
track TVOC and eCO2, we do not track particulates, CO, NOx, or CH2O, all
|
||||
of which are required for a full EPA (I)AQI score. This means the best
|
||||
we can do is approximate (I)AQI roughly, and since a scale of 0-500 based
|
||||
on approximations seems pointless, I went with much simpler 1-4/5 scores
|
||||
instead. I feel this does a good enough job to be useful for 99% of rooms.
|
||||
The sensor provides two base readings: a VOC Index, and a NOx Index. These values are both
|
||||
floating references centered at 100 (VOC) and 1 (NOx), where that value represents "normal"
|
||||
air over the previous 24 hours. These sensors are very useful for any sort of quick-change
|
||||
automations, e.g. turn on a fan if levels spike due to cooking.
|
||||
|
||||
We also cannot really debate whether the BME680 is actually any more accurate
|
||||
in this regard, since their algorithms are proprietary and all that is exposed
|
||||
normally is a single resistance value, so in my opinion this is actually
|
||||
superior to that sensor anyways with two discrete datapoints (versus one),
|
||||
even if it does still seem limited when compared to dedicated AQ sensors.
|
||||
And that is to say nothing of the issues with that sensor (constantly climbing
|
||||
IAQ values over time, poor calibration, etc.).
|
||||
In addition, we leverage AirGradient's published forumulas to convert the VOC index into
|
||||
actual VOC quantities, in both µg/m³ and ppb. While this may drift due to the sensor's regular
|
||||
internal recalibration, I feel that following what AirGradient does is sufficient enough
|
||||
for any real-world home usage. Further, we use a very rough conversion of the aforementioned
|
||||
VOC quantity into an eCO2 reading, using Isobutylene as a reference gas. These sensors are
|
||||
more useful for display purposes, to show the current levels in a room in a dashboard or
|
||||
other such place, for human consumption. Note that no such conversions are done for NOx as
|
||||
there are no (that I can find) published empirical calculations for this conversion, unlike
|
||||
for VOCs via AirGradient.
|
||||
|
||||
### Base Numeric Values
|
||||
|
||||
#### IAQ Index (1-5)
|
||||
|
||||
The IAQ index is calculated based on the TVOC and eCO2 values from the SGP30
|
||||
sensor, to provide 5 levels of air quality. This corresponds approximately
|
||||
to the levels provided by the BME680 (0-50, 50-100, 100-200, 200-300, 300+).
|
||||
|
||||
5 is "excellent": the TVOC is <65 ppb and the eCO2 is <600 ppm.
|
||||
4 is "good": the TVOC is 65-220 ppb or the eCO2 is 600-800 ppm.
|
||||
3 is "moderate": the TVOC is 220-660 ppb or the eCO2 is 800-1200 ppm.
|
||||
2 is "poor": the TVOC is 660-2200 ppb or the eCO2 is 1200-2000 ppm.
|
||||
1 is "unhealthy": the TVOC is >2200 ppb or the eCO2 is >2000 ppm.
|
||||
|
||||
#### Room Health Score (1-4)
|
||||
|
||||
The Room Health Score is calculated based on the IAQ, temperature, and humidity,
|
||||
and is designed to show how "nice" a room is to be in. Generally a 4 is a nice
|
||||
place to be, especially for someone with respiratory issues like myself, and lower
|
||||
scores indicate more deviations from the norms or poor IAQ.
|
||||
|
||||
4 is "optimal": IAQ is >= 4 ("excellent" or "good"), temperature is between 18C and 24C, and humidity is between 40% and 60%.
|
||||
3 is "fair": One of the above is not true, and IAQ is >= 3 ("moderate").
|
||||
2 is "poor": Two of the above are not true, and IAQ is >= 2 ("poor").
|
||||
1 is "bad": All of the above are not true or IAQ is 1 ("unhealthy") regardless of other values.
|
||||
|
||||
Note that IAQ levels hold a major sway over this level, and decreasing IAQ
|
||||
scores will push the room score lower regardless of temperature or humidity.
|
||||
It is best used together with the individual sensors to determine exactly
|
||||
what is wrong with the room.
|
||||
|
||||
### Derived Text Sensors
|
||||
|
||||
#### VOC Level
|
||||
|
||||
This reports the VOC level alone, based on the scale under IAQ Index, in textual form ("Excellent, "Good", etc.).
|
||||
|
||||
#### CO2 Level
|
||||
|
||||
This reports the eCO2 level alone, based on the scale under IAQ Index, in textual form ("Excellent, Good", etc.).
|
||||
|
||||
#### IAQ Classification
|
||||
|
||||
This reports the IAQ Index in textual form ("Excellent", "Good", etc.).
|
||||
|
||||
#### Room Health
|
||||
|
||||
This reports the Room Health Score in textual form ("Optimal", "Fair", "Poor", "Bad").
|
||||
Note however that like all MOx sensors, the SGP41 does not differentiate gasses, and as
|
||||
such cannot tell the difference between normal, everyday natural VOCs like those in
|
||||
breath or from e.g. ripening fruit, and dangerous VOCs from e.g. construction materials.
|
||||
These should be used only as a general indication of air quality over short periods, rather
|
||||
than an absolute reference over long periods (much to my own frustration but inevitable
|
||||
begruding acceptance).
|
||||
|
5
archive/.gitignore
vendored
5
archive/.gitignore
vendored
@ -1,5 +0,0 @@
|
||||
# Gitignore settings for ESPHome
|
||||
# This is an example and may include too much for your use-case.
|
||||
# You can modify this file to suit your needs.
|
||||
/.esphome/
|
||||
/secrets.yaml
|
@ -1,393 +0,0 @@
|
||||
---
|
||||
|
||||
###############################################################################
|
||||
# SuperSensor v1.0 ESPHome configuration
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (C) 2023 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/>.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
substitutions:
|
||||
# How long a PIR activation should be held for
|
||||
pir_holdtime: "15s"
|
||||
|
||||
esphome:
|
||||
name: supersensor
|
||||
name_add_mac_suffix: true
|
||||
friendly_name: "Supersensor"
|
||||
project:
|
||||
name: joshuaboniface.supersensor
|
||||
version: "0.1"
|
||||
includes:
|
||||
- veml7700.h
|
||||
libraries:
|
||||
- "Wire"
|
||||
- "Adafruit Unified Sensor"
|
||||
- "SPI"
|
||||
- "Adafruit BusIO"
|
||||
- "Adafruit VEML7700 Library"
|
||||
on_boot:
|
||||
- priority: 600
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash
|
||||
red: 1
|
||||
green: 1
|
||||
blue: 1
|
||||
|
||||
dashboard_import:
|
||||
package_import_url: github://joshuaboniface/supersensor/supersensor.yaml
|
||||
import_full_config: false
|
||||
|
||||
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"
|
||||
|
||||
script:
|
||||
- id: pir_handler
|
||||
then:
|
||||
- lambda: |-
|
||||
id(pir_motion).publish_state(true);
|
||||
- while:
|
||||
condition:
|
||||
binary_sensor.is_on: pir_gpio
|
||||
then:
|
||||
- delay: ${pir_holdtime}
|
||||
- lambda: |-
|
||||
id(pir_motion).publish_state(false);
|
||||
|
||||
# Enable logging only via web
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 0
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
on_client_disconnected:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash
|
||||
red: 1
|
||||
green: 1
|
||||
blue: 1
|
||||
- switch.turn_off: use_wake_word
|
||||
on_client_connected:
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
safe_mode: false
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
uart:
|
||||
id: LD1115H_UART_BUS
|
||||
rx_pin: GPIO3
|
||||
tx_pin: GPIO1
|
||||
baud_rate: 115200
|
||||
setup_priority: 200
|
||||
|
||||
i2c:
|
||||
sda: GPIO21
|
||||
scl: GPIO22
|
||||
scan: true
|
||||
|
||||
i2s_audio:
|
||||
i2s_lrclk_pin: GPIO25
|
||||
i2s_bclk_pin: GPIO26
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: mic
|
||||
adc_type: external
|
||||
i2s_din_pin: GPIO27
|
||||
pdm: false
|
||||
|
||||
voice_assistant:
|
||||
microphone: mic
|
||||
use_wake_word: false
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
volume_multiplier: 4.0
|
||||
id: assist
|
||||
on_error:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- logger.log: "Voice Assistant encountered an error; restarting it"
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 1s
|
||||
- switch.turn_on: use_wake_word
|
||||
on_wake_word_detected:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_stt_end:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
on_tts_start:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x != "Sorry, I couldn't understand that";
|
||||
then:
|
||||
- logger.log: "Command successful!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 0
|
||||
green: 1
|
||||
blue: 0
|
||||
else:
|
||||
- logger.log: "Command failed!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 1
|
||||
green: 0
|
||||
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
|
||||
colors:
|
||||
- state: true
|
||||
duration: 0.5s
|
||||
- state: false
|
||||
duration: 0.5s
|
||||
- automation:
|
||||
name: hold
|
||||
sequence:
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: rgb_r
|
||||
pin: GPIO14
|
||||
- platform: ledc
|
||||
id: rgb_g
|
||||
pin: GPIO12
|
||||
- platform: ledc
|
||||
id: rgb_b
|
||||
pin: GPIO13
|
||||
|
||||
button:
|
||||
- platform: restart
|
||||
name: "ESP32 Restart"
|
||||
icon: mdi:power-cycle
|
||||
entity_category: "diagnostic"
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Enable Wake Word"
|
||||
icon: mdi:account-voice
|
||||
id: use_wake_word
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- lambda: id(assist).set_use_wake_word(true);
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
- voice_assistant.is_running
|
||||
then:
|
||||
- voice_assistant.start_continuous
|
||||
on_turn_off:
|
||||
- voice_assistant.stop
|
||||
- lambda: id(assist).set_use_wake_word(false);
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
name: "PIR GPIO"
|
||||
id: pir_gpio
|
||||
pin: GPIO33
|
||||
internal: false
|
||||
device_class: motion
|
||||
on_press:
|
||||
- script.stop: pir_handler
|
||||
- script.execute: pir_handler
|
||||
|
||||
- platform: template
|
||||
name: "PIR Motion"
|
||||
id: pir_motion
|
||||
device_class: motion
|
||||
|
||||
- platform: gpio
|
||||
name: "LD1115H Presence"
|
||||
id: ld11115h_gpio
|
||||
pin: GPIO23
|
||||
internal: false
|
||||
device_class: motion
|
||||
|
||||
sensor:
|
||||
# - platform: bme280
|
||||
# temperature:
|
||||
# name: "BME280 Temperature"
|
||||
# pressure:
|
||||
# name: "BME280 Pressure"
|
||||
# humidity:
|
||||
# name: "BME280 Humidity"
|
||||
# update_interval: 15s
|
||||
# address: 0x76
|
||||
|
||||
- platform: bmp280
|
||||
temperature:
|
||||
name: "BMP280 Temperature"
|
||||
pressure:
|
||||
name: "BMP280 Pressure"
|
||||
update_interval: 5s
|
||||
address: 0x76
|
||||
|
||||
- platform: custom
|
||||
lambda: |-
|
||||
auto veml7700 = new VEML7700CustomSensor();
|
||||
App.register_component(veml7700);
|
||||
return {veml7700->lux_sensor};
|
||||
sensors:
|
||||
- name: "VEML7700 Illumination"
|
||||
unit_of_measurement: Lux
|
||||
accuracy_decimals: 0
|
||||
|
||||
- platform: uptime
|
||||
name: "ESP32 Uptime"
|
||||
icon: mdi:clock-alert
|
||||
update_interval: 5s
|
||||
entity_category: "diagnostic"
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "ESP32 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"
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
name: LD1115H TH1 #TH1 is Movement/Motion Sensitivity
|
||||
id: LD1115H_TH1
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "120" #Default TH1 Setting
|
||||
min_value: 20
|
||||
max_value: 1200
|
||||
step: 10
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1115H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string th1st = "th1=" + str_sprintf("%.0f",x) +" \n";
|
||||
return std::vector<uint8_t>(th1st.begin(), th1st.end());
|
||||
- platform: template
|
||||
name: LD1115H TH2 #TH2 is Occupancy/Presence Sensitivity
|
||||
id: LD1115H_TH2
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "250" #Default TH2 Setting
|
||||
min_value: 50
|
||||
max_value: 2500
|
||||
step: 10
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1115H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string th2st = "th2=" + str_sprintf("%.0f",x) +" \n";
|
||||
return std::vector<uint8_t>(th2st.begin(), th2st.end());
|
@ -1,651 +0,0 @@
|
||||
---
|
||||
|
||||
###############################################################################
|
||||
# SuperSensor v1.0 ESPHome configuration
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (C) 2023 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/>.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
# Compile-time Substitutions
|
||||
#
|
||||
substitutions:
|
||||
ignored_radar_ranges: |-
|
||||
{
|
||||
{ 2.20, 3.30 },
|
||||
{ 2.20, 2.75 }
|
||||
}
|
||||
|
||||
#
|
||||
# Core configuration
|
||||
#
|
||||
esphome:
|
||||
name: supersensor
|
||||
name_add_mac_suffix: true
|
||||
on_boot:
|
||||
- priority: 600
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash
|
||||
red: 1
|
||||
green: 1
|
||||
blue: 1
|
||||
|
||||
- priority: 100
|
||||
then:
|
||||
# Configure LD1125H via UART
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_mov_st = "mth1_mov=" + str_sprintf("%.0f",id(LD1125H_mth1_mov).state) + "\r\n";
|
||||
return std::vector<uint8_t>(mth1_mov_st.begin(), mth1_mov_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_mov_st = "mth2_mov=" + str_sprintf("%.0f",id(LD1125H_mth2_mov).state) + "\r\n";
|
||||
return std::vector<uint8_t>(mth2_mov_st.begin(), mth2_mov_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_mov_st = "mth3_mov=" + str_sprintf("%.0f",id(LD1125H_mth3_mov).state) + "\r\n";
|
||||
return std::vector<uint8_t>(mth3_mov_st.begin(), mth3_mov_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_occ_st = "mth1_occ=" + str_sprintf("%.0f",id(LD1125H_mth1_occ).state) + "\r\n";
|
||||
return std::vector<uint8_t>(mth1_occ_st.begin(), mth1_occ_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_occ_st = "mth2_occ=" + str_sprintf("%.0f",id(LD1125H_mth2_occ).state) + "\r\n";
|
||||
return std::vector<uint8_t>(mth2_occ_st.begin(), mth2_occ_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_occ_st = "mth3_occ=" + str_sprintf("%.0f",id(LD1125H_mth3_occ).state) + "\r\n";
|
||||
return std::vector<uint8_t>(mth3_occ_st.begin(), mth3_occ_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string rmaxst = "rmax=" + str_sprintf("%.1f",id(LD1125H_rmax).state) + "\r\n";
|
||||
return std::vector<uint8_t>(rmaxst.begin(), rmaxst.end());
|
||||
|
||||
- priority: -500
|
||||
then:
|
||||
- wait_until: api.connected
|
||||
- delay: 3s
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_on: use_wake_word
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
|
||||
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"
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/ssieb/custom_components
|
||||
components: [ serial ]
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
baud_rate: 115200
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
safe_mode: false
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
uart:
|
||||
id: LD1125H_UART_BUS
|
||||
rx_pin: GPIO18
|
||||
tx_pin: GPIO19
|
||||
setup_priority: 200
|
||||
baud_rate: 115200
|
||||
data_bits: 8
|
||||
stop_bits: 1
|
||||
parity: NONE
|
||||
|
||||
i2c:
|
||||
sda: GPIO21
|
||||
scl: GPIO22
|
||||
scan: true
|
||||
|
||||
#
|
||||
# Radar Globals
|
||||
#
|
||||
globals:
|
||||
- id: LD1125H_Last_Update_Time
|
||||
type: uint32_t
|
||||
restore_value: no
|
||||
initial_value: millis()
|
||||
- id: LD1125H_Last_Time
|
||||
type: time_t
|
||||
restore_value: no
|
||||
initial_value: time(NULL)
|
||||
- id: LD1125H_Last_Mov_Time
|
||||
type: time_t
|
||||
restore_value: no
|
||||
initial_value: time(NULL)
|
||||
- id: LD1125H_Clearance_Status
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: "false"
|
||||
- id: ignored_ranges
|
||||
type: std::vector<std::vector<float>>
|
||||
restore_value: no
|
||||
initial_value: ${ignored_radar_ranges}
|
||||
|
||||
#
|
||||
# Voice Control
|
||||
#
|
||||
i2s_audio:
|
||||
i2s_lrclk_pin: GPIO25
|
||||
i2s_bclk_pin: GPIO26
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: mic
|
||||
adc_type: external
|
||||
i2s_din_pin: GPIO27
|
||||
pdm: false
|
||||
|
||||
voice_assistant:
|
||||
microphone: mic
|
||||
use_wake_word: false
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
volume_multiplier: 5.0
|
||||
id: assist
|
||||
on_wake_word_detected:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_stt_end:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
on_tts_start:
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x != "Sorry, I couldn't understand that";
|
||||
then:
|
||||
- logger.log: "Command successful!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 0
|
||||
green: 1
|
||||
blue: 0
|
||||
else:
|
||||
- logger.log: "Command failed!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 1
|
||||
green: 0
|
||||
blue: 0
|
||||
|
||||
#
|
||||
# Voice Response LED
|
||||
#
|
||||
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
|
||||
colors:
|
||||
- state: true
|
||||
duration: 0.5s
|
||||
- state: false
|
||||
duration: 0.5s
|
||||
- automation:
|
||||
name: hold
|
||||
sequence:
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: rgb_r
|
||||
pin: GPIO14
|
||||
- platform: ledc
|
||||
id: rgb_g
|
||||
pin: GPIO12
|
||||
- platform: ledc
|
||||
id: rgb_b
|
||||
pin: GPIO13
|
||||
|
||||
#
|
||||
# Restart button
|
||||
#
|
||||
button:
|
||||
- platform: restart
|
||||
name: "ESP32 Restart"
|
||||
icon: mdi:power-cycle
|
||||
entity_category: "diagnostic"
|
||||
|
||||
#
|
||||
# Wake Word switch
|
||||
#
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Voice Enable Wake Word"
|
||||
icon: mdi:account-voice
|
||||
id: use_wake_word
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- lambda: id(assist).set_use_wake_word(true);
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
- voice_assistant.is_running
|
||||
then:
|
||||
- voice_assistant.start_continuous
|
||||
on_turn_off:
|
||||
- voice_assistant.stop
|
||||
- lambda: id(assist).set_use_wake_word(false);
|
||||
|
||||
#
|
||||
# Motion Sensors
|
||||
#
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: GPIO33
|
||||
name: "PIR Motion"
|
||||
device_class: motion
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Occupancy & Movement"
|
||||
id: LD1125H_MovOcc_Binary
|
||||
device_class: occupancy
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Motion"
|
||||
id: LD1125H_Mov_Binary
|
||||
device_class: motion
|
||||
|
||||
#
|
||||
# LD1125H Text Sensors
|
||||
#
|
||||
text_sensor:
|
||||
- platform: serial
|
||||
uart_id: LD1125H_UART_BUS
|
||||
name: "LD1125H UART Text"
|
||||
id: LD1125H_UART_Text
|
||||
icon: mdi:format-text
|
||||
internal: true
|
||||
on_value:
|
||||
then:
|
||||
- lambda: |-
|
||||
if ((millis() - id(LD1125H_Last_Update_Time)) < 1000) {
|
||||
return;
|
||||
} else {
|
||||
id(LD1125H_Last_Update_Time) = millis();
|
||||
}
|
||||
for (const auto& row : id(ignored_ranges)) {
|
||||
if ( ( atof(id(LD1125H_UART_Text).state.substr(9).c_str()) > row[0] ) && ( atof(id(LD1125H_UART_Text).state.substr(9).c_str()) < row[1] ) ) {
|
||||
break;
|
||||
} else {
|
||||
if (id(LD1125H_UART_Text).state.substr(0,3) == "occ") {
|
||||
id(LD1125H_Distance).publish_state(atof(id(LD1125H_UART_Text).state.substr(9).c_str()));
|
||||
if ((time(NULL)-id(LD1125H_Last_Mov_Time))>id(LD1125H_Mov_Time).state) {
|
||||
id(LD1125H_Occupancy).publish_state("Occupied");
|
||||
if (id(LD1125H_MovOcc_Binary).state == false) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(true);
|
||||
}
|
||||
if (id(LD1125H_Mov_Binary).state == true) {
|
||||
id(LD1125H_Mov_Binary).publish_state(false);
|
||||
}
|
||||
}
|
||||
if (id(LD1125H_MovOcc_Binary).state == false) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(true);
|
||||
}
|
||||
id(LD1125H_Last_Time) = time(NULL);
|
||||
if (id(LD1125H_Clearance_Status) == true) {
|
||||
id(LD1125H_Clearance_Status) = false;
|
||||
}
|
||||
}
|
||||
else if (id(LD1125H_UART_Text).state.substr(0,3) == "mov") {
|
||||
id(LD1125H_Distance).publish_state(atof(id(LD1125H_UART_Text).state.substr(9).c_str()));
|
||||
id(LD1125H_Occupancy).publish_state("Movement");
|
||||
if (id(LD1125H_MovOcc_Binary).state == false) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(true);
|
||||
}
|
||||
if (id(LD1125H_Mov_Binary).state == false) {
|
||||
id(LD1125H_Mov_Binary).publish_state(true);
|
||||
}
|
||||
id(LD1125H_Last_Mov_Time) = time(NULL);
|
||||
id(LD1125H_Last_Time) = time(NULL);
|
||||
if (id(LD1125H_Clearance_Status) == true) {
|
||||
id(LD1125H_Clearance_Status) = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Occupancy Status"
|
||||
id: LD1125H_Occupancy
|
||||
icon: "mdi:motion-sensor"
|
||||
|
||||
|
||||
#
|
||||
# Other Sensors
|
||||
#
|
||||
sensor:
|
||||
# BME280 Temperature/Humidity/Pressure sensor
|
||||
- platform: bme280
|
||||
temperature:
|
||||
name: "BME280 Temperature"
|
||||
humidity:
|
||||
name: "BME280 Humidity"
|
||||
pressure:
|
||||
name: "BME280 Pressure"
|
||||
update_interval: 15s
|
||||
address: 0x76
|
||||
|
||||
# BMP280 Temperature/Pressure sensor (TEMPORARY)
|
||||
- platform: bmp280
|
||||
temperature:
|
||||
name: "BMP280 Temperature"
|
||||
pressure:
|
||||
name: "BMP280 Pressure"
|
||||
update_interval: 15s
|
||||
address: 0x76
|
||||
|
||||
# LD1125H Distance
|
||||
- platform: template
|
||||
name: LD1125H Detect Distance
|
||||
id: LD1125H_Distance
|
||||
icon: mdi:signal-distance-variant
|
||||
unit_of_measurement: "m"
|
||||
accuracy_decimals: 2
|
||||
|
||||
# Internal sensors
|
||||
- platform: uptime
|
||||
name: "ESP32 Uptime"
|
||||
icon: mdi:clock-alert
|
||||
update_interval: 15s
|
||||
entity_category: "diagnostic"
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "ESP32 WiFi RSSI"
|
||||
icon: mdi:wifi-strength-2
|
||||
update_interval: 15s
|
||||
entity_category: "diagnostic"
|
||||
|
||||
- platform: internal_temperature
|
||||
name: "ESP32 Temperature"
|
||||
icon: mdi:thermometer
|
||||
unit_of_measurement: °C
|
||||
device_class: TEMPERATURE
|
||||
update_interval: 15s
|
||||
entity_category: "diagnostic"
|
||||
|
||||
- platform: template
|
||||
name: "ESP32 CPU Frequency"
|
||||
icon: mdi:cpu-32-bit
|
||||
accuracy_decimals: 1
|
||||
unit_of_measurement: MHz
|
||||
update_interval: 15s
|
||||
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: 15s
|
||||
lambda: |-
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
|
||||
entity_category: "diagnostic"
|
||||
|
||||
#
|
||||
# LD1125H tunables
|
||||
#
|
||||
number:
|
||||
- platform: template
|
||||
name: "LD1125H Mov 0m-2.8m"
|
||||
id: LD1125H_mth1_mov
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "80"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_mov_st = "mth1_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth1_mov_st.begin(), mth1_mov_st.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Mov 2.8m-8m"
|
||||
id: LD1125H_mth2_mov
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "50"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_mov_st = "mth2_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth2_mov_st.begin(), mth2_mov_st.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Mov 8m-∞m"
|
||||
id: LD1125H_mth3_mov
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "80"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_mov_st = "mth3_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth3_mov_st.begin(), mth3_mov_st.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Occ 0m-2.8m"
|
||||
id: LD1125H_mth1_occ
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "80"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_occ_st = "mth1_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth1_occ_st.begin(), mth1_occ_st.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Occ 2.8m-8m"
|
||||
id: LD1125H_mth2_occ
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "50"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_occ_st = "mth2_occ=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth2_occ_st.begin(), mth2_occ_st.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Occ 8m-∞m"
|
||||
id: LD1125H_mth3_occ
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "80"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_occ_st = "mth3_occ=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth3_occ_st.begin(), mth3_occ_st.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Max Dist (m)"
|
||||
id: LD1125H_rmax
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "4"
|
||||
min_value: 0.0
|
||||
max_value: 20
|
||||
step: 0.1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string rmaxst = "rmax=" + str_sprintf("%.1f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(rmaxst.begin(), rmaxst.end());
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Clear Time"
|
||||
id: LD1125H_Clearance_Time
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "15"
|
||||
min_value: 0
|
||||
max_value: 60
|
||||
step: 1
|
||||
|
||||
- platform: template
|
||||
name: "LD1125H Detect Time"
|
||||
id: LD1125H_Mov_Time
|
||||
icon: mdi:cogs
|
||||
entity_category: "config"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "0.1"
|
||||
min_value: 0.1
|
||||
max_value: 10
|
||||
step: 0.1
|
||||
|
||||
#
|
||||
# Radar Clear Scan Time Interval
|
||||
#
|
||||
interval:
|
||||
- interval: 1s
|
||||
then:
|
||||
lambda: |-
|
||||
if ((time(NULL)-id(LD1125H_Last_Time))>id(LD1125H_Clearance_Time).state) {
|
||||
if ((id(LD1125H_Clearance_Status) == false) || (id(LD1125H_Occupancy).state != "Clear")) {
|
||||
id(LD1125H_Occupancy).publish_state("Clear");
|
||||
id(LD1125H_Clearance_Status) = true;
|
||||
}
|
||||
if (id(LD1125H_MovOcc_Binary).state == true) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(false);
|
||||
}
|
||||
if (id(LD1125H_Mov_Binary).state == true) {
|
||||
id(LD1125H_Mov_Binary).publish_state(false);
|
||||
}
|
||||
}
|
@ -1,480 +0,0 @@
|
||||
---
|
||||
|
||||
###############################################################################
|
||||
# SuperSensor v1.0 ESPHome configuration
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (C) 2023 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/>.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
substitutions:
|
||||
# How long a PIR activation should be held for
|
||||
pir_holdtime: "15s"
|
||||
|
||||
esphome:
|
||||
name: supersensor
|
||||
name_add_mac_suffix: true
|
||||
friendly_name: "Supersensor"
|
||||
project:
|
||||
name: joshuaboniface.supersensor
|
||||
version: "0.1"
|
||||
# includes:
|
||||
# - veml7700.h
|
||||
# libraries:
|
||||
# - "Wire"
|
||||
# - "Adafruit Unified Sensor"
|
||||
# - "SPI"
|
||||
# - "Adafruit BusIO"
|
||||
# - "Adafruit VEML7700 Library"
|
||||
on_boot:
|
||||
- priority: 600
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash
|
||||
red: 1
|
||||
green: 1
|
||||
blue: 1
|
||||
|
||||
dashboard_import:
|
||||
package_import_url: github://joshuaboniface/supersensor/supersensor.yaml
|
||||
import_full_config: false
|
||||
|
||||
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"
|
||||
|
||||
script:
|
||||
- id: pir_handler
|
||||
then:
|
||||
- lambda: |-
|
||||
id(pir_motion).publish_state(true);
|
||||
- while:
|
||||
condition:
|
||||
binary_sensor.is_on: pir_gpio
|
||||
then:
|
||||
- delay: ${pir_holdtime}
|
||||
- lambda: |-
|
||||
id(pir_motion).publish_state(false);
|
||||
|
||||
# Enable logging only via web
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 115200
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
on_client_disconnected:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash
|
||||
red: 1
|
||||
green: 1
|
||||
blue: 1
|
||||
- switch.turn_off: use_wake_word
|
||||
on_client_connected:
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
safe_mode: false
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
uart:
|
||||
id: ld2410_uart
|
||||
rx_pin: GPIO18
|
||||
tx_pin: GPIO19
|
||||
baud_rate: 256000
|
||||
data_bits: 8
|
||||
stop_bits: 1
|
||||
parity: NONE
|
||||
|
||||
i2c:
|
||||
sda: GPIO16
|
||||
scl: GPIO17
|
||||
scan: true
|
||||
|
||||
i2s_audio:
|
||||
i2s_lrclk_pin: GPIO27
|
||||
i2s_bclk_pin: GPIO26
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: mic
|
||||
adc_type: external
|
||||
i2s_din_pin: GPIO14
|
||||
pdm: false
|
||||
|
||||
voice_assistant:
|
||||
microphone: mic
|
||||
use_wake_word: false
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
volume_multiplier: 4.0
|
||||
id: assist
|
||||
on_error:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- logger.log: "Voice Assistant encountered an error; restarting it"
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 1s
|
||||
- switch.turn_on: use_wake_word
|
||||
on_wake_word_detected:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_stt_end:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
on_tts_start:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x != "Sorry, I couldn't understand that";
|
||||
then:
|
||||
- logger.log: "Command successful!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 0
|
||||
green: 1
|
||||
blue: 0
|
||||
else:
|
||||
- logger.log: "Command failed!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 1
|
||||
green: 0
|
||||
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
|
||||
colors:
|
||||
- state: true
|
||||
duration: 0.5s
|
||||
- state: false
|
||||
duration: 0.5s
|
||||
- automation:
|
||||
name: hold
|
||||
sequence:
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
|
||||
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
|
||||
# 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: 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 Motion"
|
||||
id: pir_motion
|
||||
device_class: motion
|
||||
|
||||
- platform: ld2410
|
||||
ld2410_id: ld2410_radar
|
||||
has_target:
|
||||
name: "LD2410C Presence"
|
||||
has_moving_target:
|
||||
name: "LD2410C Moving Target"
|
||||
has_still_target:
|
||||
name: "LD2410C Still Target"
|
||||
|
||||
sensor:
|
||||
# - platform: bme280
|
||||
# temperature:
|
||||
# name: "BME280 Temperature"
|
||||
# pressure:
|
||||
# name: "BME280 Pressure"
|
||||
# humidity:
|
||||
# name: "BME280 Humidity"
|
||||
# update_interval: 15s
|
||||
# address: 0x76
|
||||
|
||||
- platform: bmp280
|
||||
temperature:
|
||||
name: "BMP280 Temperature"
|
||||
pressure:
|
||||
name: "BMP280 Pressure"
|
||||
update_interval: 5s
|
||||
address: 0x76
|
||||
|
||||
# - platform: custom
|
||||
# lambda: |-
|
||||
# auto veml7700 = new VEML7700CustomSensor();
|
||||
# App.register_component(veml7700);
|
||||
# return {veml7700->lux_sensor};
|
||||
# sensors:
|
||||
# - name: "VEML7700 Illumination"
|
||||
# unit_of_measurement: Lux
|
||||
# accuracy_decimals: 0
|
||||
|
||||
- platform: ld2410
|
||||
ld2410_id: ld2410_radar
|
||||
moving_distance:
|
||||
name: "LD2410C Moving Distance"
|
||||
id: moving_distance
|
||||
still_distance:
|
||||
name: "LD2410C Still Distance"
|
||||
id: still_distance
|
||||
moving_energy:
|
||||
name: "LD2410C Move Energy"
|
||||
still_energy:
|
||||
name: "LD2410C Still Energy"
|
||||
detection_distance:
|
||||
name: "LD2410C Presence Distance"
|
||||
|
||||
- platform: uptime
|
||||
name: "ESP32 Uptime"
|
||||
icon: mdi:clock-alert
|
||||
update_interval: 5s
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "ESP32 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: ld2410
|
||||
version:
|
||||
name: "LD2410C Firmware Version"
|
||||
mac_address:
|
||||
name: "LD2410C MAC Address"
|
||||
|
||||
button:
|
||||
- platform: ld2410
|
||||
restart:
|
||||
name: "LD2410C Restart"
|
||||
factory_reset:
|
||||
name: "LD2410C Factory Reset"
|
||||
|
||||
- platform: restart
|
||||
name: "ESP32 Restart"
|
||||
icon: mdi:power-cycle
|
||||
entity_category: diagnostic
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Enable Wake Word"
|
||||
icon: mdi:account-voice
|
||||
id: use_wake_word
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- lambda: id(assist).set_use_wake_word(true);
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
- voice_assistant.is_running
|
||||
then:
|
||||
- voice_assistant.start_continuous
|
||||
on_turn_off:
|
||||
- voice_assistant.stop
|
||||
- lambda: id(assist).set_use_wake_word(false);
|
||||
|
||||
- platform: ld2410
|
||||
engineering_mode:
|
||||
name: "LD2410C Engineering Mode"
|
||||
bluetooth:
|
||||
name: "LD2410C Bluetooth"
|
||||
|
||||
number:
|
||||
- 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:
|
||||
- platform: ld2410
|
||||
distance_resolution:
|
||||
name: "LD2410C Distance Resolution"
|
@ -1,365 +0,0 @@
|
||||
substitutions:
|
||||
devicename: "supersensor" #Rename the device what you want.
|
||||
upper_devicename: ESP Radar #Rename the device what you want.
|
||||
entity_name_prefix: ""
|
||||
ignored_radar_ranges: |-
|
||||
{
|
||||
{2.20,3.30},
|
||||
{2.20,2.75}
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 115200
|
||||
logs:
|
||||
text_sensor: INFO
|
||||
|
||||
esphome:
|
||||
name: supersensor
|
||||
name_add_mac_suffix: false
|
||||
on_boot:
|
||||
priority: 200
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_mov_st = "mth1_mov=" + str_sprintf("%.0f",id(LD1125H_mth1_mov).state) +"\r\n";
|
||||
return std::vector<uint8_t>(mth1_mov_st.begin(), mth1_mov_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_mov_st = "mth2_mov=" + str_sprintf("%.0f",id(LD1125H_mth2_mov).state) +"\r\n";
|
||||
return std::vector<uint8_t>(mth2_mov_st.begin(), mth2_mov_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_mov_st = "mth3_mov=" + str_sprintf("%.0f",id(LD1125H_mth3_mov).state) +"\r\n";
|
||||
return std::vector<uint8_t>(mth3_mov_st.begin(), mth3_mov_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_occ_st = "mth1_occ=" + str_sprintf("%.0f",id(LD1125H_mth1_occ).state) +"\r\n";
|
||||
return std::vector<uint8_t>(mth1_occ_st.begin(), mth1_occ_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_occ_st = "mth2_occ=" + str_sprintf("%.0f",id(LD1125H_mth2_occ).state) +"\r\n";
|
||||
return std::vector<uint8_t>(mth2_occ_st.begin(), mth2_occ_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_occ_st = "mth3_occ=" + str_sprintf("%.0f",id(LD1125H_mth3_occ).state) +"\r\n";
|
||||
return std::vector<uint8_t>(mth3_occ_st.begin(), mth3_occ_st.end());
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string rmaxst = "rmax=" + str_sprintf("%.1f",id(LD1125H_rmax).state) +"\r\n";
|
||||
return std::vector<uint8_t>(rmaxst.begin(), rmaxst.end());
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/ssieb/custom_components #Thanks for @ssieb components.
|
||||
components: [ serial ]
|
||||
|
||||
uart:
|
||||
id: LD1125H_UART_BUS
|
||||
rx_pin: GPIO16
|
||||
tx_pin: GPIO17
|
||||
baud_rate: 115200
|
||||
data_bits: 8
|
||||
stop_bits: 1
|
||||
parity: NONE
|
||||
# debug:
|
||||
# direction: BOTH
|
||||
# dummy_receiver: false
|
||||
# after:
|
||||
# delimiter: "\n"
|
||||
# sequence:
|
||||
# - lambda: UARTDebug::log_string(direction, bytes);
|
||||
status_led:
|
||||
pin:
|
||||
number: GPIO2
|
||||
inverted: false
|
||||
globals:
|
||||
- id: LD1125H_Last_Time
|
||||
type: time_t
|
||||
restore_value: no
|
||||
initial_value: time(NULL)
|
||||
- id: LD1125H_Last_Mov_Time
|
||||
type: time_t
|
||||
restore_value: no
|
||||
initial_value: time(NULL)
|
||||
- id: LD1125H_Clearance_Status
|
||||
type: bool
|
||||
restore_value: no
|
||||
initial_value: "false"
|
||||
- id: ignored_ranges
|
||||
type: std::vector<std::vector<float>>
|
||||
restore_value: no
|
||||
initial_value: ${ignored_radar_ranges}
|
||||
interval:
|
||||
- interval: 1s #Clearance Scan Time
|
||||
setup_priority: 200
|
||||
then:
|
||||
lambda: |-
|
||||
if ((time(NULL)-id(LD1125H_Last_Time))>id(LD1125H_Clearance_Time).state) {
|
||||
if ((id(LD1125H_Clearance_Status) == false) || (id(LD1125H_Occupancy).state != "Clear")) {
|
||||
id(LD1125H_Occupancy).publish_state("Clear");
|
||||
id(LD1125H_Clearance_Status) = true;
|
||||
}
|
||||
if (id(LD1125H_MovOcc_Binary).state == true) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(false);
|
||||
}
|
||||
if (id(LD1125H_Mov_Binary).state == true) {
|
||||
id(LD1125H_Mov_Binary).publish_state(false);
|
||||
}
|
||||
}
|
||||
number:
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} 0-2.8m Movement Minimum Signal
|
||||
id: LD1125H_mth1_mov
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "80"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_mov_st = "mth1_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth1_mov_st.begin(), mth1_mov_st.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} 2.8m-8m Movement Minimum Signal
|
||||
id: LD1125H_mth2_mov
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "50"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_mov_st = "mth2_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth2_mov_st.begin(), mth2_mov_st.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} 8m-∞ Movement Minimum Signal
|
||||
id: LD1125H_mth3_mov
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "20"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_mov_st = "mth3_mov=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth3_mov_st.begin(), mth3_mov_st.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} 0-2.8m Occupancy Minimum Signal
|
||||
id: LD1125H_mth1_occ
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
initial_value: "60"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth1_occ_st = "mth1_occ=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth1_occ_st.begin(), mth1_occ_st.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} 2.8m-8m Occupancy Minimum Signal
|
||||
id: LD1125H_mth2_occ
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "55"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth2_occ_st = "mth2_occ=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth2_occ_st.begin(), mth2_occ_st.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} 8m-∞ Occupancy Minimum Signal
|
||||
id: LD1125H_mth3_occ
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "20"
|
||||
min_value: 0
|
||||
max_value: 1500
|
||||
step: 1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string mth3_occ_st = "mth3_occ=" + str_sprintf("%.0f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(mth3_occ_st.begin(), mth3_occ_st.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Max Detection Distance (m) #rmax is max detection distance.
|
||||
id: LD1125H_rmax
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "8" #Default rmax Setting
|
||||
min_value: 0.0
|
||||
max_value: 60
|
||||
step: 0.1
|
||||
set_action:
|
||||
then:
|
||||
- uart.write:
|
||||
id: LD1125H_UART_BUS
|
||||
data: !lambda |-
|
||||
std::string rmaxst = "rmax=" + str_sprintf("%.1f",x) +"\r\n";
|
||||
return std::vector<uint8_t>(rmaxst.begin(), rmaxst.end());
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Seconds to Clear Mov/Occ
|
||||
id: LD1125H_Clearance_Time
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "20" #LD1125H Mov/Occ > Clearance Time Here
|
||||
min_value: 0
|
||||
max_value: 60
|
||||
step: 1
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Seconds to Detection
|
||||
id: LD1125H_Mov_Time
|
||||
icon: "mdi:cogs"
|
||||
optimistic: true
|
||||
restore_value: true #If you don't want to store the setting at ESP, set it to false.
|
||||
initial_value: "1" #LD1125H Mov > Occ Time Here
|
||||
min_value: 0.1
|
||||
max_value: 10
|
||||
step: 0.1
|
||||
sensor:
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Objects Distance
|
||||
id: LD1125H_Distance
|
||||
icon: "mdi:signal-distance-variant"
|
||||
unit_of_measurement: "m"
|
||||
accuracy_decimals: 2
|
||||
filters: # Use Filter To Debounce
|
||||
- sliding_window_moving_average:
|
||||
window_size: 200
|
||||
send_every: 10
|
||||
- heartbeat: 0.2s
|
||||
text_sensor:
|
||||
- platform: serial
|
||||
uart_id: LD1125H_UART_BUS
|
||||
name: ${entity_name_prefix} UART Text
|
||||
id: LD1125H_UART_Text
|
||||
icon: "mdi:format-text"
|
||||
internal: True
|
||||
on_value:
|
||||
lambda: |-
|
||||
for (const auto& row : id(ignored_ranges)) {
|
||||
if ( ( atof(id(LD1125H_UART_Text).state.substr(9).c_str()) > row[0] ) && ( atof(id(LD1125H_UART_Text).state.substr(9).c_str()) < row[1] ) ) {
|
||||
break;
|
||||
} else {
|
||||
if (id(LD1125H_UART_Text).state.substr(0,3) == "occ") {
|
||||
id(LD1125H_Distance).publish_state(atof(id(LD1125H_UART_Text).state.substr(9).c_str()));
|
||||
if ((time(NULL)-id(LD1125H_Last_Mov_Time))>id(LD1125H_Mov_Time).state) {
|
||||
id(LD1125H_Occupancy).publish_state("Occupied");
|
||||
if (id(LD1125H_MovOcc_Binary).state == false) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(true);
|
||||
}
|
||||
if (id(LD1125H_Mov_Binary).state == true) {
|
||||
id(LD1125H_Mov_Binary).publish_state(false);
|
||||
}
|
||||
}
|
||||
if (id(LD1125H_MovOcc_Binary).state == false) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(true);
|
||||
}
|
||||
id(LD1125H_Last_Time) = time(NULL);
|
||||
if (id(LD1125H_Clearance_Status) == true) {
|
||||
id(LD1125H_Clearance_Status) = false;
|
||||
}
|
||||
}
|
||||
else if (id(LD1125H_UART_Text).state.substr(0,3) == "mov") {
|
||||
id(LD1125H_Distance).publish_state(atof(id(LD1125H_UART_Text).state.substr(9).c_str()));
|
||||
id(LD1125H_Occupancy).publish_state("Movement");
|
||||
if (id(LD1125H_MovOcc_Binary).state == false) {
|
||||
id(LD1125H_MovOcc_Binary).publish_state(true);
|
||||
}
|
||||
if (id(LD1125H_Mov_Binary).state == false) {
|
||||
id(LD1125H_Mov_Binary).publish_state(true);
|
||||
}
|
||||
id(LD1125H_Last_Mov_Time) = time(NULL);
|
||||
id(LD1125H_Last_Time) = time(NULL);
|
||||
if (id(LD1125H_Clearance_Status) == true) {
|
||||
id(LD1125H_Clearance_Status) = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Occupancy Status
|
||||
id: LD1125H_Occupancy
|
||||
icon: "mdi:motion-sensor"
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Occupancy or Movement
|
||||
id: LD1125H_MovOcc_Binary
|
||||
device_class: occupancy
|
||||
- platform: template
|
||||
name: ${entity_name_prefix} Motion
|
||||
id: LD1125H_Mov_Binary
|
||||
device_class: motion
|
@ -1,103 +0,0 @@
|
||||
|
||||
esphome:
|
||||
name: supersensor
|
||||
name_add_mac_suffix: true
|
||||
friendly_name: "Supersensor"
|
||||
project:
|
||||
name: joshuaboniface.supersensor
|
||||
version: "0.1"
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 115200
|
||||
|
||||
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"
|
||||
|
||||
script:
|
||||
- id: ledtest
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash_white
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
- delay: 5s
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 1
|
||||
green: 0
|
||||
blue: 0
|
||||
- delay: 5s
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 1
|
||||
blue: 0
|
||||
- delay: 5s
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
|
||||
interval:
|
||||
- interval: 30s
|
||||
then:
|
||||
- script.stop: ledtest
|
||||
- light.turn_off: output_led
|
||||
- script.execute: ledtest
|
||||
|
||||
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: 50%
|
||||
red: 40%
|
||||
green: 50%
|
||||
blue: 45%
|
||||
duration: 0.5s
|
||||
- state: false
|
||||
duration: 0.5s
|
||||
- automation:
|
||||
name: hold
|
||||
sequence:
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: rgb_r
|
||||
pin: GPIO32
|
||||
max_power: 100%
|
||||
- platform: ledc
|
||||
id: rgb_g
|
||||
pin: GPIO33
|
||||
max_power: 100%
|
||||
- platform: ledc
|
||||
id: rgb_b
|
||||
pin: GPIO25
|
||||
max_power: 100%
|
@ -1,99 +0,0 @@
|
||||
---
|
||||
|
||||
###############################################################################
|
||||
# SuperSensor v1.0 ESPHome configuration
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (C) 2023 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: pirtester
|
||||
friendly_name: "PIRTester"
|
||||
|
||||
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"
|
||||
|
||||
# Enable logging only via web
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 115200
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
safe_mode: false
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: GPIO32
|
||||
name: "PIR 32"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO33
|
||||
name: "PIR 33"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO25
|
||||
name: "PIR 25"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO26
|
||||
name: "PIR 26"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO27
|
||||
name: "PIR 27"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO14
|
||||
name: "PIR 14"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO12
|
||||
name: "PIR 12"
|
||||
device_class: motion
|
||||
- platform: gpio
|
||||
pin: GPIO13
|
||||
name: "PIR 13"
|
||||
device_class: motion
|
@ -1,866 +0,0 @@
|
||||
---
|
||||
|
||||
###############################################################################
|
||||
# SuperSensor v1.0 ESPHome configuration
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (C) 2023 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: "0.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);
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash_white
|
||||
|
||||
dashboard_import:
|
||||
package_import_url: github://joshuaboniface/supersensor/supersensor.yaml
|
||||
import_full_config: false
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
|
||||
globals:
|
||||
- id: pir_hold_time
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "15"
|
||||
|
||||
- id: light_presence_threshold
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "30"
|
||||
|
||||
- id: occupancy_detect_mode
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "0"
|
||||
|
||||
- id: occupancy_clear_mode
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "0"
|
||||
|
||||
script:
|
||||
- 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
|
||||
if (pir_counts & radar_counts & light_counts) {
|
||||
// Logical AND of pir & radar & light
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR & Radar & Light: %i", pir & radar & light);
|
||||
id(supersensor_occupancy).publish_state(pir & radar & light);
|
||||
} else if (pir_counts & radar_counts) {
|
||||
// Logical AND of pir & radar
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR & Radar: %i", pir & radar);
|
||||
id(supersensor_occupancy).publish_state(pir & radar);
|
||||
} else if (pir_counts & light_counts) {
|
||||
// Logical AND of pir & light
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR & Light: %i", pir & light);
|
||||
id(supersensor_occupancy).publish_state(pir & light);
|
||||
} else if (radar_counts & light_counts) {
|
||||
// Logical AND of radar & light
|
||||
ESP_LOGD("occupancy_detect_handler", "Radar & Light: %i", radar & light);
|
||||
id(supersensor_occupancy).publish_state(radar & light);
|
||||
} else if (pir_counts) {
|
||||
// Only pir
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR: %i", pir);
|
||||
id(supersensor_occupancy).publish_state(pir);
|
||||
} else if (radar_counts) {
|
||||
// Only radar
|
||||
ESP_LOGD("occupancy_detect_handler", "Radar: %i", radar);
|
||||
id(supersensor_occupancy).publish_state(radar);
|
||||
} else if (light_counts) {
|
||||
// Only light
|
||||
ESP_LOGD("occupancy_detect_handler", "Light: %i", light);
|
||||
id(supersensor_occupancy).publish_state(light);
|
||||
} else {
|
||||
ESP_LOGD("occupancy_detect_handler", "None");
|
||||
id(supersensor_occupancy).publish_state(false);
|
||||
}
|
||||
|
||||
- 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
|
||||
if (pir_counts & radar_counts & light_counts) {
|
||||
// Logical AND of pir & radar & light
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR & Radar & Light: %i", pir & radar & light);
|
||||
id(supersensor_occupancy).publish_state(pir & radar & light);
|
||||
} else if (pir_counts & radar_counts) {
|
||||
// Logical AND of pir & radar
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR & Radar: %i", pir & radar);
|
||||
id(supersensor_occupancy).publish_state(pir & radar);
|
||||
} else if (pir_counts & light_counts) {
|
||||
// Logical AND of pir & light
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR & Light: %i", pir & light);
|
||||
id(supersensor_occupancy).publish_state(pir & light);
|
||||
} else if (radar_counts & light_counts) {
|
||||
// Logical AND of radar & light
|
||||
ESP_LOGD("occupancy_clear_handler", "Radar & Light: %i", radar & light);
|
||||
id(supersensor_occupancy).publish_state(radar & light);
|
||||
} else if (pir_counts) {
|
||||
// Only pir
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR: %i", pir);
|
||||
id(supersensor_occupancy).publish_state(pir);
|
||||
} else if (radar_counts) {
|
||||
// Only radar
|
||||
ESP_LOGD("occupancy_clear_handler", "Radar: %i", radar);
|
||||
id(supersensor_occupancy).publish_state(radar);
|
||||
} else if (light_counts) {
|
||||
// Only light
|
||||
ESP_LOGD("occupancy_clear_handler", "Light: %i", light);
|
||||
id(supersensor_occupancy).publish_state(light);
|
||||
} else {
|
||||
ESP_LOGD("occupancy_clear_handler", "None");
|
||||
id(supersensor_occupancy).publish_state(false);
|
||||
}
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 115200
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
on_client_disconnected:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash_white
|
||||
- switch.turn_off: use_wake_word
|
||||
on_client_connected:
|
||||
# This absolute absurdity is required to prevent
|
||||
# "no wake word detected" failure loops.
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
safe_mode: false
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
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
|
||||
|
||||
voice_assistant:
|
||||
microphone: mic
|
||||
use_wake_word: false
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
volume_multiplier: 4.0
|
||||
id: assist
|
||||
on_error:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- logger.log: "Voice Assistant encountered an error; restarting it"
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 1s
|
||||
- switch.turn_on: use_wake_word
|
||||
on_wake_word_detected:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_stt_end:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
on_tts_start:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x != "Sorry, I couldn't understand that";
|
||||
then:
|
||||
- logger.log: "Command successful!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 0
|
||||
green: 1
|
||||
blue: 0
|
||||
else:
|
||||
- logger.log: "Command failed!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 1
|
||||
green: 0
|
||||
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: 50%
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
duration: 0.5s
|
||||
- state: false
|
||||
duration: 0.5s
|
||||
- automation:
|
||||
name: hold
|
||||
sequence:
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
|
||||
output:
|
||||
- platform: ledc
|
||||
id: rgb_r
|
||||
pin: GPIO32
|
||||
- platform: ledc
|
||||
id: rgb_g
|
||||
pin: GPIO33
|
||||
- platform: ledc
|
||||
id: rgb_b
|
||||
pin: GPIO25
|
||||
|
||||
bme680_bsec:
|
||||
address: 0x77
|
||||
id: bme680_sensor
|
||||
sample_rate: lp
|
||||
state_save_interval: 1h
|
||||
iaq_mode: static
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
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_bsec
|
||||
bme680_bsec_id: bme680_sensor
|
||||
temperature:
|
||||
name: "BME680 Temperature"
|
||||
id: bme680_temperature
|
||||
pressure:
|
||||
name: "BME680 Pressure"
|
||||
id: bme680_pressure
|
||||
humidity:
|
||||
name: "BME680 Relative Humidity"
|
||||
id: bme680_humidity
|
||||
iaq:
|
||||
name: "BME680 IAQ"
|
||||
id: bme680_iaq
|
||||
co2_equivalent:
|
||||
name: "BME680 CO2 Equivalent"
|
||||
breath_voc_equivalent:
|
||||
name: "BME680 Breath VOC Equivalent"
|
||||
gas_resistance:
|
||||
name: "BME680 Gas Resistance"
|
||||
id: bme680_gas_resistance
|
||||
internal: yes
|
||||
|
||||
- platform: absolute_humidity
|
||||
name: "BME680 Absolute Humidity"
|
||||
temperature: bme680_temperature
|
||||
humidity: bme680_humidity
|
||||
|
||||
- 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: Lux
|
||||
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: bme680_bsec
|
||||
bme680_bsec_id: bme680_sensor
|
||||
iaq_accuracy:
|
||||
name: "BME680 IAQ Accuracy"
|
||||
|
||||
- platform: template
|
||||
name: "BME680 IAQ Classification"
|
||||
icon: "mdi:air-filter"
|
||||
update_interval: 15s
|
||||
lambda: |-
|
||||
int iaq = int(id(bme680_iaq).state);
|
||||
if (iaq <= 50) {
|
||||
return {"Excellent (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 100) {
|
||||
return {"Good (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 150) {
|
||||
return {"Fair (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 200) {
|
||||
return {"Moderate (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 250) {
|
||||
return {"Bad (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 350) {
|
||||
return {"Very Bad (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 500) {
|
||||
return {"Terrible (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else {
|
||||
return {"Unknown (" + to_string(iaq) + ")"};
|
||||
}
|
||||
|
||||
- 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"
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: restart
|
||||
name: "ESP32 Restart"
|
||||
icon: mdi:power-cycle
|
||||
entity_category: diagnostic
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Enable Wake Word"
|
||||
icon: mdi:account-voice
|
||||
id: use_wake_word
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- lambda: id(assist).set_use_wake_word(true);
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
- voice_assistant.is_running
|
||||
then:
|
||||
- voice_assistant.start_continuous
|
||||
on_turn_off:
|
||||
- voice_assistant.stop
|
||||
- lambda: id(assist).set_use_wake_word(false);
|
||||
|
||||
- platform: ld2410
|
||||
engineering_mode:
|
||||
name: "LD2410C Engineering Mode"
|
||||
entity_category: diagnostic
|
||||
bluetooth:
|
||||
name: "LD2410C Bluetooth"
|
||||
entity_category: diagnostic
|
||||
|
||||
number:
|
||||
# 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: yes
|
||||
restore_value: yes
|
||||
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: yes
|
||||
restore_value: yes
|
||||
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"
|
@ -1,861 +0,0 @@
|
||||
---
|
||||
|
||||
###############################################################################
|
||||
# SuperSensor v1.0 ESPHome configuration
|
||||
###############################################################################
|
||||
#
|
||||
# Copyright (C) 2023 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: "0.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);
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash_white
|
||||
|
||||
dashboard_import:
|
||||
package_import_url: github://joshuaboniface/supersensor/supersensor.yaml
|
||||
import_full_config: false
|
||||
|
||||
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"
|
||||
|
||||
globals:
|
||||
- id: pir_hold_time
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "15"
|
||||
|
||||
- id: light_presence_threshold
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "30"
|
||||
|
||||
- id: occupancy_detect_mode
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "0"
|
||||
|
||||
- id: occupancy_clear_mode
|
||||
type: int
|
||||
restore_value: yes
|
||||
initial_value: "0"
|
||||
|
||||
script:
|
||||
- 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
|
||||
if (pir_counts & radar_counts & light_counts) {
|
||||
// Logical AND of pir & radar & light
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR & Radar & Light: %i", pir & radar & light);
|
||||
id(supersensor_occupancy).publish_state(pir & radar & light);
|
||||
} else if (pir_counts & radar_counts) {
|
||||
// Logical AND of pir & radar
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR & Radar: %i", pir & radar);
|
||||
id(supersensor_occupancy).publish_state(pir & radar);
|
||||
} else if (pir_counts & light_counts) {
|
||||
// Logical AND of pir & light
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR & Light: %i", pir & light);
|
||||
id(supersensor_occupancy).publish_state(pir & light);
|
||||
} else if (radar_counts & light_counts) {
|
||||
// Logical AND of radar & light
|
||||
ESP_LOGD("occupancy_detect_handler", "Radar & Light: %i", radar & light);
|
||||
id(supersensor_occupancy).publish_state(radar & light);
|
||||
} else if (pir_counts) {
|
||||
// Only pir
|
||||
ESP_LOGD("occupancy_detect_handler", "PIR: %i", pir);
|
||||
id(supersensor_occupancy).publish_state(pir);
|
||||
} else if (radar_counts) {
|
||||
// Only radar
|
||||
ESP_LOGD("occupancy_detect_handler", "Radar: %i", radar);
|
||||
id(supersensor_occupancy).publish_state(radar);
|
||||
} else if (light_counts) {
|
||||
// Only light
|
||||
ESP_LOGD("occupancy_detect_handler", "Light: %i", light);
|
||||
id(supersensor_occupancy).publish_state(light);
|
||||
} else {
|
||||
ESP_LOGD("occupancy_detect_handler", "None");
|
||||
id(supersensor_occupancy).publish_state(false);
|
||||
}
|
||||
|
||||
- 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
|
||||
if (pir_counts & radar_counts & light_counts) {
|
||||
// Logical AND of pir & radar & light
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR & Radar & Light: %i", pir & radar & light);
|
||||
id(supersensor_occupancy).publish_state(pir & radar & light);
|
||||
} else if (pir_counts & radar_counts) {
|
||||
// Logical AND of pir & radar
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR & Radar: %i", pir & radar);
|
||||
id(supersensor_occupancy).publish_state(pir & radar);
|
||||
} else if (pir_counts & light_counts) {
|
||||
// Logical AND of pir & light
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR & Light: %i", pir & light);
|
||||
id(supersensor_occupancy).publish_state(pir & light);
|
||||
} else if (radar_counts & light_counts) {
|
||||
// Logical AND of radar & light
|
||||
ESP_LOGD("occupancy_clear_handler", "Radar & Light: %i", radar & light);
|
||||
id(supersensor_occupancy).publish_state(radar & light);
|
||||
} else if (pir_counts) {
|
||||
// Only pir
|
||||
ESP_LOGD("occupancy_clear_handler", "PIR: %i", pir);
|
||||
id(supersensor_occupancy).publish_state(pir);
|
||||
} else if (radar_counts) {
|
||||
// Only radar
|
||||
ESP_LOGD("occupancy_clear_handler", "Radar: %i", radar);
|
||||
id(supersensor_occupancy).publish_state(radar);
|
||||
} else if (light_counts) {
|
||||
// Only light
|
||||
ESP_LOGD("occupancy_clear_handler", "Light: %i", light);
|
||||
id(supersensor_occupancy).publish_state(light);
|
||||
} else {
|
||||
ESP_LOGD("occupancy_clear_handler", "None");
|
||||
id(supersensor_occupancy).publish_state(false);
|
||||
}
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
baud_rate: 115200
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_encryption_key
|
||||
on_client_disconnected:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: flash_white
|
||||
- switch.turn_off: use_wake_word
|
||||
on_client_connected:
|
||||
# This absolute absurdity is required to prevent
|
||||
# "no wake word detected" failure loops.
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 2s
|
||||
- switch.turn_on: use_wake_word
|
||||
- delay: 2s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
|
||||
ota:
|
||||
password: !secret ota_password
|
||||
safe_mode: false
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
auth:
|
||||
username: !secret web_auth_username
|
||||
password: !secret web_auth_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
domain: !secret wifi_domain
|
||||
power_save_mode: LIGHT
|
||||
reboot_timeout: 5min
|
||||
|
||||
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
|
||||
|
||||
voice_assistant:
|
||||
microphone: mic
|
||||
use_wake_word: false
|
||||
noise_suppression_level: 2
|
||||
auto_gain: 31dBFS
|
||||
volume_multiplier: 4.0
|
||||
id: assist
|
||||
on_error:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- logger.log: "Voice Assistant encountered an error; restarting it"
|
||||
- switch.turn_off: use_wake_word
|
||||
- delay: 1s
|
||||
- switch.turn_on: use_wake_word
|
||||
on_wake_word_detected:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_listening:
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 1
|
||||
on_stt_end:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
on_tts_start:
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
- if:
|
||||
condition:
|
||||
lambda: return x != "Sorry, I couldn't understand that";
|
||||
then:
|
||||
- logger.log: "Command successful!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 0
|
||||
green: 1
|
||||
blue: 0
|
||||
else:
|
||||
- logger.log: "Command failed!"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
effect: hold
|
||||
red: 1
|
||||
green: 0
|
||||
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: 50%
|
||||
red: 100%
|
||||
green: 100%
|
||||
blue: 100%
|
||||
duration: 0.5s
|
||||
- state: false
|
||||
duration: 0.5s
|
||||
- automation:
|
||||
name: hold
|
||||
sequence:
|
||||
- delay: 5s
|
||||
- light.turn_off:
|
||||
id: output_led
|
||||
transition_length: 1s
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
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
|
||||
temperature:
|
||||
name: "BME680 Temperature"
|
||||
id: bme680_temperature
|
||||
pressure:
|
||||
name: "BME680 Pressure"
|
||||
id: bme680_pressure
|
||||
humidity:
|
||||
name: "BME680 Relative Humidity"
|
||||
id: bme680_humidity
|
||||
gas_resistance:
|
||||
name: "BME680 Gas Resistance"
|
||||
id: bme680_gas_resistance
|
||||
address: 0x77
|
||||
update_interval: 5s
|
||||
|
||||
- platform: absolute_humidity
|
||||
name: "BME680 Absolute Humidity"
|
||||
temperature: bme680_temperature
|
||||
humidity: bme680_humidity
|
||||
|
||||
- platform: template
|
||||
name: "BME680 Indoor Air Quality"
|
||||
id: bme680_iaq
|
||||
icon: "mdi:gauge"
|
||||
# caulculation: comp_gas = log(R_gas[ohm]) + 0.04 log(Ohm)/%rh * hum[%rh]
|
||||
lambda: |-
|
||||
return float(log(id(bme680_gas_resistance).state) + 0.04 * id(bme680_humidity).state);
|
||||
|
||||
- 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: Lux
|
||||
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
|
||||
still_distance:
|
||||
name: "LD2410C Still Distance"
|
||||
id: still_distance
|
||||
moving_energy:
|
||||
name: "LD2410C Move Energy"
|
||||
still_energy:
|
||||
name: "LD2410C Still Energy"
|
||||
detection_distance:
|
||||
name: "LD2410C Presence Distance"
|
||||
|
||||
- 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 IAQ Classification"
|
||||
icon: "mdi:air-filter"
|
||||
update_interval: 15s
|
||||
lambda: |-
|
||||
int iaq = int(id(bme680_iaq).state);
|
||||
if (iaq <= 50) {
|
||||
return {"Excellent (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 100) {
|
||||
return {"Good (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 150) {
|
||||
return {"Fair (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 200) {
|
||||
return {"Moderate (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 250) {
|
||||
return {"Bad (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 350) {
|
||||
return {"Very Bad (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else if (iaq <= 500) {
|
||||
return {"Terrible (" + to_string(iaq) + ")"};
|
||||
}
|
||||
else {
|
||||
return {"Unknown (" + to_string(iaq) + ")"};
|
||||
}
|
||||
|
||||
- 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"
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: restart
|
||||
name: "ESP32 Restart"
|
||||
icon: mdi:power-cycle
|
||||
entity_category: diagnostic
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Enable Wake Word"
|
||||
icon: mdi:account-voice
|
||||
id: use_wake_word
|
||||
optimistic: true
|
||||
restore_mode: ALWAYS_OFF
|
||||
entity_category: config
|
||||
on_turn_on:
|
||||
- lambda: id(assist).set_use_wake_word(true);
|
||||
- if:
|
||||
condition:
|
||||
not:
|
||||
- voice_assistant.is_running
|
||||
then:
|
||||
- voice_assistant.start_continuous
|
||||
on_turn_off:
|
||||
- voice_assistant.stop
|
||||
- lambda: id(assist).set_use_wake_word(false);
|
||||
|
||||
- platform: ld2410
|
||||
engineering_mode:
|
||||
name: "LD2410C Engineering Mode"
|
||||
entity_category: diagnostic
|
||||
bluetooth:
|
||||
name: "LD2410C Bluetooth"
|
||||
entity_category: diagnostic
|
||||
|
||||
number:
|
||||
# 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: yes
|
||||
restore_value: yes
|
||||
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: yes
|
||||
restore_value: yes
|
||||
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"
|
51124
board/supersensor.dxf
51124
board/supersensor.dxf
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -27,6 +27,7 @@ esphome:
|
||||
project:
|
||||
name: joshuaboniface.supersensor
|
||||
version: "2.0"
|
||||
min_version: 2025.5.0
|
||||
on_boot:
|
||||
- priority: 600
|
||||
then:
|
||||
@ -35,32 +36,24 @@ esphome:
|
||||
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
|
||||
- priority: -100
|
||||
then:
|
||||
- wait_until:
|
||||
api.connected:
|
||||
- delay: 5s
|
||||
- if:
|
||||
condition:
|
||||
switch.is_on: enable_voice_support
|
||||
- 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
|
||||
- micro_wake_word.start:
|
||||
|
||||
preferences:
|
||||
flash_write_interval: 15sec
|
||||
|
||||
dashboard_import:
|
||||
package_import_url: github://joshuaboniface/supersensor/supersensor-2.x.yaml
|
||||
package_import_url: github://joshuaboniface/supersensor2/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"
|
||||
@ -74,10 +67,11 @@ esp32:
|
||||
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: "0.0"
|
||||
initial_value: "-5.0"
|
||||
|
||||
- id: humidity_offset
|
||||
type: float
|
||||
@ -109,6 +103,11 @@ globals:
|
||||
restore_value: no
|
||||
initial_value: "0"
|
||||
|
||||
- id: current_wake_word
|
||||
type: std::string
|
||||
restore_value: yes
|
||||
initial_value: '"mww_computer"'
|
||||
|
||||
script:
|
||||
- id: light_off
|
||||
then:
|
||||
@ -258,28 +257,69 @@ script:
|
||||
id(supersensor_occupancy).publish_state(new_state);
|
||||
}
|
||||
|
||||
preferences:
|
||||
flash_write_interval: 15sec
|
||||
interval:
|
||||
# Regular MWW state check every 30s
|
||||
- interval: 30s
|
||||
then:
|
||||
- if:
|
||||
condition:
|
||||
- switch.is_on: enable_voice_support
|
||||
- not:
|
||||
micro_wake_word.is_running
|
||||
then:
|
||||
- micro_wake_word.start:
|
||||
|
||||
# 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: INFO
|
||||
level: DEBUG
|
||||
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
|
||||
# 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
|
||||
@ -306,6 +346,9 @@ time:
|
||||
then:
|
||||
- logger.log: "Time synchronized with Home Assistant"
|
||||
|
||||
debug:
|
||||
update_interval: 15s
|
||||
|
||||
uart:
|
||||
id: ld2410_uart
|
||||
rx_pin: GPIO19
|
||||
@ -321,100 +364,59 @@ i2c:
|
||||
scan: true
|
||||
|
||||
i2s_audio:
|
||||
i2s_lrclk_pin: GPIO17 # WS
|
||||
i2s_bclk_pin: GPIO16 # SCK
|
||||
- id: i2s_input
|
||||
i2s_lrclk_pin:
|
||||
number: GPIO17 # WS
|
||||
i2s_bclk_pin:
|
||||
number: GPIO16 # SCK
|
||||
|
||||
microphone:
|
||||
- platform: i2s_audio
|
||||
id: mic
|
||||
adc_type: external
|
||||
i2s_audio_id: i2s_input
|
||||
i2s_din_pin: GPIO4 # SD
|
||||
adc_type: external
|
||||
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();
|
||||
}
|
||||
channel: left
|
||||
|
||||
micro_wake_word:
|
||||
model: hey_jarvis
|
||||
id: mww
|
||||
microphone:
|
||||
microphone: mic
|
||||
gain_factor: 8
|
||||
stop_after_detection: false
|
||||
models:
|
||||
- model: github://genehand/Custom_V2_MicroWakeWords/models/computer/computer.json@update-json
|
||||
id: mww_computer
|
||||
- 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;
|
||||
|
||||
# Include the Espressif Audio Development Framework for VAD support
|
||||
esp_adf:
|
||||
external_components:
|
||||
- source: github://pr#5230
|
||||
components:
|
||||
- esp_adf
|
||||
refresh: 0s
|
||||
|
||||
voice_assistant:
|
||||
id: va
|
||||
microphone: mic
|
||||
micro_wake_word: mww
|
||||
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
|
||||
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%
|
||||
@ -422,6 +424,7 @@ voice_assistant:
|
||||
green: 0
|
||||
blue: 1
|
||||
on_listening:
|
||||
- logger.log: "Listening for commands"
|
||||
- light.turn_on:
|
||||
id: output_led
|
||||
brightness: 100%
|
||||
@ -429,14 +432,13 @@ voice_assistant:
|
||||
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_stt_end:
|
||||
- script.execute: light_off
|
||||
on_tts_start:
|
||||
- if:
|
||||
condition:
|
||||
@ -491,42 +493,139 @@ light:
|
||||
output:
|
||||
- platform: ledc
|
||||
id: rgb_r
|
||||
pin: GPIO32
|
||||
pin: GPIO23
|
||||
- platform: ledc
|
||||
id: rgb_g
|
||||
pin: GPIO33
|
||||
pin: GPIO22
|
||||
- platform: ledc
|
||||
id: rgb_b
|
||||
pin: GPIO25
|
||||
pin: GPIO21
|
||||
|
||||
ld2410:
|
||||
id: ld2410_radar
|
||||
uart_id: ld2410_uart
|
||||
|
||||
sensor:
|
||||
- platform: sgp30
|
||||
eco2:
|
||||
name: "SGP30 eCO2"
|
||||
id: sgp30_eco2
|
||||
accuracy_decimals: 1
|
||||
tvoc:
|
||||
name: "SGP30 TVOC"
|
||||
id: sgp30_tvoc
|
||||
accuracy_decimals: 1
|
||||
store_baseline: yes
|
||||
update_interval: 15s
|
||||
- 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: debug
|
||||
free:
|
||||
name: "Heap Free"
|
||||
block:
|
||||
name: "Heap Max Block"
|
||||
loop_time:
|
||||
name: "Loop Time"
|
||||
cpu_frequency:
|
||||
name: "CPU Frequency"
|
||||
|
||||
- platform: sgp4x
|
||||
voc:
|
||||
name: "SGP41 VOC Index"
|
||||
id: sgp41_voc_index
|
||||
accuracy_decimals: 0
|
||||
icon: mdi:waves-arrow-up
|
||||
filters:
|
||||
- sliding_window_moving_average: # We take a reading every 15 seconds, but calculate the sliding
|
||||
window_size: 12 # average over 12 readings i.e. 60 seconds/1 minute to normalize
|
||||
send_every: 3 # brief spikes while still sending a value every 15 seconds.
|
||||
nox:
|
||||
name: "SGP41 NOx Index"
|
||||
id: sgp41_nox_index
|
||||
accuracy_decimals: 0
|
||||
icon: mdi:waves-arrow-up
|
||||
filters:
|
||||
- sliding_window_moving_average:
|
||||
window_size: 12
|
||||
send_every: 3
|
||||
compensation:
|
||||
temperature_source: sht45_temperature
|
||||
humidity_source: sht45_humidity
|
||||
store_baseline: true
|
||||
update_interval: 5s
|
||||
|
||||
- platform: template
|
||||
name: "SGP41 TVOC (µg/m³)"
|
||||
id: sgp41_tvoc_ugm3
|
||||
icon: mdi:molecule
|
||||
lambda: |-
|
||||
float i = id(sgp41_voc_index).state;
|
||||
if (i < 1) return NAN;
|
||||
float tvoc = (log(501.0 - i) - 6.24) * -878.53;
|
||||
return tvoc;
|
||||
unit_of_measurement: "µg/m³"
|
||||
accuracy_decimals: 0
|
||||
|
||||
- platform: template
|
||||
name: "SGP41 TVOC (ppb)"
|
||||
id: sgp41_tvoc_ppb
|
||||
icon: mdi:molecule
|
||||
lambda: |-
|
||||
float tvoc_ugm3 = id(sgp41_tvoc_ugm3).state;
|
||||
float tvoc_ppm = tvoc_ugm3 * 0.436; // ppb estimated using isobutylene MW (56.1 g/mol)
|
||||
return tvoc_ppm;
|
||||
unit_of_measurement: "ppb"
|
||||
accuracy_decimals: 0
|
||||
|
||||
- platform: template
|
||||
name: "SGP41 eCO2 (appr.)"
|
||||
id: sgp41_eco2_appr
|
||||
icon: mdi:molecule-co2
|
||||
lambda: |-
|
||||
float tvoc_ppb = id(sgp41_tvoc_ppb).state;
|
||||
float eco2_ppm = 400.0 + 1.5 * tvoc_ppb;
|
||||
if (eco2_ppm > 2000) eco2_ppm = 2000;
|
||||
return eco2_ppm;
|
||||
unit_of_measurement: "ppm"
|
||||
accuracy_decimals: 0
|
||||
|
||||
- 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
|
||||
@ -535,9 +634,9 @@ sensor:
|
||||
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: |-
|
||||
@ -549,47 +648,38 @@ sensor:
|
||||
return (b * alpha) / (a - alpha);
|
||||
update_interval: 15s
|
||||
|
||||
# IAQ Index (1-5, 5=Excellent)
|
||||
- platform: template
|
||||
name: "IAQ Index"
|
||||
id: iaq_index
|
||||
lambda: |-
|
||||
int tvoc = id(sgp30_tvoc).state;
|
||||
int eco2 = id(sgp30_eco2).state;
|
||||
if (tvoc > 2200 || eco2 > 2000) return 1; // Unhealthy
|
||||
if (tvoc > 660 || eco2 > 1200) return 2; // Poor
|
||||
if (tvoc > 220 || eco2 > 800) return 3; // Moderate
|
||||
if (tvoc > 65 || eco2 > 600) return 4; // Good
|
||||
return 5; // Excellent
|
||||
update_interval: 15s
|
||||
|
||||
# Room Health Score (1-4, 4=Optimal)
|
||||
- platform: template
|
||||
name: "Room Health Score"
|
||||
id: room_health
|
||||
id: room_health_score
|
||||
unit_of_measurement: "%"
|
||||
accuracy_decimals: 0
|
||||
icon: mdi:home-heart
|
||||
lambda: |-
|
||||
float voc_index = id(sgp41_voc_index).state;
|
||||
float temp = id(sht45_temperature).state;
|
||||
float rh = id(sht45_humidity).state;
|
||||
int iaq = id(iaq_index).state;
|
||||
float humidity = id(sht45_humidity).state;
|
||||
|
||||
bool temp_ok = (temp >= 18 && temp <= 24);
|
||||
bool hum_ok = (rh >= 40 && rh <= 60);
|
||||
bool iaq_ok = (iaq >= 4);
|
||||
// VOC Score (0–100)
|
||||
float voc_score = 0;
|
||||
if (voc_index <= 100) voc_score = 100;
|
||||
else if (voc_index <= 200) voc_score = 80;
|
||||
else if (voc_index <= 300) voc_score = 60;
|
||||
else if (voc_index <= 400) voc_score = 40;
|
||||
else if (voc_index <= 500) voc_score = 50;
|
||||
else voc_score = 0;
|
||||
|
||||
int conditions_met = 0;
|
||||
if (temp_ok) conditions_met++;
|
||||
if (hum_ok) conditions_met++;
|
||||
if (iaq_ok) conditions_met++;
|
||||
// Temperature Score (0–100)
|
||||
float temp_score = 100.0 - abs(temp - 23.0) * 10.0;
|
||||
if (temp_score < 0) temp_score = 0;
|
||||
|
||||
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
|
||||
}
|
||||
// Humidity Score (0–100), ideal range 35–55%
|
||||
float humidity_score = 100.0 - abs(humidity - 50.0) * 3.0;
|
||||
if (humidity_score < 0) humidity_score = 0;
|
||||
|
||||
// Weighted average
|
||||
float overall_score = (voc_score * 0.5 + temp_score * 0.25 + humidity_score * 0.25);
|
||||
|
||||
return round(overall_score);
|
||||
update_interval: 15s
|
||||
|
||||
- platform: tsl2591
|
||||
@ -637,60 +727,21 @@ sensor:
|
||||
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
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: "SuperSensor Occupancy"
|
||||
id: supersensor_occupancy
|
||||
device_class: occupancy
|
||||
on_press:
|
||||
- script.execute: light_off
|
||||
on_release:
|
||||
on_state:
|
||||
then:
|
||||
- script.execute: light_off
|
||||
|
||||
- platform: gpio
|
||||
name: "PIR GPIO"
|
||||
id: pir_gpio
|
||||
pin: GPIO13
|
||||
pin:
|
||||
number: GPIO32
|
||||
mode: INPUT_PULLUP
|
||||
internal: false
|
||||
device_class: motion
|
||||
on_press:
|
||||
@ -731,6 +782,61 @@ binary_sensor:
|
||||
has_still_target:
|
||||
name: "LD2410C Still Target"
|
||||
|
||||
text_sensor:
|
||||
- platform: version
|
||||
name: "ESPHome Version"
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: wifi_info
|
||||
ip_address:
|
||||
name: "WiFi IP Address"
|
||||
ssid:
|
||||
name: "WiFi SSID"
|
||||
bssid:
|
||||
name: "WiFi BSSID"
|
||||
mac_address:
|
||||
name: "WiFi MAC Address"
|
||||
|
||||
- platform: debug
|
||||
device:
|
||||
name: "Device Info"
|
||||
reset_reason:
|
||||
name: "Reset Reason"
|
||||
|
||||
- platform: ld2410
|
||||
version:
|
||||
name: "LD2410C Firmware Version"
|
||||
mac_address:
|
||||
name: "LD2410C MAC Address"
|
||||
|
||||
- platform: template
|
||||
name: "Chemical Pollution"
|
||||
id: sgp41_chemical_pollution
|
||||
icon: mdi:molecule
|
||||
lambda: |-
|
||||
float voc_index = id(sgp41_voc_index).state;
|
||||
if (voc_index < 1 || voc_index > 500) return {"Unknown"};
|
||||
if (voc_index <= 100) return {"Excellent"};
|
||||
else if (voc_index <= 200) return {"Good"};
|
||||
else if (voc_index <= 300) return {"Moderate"};
|
||||
else if (voc_index <= 400) return {"Unhealthy"};
|
||||
else return {"Hazardous"};
|
||||
update_interval: 15s
|
||||
|
||||
- platform: template
|
||||
name: "Room Health"
|
||||
id: room_health_text
|
||||
icon: mdi:home-heart
|
||||
lambda: |-
|
||||
float score = id(room_health_score).state;
|
||||
if (score < 0) return {"Unknown"};
|
||||
else if (score >= 90.0) return {"Great"};
|
||||
else if (score >= 80.0) return {"Good"};
|
||||
else if (score >= 60.0) return {"Fair"};
|
||||
else if (score >= 40.0) return {"Poor"};
|
||||
else return {"Bad"};
|
||||
update_interval: 15s
|
||||
|
||||
button:
|
||||
- platform: ld2410
|
||||
restart:
|
||||
@ -761,26 +867,9 @@ switch:
|
||||
optimistic: true
|
||||
restore_mode: RESTORE_DEFAULT_OFF
|
||||
on_turn_on:
|
||||
- switch.turn_on: voice_support_active
|
||||
- micro_wake_word.start:
|
||||
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);
|
||||
- micro_wake_word.stop:
|
||||
|
||||
# Global enable/disable for presence LED
|
||||
- platform: template
|
||||
@ -804,12 +893,12 @@ switch:
|
||||
|
||||
number:
|
||||
# Temperature offset:
|
||||
# A calibration from -7 to +3 for the temperature sensor
|
||||
# A calibration from -30 to +5 for the temperature sensor
|
||||
- platform: template
|
||||
name: "Temperature Offset"
|
||||
id: temperature_offset_setter
|
||||
min_value: -7
|
||||
max_value: 3
|
||||
min_value: -30
|
||||
max_value: 10
|
||||
step: 0.1
|
||||
lambda: |-
|
||||
return id(temperature_offset);
|
||||
@ -820,12 +909,12 @@ number:
|
||||
value: !lambda 'return float(x);'
|
||||
|
||||
# Humidity offset:
|
||||
# A calibration from -10 to +10 for the humidity sensor
|
||||
# A calibration from -20 to +20 for the humidity sensor
|
||||
- platform: template
|
||||
name: "Humidity Offset"
|
||||
id: humidity_offset_setter
|
||||
min_value: -10
|
||||
max_value: 10
|
||||
min_value: -20
|
||||
max_value: 20
|
||||
step: 0.1
|
||||
lambda: |-
|
||||
return id(humidity_offset);
|
||||
@ -923,7 +1012,6 @@ number:
|
||||
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.
|
||||
@ -1052,49 +1140,101 @@ select:
|
||||
distance_resolution:
|
||||
name: "LD2410C Distance Resolution"
|
||||
|
||||
text_sensor:
|
||||
# VOC Level
|
||||
- platform: template
|
||||
name: "VOC Level"
|
||||
lambda: |-
|
||||
int tvoc = id(sgp30_tvoc).state;
|
||||
if (tvoc < 65) return {"Excellent"};
|
||||
if (tvoc < 220) return {"Good"};
|
||||
if (tvoc < 660) return {"Moderate"};
|
||||
if (tvoc < 2200) return {"Poor"};
|
||||
return {"Unhealthy"};
|
||||
update_interval: 15s
|
||||
name: "Wake Word Selector"
|
||||
id: wake_word_selector
|
||||
options:
|
||||
- "Computer"
|
||||
- "Hey Jarvis"
|
||||
- "Hey Mycroft"
|
||||
- "Okay Nabu"
|
||||
- "Alexa"
|
||||
initial_option: "Computer"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
set_action:
|
||||
# Disable models that aren't selected
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x != "Computer";'
|
||||
then:
|
||||
- micro_wake_word.disable_model: mww_computer
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x != "Hey Jarvis";'
|
||||
then:
|
||||
- micro_wake_word.disable_model: mww_hey_jarvis
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x != "Hey Mycroft";'
|
||||
then:
|
||||
- micro_wake_word.disable_model: mww_hey_mycroft
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x != "Okay Nabu";'
|
||||
then:
|
||||
- micro_wake_word.disable_model: mww_okay_nabu
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x != "Alexa";'
|
||||
then:
|
||||
- micro_wake_word.disable_model: mww_alexa
|
||||
# Enable model we selected
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x == "Computer";'
|
||||
then:
|
||||
- micro_wake_word.enable_model: mww_computer
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x == "Hey Jarvis";'
|
||||
then:
|
||||
- micro_wake_word.enable_model: mww_hey_jarvis
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x == "Hey Mycroft";'
|
||||
then:
|
||||
- micro_wake_word.enable_model: mww_hey_mycroft
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x == "Okay Nabu";'
|
||||
then:
|
||||
- micro_wake_word.enable_model: mww_okay_nabu
|
||||
- if:
|
||||
condition:
|
||||
lambda: 'return x == "Alexa";'
|
||||
then:
|
||||
- micro_wake_word.enable_model: mww_alexa
|
||||
|
||||
# CO2 Level
|
||||
- platform: template
|
||||
name: "CO2 Level"
|
||||
lambda: |-
|
||||
int eco2 = id(sgp30_eco2).state;
|
||||
if (eco2 < 800) return {"Good"};
|
||||
if (eco2 < 1200) return {"Moderate"};
|
||||
if (eco2 < 2000) return {"Poor"};
|
||||
return {"Unhealthy"};
|
||||
update_interval: 15s
|
||||
|
||||
# IAQ Classification
|
||||
- platform: template
|
||||
name: "IAQ Classification"
|
||||
lambda: |-
|
||||
int iaq = id(iaq_index).state;
|
||||
if (iaq == 5) return {"Excellent"};
|
||||
if (iaq == 4) return {"Good"};
|
||||
if (iaq == 3) return {"Moderate"};
|
||||
if (iaq == 2) return {"Poor"};
|
||||
return {"Unhealthy"};
|
||||
update_interval: 15s
|
||||
|
||||
# Room Health
|
||||
- platform: template
|
||||
name: "Room Health"
|
||||
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
|
||||
name: "Wake Word Sensitivity"
|
||||
optimistic: true
|
||||
initial_option: Default
|
||||
restore_value: true
|
||||
options:
|
||||
- Default
|
||||
- More sensitive
|
||||
- Very sensitive
|
||||
set_action:
|
||||
# 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
|
||||
- lambda: |-
|
||||
if (x == "Default") {
|
||||
id(mww_computer).set_probability_cutoff(168); // 0.66 (default)
|
||||
id(mww_hey_jarvis).set_probability_cutoff(247); // 0.97 (default)
|
||||
id(mww_hey_mycroft).set_probability_cutoff(242); // 0.95 (default)
|
||||
id(mww_okay_nabu).set_probability_cutoff(217); // 0.85 (default)
|
||||
id(mww_alexa).set_probability_cutoff(217); // 0.85 (default)
|
||||
} else if (x == "More sensitive") {
|
||||
id(mww_computer).set_probability_cutoff(153); // 0.60
|
||||
id(mww_hey_jarvis).set_probability_cutoff(235); // 0.92
|
||||
id(mww_hey_mycroft).set_probability_cutoff(237); // 0.93
|
||||
id(mww_okay_nabu).set_probability_cutoff(176); // 0.69
|
||||
id(mww_alexa).set_probability_cutoff(176); // 0.69
|
||||
} else if (x == "Very sensitive") {
|
||||
id(mww_computer).set_probability_cutoff(138); // 0.54
|
||||
id(mww_hey_jarvis).set_probability_cutoff(212); // 0.83
|
||||
id(mww_hey_mycroft).set_probability_cutoff(230); // 0.90
|
||||
id(mww_okay_nabu).set_probability_cutoff(143); // 0.56
|
||||
id(mww_alexa).set_probability_cutoff(143); // 0.56
|
||||
}
|
139
utemp.yaml
139
utemp.yaml
@ -1,139 +0,0 @@
|
||||
---
|
||||
|
||||
#
|
||||
# utemp is a tiny sensor with a BMP280 and an SGP30 on it that I use for basic temperature/AQ monitoring
|
||||
# in my rack. It's here so I can use it as a package but I don't really expect anyone else to build one.
|
||||
#
|
||||
|
||||
esphome:
|
||||
name: utemp
|
||||
name_add_mac_suffix: true
|
||||
friendly_name: "µtemp Sensor"
|
||||
project:
|
||||
name: "Joshua Boniface.µtemp"
|
||||
version: "1.1"
|
||||
|
||||
dashboard_import:
|
||||
package_import_url: github://joshuaboniface/supersensor/utemp.yaml
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
restore_from_flash: true
|
||||
|
||||
preferences:
|
||||
flash_write_interval: 15sec
|
||||
|
||||
globals:
|
||||
- id: temperature_offset
|
||||
type: float
|
||||
restore_value: true
|
||||
initial_value: "0.0"
|
||||
|
||||
logger:
|
||||
level: INFO
|
||||
baud_rate: 0
|
||||
|
||||
api:
|
||||
reboot_timeout: 15min
|
||||
|
||||
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
|
||||
|
||||
i2c:
|
||||
- id: i2c_bus
|
||||
sda: GPIO5
|
||||
scl: GPIO4
|
||||
scan: true
|
||||
|
||||
sensor:
|
||||
- platform: sgp30
|
||||
eco2:
|
||||
name: "eCO2"
|
||||
accuracy_decimals: 1
|
||||
tvoc:
|
||||
name: "TVOC"
|
||||
accuracy_decimals: 1
|
||||
store_baseline: yes
|
||||
address: 0x58
|
||||
update_interval: 15s
|
||||
|
||||
- platform: bmp280_i2c
|
||||
temperature:
|
||||
name: "Temperature"
|
||||
oversampling: 16x
|
||||
filters:
|
||||
- offset: !lambda return id(temperature_offset);
|
||||
pressure:
|
||||
name: "Pressure"
|
||||
address: 0x76
|
||||
update_interval: 15s
|
||||
iir_filter: 16x
|
||||
|
||||
- platform: wifi_signal
|
||||
name: "WiFi Signal"
|
||||
update_interval: 60s
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: uptime
|
||||
name: "Uptime"
|
||||
update_interval: 60s
|
||||
entity_category: diagnostic
|
||||
|
||||
text_sensor:
|
||||
- platform: version
|
||||
name: "ESPHome Version"
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: wifi_info
|
||||
ip_address:
|
||||
name: "WiFi IP Address"
|
||||
ssid:
|
||||
name: "WiFi SSID"
|
||||
bssid:
|
||||
name: "WiFi BSSID"
|
||||
mac_address:
|
||||
name: "WiFi MAC Address"
|
||||
|
||||
button:
|
||||
- platform: restart
|
||||
name: "ESP8266 Restart"
|
||||
icon: mdi:power-cycle
|
||||
entity_category: diagnostic
|
||||
|
||||
- platform: factory_reset
|
||||
name: "ESP8266 Factory Reset"
|
||||
icon: mdi:restart-alert
|
||||
entity_category: diagnostic
|
||||
|
||||
number:
|
||||
# Temperature offset:
|
||||
# A calibration from -10 to +6 for the temperature sensor of the BMP280
|
||||
- platform: template
|
||||
name: "Temperature Offset"
|
||||
id: temperature_offset_setter
|
||||
min_value: -10
|
||||
max_value: 6
|
||||
step: 0.1
|
||||
lambda: |-
|
||||
return id(temperature_offset);
|
||||
set_action:
|
||||
then:
|
||||
- globals.set:
|
||||
id: temperature_offset
|
||||
value: !lambda 'return float(x);'
|
||||
|
Reference in New Issue
Block a user