Compare commits
21 Commits
e224044de3
...
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 |
168
README.md
168
README.md
@ -1,5 +1,8 @@
|
|||||||
# SuperSensor v2.x
|
# SuperSensor v2.x
|
||||||
|
|
||||||
|
**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
|
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
|
quality, and light sensor, built on an ESP32 with ESPHome, and inspired
|
||||||
heavily by the EverythingSmartHome Everything Presence One sensor and the
|
heavily by the EverythingSmartHome Everything Presence One sensor and the
|
||||||
@ -18,8 +21,7 @@ it bare if you like the "PCB on a wall" aesthetic.
|
|||||||
To Use:
|
To Use:
|
||||||
|
|
||||||
* Install the ESPHome configuration `supersensor.yaml` to a compatible ESP32 devkit (below).
|
* Install the ESPHome configuration `supersensor.yaml` to a compatible ESP32 devkit (below).
|
||||||
* Install the ESP32 and sensors into the custom PCB (if desired).
|
* Install the ESP32 and sensors into the custom PCB.
|
||||||
* [Optional] 3D Print the custom case.
|
|
||||||
* Power up the SuperSensor, connect to the WiFi AP, and connect it to your network.
|
* Power up the SuperSensor, connect to the WiFi AP, and connect it to your network.
|
||||||
* Install the SuperSensor somewhere that makes sense.
|
* Install the SuperSensor somewhere that makes sense.
|
||||||
* Add/adopt the SuperSensor to HomeAssistant using the automatic name.
|
* Add/adopt the SuperSensor to HomeAssistant using the automatic name.
|
||||||
@ -39,15 +41,15 @@ and [my update post on version 2.0](https://www.boniface.me/the-supersensor-2.0)
|
|||||||
|
|
||||||
## Major Changes from 1.x
|
## 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 was fairly accurate (internal heating and offset notwithstanding),
|
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
|
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
|
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.
|
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,
|
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.
|
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.
|
2. Replaced the SR602 PIR sensor with the AM312 PIR sensor.
|
||||||
|
|
||||||
@ -61,15 +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.
|
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.
|
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
|
## Parts List
|
||||||
|
|
||||||
| Qty | Component | Cost (2025/05 CAD, ex. shipping) | Links |
|
| 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 | 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 | SR602 | $0.81 | [AliExpress](https://www.aliexpress.com/item/1005001572550300.html) |
|
||||||
| 1 | TSL2591 | $4.59 | [AliExpress](https://www.aliexpress.com/item/1005008619462097.html) |
|
| 1 | TSL2591 | $4.59 | [AliExpress](https://www.aliexpress.com/item/1005008619462097.html) |
|
||||||
@ -78,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)* |
|
| 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) |
|
| 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 | 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 | 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** | | **$40.58** | |
|
||||||
| **TOTAL** | | **$33.64** | |
|
|
||||||
|
|
||||||
`*` Ensure you select the correct device on the page as it shows multiple options.
|
`*` 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.
|
`†` 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).
|
||||||
|
|
||||||
`‡` Providing a price is impossible due to the wide range of possible fillament types and brands, but should be negligible.
|
### 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
|
## Configurable Options
|
||||||
|
|
||||||
@ -109,17 +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
|
If enabled (the default), when overall presence is detected, the LEDs will
|
||||||
glow "white" at 15% power to signal presence.
|
glow "white" at 15% power to signal presence.
|
||||||
|
|
||||||
### Temperature Offset (selector, -10 to +5 @ 0.1, -5 default)
|
### Temperature Offset (selector, -30 to +10 @ 0.1, -5 default)
|
||||||
|
|
||||||
Allows calibration of the SHT45 temperature sensor with an offset from -10 to +5
|
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
|
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
|
to internal heating of the SHT45 by the ESP32, this defaults to -5; further
|
||||||
calibration may be needed for your sensors and environment.
|
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
|
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)
|
### PIR Hold Time (selector, 0 to +60 @ 5, 0 default)
|
||||||
|
|
||||||
@ -227,70 +271,28 @@ is likely not useful.
|
|||||||
|
|
||||||
## AQ Details
|
## AQ Details
|
||||||
|
|
||||||
The SuperSensor 2.x provides 2 base air quality sensors (numeric), from which
|
The SuperSensor 2.0 features an SGP41 air quality sensor by Sensirion. This is a powerful AQ
|
||||||
4 human-readable text sensors are derived.
|
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
|
The sensor provides two base readings: a VOC Index, and a NOx Index. These values are both
|
||||||
room, not specific contaminants or conditions. Because the SGP30 can only
|
floating references centered at 100 (VOC) and 1 (NOx), where that value represents "normal"
|
||||||
track TVOC and eCO2, we do not track particulates, CO, NOx, or CH2O, all
|
air over the previous 24 hours. These sensors are very useful for any sort of quick-change
|
||||||
of which are required for a full EPA (I)AQI score. This means the best
|
automations, e.g. turn on a fan if levels spike due to cooking.
|
||||||
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.
|
|
||||||
|
|
||||||
We also cannot really debate whether the BME680 is actually any more accurate
|
In addition, we leverage AirGradient's published forumulas to convert the VOC index into
|
||||||
in this regard, since their algorithms are proprietary and all that is exposed
|
actual VOC quantities, in both µg/m³ and ppb. While this may drift due to the sensor's regular
|
||||||
normally is a single resistance value, so in my opinion this is actually
|
internal recalibration, I feel that following what AirGradient does is sufficient enough
|
||||||
superior to that sensor anyways with two discrete datapoints (versus one),
|
for any real-world home usage. Further, we use a very rough conversion of the aforementioned
|
||||||
even if it does still seem limited when compared to dedicated AQ sensors.
|
VOC quantity into an eCO2 reading, using Isobutylene as a reference gas. These sensors are
|
||||||
And that is to say nothing of the issues with that sensor (constantly climbing
|
more useful for display purposes, to show the current levels in a room in a dashboard or
|
||||||
IAQ values over time, poor calibration, etc.).
|
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
|
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
|
||||||
#### IAQ Index (1-5)
|
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
|
||||||
The IAQ index is calculated based on the TVOC and eCO2 values from the SGP30
|
than an absolute reference over long periods (much to my own frustration but inevitable
|
||||||
sensor, to provide 5 levels of air quality. This corresponds approximately
|
begruding acceptance).
|
||||||
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").
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
441
supersensor.yaml
441
supersensor.yaml
@ -36,9 +36,13 @@ esphome:
|
|||||||
id(pir_presence).publish_state(false);
|
id(pir_presence).publish_state(false);
|
||||||
id(light_presence).publish_state(false);
|
id(light_presence).publish_state(false);
|
||||||
id(radar_presence).publish_state(false);
|
id(radar_presence).publish_state(false);
|
||||||
- light.turn_on:
|
- priority: -100
|
||||||
id: output_led
|
then:
|
||||||
effect: flash_white
|
- if:
|
||||||
|
condition:
|
||||||
|
- switch.is_on: enable_voice_support
|
||||||
|
then:
|
||||||
|
- micro_wake_word.start:
|
||||||
|
|
||||||
preferences:
|
preferences:
|
||||||
flash_write_interval: 15sec
|
flash_write_interval: 15sec
|
||||||
@ -99,6 +103,11 @@ globals:
|
|||||||
restore_value: no
|
restore_value: no
|
||||||
initial_value: "0"
|
initial_value: "0"
|
||||||
|
|
||||||
|
- id: current_wake_word
|
||||||
|
type: std::string
|
||||||
|
restore_value: yes
|
||||||
|
initial_value: '"mww_computer"'
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: light_off
|
- id: light_off
|
||||||
then:
|
then:
|
||||||
@ -249,6 +258,17 @@ script:
|
|||||||
}
|
}
|
||||||
|
|
||||||
interval:
|
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
|
# Regular occupancy state reporting to HASS every 30s
|
||||||
- interval: 30s
|
- interval: 30s
|
||||||
then:
|
then:
|
||||||
@ -269,37 +289,37 @@ interval:
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
level: WARN
|
level: DEBUG
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
|
||||||
api:
|
api:
|
||||||
reboot_timeout: 15min
|
reboot_timeout: 15min
|
||||||
on_client_connected:
|
# on_client_connected:
|
||||||
- logger.log:
|
# - logger.log:
|
||||||
format: "Client %s (IP %s) connected to API"
|
# format: "Client %s (IP %s) connected to API"
|
||||||
args: ["client_info.c_str()", "client_address.c_str()"]
|
# args: ["client_info.c_str()", "client_address.c_str()"]
|
||||||
- script.execute: light_off
|
# - script.execute: light_off
|
||||||
- if:
|
# - if:
|
||||||
condition:
|
# condition:
|
||||||
lambda: |-
|
# lambda: |-
|
||||||
return id(enable_voice_support).state &&
|
# return id(enable_voice_support).state &&
|
||||||
!id(mww).is_running();
|
# !id(mww).is_running();
|
||||||
then:
|
# then:
|
||||||
- micro_wake_word.start:
|
# - micro_wake_word.start:
|
||||||
on_client_disconnected:
|
# on_client_disconnected:
|
||||||
- logger.log:
|
# - logger.log:
|
||||||
format: "Client %s (IP %s) disconnected from API"
|
# format: "Client %s (IP %s) disconnected from API"
|
||||||
args: ["client_info.c_str()", "client_address.c_str()"]
|
# args: ["client_info.c_str()", "client_address.c_str()"]
|
||||||
- if:
|
# - if:
|
||||||
condition:
|
# condition:
|
||||||
lambda: |-
|
# lambda: |-
|
||||||
return id(enable_voice_support).state &&
|
# return id(enable_voice_support).state &&
|
||||||
id(mww).is_running();
|
# id(mww).is_running();
|
||||||
then:
|
# then:
|
||||||
- micro_wake_word.stop:
|
# - micro_wake_word.stop:
|
||||||
- light.turn_on:
|
# - light.turn_on:
|
||||||
id: output_led
|
# id: output_led
|
||||||
effect: flash_white
|
# effect: flash_white
|
||||||
|
|
||||||
ota:
|
ota:
|
||||||
platform: esphome
|
platform: esphome
|
||||||
@ -326,6 +346,9 @@ time:
|
|||||||
then:
|
then:
|
||||||
- logger.log: "Time synchronized with Home Assistant"
|
- logger.log: "Time synchronized with Home Assistant"
|
||||||
|
|
||||||
|
debug:
|
||||||
|
update_interval: 15s
|
||||||
|
|
||||||
uart:
|
uart:
|
||||||
id: ld2410_uart
|
id: ld2410_uart
|
||||||
rx_pin: GPIO19
|
rx_pin: GPIO19
|
||||||
@ -360,9 +383,11 @@ micro_wake_word:
|
|||||||
id: mww
|
id: mww
|
||||||
microphone:
|
microphone:
|
||||||
microphone: mic
|
microphone: mic
|
||||||
gain_factor: 31
|
gain_factor: 8
|
||||||
stop_after_detection: false
|
stop_after_detection: false
|
||||||
models:
|
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
|
- model: github://esphome/micro-wake-word-models/models/v2/hey_jarvis.json
|
||||||
id: mww_hey_jarvis
|
id: mww_hey_jarvis
|
||||||
- model: github://esphome/micro-wake-word-models/models/v2/hey_mycroft.json
|
- model: github://esphome/micro-wake-word-models/models/v2/hey_mycroft.json
|
||||||
@ -389,7 +414,7 @@ voice_assistant:
|
|||||||
use_wake_word: false
|
use_wake_word: false
|
||||||
noise_suppression_level: 3
|
noise_suppression_level: 3
|
||||||
auto_gain: 31 dbfs
|
auto_gain: 31 dbfs
|
||||||
volume_multiplier: 8
|
volume_multiplier: 4
|
||||||
on_wake_word_detected:
|
on_wake_word_detected:
|
||||||
- logger.log: "Wake word detected in VA pipeline"
|
- logger.log: "Wake word detected in VA pipeline"
|
||||||
- light.turn_on:
|
- light.turn_on:
|
||||||
@ -413,7 +438,7 @@ voice_assistant:
|
|||||||
brightness: 75%
|
brightness: 75%
|
||||||
red: 0
|
red: 0
|
||||||
green: 1
|
green: 1
|
||||||
blue: 1
|
blue: 1
|
||||||
on_tts_start:
|
on_tts_start:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
@ -511,34 +536,75 @@ sensor:
|
|||||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
|
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
|
||||||
entity_category: diagnostic
|
entity_category: diagnostic
|
||||||
|
|
||||||
- platform: sgp30
|
- platform: debug
|
||||||
eco2:
|
free:
|
||||||
name: "SGP30 eCO2"
|
name: "Heap Free"
|
||||||
id: sgp30_eco2
|
block:
|
||||||
accuracy_decimals: 1
|
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:
|
filters:
|
||||||
- sliding_window_moving_average:
|
- sliding_window_moving_average:
|
||||||
window_size: 20
|
window_size: 12
|
||||||
send_every: 1
|
send_every: 3
|
||||||
tvoc:
|
|
||||||
name: "SGP30 TVOC"
|
|
||||||
id: sgp30_tvoc
|
|
||||||
accuracy_decimals: 1
|
|
||||||
filters:
|
|
||||||
- sliding_window_moving_average:
|
|
||||||
window_size: 20
|
|
||||||
send_every: 1
|
|
||||||
eco2_baseline:
|
|
||||||
name: "SGP30 Baseline eCO2"
|
|
||||||
id: sgp30_baseline_ec02
|
|
||||||
tvoc_baseline:
|
|
||||||
name: "SGP30 Baseline TVOC"
|
|
||||||
id: sgp30_baseline_tvoc
|
|
||||||
compensation:
|
compensation:
|
||||||
temperature_source: sht45_temperature
|
temperature_source: sht45_temperature
|
||||||
humidity_source: sht45_humidity
|
humidity_source: sht45_humidity
|
||||||
store_baseline: yes
|
store_baseline: true
|
||||||
update_interval: 15s
|
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
|
- platform: sht4x
|
||||||
temperature:
|
temperature:
|
||||||
@ -548,7 +614,7 @@ sensor:
|
|||||||
filters:
|
filters:
|
||||||
- offset: !lambda return id(temperature_offset);
|
- offset: !lambda return id(temperature_offset);
|
||||||
- sliding_window_moving_average:
|
- sliding_window_moving_average:
|
||||||
window_size: 4
|
window_size: 20
|
||||||
send_every: 1
|
send_every: 1
|
||||||
humidity:
|
humidity:
|
||||||
name: "SHT45 Relative Humidity"
|
name: "SHT45 Relative Humidity"
|
||||||
@ -568,9 +634,9 @@ sensor:
|
|||||||
humidity: sht45_humidity
|
humidity: sht45_humidity
|
||||||
id: sht45_absolute_humidity
|
id: sht45_absolute_humidity
|
||||||
|
|
||||||
# Dew Point
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "SHT45 Dew Point"
|
name: "SHT45 Dew Point"
|
||||||
|
icon: mdi:thermometer-water
|
||||||
id: sht45_dew_point
|
id: sht45_dew_point
|
||||||
unit_of_measurement: "°C"
|
unit_of_measurement: "°C"
|
||||||
lambda: |-
|
lambda: |-
|
||||||
@ -582,47 +648,38 @@ sensor:
|
|||||||
return (b * alpha) / (a - alpha);
|
return (b * alpha) / (a - alpha);
|
||||||
update_interval: 15s
|
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 > 500) return 4; // Good
|
|
||||||
return 5; // Excellent
|
|
||||||
update_interval: 15s
|
|
||||||
|
|
||||||
# Room Health Score (1-4, 4=Optimal)
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Room Health Score"
|
name: "Room Health Score"
|
||||||
id: room_health
|
id: room_health_score
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
accuracy_decimals: 0
|
||||||
|
icon: mdi:home-heart
|
||||||
lambda: |-
|
lambda: |-
|
||||||
|
float voc_index = id(sgp41_voc_index).state;
|
||||||
float temp = id(sht45_temperature).state;
|
float temp = id(sht45_temperature).state;
|
||||||
float rh = id(sht45_humidity).state;
|
float humidity = id(sht45_humidity).state;
|
||||||
int iaq = id(iaq_index).state;
|
|
||||||
|
// VOC Score (0–100)
|
||||||
bool temp_ok = (temp >= 18 && temp <= 24);
|
float voc_score = 0;
|
||||||
bool hum_ok = (rh >= 40 && rh <= 60);
|
if (voc_index <= 100) voc_score = 100;
|
||||||
bool iaq_ok = (iaq >= 4);
|
else if (voc_index <= 200) voc_score = 80;
|
||||||
|
else if (voc_index <= 300) voc_score = 60;
|
||||||
int conditions_met = 0;
|
else if (voc_index <= 400) voc_score = 40;
|
||||||
if (temp_ok) conditions_met++;
|
else if (voc_index <= 500) voc_score = 50;
|
||||||
if (hum_ok) conditions_met++;
|
else voc_score = 0;
|
||||||
if (iaq_ok) conditions_met++;
|
|
||||||
|
// Temperature Score (0–100)
|
||||||
if (iaq_ok && temp_ok && hum_ok) {
|
float temp_score = 100.0 - abs(temp - 23.0) * 10.0;
|
||||||
return 4; // Optimal: All conditions met and IAQ is excellent/good
|
if (temp_score < 0) temp_score = 0;
|
||||||
} else if (iaq >= 3 && conditions_met >= 2) {
|
|
||||||
return 3; // Fair: IAQ is moderate and at least 2 conditions met
|
// Humidity Score (0–100), ideal range 35–55%
|
||||||
} else if (iaq >= 2 && conditions_met >= 1) {
|
float humidity_score = 100.0 - abs(humidity - 50.0) * 3.0;
|
||||||
return 2; // Poor: IAQ is poor and at least 1 condition met
|
if (humidity_score < 0) humidity_score = 0;
|
||||||
} else {
|
|
||||||
return 1; // Bad: All conditions failed or IAQ is unhealthy
|
// Weighted average
|
||||||
}
|
float overall_score = (voc_score * 0.5 + temp_score * 0.25 + humidity_score * 0.25);
|
||||||
|
|
||||||
|
return round(overall_score);
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
||||||
- platform: tsl2591
|
- platform: tsl2591
|
||||||
@ -675,10 +732,9 @@ binary_sensor:
|
|||||||
name: "SuperSensor Occupancy"
|
name: "SuperSensor Occupancy"
|
||||||
id: supersensor_occupancy
|
id: supersensor_occupancy
|
||||||
device_class: occupancy
|
device_class: occupancy
|
||||||
on_press:
|
on_state:
|
||||||
- script.execute: light_off
|
then:
|
||||||
on_release:
|
- script.execute: light_off
|
||||||
- script.execute: light_off
|
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: "PIR GPIO"
|
name: "PIR GPIO"
|
||||||
@ -727,6 +783,10 @@ binary_sensor:
|
|||||||
name: "LD2410C Still Target"
|
name: "LD2410C Still Target"
|
||||||
|
|
||||||
text_sensor:
|
text_sensor:
|
||||||
|
- platform: version
|
||||||
|
name: "ESPHome Version"
|
||||||
|
entity_category: diagnostic
|
||||||
|
|
||||||
- platform: wifi_info
|
- platform: wifi_info
|
||||||
ip_address:
|
ip_address:
|
||||||
name: "WiFi IP Address"
|
name: "WiFi IP Address"
|
||||||
@ -737,57 +797,44 @@ text_sensor:
|
|||||||
mac_address:
|
mac_address:
|
||||||
name: "WiFi MAC Address"
|
name: "WiFi MAC Address"
|
||||||
|
|
||||||
|
- platform: debug
|
||||||
|
device:
|
||||||
|
name: "Device Info"
|
||||||
|
reset_reason:
|
||||||
|
name: "Reset Reason"
|
||||||
|
|
||||||
- platform: ld2410
|
- platform: ld2410
|
||||||
version:
|
version:
|
||||||
name: "LD2410C Firmware Version"
|
name: "LD2410C Firmware Version"
|
||||||
mac_address:
|
mac_address:
|
||||||
name: "LD2410C MAC Address"
|
name: "LD2410C MAC Address"
|
||||||
|
|
||||||
# VOC Level
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "VOC Level"
|
name: "Chemical Pollution"
|
||||||
|
id: sgp41_chemical_pollution
|
||||||
|
icon: mdi:molecule
|
||||||
lambda: |-
|
lambda: |-
|
||||||
int tvoc = id(sgp30_tvoc).state;
|
float voc_index = id(sgp41_voc_index).state;
|
||||||
if (tvoc < 65) return {"Excellent"};
|
if (voc_index < 1 || voc_index > 500) return {"Unknown"};
|
||||||
if (tvoc < 220) return {"Good"};
|
if (voc_index <= 100) return {"Excellent"};
|
||||||
if (tvoc < 660) return {"Moderate"};
|
else if (voc_index <= 200) return {"Good"};
|
||||||
if (tvoc < 2200) return {"Poor"};
|
else if (voc_index <= 300) return {"Moderate"};
|
||||||
return {"Unhealthy"};
|
else if (voc_index <= 400) return {"Unhealthy"};
|
||||||
|
else return {"Hazardous"};
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
||||||
# CO2 Level
|
|
||||||
- platform: template
|
|
||||||
name: "CO2 Level"
|
|
||||||
lambda: |-
|
|
||||||
int eco2 = id(sgp30_eco2).state;
|
|
||||||
if (eco2 < 500) return {"Excellent"};
|
|
||||||
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
|
- platform: template
|
||||||
name: "Room Health"
|
name: "Room Health"
|
||||||
|
id: room_health_text
|
||||||
|
icon: mdi:home-heart
|
||||||
lambda: |-
|
lambda: |-
|
||||||
int score = id(room_health).state;
|
float score = id(room_health_score).state;
|
||||||
if (score == 4) return {"Optimal"};
|
if (score < 0) return {"Unknown"};
|
||||||
if (score == 3) return {"Fair"};
|
else if (score >= 90.0) return {"Great"};
|
||||||
if (score == 2) return {"Poor"};
|
else if (score >= 80.0) return {"Good"};
|
||||||
return {"Bad"};
|
else if (score >= 60.0) return {"Fair"};
|
||||||
|
else if (score >= 40.0) return {"Poor"};
|
||||||
|
else return {"Bad"};
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
||||||
button:
|
button:
|
||||||
@ -1094,34 +1141,100 @@ select:
|
|||||||
name: "LD2410C Distance Resolution"
|
name: "LD2410C Distance Resolution"
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Wake word sensitivity"
|
name: "Wake Word Selector"
|
||||||
optimistic: true
|
id: wake_word_selector
|
||||||
initial_option: Moderately sensitive
|
|
||||||
restore_value: true
|
|
||||||
entity_category: config
|
|
||||||
options:
|
options:
|
||||||
- Slightly sensitive
|
- "Computer"
|
||||||
- Moderately sensitive
|
- "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
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: "Wake Word Sensitivity"
|
||||||
|
optimistic: true
|
||||||
|
initial_option: Default
|
||||||
|
restore_value: true
|
||||||
|
options:
|
||||||
|
- Default
|
||||||
|
- More sensitive
|
||||||
- Very sensitive
|
- Very sensitive
|
||||||
on_value:
|
set_action:
|
||||||
# Sets specific wake word probabilities computed for each particular model
|
# 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
|
# Note probability cutoffs are set as a quantized uint8 value, each comment has the corresponding floating point cutoff
|
||||||
# False Accepts per Hour values are tested against all units and channels from the Dinner Party Corpus.
|
- lambda: |-
|
||||||
# These cutoffs apply only to the specific models included in the firmware: okay_nabu@20241226.3, hey_jarvis@v2, hey_mycroft@v2
|
if (x == "Default") {
|
||||||
lambda: |-
|
id(mww_computer).set_probability_cutoff(168); // 0.66 (default)
|
||||||
if (x == "Slightly sensitive") {
|
id(mww_hey_jarvis).set_probability_cutoff(247); // 0.97 (default)
|
||||||
id(mww_hey_jarvis).set_probability_cutoff(247); // 0.97 -> 0.563 FAPH on DipCo (Manifest's default)
|
id(mww_hey_mycroft).set_probability_cutoff(242); // 0.95 (default)
|
||||||
id(mww_hey_mycroft).set_probability_cutoff(253); // 0.99 -> 0.567 FAPH on DipCo
|
id(mww_okay_nabu).set_probability_cutoff(217); // 0.85 (default)
|
||||||
id(mww_okay_nabu).set_probability_cutoff(217); // 0.85 -> 0.000 FAPH on DipCo (Manifest's default)
|
id(mww_alexa).set_probability_cutoff(217); // 0.85 (default)
|
||||||
id(mww_alexa).set_probability_cutoff(217); // 0.85 -> 0.000 FAPH on DipCo (Manifest's default)
|
} else if (x == "More sensitive") {
|
||||||
} else if (x == "Moderately sensitive") {
|
id(mww_computer).set_probability_cutoff(153); // 0.60
|
||||||
id(mww_hey_jarvis).set_probability_cutoff(235); // 0.92 -> 0.939 FAPH on DipCo
|
id(mww_hey_jarvis).set_probability_cutoff(235); // 0.92
|
||||||
id(mww_hey_mycroft).set_probability_cutoff(242); // 0.95 -> 1.502 FAPH on DipCo (Manifest's default)
|
id(mww_hey_mycroft).set_probability_cutoff(237); // 0.93
|
||||||
id(mww_okay_nabu).set_probability_cutoff(176); // 0.69 -> 0.376 FAPH on DipCo
|
id(mww_okay_nabu).set_probability_cutoff(176); // 0.69
|
||||||
id(mww_alexa).set_probability_cutoff(176); // 0.69 -> 0.376 FAPH on DipCo
|
id(mww_alexa).set_probability_cutoff(176); // 0.69
|
||||||
} else if (x == "Very sensitive") {
|
} else if (x == "Very sensitive") {
|
||||||
id(mww_hey_jarvis).set_probability_cutoff(212); // 0.83 -> 1.502 FAPH on DipCo
|
id(mww_computer).set_probability_cutoff(138); // 0.54
|
||||||
id(mww_hey_mycroft).set_probability_cutoff(237); // 0.93 -> 1.878 FAPH on DipCo
|
id(mww_hey_jarvis).set_probability_cutoff(212); // 0.83
|
||||||
id(mww_okay_nabu).set_probability_cutoff(143); // 0.56 -> 0.751 FAPH on DipCo
|
id(mww_hey_mycroft).set_probability_cutoff(230); // 0.90
|
||||||
id(mww_alexa).set_probability_cutoff(143); // 0.56 -> 0.751 FAPH on DipCo
|
id(mww_okay_nabu).set_probability_cutoff(143); // 0.56
|
||||||
}
|
id(mww_alexa).set_probability_cutoff(143); // 0.56
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user