Add Shelly 2.5 overtemp functionality

* Add all temperature, humidity and pressure for global access
 * Add Shelly 2.5 overtemp functionality
 * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592)
This commit is contained in:
Theo Arends 2019-04-15 18:12:42 +02:00
parent 6adb513cd6
commit 5e810f1ff3
25 changed files with 80 additions and 38 deletions

View File

@ -2,6 +2,9 @@
* Fix use of SerialDelimiter value 128 (#5634) * Fix use of SerialDelimiter value 128 (#5634)
* Fix lost syslog connection regression from 6.5.0.4 * Fix lost syslog connection regression from 6.5.0.4
* Add Shelly 2.5 Energy Monitoring (#5592) * Add Shelly 2.5 Energy Monitoring (#5592)
* Add all temperature, humidity and pressure for global access
* Add Shelly 2.5 overtemp functionality
* Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592)
* *
* 6.5.0.7 20190410 * 6.5.0.7 20190410
* Add command LedMask to assign which relay has access to power LED (#5602, #5612) * Add command LedMask to assign which relay has access to power LED (#5602, #5612)

View File

@ -255,12 +255,13 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUN
FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER,
FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA,
FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR,
FUNC_ENERGY_EVERY_SECOND,
FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED,
FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS};
enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER,
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX }; SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX };
const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Light|Knx|Display|Wemo|Hue|Retry"; const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry";
const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 };

View File

@ -127,6 +127,7 @@ uint32_t loop_load_avg = 0; // Indicative loop load average
uint32_t global_update = 0; // Timestamp of last global temperature and humidity update uint32_t global_update = 0; // Timestamp of last global temperature and humidity update
float global_temperature = 0; // Provide a global temperature to be used by some sensors float global_temperature = 0; // Provide a global temperature to be used by some sensors
float global_humidity = 0; // Provide a global humidity to be used by some sensors float global_humidity = 0; // Provide a global humidity to be used by some sensors
float global_pressure = 0; // Provide a global pressure to be used by some sensors
char *ota_url; // OTA url string pointer char *ota_url; // OTA url string pointer
uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command
uint16_t blink_counter = 0; // Number of blink cycles uint16_t blink_counter = 0; // Number of blink cycles
@ -1609,6 +1610,18 @@ void StopAllPowerBlink(void)
} }
} }
void SetAllPower(uint8_t state, int source)
{
if ((POWER_ALL_OFF == state) || (POWER_ALL_ON == state)) {
power = 0;
if (POWER_ALL_ON == state) {
power = (1 << devices_present) -1;
}
SetDevicePower(power, source);
MqttPublishAllPowerState();
}
}
void ExecuteCommand(char *cmnd, int source) void ExecuteCommand(char *cmnd, int source)
{ {
char *start; char *start;

View File

@ -575,6 +575,9 @@ float ConvertTemp(float c)
{ {
float result = c; float result = c;
global_update = uptime;
global_temperature = c;
if (!isnan(c) && Settings.flag.temperature_conversion) { if (!isnan(c) && Settings.flag.temperature_conversion) {
result = c * 1.8 + 32; // Fahrenheit result = c * 1.8 + 32; // Fahrenheit
} }
@ -586,10 +589,21 @@ char TempUnit(void)
return (Settings.flag.temperature_conversion) ? 'F' : 'C'; return (Settings.flag.temperature_conversion) ? 'F' : 'C';
} }
float ConvertHumidity(float h)
{
global_update = uptime;
global_humidity = h;
return h;
}
float ConvertPressure(float p) float ConvertPressure(float p)
{ {
float result = p; float result = p;
global_update = uptime;
global_pressure = p;
if (!isnan(p) && Settings.flag.pressure_conversion) { if (!isnan(p) && Settings.flag.pressure_conversion) {
result = p * 0.75006375541921; // mmHg result = p * 0.75006375541921; // mmHg
} }
@ -601,19 +615,13 @@ String PressureUnit(void)
return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE);
} }
void SetGlobalValues(float temperature, float humidity)
{
global_update = uptime;
global_temperature = temperature;
global_humidity = humidity;
}
void ResetGlobalValues(void) void ResetGlobalValues(void)
{ {
if ((uptime - global_update) > GLOBAL_VALUES_VALID) { // Reset after 5 minutes if ((uptime - global_update) > GLOBAL_VALUES_VALID) { // Reset after 5 minutes
global_update = 0; global_update = 0;
global_temperature = 0; global_temperature = 0;
global_humidity = 0; global_humidity = 0;
global_pressure = 0;
} }
} }

View File

@ -390,7 +390,9 @@ void GetFeatures(void)
#ifdef USE_HRE #ifdef USE_HRE
feature_sns2 |= 0x00800000; // xsns_43_hre.ino feature_sns2 |= 0x00800000; // xsns_43_hre.ino
#endif #endif
// feature_sns2 |= 0x01000000; #ifdef USE_ADE7953
feature_sns2 |= 0x01000000; // xnrg_07_ade7953.ino
#endif
// feature_sns2 |= 0x02000000; // feature_sns2 |= 0x02000000;
// feature_sns2 |= 0x04000000; // feature_sns2 |= 0x04000000;
// feature_sns2 |= 0x08000000; // feature_sns2 |= 0x08000000;

View File

@ -249,6 +249,14 @@ void MqttPublishPowerState(uint8_t device)
} }
} }
void MqttPublishAllPowerState()
{
for (uint8_t i = 1; i <= devices_present; i++) {
MqttPublishPowerState(i);
if (SONOFF_IFAN02 == my_module_type) { break; } // Report status of light relay only
}
}
void MqttPublishPowerBlinkState(uint8_t device) void MqttPublishPowerBlinkState(uint8_t device)
{ {
char scommand[33]; char scommand[33];
@ -321,10 +329,7 @@ void MqttConnected(void)
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), (GetResetReason() == "Exception") ? ESP.getResetInfo().c_str() : GetResetReason().c_str()); Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), (GetResetReason() == "Exception") ? ESP.getResetInfo().c_str() : GetResetReason().c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3"));
for (uint8_t i = 1; i <= devices_present; i++) { MqttPublishAllPowerState();
MqttPublishPowerState(i);
if (SONOFF_IFAN02 == my_module_type) { break; } // Report status of light relay only
}
if (Settings.tele_period) { tele_period = Settings.tele_period -9; } // Enable TelePeriod in 9 seconds if (Settings.tele_period) { tele_period = Settings.tele_period -9; } // Enable TelePeriod in 9 seconds
rules_flag.system_boot = 1; rules_flag.system_boot = 1;
XdrvCall(FUNC_MQTT_INIT); XdrvCall(FUNC_MQTT_INIT);

View File

@ -116,7 +116,7 @@ void Energy200ms(void)
if (5 == energy_fifth_second) { if (5 == energy_fifth_second) {
energy_fifth_second = 0; energy_fifth_second = 0;
XnrgCall(FUNC_EVERY_SECOND); XnrgCall(FUNC_ENERGY_EVERY_SECOND);
if (RtcTime.valid) { if (RtcTime.valid) {
if (LocalTime() == Midnight()) { if (LocalTime() == Midnight()) {
@ -717,6 +717,7 @@ bool Xsns03(uint8_t function)
break; break;
case FUNC_EVERY_SECOND: case FUNC_EVERY_SECOND:
EnergyMarginCheck(); EnergyMarginCheck();
XnrgCall(FUNC_EVERY_SECOND);
break; break;
case FUNC_JSON_APPEND: case FUNC_JSON_APPEND:
EnergyShow(true); EnergyShow(true);

View File

@ -288,7 +288,7 @@ int Xnrg01(uint8_t function)
case FUNC_INIT: case FUNC_INIT:
HlwSnsInit(); HlwSnsInit();
break; break;
case FUNC_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
HlwEverySecond(); HlwEverySecond();
break; break;
case FUNC_EVERY_200_MSECOND: case FUNC_EVERY_200_MSECOND:

View File

@ -236,7 +236,7 @@ int Xnrg02(uint8_t function)
} }
else if (XNRG_02 == energy_flg) { else if (XNRG_02 == energy_flg) {
switch (function) { switch (function) {
case FUNC_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
CseEverySecond(); CseEverySecond();
break; break;
case FUNC_COMMAND: case FUNC_COMMAND:

View File

@ -658,7 +658,7 @@ int Xnrg04(uint8_t function)
case FUNC_INIT: case FUNC_INIT:
McpSnsInit(); McpSnsInit();
break; break;
case FUNC_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
if (McpSerial) { McpEverySecond(); } if (McpSerial) { McpEverySecond(); }
break; break;
case FUNC_COMMAND: case FUNC_COMMAND:

View File

@ -115,7 +115,7 @@ int Xnrg05(uint8_t function)
case FUNC_INIT: case FUNC_INIT:
PzemAcSnsInit(); PzemAcSnsInit();
break; break;
case FUNC_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
PzemAcEverySecond(); PzemAcEverySecond();
break; break;
} }

View File

@ -114,7 +114,7 @@ int Xnrg06(uint8_t function)
case FUNC_INIT: case FUNC_INIT:
PzemDcSnsInit(); PzemDcSnsInit();
break; break;
case FUNC_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
PzemDcEverySecond(); PzemDcEverySecond();
break; break;
} }

View File

@ -34,6 +34,8 @@
#define ADE7953_UREF 26000 #define ADE7953_UREF 26000
#define ADE7953_IREF 10000 #define ADE7953_IREF 10000
#define ADE7953_OVERTEMP 73.0 // Industry standard lowest overtemp in Celsius
#define ADE7953_ADDR 0x38 #define ADE7953_ADDR 0x38
uint32_t ade7953_active_power = 0; uint32_t ade7953_active_power = 0;
@ -141,7 +143,7 @@ void Ade7953GetData(void)
} }
} }
void Ade7953EverySecond() void Ade7953EnergyEverySecond()
{ {
if (ade7953_active_power) { if (ade7953_active_power) {
energy_kWhtoday_delta += ((ade7953_active_power * (100000 / (Settings.energy_power_calibration / 10))) / 3600); energy_kWhtoday_delta += ((ade7953_active_power * (100000 / (Settings.energy_power_calibration / 10))) / 3600);
@ -158,6 +160,13 @@ void Ade7953EverySecond()
} }
} }
void Ade7953EverySecond()
{
if (power && (global_temperature > ADE7953_OVERTEMP)) { // Device overtemp, turn off relays
SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP);
}
}
void Ade7953DrvInit(void) void Ade7953DrvInit(void)
{ {
if (!energy_flg) { if (!energy_flg) {
@ -228,6 +237,9 @@ int Xnrg07(uint8_t function)
} }
else if (XNRG_07 == energy_flg) { else if (XNRG_07 == energy_flg) {
switch (function) { switch (function) {
case FUNC_ENERGY_EVERY_SECOND:
Ade7953EnergyEverySecond();
break;
case FUNC_EVERY_SECOND: case FUNC_EVERY_SECOND:
Ade7953EverySecond(); Ade7953EverySecond();
break; break;

View File

@ -112,7 +112,7 @@ void SonoffScShow(bool json)
{ {
if (sc_value[0] > 0) { if (sc_value[0] > 0) {
float t = ConvertTemp(sc_value[1]); float t = ConvertTemp(sc_value[1]);
float h = sc_value[0]; float h = ConvertHumidity(sc_value[0]);
char temperature[33]; char temperature[33];
dtostrfd(t, Settings.flag2.temperature_resolution, temperature); dtostrfd(t, Settings.flag2.temperature_resolution, temperature);

View File

@ -155,6 +155,7 @@ void DhtReadTempHum(uint8_t sensor)
break; break;
} }
Dht[sensor].t = ConvertTemp(Dht[sensor].t); Dht[sensor].t = ConvertTemp(Dht[sensor].t);
Dht[sensor].h = ConvertHumidity(Dht[sensor].h);
Dht[sensor].lastresult = 0; Dht[sensor].lastresult = 0;
} else { } else {
Dht[sensor].lastresult++; Dht[sensor].lastresult++;

View File

@ -148,8 +148,7 @@ bool ShtRead(void)
float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw;
sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear; sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear;
sht_temperature = ConvertTemp(sht_temperature); sht_temperature = ConvertTemp(sht_temperature);
ConvertHumidity(sht_humidity); // Set global humidity
SetGlobalValues(sht_temperature, sht_humidity);
sht_valid = SENSOR_MAX_MISS; sht_valid = SENSOR_MAX_MISS;
return true; return true;

View File

@ -186,8 +186,7 @@ bool HtuRead(void)
if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) { if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) {
htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity; htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity;
} }
ConvertHumidity(htu_humidity); // Set global humidity
SetGlobalValues(htu_temperature, htu_humidity);
htu_valid = SENSOR_MAX_MISS; htu_valid = SENSOR_MAX_MISS;
return true; return true;

View File

@ -509,7 +509,8 @@ void BmpRead(void)
#endif // USE_BME680 #endif // USE_BME680
} }
} }
SetGlobalValues(ConvertTemp(bmp_sensors[0].bmp_temperature), bmp_sensors[0].bmp_humidity); ConvertTemp(bmp_sensors[0].bmp_temperature); // Set global temperature
ConvertHumidity(bmp_sensors[0].bmp_humidity); // Set global humidity
} }
void BmpEverySecond(void) void BmpEverySecond(void)

View File

@ -306,7 +306,7 @@ bool Xsns11(uint8_t function)
{ {
bool result = false; bool result = false;
if (i2c_flg) { if (i2c_flg && !(pin[GPIO_ADE7953_IRQ] < 99)) { // The ADE7953 uses I2C address 0x38 too but needs priority
switch (function) { switch (function) {
case FUNC_INIT: case FUNC_INIT:
Veml6070Detect(); // 1[ms], detect and init the sensor Veml6070Detect(); // 1[ms], detect and init the sensor

View File

@ -70,7 +70,7 @@ bool Sht3xRead(float &t, float &h, uint8_t sht3x_address)
data[i] = Wire.read(); // cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc data[i] = Wire.read(); // cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc
}; };
t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45);
h = (float)((((data[3] << 8) | data[4]) * 100) / 65535.0); h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); // Set global humidity
return (!isnan(t) && !isnan(h)); return (!isnan(t) && !isnan(h));
} }
@ -100,9 +100,6 @@ void Sht3xShow(bool json)
char types[11]; char types[11];
for (uint8_t i = 0; i < sht3x_count; i++) { for (uint8_t i = 0; i < sht3x_count; i++) {
if (Sht3xRead(t, h, sht3x_sensors[i].address)) { if (Sht3xRead(t, h, sht3x_sensors[i].address)) {
if (0 == i) { SetGlobalValues(t, h); }
char temperature[33]; char temperature[33];
dtostrfd(t, Settings.flag2.temperature_resolution, temperature); dtostrfd(t, Settings.flag2.temperature_resolution, temperature);
char humidity[33]; char humidity[33];

View File

@ -90,7 +90,7 @@ void Senseair250ms(void) // Every 250 mSec
senseair_temperature = ConvertTemp((float)value / 100); senseair_temperature = ConvertTemp((float)value / 100);
break; break;
case 4: // 0x05 (5) READ_HUMIDITY - S8: fe 84 02 f2 f1 - Illegal Data Address case 4: // 0x05 (5) READ_HUMIDITY - S8: fe 84 02 f2 f1 - Illegal Data Address
senseair_humidity = (float)value / 100; senseair_humidity = ConvertHumidity((float)value / 100);
break; break;
case 5: // 0x1C (28) READ_RELAY_STATE - S8: fe 04 02 01 54 ad 4b - firmware version case 5: // 0x1C (28) READ_RELAY_STATE - S8: fe 04 02 01 54 ad 4b - firmware version
{ {

View File

@ -310,7 +310,7 @@ void RfSnsTheoV2Show(bool json)
} }
} else { } else {
float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100);
float humi = (float)rfsns_theo_v2_t2[i].hum / 100; float humi = ConvertHumidity((float)rfsns_theo_v2_t2[i].hum / 100);
char temperature[33]; char temperature[33];
dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); dtostrfd(temp, Settings.flag2.temperature_resolution, temperature);
char humidity[33]; char humidity[33];
@ -558,7 +558,7 @@ void RfSnsAlectoV2Show(bool json)
float temp = ConvertTemp(rfsns_alecto_v2->temp); float temp = ConvertTemp(rfsns_alecto_v2->temp);
char temperature[33]; char temperature[33];
dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); dtostrfd(temp, Settings.flag2.temperature_resolution, temperature);
float humi = (float)rfsns_alecto_v2->humi; float humi = ConvertHumidity((float)rfsns_alecto_v2->humi);
char humidity[33]; char humidity[33];
dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); dtostrfd(humi, Settings.flag2.humidity_resolution, humidity);
char rain[33]; char rain[33];

View File

@ -232,7 +232,7 @@ void AzEverySecond(void)
return; return;
} }
response_substr[j] = 0; // add null terminator response_substr[j] = 0; // add null terminator
az_humidity = CharToDouble((char*)response_substr); az_humidity = ConvertHumidity(CharToDouble((char*)response_substr));
} }
} }

View File

@ -442,7 +442,7 @@ void Scd30Show(bool json)
if (scd30Found && scd30IsDataValid) if (scd30Found && scd30IsDataValid)
{ {
dtostrfd(scd30_Humid, Settings.flag2.humidity_resolution, humidity); dtostrfd(ConvertHumidity(scd30_Humid), Settings.flag2.humidity_resolution, humidity);
dtostrfd(ConvertTemp(scd30_Temp), Settings.flag2.temperature_resolution, temperature); dtostrfd(ConvertTemp(scd30_Temp), Settings.flag2.temperature_resolution, temperature);
if (json) { if (json) {
//ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), scd30_CO2, temperature, humidity); //ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), scd30_CO2, temperature, humidity);

View File

@ -147,7 +147,7 @@ a_features = [[
"USE_PZEM_DC","USE_TX20_WIND_SENSOR","USE_MGC3130","USE_RF_SENSOR", "USE_PZEM_DC","USE_TX20_WIND_SENSOR","USE_MGC3130","USE_RF_SENSOR",
"USE_THEO_V2","USE_ALECTO_V2","USE_AZ7798","USE_MAX31855", "USE_THEO_V2","USE_ALECTO_V2","USE_AZ7798","USE_MAX31855",
"USE_PN532_I2C","USE_MAX44009","USE_SCD30","USE_HRE", "USE_PN532_I2C","USE_MAX44009","USE_SCD30","USE_HRE",
"","","","", "USE_ADE7953","","","",
"","","",""]] "","","",""]]
usage = "usage: decode-status {-d | -f} arg" usage = "usage: decode-status {-d | -f} arg"