mirror of
https://github.com/bvanroll/_dotfiles.git
synced 2025-08-29 20:12:42 +00:00
250 lines
6.4 KiB
Perl
Executable File
250 lines
6.4 KiB
Perl
Executable File
#!/bin/perl
|
|
# For temperature: subtract 8 from the F value for the actual temperature
|
|
|
|
# Get PurpleAir sensor ID
|
|
if ($ENV{'SENSOR_ID'} == "") {
|
|
print STDERR "Missing required environment variable 'SENSOR_ID'.\n";
|
|
exit 1;
|
|
}
|
|
$sensor_id = $ENV{'SENSOR_ID'};
|
|
|
|
# Get measurement type. Default: US_AQI
|
|
@valid_types = ("US_AQI", "EU_AQI", "CA_AQHI", "IMECA", "IAS");
|
|
$type = 'US_AQI';
|
|
if (grep(/^$ENV{'TYPE'}$/, @valid_types)) {
|
|
$type = $ENV{'TYPE'};
|
|
} elsif ($ENV{'TYPE'}) {
|
|
print STDERR "Unrecognized TYPE: $ENV{'TYPE'}. Valid values are: [@valid_types]";
|
|
exit 1;
|
|
}
|
|
|
|
# Read pm2.5 10-minute average from the PurpleAir sensor
|
|
$pm2_5 = `curl "http://purpleair.com/json?show=$sensor_id" 2>/dev/null \\
|
|
| jq -r '.results | map(select(has("Stats")) | .Stats | fromjson | .v1) | add / length'`;
|
|
|
|
###################
|
|
# IMPLEMENTATIONS #
|
|
###################
|
|
|
|
sub UsAqi {
|
|
# See https://forum.airnowtech.org/t/the-aqi-equation/169
|
|
#
|
|
# PM2.5 | ConcLo | ConcHi | AQILo | AQIHi
|
|
# Good | 0.0 | 12.0 | 0 | 50
|
|
# Moderate | 12.1 | 35.4 | 51 | 100
|
|
# Unhealthy Sensitive | 35.5 | 55.4 | 101 | 150
|
|
# Unhealthy | 55.5 | 150.4 | 151 | 200
|
|
# Very Unhealthy | 150.5 | 250.4 | 201 | 300
|
|
# Hazardous | 250.5 | 500.4 | 301 | 500
|
|
#
|
|
# |------------Multiplier-----------| Threshold Addition
|
|
# (AQIHi - AQILo) / (ConcHi - ConcLo) * (ConcNow - ConcLo) + AQILo
|
|
|
|
@colors = ("#00e400", "#ffff00", "#ff7e00", "#ff0000", "#8f3f97", "#7e0023");
|
|
$pm2_5 = $_[0];
|
|
|
|
# Table of values: Threshold, Multiplier, Addition
|
|
@t = (
|
|
[0.0, 4.1667, 0.0],
|
|
[12.1, 2.1030, 51],
|
|
[35.5, 2.4623, 101],
|
|
[55.5, 0.5163, 151],
|
|
[150.5, 0.9910, 201],
|
|
[250.5, 0.7963, 301]
|
|
);
|
|
|
|
# Find relevant row for computation based on pm2.5 threshold
|
|
for ($i = 5; $i >= 0; $i--) {
|
|
if ($pm2_5 >= $t[$i][0]) {
|
|
$r = $i;
|
|
last;
|
|
}
|
|
}
|
|
|
|
# Compute AQI from raw pm2.5 concentration
|
|
$aqi = $t[$r][1] * ($pm2_5 - $t[$r][0]) + $t[$r][2];
|
|
|
|
return (
|
|
'val' => sprintf("%.0f", $aqi),
|
|
'colors' => [@colors],
|
|
'color_idx' => $r
|
|
);
|
|
}
|
|
|
|
sub EuAqi {
|
|
# See https://airindex.eea.europa.eu/Map/AQI/Viewer/
|
|
# European Air Quality Index uses the pm2.5 concentration directly.
|
|
|
|
@colors = ("#50f0e6", "#50ccaa", "#f0e641", "#ff5050", "#960032", "#7d2181");
|
|
$pm2_5 = $_[0];
|
|
|
|
# Thresholds for color indices
|
|
@t = (0.0, 10.0, 20.0, 25.0, 50.0, 75.0);
|
|
|
|
# Find relevant row for computation based on pm2.5 threshold
|
|
for ($i = 5; $i >= 0; $i--) {
|
|
if ($pm2_5 >= $t[$i]) {
|
|
$r = $i;
|
|
last;
|
|
}
|
|
}
|
|
|
|
return (
|
|
'val' => sprintf("%.0f", $pm2_5),
|
|
'colors' => [@colors],
|
|
'color_idx' => $r
|
|
);
|
|
}
|
|
|
|
sub CaAqhi {
|
|
# See https://en.wikipedia.org/wiki/Air_Quality_Health_Index_(Canada)#Calculation
|
|
# Note: AQHI should also incorporate O3 and NO2, but PurpleAir only has PM2.5
|
|
# The pm2.5 calculation is used alone and multiplied by 3.
|
|
# 1-3: Low
|
|
# 4-6: Moderate
|
|
# 7-10: High
|
|
# 11+: Very High
|
|
|
|
@colors = (
|
|
"#00ccff", "#0099cc", "#006699",
|
|
"#ffff00", "#ffcc00", "#ff9933",
|
|
"#ff6666", "#ff0000", "#cc0000",
|
|
"#990000", "#660000");
|
|
$pm2_5 = $_[0];
|
|
|
|
$aqhi = 3 * 1000 / 10.4 * ((exp(0.000487 * $pm2_5) - 1));
|
|
|
|
return (
|
|
'val' => sprintf("%.0f", $aqhi >= 1 ? $aqhi : 1),
|
|
'colors' => [@colors],
|
|
'color_idx' => $r
|
|
);
|
|
}
|
|
|
|
sub Imeca {
|
|
# See http://rama.edomex.gob.mx/imeca
|
|
#
|
|
# PM2.5 | Threshold | Multiplier | Addition
|
|
# Buena | 0.0 | 4.17 | 0
|
|
# Regular | 12.1 | 1.49 | 51
|
|
# Mala | 45.1 | 0.94 | 101
|
|
# Muy Mala | 97.5 | 0.93 | 151
|
|
# Extremadamente Mala | 150.5 | 0.99 | 201
|
|
# Extremadamente Mala | 250.5 | 0.99 | 301
|
|
# Extremadamente Mala | 350.5 | 0.66 | 401
|
|
#
|
|
# Multiplier * (pm2.5 - Threshold) + Addition
|
|
|
|
@colors = ("#00e400", "#ffff00", "#ff7e00", "#ff0000", "#99004c", "#99004c", "#99004c");
|
|
$pm2_5 = $_[0];
|
|
|
|
# Table of values: Threshold, Multiplier, Addition
|
|
@t = (
|
|
[0.0, 4.17, 0.0],
|
|
[12.1, 1.49, 51.0],
|
|
[45.1, 0.94, 101.0],
|
|
[97.5, 0.93, 151.0],
|
|
[150.5, 0.99, 201.0],
|
|
[250.5, 0.99, 301.0],
|
|
[350.5, 0.66, 401.0]
|
|
);
|
|
|
|
# Find relevant row for computation based on pm2.5 threshold
|
|
for ($i = 6; $i >= 0; $i--) {
|
|
if ($pm2_5 >= $t[$i][0]) {
|
|
$r = $i;
|
|
last;
|
|
}
|
|
}
|
|
|
|
# Compute AQI from raw pm2.5 concentration
|
|
$aqi = $t[$r][1] * ($pm2_5 - $t[$r][0]) + $t[$r][2];
|
|
|
|
return (
|
|
'val' => sprintf("%.0f", $aqi),
|
|
'colors' => [@colors],
|
|
'color_idx' => $r
|
|
);
|
|
}
|
|
|
|
sub Ias {
|
|
# See https://www.dof.gob.mx/nota_detalle_popup.php?codigo=5576807
|
|
# Índice de aire y salud uses the pm2.5 concentration directly.
|
|
|
|
@colors = ("#00e400", "#ffff00", "#ff7e00", "#ff0000", "#99004c");
|
|
$pm2_5 = $_[0];
|
|
|
|
# Thresholds for color indices
|
|
@t = (0.0, 26.0, 46.0, 80.0, 148.0);
|
|
|
|
# Find relevant row for computation based on pm2.5 threshold
|
|
for ($i = 4; $i >= 0; $i--) {
|
|
if ($pm2_5 >= $t[$i]) {
|
|
$r = $i;
|
|
last;
|
|
}
|
|
}
|
|
|
|
return (
|
|
'val' => sprintf("%.0f", $pm2_5),
|
|
'colors' => [@colors],
|
|
'color_idx' => $r
|
|
);
|
|
}
|
|
|
|
##################
|
|
# OUTPUT RESULTS #
|
|
##################
|
|
|
|
# Compute requested value
|
|
# Expected return value is a hash of the form:
|
|
# (
|
|
# 'val' => <numeric measurement value>,
|
|
# 'colors' => [<color hex codes>],
|
|
# 'color_idx' => <0-based index of color array>,
|
|
# )
|
|
if ($type eq "US_AQI") {
|
|
%result = UsAqi($pm2_5);
|
|
} elsif ($type eq "EU_AQI") {
|
|
%result = EuAqi($pm2_5);
|
|
} elsif ($type eq "CA_AQHI") {
|
|
%result = CaAqhi($pm2_5);
|
|
} elsif ($type eq "IMECA") {
|
|
%result = Imeca($pm2_5);
|
|
} elsif ($type eq "IAS") {
|
|
%result = Ias($pm2_5);
|
|
} else {
|
|
# It should not be possible to reach here.
|
|
%result = (
|
|
'val' => 'ERR',
|
|
'colors' => ("#ff0000"),
|
|
'color_idx' => 0,
|
|
'num_colors' => 1
|
|
);
|
|
}
|
|
|
|
# Get color overrides, if any.
|
|
if ($ENV{'COLORS'}) {
|
|
@colors = split(',', $ENV{'COLORS'});
|
|
if (scalar(@colors) < scalar(@{ $result{'colors'} })) {
|
|
print STDERR "Warning: number of color overrides is fewer than the number".
|
|
" of buckets in type $type\n";
|
|
}
|
|
} else {
|
|
@colors = @{ $result{'colors'} };
|
|
}
|
|
|
|
# Adjust color index if greater than colors length
|
|
$color_idx = $result{'color_idx'};
|
|
if ($color_idx >= scalar(@colors)) {
|
|
$color_idx = scalar(@colors) - 1;
|
|
}
|
|
|
|
# Full text, short text, color
|
|
print "$result{'val'}\n";
|
|
print "$result{'val'}\n";
|
|
|
|
if (!$ENV{'NO_COLOR'}) {
|
|
print "$colors[$color_idx]\n";
|
|
}
|