Add customizable room health options and tweak
Permits the user customization of the parameters of the Room Health Score, enabling fine-grained control over this metric instead of hardcoding values that may not be suitable for each person or room.
This commit is contained in:
308
README.md
308
README.md
@@ -118,144 +118,7 @@ functionality, so I recommend using the provided models, but this is up to the b
|
|||||||
|
|
||||||
No other parts can be easily swapped without code or PCB design changes.
|
No other parts can be easily swapped without code or PCB design changes.
|
||||||
|
|
||||||
## Configurable Options
|
## Air Quality Handling
|
||||||
|
|
||||||
There are several UI-configurable options with the SuperSensor to help you
|
|
||||||
get the most out of the sensor for your particular use-case.
|
|
||||||
|
|
||||||
**Note:** Configuration of the LD2410C is excluded here, as it is extensively
|
|
||||||
configurable. See [the documentation](https://esphome.io/components/sensor/ld2410.html) for more details on its options.
|
|
||||||
|
|
||||||
### Enable Voice Support (switch)
|
|
||||||
|
|
||||||
If enabled (the default), the SuperSensor's voice functionality including
|
|
||||||
wake word will be started, or it can be disabled to use the SuperSensor
|
|
||||||
purely as a presence/environmental sensor.
|
|
||||||
|
|
||||||
### Enable Presence LED (switch)
|
|
||||||
|
|
||||||
If enabled (the default), when overall presence is detected, the LEDs will
|
|
||||||
glow "white" at 15% power to signal presence.
|
|
||||||
|
|
||||||
### Temperature Offset (selector, -30 to +10 @ 0.1, -5 default)
|
|
||||||
|
|
||||||
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, -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
|
|
||||||
based on an external reference.
|
|
||||||
|
|
||||||
### PIR Hold Time (selector, 0 to +60 @ 5, 0 default)
|
|
||||||
|
|
||||||
The SuperSensor uses an AM312 PIR sensor, which has a stock hold time of ~2.5
|
|
||||||
seconds. This setting allows increasing that value, with retrigger support, to
|
|
||||||
up to 60 seconds, allowing the PIR detection to report for longer. 0 represents
|
|
||||||
"as long as the AM312 fires".
|
|
||||||
|
|
||||||
### Light Threshold Control (selector, 0 to +200 @ 5, 30 default)
|
|
||||||
|
|
||||||
The SuperSensor features a "light presence" binary sensor based on the light
|
|
||||||
level reported by the TSL2591 sensor. This control defines the minimum lux
|
|
||||||
value from the sensor to be considered "presence". For instance, if you have
|
|
||||||
a room that is usually dark at 0-5 lux, but illuminated to 100 lux when a
|
|
||||||
(non-automated) light switch is turned on, you could set a threshold here
|
|
||||||
of say 30 lux: then, while the light is on, "light presence" is detected,
|
|
||||||
and when the light is off, "light presence" is cleared. Light presence can
|
|
||||||
be used standalone or as part of the integrated occupancy sensor (below).
|
|
||||||
|
|
||||||
### Integrated Occupancy Sensor (Selector)
|
|
||||||
|
|
||||||
The SuperSensor features a fully integrated "occupancy" sensor, which can be
|
|
||||||
configured to provide exactly the sort of occupancy detection you may want
|
|
||||||
for your room.
|
|
||||||
|
|
||||||
There are 7 options (plus "None"/disabled), with both "detect" and "clear"
|
|
||||||
handled separately:
|
|
||||||
|
|
||||||
#### PIR + Radar + Light
|
|
||||||
|
|
||||||
Occupancy is detected when all 3 sensors report detected, and occupancy is
|
|
||||||
cleared when any of the sensors report cleared.
|
|
||||||
|
|
||||||
For detect, this provides the most "safety" against misfires, but requires
|
|
||||||
a normally-dark room with a non-automated light source and clear PIR
|
|
||||||
detection positioning.
|
|
||||||
|
|
||||||
For clear, this option is probably not very useful as it is likely to clear
|
|
||||||
quite frequently from the PIR, but is provided for completeness.
|
|
||||||
|
|
||||||
#### PIR + Radar
|
|
||||||
|
|
||||||
Occupancy is detected when both sensors report detected, and occupancy is
|
|
||||||
cleared when either of the sensors report cleared.
|
|
||||||
|
|
||||||
For detect, this provides good "safety" against PIR misfires without
|
|
||||||
needing a normally-dark room, though detection may be slightly delayed
|
|
||||||
from either sensor.
|
|
||||||
|
|
||||||
For clear, this option is probably not very useful as it is likely to clear
|
|
||||||
quite frequently from the PIR, but is provided for completeness.
|
|
||||||
|
|
||||||
#### PIR + Light
|
|
||||||
|
|
||||||
Occupancy is detected when both sensors report detected, and occupancy is
|
|
||||||
cleared when either of the sensors report cleared.
|
|
||||||
|
|
||||||
For detect, this provides some "safety" against PIR misfires, but requires
|
|
||||||
a normally-dark room with a non-automated light source and clear PIR
|
|
||||||
detection positioning.
|
|
||||||
|
|
||||||
For clear, this option is probably not very useful as it is likely to clear
|
|
||||||
quite frequently from the PIR, but is provided for completeness.
|
|
||||||
|
|
||||||
#### Radar + Light
|
|
||||||
|
|
||||||
Occupancy is detected when both sensors report detected, and occupancy is
|
|
||||||
cleared when either of the sensors report cleared.
|
|
||||||
|
|
||||||
For detect, this allows for radar detection while suppressing occupancy
|
|
||||||
without light, for instance in a hallway where one might not want a late
|
|
||||||
night bathroom visit to turn on the lights, or something to that effect.
|
|
||||||
|
|
||||||
For clear, this option can provide a useful option to clear presence
|
|
||||||
quickly if the lights go out, while still providing Radar presence.
|
|
||||||
|
|
||||||
#### PIR Only
|
|
||||||
|
|
||||||
Occupancy is based entirely on the PIR sensor for both detect and clear.
|
|
||||||
|
|
||||||
Prone to misfires, but otherwise a good option for quick detection and
|
|
||||||
clearance in a primarily-moving zone (e.g. hallway).
|
|
||||||
|
|
||||||
#### Radar Only
|
|
||||||
|
|
||||||
Occupancy is based entirely on the Radar sensor for both detect and clear.
|
|
||||||
|
|
||||||
Useful for an area with no consistent motion or light level.
|
|
||||||
|
|
||||||
#### Light Only
|
|
||||||
|
|
||||||
Occupancy is based entirely on the Light sensor for both detect and clear.
|
|
||||||
|
|
||||||
Useful for full dependence on an external light source.
|
|
||||||
|
|
||||||
#### None
|
|
||||||
|
|
||||||
Disable the functionality in either direction.
|
|
||||||
|
|
||||||
For detect, no occupancy will ever fire.
|
|
||||||
|
|
||||||
For clear, no states will clear occupancy; with any detect option, this
|
|
||||||
means that occupancy will be detected only once and never clear, which
|
|
||||||
is likely not useful.
|
|
||||||
|
|
||||||
## AQ Details
|
|
||||||
|
|
||||||
The SuperSensor 2.0 features an SGP41 air quality sensor by Sensirion. This is a powerful AQ
|
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
|
sensor which powers several commercial devices including the AirGradient One, which gave
|
||||||
@@ -283,3 +146,172 @@ It also reacts strongly to heavy humidity, resulting in higher values in such en
|
|||||||
These should be used only as a general indication of air quality over short periods, rather
|
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
|
than an absolute reference over long periods (much to my own frustration but inevitable
|
||||||
begruding acceptance).
|
begruding acceptance).
|
||||||
|
|
||||||
|
## Room Health
|
||||||
|
|
||||||
|
The SuperSensor 2.0 leverages the outputs of the SHT45 and SGP41 sensors to calculate a
|
||||||
|
"Room Health", expressed as a percentage, which represents how "healthy" i.e. comfortable
|
||||||
|
a room is for a person to be in.
|
||||||
|
|
||||||
|
The room health is calculated based on the VOC level, temperature, and relative humidity.
|
||||||
|
First, the raw value is converted to a per-sensor 100-0 scale as follows:
|
||||||
|
|
||||||
|
* For VOC levels, there is a set of linear scales based on common VOC level
|
||||||
|
mappings, such that less than 200 ppb is 100, 200-400 maps to 100-90,
|
||||||
|
400-600 maps to 90-70, 600-1500 maps to 70-40, 1500-3000 maps to 40-0, and
|
||||||
|
greater than 3000 is 0.
|
||||||
|
* For temperature and humidity, there is a single linear scale based on a
|
||||||
|
configurable penalty value, such that a value between the configurable
|
||||||
|
minimum and maximum is 100, and each degree C or %RH outside of that range
|
||||||
|
decreases the value by the penalty value.
|
||||||
|
|
||||||
|
Next, each indivdual per-sensor value is applied to the total 100-0 value by a configurable
|
||||||
|
weight, with the defaults being 40% to VOC level, 30% to temperature, and 30% to humidity. The
|
||||||
|
values can never total more than 100% or total to 0% but are otherwise normalized (i.e. decrease
|
||||||
|
others before increasing one, or the values will not be accepted; and at least one weight
|
||||||
|
must be >0).
|
||||||
|
|
||||||
|
The final result is thus a 100-0% range that, in broad strokes, describes the overall
|
||||||
|
health of the room. For some examples, assuming all of the default values below:
|
||||||
|
|
||||||
|
* Perfect: Temp 23C, humidity 50%RH, and VOC level 150ppb = 100% health
|
||||||
|
* A little warm: Temp 25C (+1), humidity 50%RH, and VOC level 250ppb = 97% health
|
||||||
|
* Dry: Temp 22C, humidity 30%RH (-10), VOC level 150ppb = 91% health
|
||||||
|
* Dirty air: Temp 23C, humidity 50%RH, VOC level 800ppb = 85% health
|
||||||
|
* Hot & humid: Temp 28C, humidity 70%RH, VOC level 250ppb = 84% health
|
||||||
|
* All-around bad: Temp 30C, humidity 30%RH, VOC level 2000ppb = 52% health
|
||||||
|
|
||||||
|
These are then mapped to textual values as well with the following bands:
|
||||||
|
|
||||||
|
* 100%-95%: Great
|
||||||
|
* 95%-90%: Good
|
||||||
|
* 90%-80%: Fair
|
||||||
|
* 80%-60%: Poor
|
||||||
|
* 60%-0%: Bad
|
||||||
|
|
||||||
|
As mentioned above, most portions of this are configurable; see the section below for
|
||||||
|
specific details of each configuration value.
|
||||||
|
|
||||||
|
## Configurable Options
|
||||||
|
|
||||||
|
There are several UI-configurable options with the SuperSensor to help you
|
||||||
|
get the most out of the sensor for your particular use-case.
|
||||||
|
|
||||||
|
**Note:** Configuration of the LD2410C is excluded here, as it is extensively
|
||||||
|
configurable. See [the documentation](https://esphome.io/components/sensor/ld2410.html) for more details on its options.
|
||||||
|
|
||||||
|
### Enable Voice Support (switch)
|
||||||
|
|
||||||
|
If enabled (the default), the SuperSensor's voice functionality including
|
||||||
|
wake word will be started, or it can be disabled to use the SuperSensor
|
||||||
|
purely as a presence/environmental sensor.
|
||||||
|
|
||||||
|
### Enable Presence LED (switch)
|
||||||
|
|
||||||
|
If enabled (the default), when overall presence is detected, the LEDs will
|
||||||
|
glow "white" at 15% power to signal presence.
|
||||||
|
|
||||||
|
### Temperature Offset (number, -30 to +10 @ 0.1, -5 default)
|
||||||
|
|
||||||
|
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 (number, -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
|
||||||
|
based on an external reference.
|
||||||
|
|
||||||
|
### PIR Hold Time (number, 0 to +60 @ 5, 0 default)
|
||||||
|
|
||||||
|
The SuperSensor uses an AM312 PIR sensor, which has a stock hold time of ~2.5
|
||||||
|
seconds. This setting allows increasing that value, with retrigger support, to
|
||||||
|
up to 60 seconds, allowing the PIR detection to report for longer. 0 represents
|
||||||
|
"as long as the AM312 fires".
|
||||||
|
|
||||||
|
### Light Threshold Control (number, 0 to +200 @ 5, 30 default)
|
||||||
|
|
||||||
|
The SuperSensor features a "light presence" binary sensor based on the light
|
||||||
|
level reported by the TSL2591 sensor. This control defines the minimum lux
|
||||||
|
value from the sensor to be considered "presence". For instance, if you have
|
||||||
|
a room that is usually dark at 0-5 lux, but illuminated to 100 lux when a
|
||||||
|
(non-automated) light switch is turned on, you could set a threshold here
|
||||||
|
of say 30 lux: then, while the light is on, "light presence" is detected,
|
||||||
|
and when the light is off, "light presence" is cleared. Light presence can
|
||||||
|
be used standalone or as part of the integrated occupancy sensor (below).
|
||||||
|
|
||||||
|
### Integrated Occupancy Sensor (selector)
|
||||||
|
|
||||||
|
The SuperSensor features a fully integrated "occupancy" sensor, which can be
|
||||||
|
configured to provide exactly the sort of occupancy detection you may want
|
||||||
|
for your room.
|
||||||
|
|
||||||
|
There are 7 options (plus "None"/disabled), with both "detect" and "clear"
|
||||||
|
handled separately. Occupancy is always detected when ALL of the selected
|
||||||
|
sensors report detection, and occupancy is always cleared when ANY of the
|
||||||
|
selected sensors stop reporting detection (logical AND in, logical OR out).
|
||||||
|
|
||||||
|
* PIR + Radar + Light
|
||||||
|
* PIR + Radar
|
||||||
|
* PIR + Light
|
||||||
|
* Radar + Light
|
||||||
|
* PIR Only
|
||||||
|
* Radar Only
|
||||||
|
* Light Only
|
||||||
|
* None
|
||||||
|
|
||||||
|
### Room Health Sensor
|
||||||
|
|
||||||
|
#### Minimum Temperature (number, 15 to 30 @ 0.5, 21 default)
|
||||||
|
|
||||||
|
The lower bounds of a fully comfortable temperature; temperature values below
|
||||||
|
this value will begin decreasing the room health score.
|
||||||
|
|
||||||
|
#### Maximum Temperature (number, 15 to 30 @ 0.5, 24 default)
|
||||||
|
|
||||||
|
The upper bounds of a fully comfortable temperature; temperature values above
|
||||||
|
this value will begin decreasing the room health score.
|
||||||
|
|
||||||
|
#### Temperature Penalty (number, 1 to 20 @ 1, 10 default)
|
||||||
|
|
||||||
|
The penalty value per degree of temperature deviation from ideal levels, applied
|
||||||
|
to the pre-weighting value for temperature.
|
||||||
|
|
||||||
|
#### Minimum Humidity (number, 20 to 80 @ 1, 40 default)
|
||||||
|
|
||||||
|
The lower bounds of a fully comfortable relative humidity level; relative
|
||||||
|
humidity values below this value will begin decreasing the room health score.
|
||||||
|
|
||||||
|
#### Maximum Humidity (number, 20 to 80 @ 1, 60 default)
|
||||||
|
|
||||||
|
The upper bounds of a fully comfortable relative humidity level; relative
|
||||||
|
humidity values above this value will begin decreasing the room health score.
|
||||||
|
|
||||||
|
#### Humidity Penalty (number, 1 to 10 @ 1, 5 default)
|
||||||
|
|
||||||
|
The penalty value per % of relative humidity deviation from ideal levels, applied
|
||||||
|
to the pre-weighting value for humidity.
|
||||||
|
|
||||||
|
#### VOC Weight (number, 0.0 to 1.0, 0.4 default)
|
||||||
|
|
||||||
|
The weighting value of the VOC score relative to the other two for calculating
|
||||||
|
the total room health.
|
||||||
|
|
||||||
|
Note: Cannot exceed 0.4 without first decreasing one of the other weights (total max of 1.0).
|
||||||
|
|
||||||
|
#### Temperature Weight (number, 0.0 to 1.0, 0.3 default)
|
||||||
|
|
||||||
|
The weighting value of the Temperature score relative to the other two for
|
||||||
|
calculating the total room health.
|
||||||
|
|
||||||
|
Note: Cannot exceed 0.3 without first decreasing one of the other weights (total max of 1.0).
|
||||||
|
|
||||||
|
#### Humidity Weight (number, 0.0 to 1.0, 0.3 default)
|
||||||
|
|
||||||
|
The weighting value of the Humidity score relative to the other two for calculating
|
||||||
|
the total room health.
|
||||||
|
|
||||||
|
Note: Cannot exceed 0.3 without first decreasing one of the other weights (total max of 1.0).
|
||||||
|
275
supersensor.yaml
275
supersensor.yaml
@@ -89,6 +89,51 @@ globals:
|
|||||||
restore_value: true
|
restore_value: true
|
||||||
initial_value: "0.0"
|
initial_value: "0.0"
|
||||||
|
|
||||||
|
- id: room_health_temperature_min
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "21.0"
|
||||||
|
|
||||||
|
- id: room_health_temperature_max
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "24.0"
|
||||||
|
|
||||||
|
- id: room_health_temperature_penalty
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "10.0"
|
||||||
|
|
||||||
|
- id: room_health_humidity_min
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "40.0"
|
||||||
|
|
||||||
|
- id: room_health_humidity_max
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "60.0"
|
||||||
|
|
||||||
|
- id: room_health_humidity_penalty
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "5.0"
|
||||||
|
|
||||||
|
- id: room_health_voc_weight
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "0.4"
|
||||||
|
|
||||||
|
- id: room_health_temperature_weight
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "0.3"
|
||||||
|
|
||||||
|
- id: room_health_humidity_weight
|
||||||
|
type: float
|
||||||
|
restore_value: true
|
||||||
|
initial_value: "0.3"
|
||||||
|
|
||||||
- id: pir_hold_time
|
- id: pir_hold_time
|
||||||
type: int
|
type: int
|
||||||
restore_value: true
|
restore_value: true
|
||||||
@@ -665,27 +710,57 @@ sensor:
|
|||||||
float voc_index = id(sgp41_voc_index).state;
|
float voc_index = id(sgp41_voc_index).state;
|
||||||
float temp = id(sht45_temperature).state;
|
float temp = id(sht45_temperature).state;
|
||||||
float humidity = id(sht45_humidity).state;
|
float humidity = id(sht45_humidity).state;
|
||||||
|
|
||||||
// VOC Score (0–100)
|
float temp_min = id(room_health_temperature_min);
|
||||||
float voc_score = 0;
|
float temp_max = id(room_health_temperature_max);
|
||||||
if (voc_index <= 100) voc_score = 100;
|
float temp_penalty = id(room_health_temperature_penalty);
|
||||||
else if (voc_index <= 200) voc_score = 80;
|
float humid_min = id(room_health_humidity_min);
|
||||||
else if (voc_index <= 300) voc_score = 60;
|
float humid_max = id(room_health_humidity_max);
|
||||||
else if (voc_index <= 400) voc_score = 40;
|
float humid_penalty = id(room_health_humidity_penalty);
|
||||||
else if (voc_index <= 500) voc_score = 50;
|
float voc_weight = id(room_health_voc_weight);
|
||||||
else voc_score = 0;
|
float temp_weight = id(room_health_temperature_weight);
|
||||||
|
float humid_weight = id(room_health_humidity_weight);
|
||||||
// Temperature Score (0–100)
|
|
||||||
float temp_score = 100.0 - abs(temp - 23.0) * 10.0;
|
// VOC score (0–100) mapped to categories from Chemical Pollution levels below
|
||||||
|
float voc_score;
|
||||||
|
if (voc_index <= 200) {
|
||||||
|
voc_score = 100.0;
|
||||||
|
} else if (voc_index <= 400) {
|
||||||
|
// 200–400: 100 → 90
|
||||||
|
voc_score = 100.0 - (voc_index - 200) * (10.0 / 200.0);
|
||||||
|
} else if (voc_index <= 600) {
|
||||||
|
// 400–600: 90 → 70
|
||||||
|
voc_score = 90.0 - (voc_index - 400) * (20.0 / 200.0);
|
||||||
|
} else if (voc_index <= 1500) {
|
||||||
|
// 600–1500: 70 → 40
|
||||||
|
voc_score = 70.0 - (voc_index - 600) * (30.0 / 900.0);
|
||||||
|
} else if (voc_index <= 3000) {
|
||||||
|
// 1500–3000: 40 → 0
|
||||||
|
voc_score = 40.0 - (voc_index - 1500) * (40.0 / 1500.0);
|
||||||
|
} else {
|
||||||
|
voc_score = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temperature score
|
||||||
|
float temp_score = 100;
|
||||||
|
if (temp < temp_min) temp_score = 100 - (temp_min - temp) * temp_penalty;
|
||||||
|
else if (temp > temp_max) temp_score = 100 - (temp - temp_max) * temp_penalty;
|
||||||
if (temp_score < 0) temp_score = 0;
|
if (temp_score < 0) temp_score = 0;
|
||||||
|
|
||||||
// Humidity Score (0–100), ideal range 35–55%
|
// Humidity score
|
||||||
float humidity_score = 100.0 - abs(humidity - 50.0) * 3.0;
|
float humidity_score = 100;
|
||||||
|
if (humidity < humid_min) humidity_score = 100 - (humid_min - humidity) * humid_penalty;
|
||||||
|
else if (humidity > humid_max) humidity_score = 100 - (humidity - humid_max) * humid_penalty;
|
||||||
if (humidity_score < 0) humidity_score = 0;
|
if (humidity_score < 0) humidity_score = 0;
|
||||||
|
|
||||||
// Weighted average
|
// Weighted average
|
||||||
float overall_score = (voc_score * 0.5 + temp_score * 0.25 + humidity_score * 0.25);
|
float total_weights = voc_weight + temp_weight + humid_weight;
|
||||||
|
if (total_weights <= 0) total_weights = 1.0;
|
||||||
|
voc_weight /= total;
|
||||||
|
temp_weight /= total;
|
||||||
|
humid_weight /= total;
|
||||||
|
float overall_score = (voc_score * voc_weight + temp_score * temp_weight + humidity_score * humid_weight);
|
||||||
|
|
||||||
return (int) round(overall_score);
|
return (int) round(overall_score);
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
||||||
@@ -825,10 +900,10 @@ text_sensor:
|
|||||||
lambda: |-
|
lambda: |-
|
||||||
float voc_index = id(sgp41_voc_index).state;
|
float voc_index = id(sgp41_voc_index).state;
|
||||||
if (voc_index < 1 || voc_index > 500) return {"Unknown"};
|
if (voc_index < 1 || voc_index > 500) return {"Unknown"};
|
||||||
if (voc_index <= 100) return {"Excellent"};
|
if (voc_index <= 200) return {"Excellent"};
|
||||||
else if (voc_index <= 200) return {"Good"};
|
else if (voc_index <= 400) return {"Good"};
|
||||||
else if (voc_index <= 300) return {"Moderate"};
|
else if (voc_index <= 600) return {"Moderate"};
|
||||||
else if (voc_index <= 400) return {"Unhealthy"};
|
else if (voc_index <= 1500) return {"Unhealthy"};
|
||||||
else return {"Hazardous"};
|
else return {"Hazardous"};
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
||||||
@@ -839,10 +914,10 @@ text_sensor:
|
|||||||
lambda: |-
|
lambda: |-
|
||||||
float score = id(room_health_score).state;
|
float score = id(room_health_score).state;
|
||||||
if (score < 0) return {"Unknown"};
|
if (score < 0) return {"Unknown"};
|
||||||
else if (score >= 90.0) return {"Great"};
|
else if (score >= 95.0) return {"Great"};
|
||||||
else if (score >= 80.0) return {"Good"};
|
else if (score >= 90.0) return {"Good"};
|
||||||
else if (score >= 60.0) return {"Fair"};
|
else if (score >= 80.0) return {"Fair"};
|
||||||
else if (score >= 40.0) return {"Poor"};
|
else if (score >= 60.0) return {"Poor"};
|
||||||
else return {"Bad"};
|
else return {"Bad"};
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
||||||
@@ -965,6 +1040,154 @@ number:
|
|||||||
id: light_presence_threshold
|
id: light_presence_threshold
|
||||||
value: !lambda 'return int(x);'
|
value: !lambda 'return int(x);'
|
||||||
|
|
||||||
|
# Room Health Calibration Values
|
||||||
|
# These values allow the user to tweak the values of the room health calculation
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Min Temperature"
|
||||||
|
id: room_health_temperature_min_setter
|
||||||
|
min_value: 15
|
||||||
|
max_value: 30
|
||||||
|
step: 0.5
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_temperature_min);
|
||||||
|
set_action:
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_temperature_min
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Max Temperature"
|
||||||
|
id: room_health_temperature_max_setter
|
||||||
|
min_value: 15
|
||||||
|
max_value: 30
|
||||||
|
step: 0.5
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_temperature_max);
|
||||||
|
set_action:
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_temperature_max
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Temperature Penalty"
|
||||||
|
id: room_health_temperature_penalty_setter
|
||||||
|
min_value: 1
|
||||||
|
max_value: 20
|
||||||
|
step: 1
|
||||||
|
lambda: |-
|
||||||
|
return int(id(room_health_temperature_penalty));
|
||||||
|
set_action:
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_temperature_penalty
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Min Humidity"
|
||||||
|
id: room_health_humidity_min_setter
|
||||||
|
min_value: 20
|
||||||
|
max_value: 80
|
||||||
|
step: 1.0
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_humidity_min);
|
||||||
|
set_action:
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_humidity_min
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Max Humidity"
|
||||||
|
id: room_health_humidity_max_setter
|
||||||
|
min_value: 20
|
||||||
|
max_value: 80
|
||||||
|
step: 1.0
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_humidity_max);
|
||||||
|
set_action:
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_humidity_max
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Humidity Penalty"
|
||||||
|
id: room_health_humidity_penalty_setter
|
||||||
|
min_value: 1
|
||||||
|
max_value: 10
|
||||||
|
step: 1
|
||||||
|
lambda: |-
|
||||||
|
return int(id(room_health_humidity_penalty));
|
||||||
|
set_action:
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_humidity_penalty
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health VOC Weight"
|
||||||
|
id: room_health_voc_weight_setter
|
||||||
|
min_value: 0.00
|
||||||
|
max_value: 1.00
|
||||||
|
step: 0.01
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_voc_weight);
|
||||||
|
set_action:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
float total = x + id(room_health_temperature_weight) + id(room_health_humidity_weight);
|
||||||
|
return (total > 0.0) && (total <= 1.0);
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_voc_weight
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
else:
|
||||||
|
- logger.log:
|
||||||
|
format: "Rejected VOC weight %.2f (total would be out of range: must be > 0.0 and ≤ 1.0)"
|
||||||
|
args: [ 'x' ]
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Temperature Weight"
|
||||||
|
id: room_health_temperature_weight_setter
|
||||||
|
min_value: 0.00
|
||||||
|
max_value: 1.00
|
||||||
|
step: 0.01
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_temperature_weight);
|
||||||
|
set_action:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
float total = x + id(room_health_voc_weight) + id(room_health_humidity_weight);
|
||||||
|
return (total > 0.0) && (total <= 1.0);
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_temperature_weight
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
else:
|
||||||
|
- logger.log:
|
||||||
|
format: "Rejected Temperature weight %.2f (total would be out of range: must be > 0.0 and ≤ 1.0)"
|
||||||
|
args: [ 'x' ]
|
||||||
|
- platform: template:
|
||||||
|
name: "Room Health Humitity Weight"
|
||||||
|
id: room_health_humidity_weight_setter
|
||||||
|
min_value: 0.00
|
||||||
|
max_value: 1.00
|
||||||
|
step: 0.01
|
||||||
|
lambda: |-
|
||||||
|
return id(room_health_humidity_weight);
|
||||||
|
set_action:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
float total = x + id(room_health_temperature_weight) + id(room_health_voc_weight);
|
||||||
|
return (total > 0.0) && (total <= 1.0);
|
||||||
|
then:
|
||||||
|
- globals.set:
|
||||||
|
id: room_health_humidity_weight
|
||||||
|
value: !lambda 'return float(x);'
|
||||||
|
else:
|
||||||
|
- logger.log:
|
||||||
|
format: "Rejected Humidity weight %.2f (total would be out of range: must be > 0.0 and ≤ 1.0)"
|
||||||
|
args: [ 'x' ]
|
||||||
|
|
||||||
|
# LD2410c configuration values
|
||||||
- platform: ld2410
|
- platform: ld2410
|
||||||
timeout:
|
timeout:
|
||||||
name: "LD2410C Timeout"
|
name: "LD2410C Timeout"
|
||||||
|
Reference in New Issue
Block a user