ESPHome
2026.8.0-dev
Loading...
Searching...
No Matches
esphome
components
aqi
caqi_calculator.h
Go to the documentation of this file.
1
#pragma once
2
3
#include <algorithm>
4
#include <cmath>
5
#include <limits>
6
#include "
abstract_aqi_calculator.h
"
7
8
namespace
esphome::aqi
{
9
10
class
CAQICalculator
:
public
AbstractAQICalculator
{
11
public
:
12
// The CAQI (CITEAIR) scale defines no maximum: its top "Very high" class is simply ">100". We
13
// therefore always extrapolate the top band past 100 without limit, so the extended_range flag
14
// (which lifts the AQI calculator's fixed 500 cap) has no meaning here and is ignored.
15
uint16_t
get_aqi
(
float
pm2_5_value,
float
pm10_0_value,
bool
/*extended_range*/
)
override
{
16
float
pm2_5_index =
calculate_index
(pm2_5_value,
PM2_5_GRID
);
17
float
pm10_0_index =
calculate_index
(pm10_0_value,
PM10_0_GRID
);
18
float
aqi = std::max({pm2_5_index, pm10_0_index, 0.0f});
19
aqi = std::min(aqi,
static_cast<
float
>
(std::numeric_limits<uint16_t>::max()));
20
return
static_cast<
uint16_t
>
(std::lround(aqi));
21
}
22
23
protected
:
24
static
constexpr
int
NUM_LEVELS
= 4;
25
26
static
constexpr
int
INDEX_GRID
[
NUM_LEVELS
][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}};
27
28
static
constexpr
float
PM2_5_GRID
[
NUM_LEVELS
][2] = {
29
// clang-format off
30
{0.0f, 15.1f},
31
{15.1f, 30.1f},
32
{30.1f, 55.1f},
33
{55.1f, 110.1f}
34
// clang-format on
35
};
36
37
static
constexpr
float
PM10_0_GRID
[
NUM_LEVELS
][2] = {
38
// clang-format off
39
{0.0f, 25.1f},
40
{25.1f, 50.1f},
41
{50.1f, 90.1f},
42
{90.1f, 180.1f}
43
// clang-format on
44
};
45
46
static
float
calculate_index
(
float
value,
const
float
array[
NUM_LEVELS
][2]) {
47
int
grid_index =
get_grid_index
(value, array);
48
if
(grid_index == -1) {
49
return
-1.0f;
50
}
51
52
float
aqi_lo =
INDEX_GRID
[grid_index][0];
53
float
aqi_hi =
INDEX_GRID
[grid_index][1];
54
float
conc_lo = array[grid_index][0];
55
float
conc_hi = array[grid_index][1];
56
57
// The top band is open-ended (see get_grid_index), so for concentrations above the last
58
// breakpoint this linear fit extrapolates past 100 unbounded, matching CAQI's open ">100" class.
59
return
(value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
60
}
61
62
static
int
get_grid_index
(
float
value,
const
float
array[
NUM_LEVELS
][2]) {
63
for
(
int
i = 0; i <
NUM_LEVELS
; i++) {
64
// The top band is open-ended: any value at or above its lower breakpoint falls into it.
65
const
bool
in_range = (value >= array[i][0]) && (i ==
NUM_LEVELS
- 1 || value < array[i][1]);
66
if
(in_range) {
67
return
i;
68
}
69
}
70
return
-1;
71
}
72
};
73
74
}
// namespace esphome::aqi
abstract_aqi_calculator.h
esphome::aqi::AbstractAQICalculator
Definition
abstract_aqi_calculator.h:7
esphome::aqi::CAQICalculator
Definition
caqi_calculator.h:10
esphome::aqi::CAQICalculator::INDEX_GRID
static constexpr int INDEX_GRID[NUM_LEVELS][2]
Definition
caqi_calculator.h:26
esphome::aqi::CAQICalculator::PM10_0_GRID
static constexpr float PM10_0_GRID[NUM_LEVELS][2]
Definition
caqi_calculator.h:37
esphome::aqi::CAQICalculator::get_aqi
uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool) override
Definition
caqi_calculator.h:15
esphome::aqi::CAQICalculator::PM2_5_GRID
static constexpr float PM2_5_GRID[NUM_LEVELS][2]
Definition
caqi_calculator.h:28
esphome::aqi::CAQICalculator::calculate_index
static float calculate_index(float value, const float array[NUM_LEVELS][2])
Definition
caqi_calculator.h:46
esphome::aqi::CAQICalculator::get_grid_index
static int get_grid_index(float value, const float array[NUM_LEVELS][2])
Definition
caqi_calculator.h:62
esphome::aqi::CAQICalculator::NUM_LEVELS
static constexpr int NUM_LEVELS
Definition
caqi_calculator.h:24
esphome::aqi
Definition
abstract_aqi_calculator.h:5
Generated by
1.12.0