[MQTT] Add support for more VTypes, implement more VTypes, small improvements

This commit is contained in:
Ton Huisman
2025-01-10 22:48:06 +01:00
parent d14b111ed3
commit e4b0e4eceb
8 changed files with 2530 additions and 2229 deletions
+6 -2
View File
@@ -18,7 +18,7 @@
/** Changelog:
* 2024-11-29 tonhuisman: Add Discovery trigger setting
* 2024-11-11 tonhuisman: Add AutoDiscovery options
* Home Assistant suggested discovery topic: "homeassistant/%devclass%/%sysname%/%tskname%/config"
* Home Assistant suggested discovery topic: "homeassistant/%devclass%/%sysname%/%tskname%"
* 2023-08-18 tonhuisman: Clean up source for pull request
* 2023-03-15 tonhuisman: Add processing of topic endpoint /set to issue a TaskValueSet,taskname,taskvalue,payload command for
* topic %sysname%/#/taskname/valuename/set
@@ -151,7 +151,11 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String&
// We have received the Discovery request topic
// Generate random time-offset in 0.1 sec, range 1..10 seconds
mqttDiscoveryTimeout = random(10, 100);
addLog(LOG_LEVEL_INFO, strformat(F("C005 : Request for AutoDiscovery received. %d"), mqttDiscoveryTimeout));
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO, strformat(F("C005 : Request for AutoDiscovery received. %.1f sec."),
mqttDiscoveryTimeout / 10.0f));
}
// FIXME Generate event when request received?
} else
+500 -487
View File
@@ -1,487 +1,500 @@
#include "_Plugin_Helper.h"
#ifdef USES_P052
# include "src/PluginStructs/P052_data_struct.h"
// #######################################################################################################
// ############################# Plugin 052: Senseair CO2 Sensors ########################################
// #######################################################################################################
/** Changelog:
* 25025-01-03 tonhuisman: Small code size reduction
*/
/*
Plugin originally written by: Daniel Tedenljung
info__AT__tedenljungconsulting.com
Rewritten by: Mikael Trieb mikael__AT__triebconsulting.se
This plugin reads available values of Senseair Co2 Sensors.
Datasheet can be found here:
S8: http://www.senseair.com/products/oem-modules/senseair-s8/
K30: http://www.senseair.com/products/oem-modules/k30/
K70/tSENSE: http://www.senseair.com/products/wall-mount/tsense/
Circuit wiring
GPIO Setting 1 -> RX
GPIO Setting 2 -> TX
Use 1kOhm in serie on datapins!
*/
# define PLUGIN_052
# define PLUGIN_ID_052 52
# define PLUGIN_NAME_052 "Gases - CO2 Senseair"
boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string) {
boolean success = false;
switch (function) {
case PLUGIN_DEVICE_ADD: {
auto& dev = Device[++deviceCount];
dev.Number = PLUGIN_ID_052;
dev.Type = DEVICE_TYPE_SERIAL;
dev.VType = Sensor_VType::SENSOR_TYPE_SINGLE;
dev.FormulaOption = true;
dev.ValueCount = 1;
dev.SendDataOption = true;
dev.TimerOption = true;
dev.OutputDataType = Output_Data_type_t::Simple;
dev.ExitTaskBeforeSave = false;
dev.PluginStats = true;
break;
}
case PLUGIN_GET_DEVICENAME: {
string = F(PLUGIN_NAME_052);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES: {
for (uint8_t i = 0; i < VARS_PER_TASK; ++i) {
if (i < P052_NR_OUTPUT_VALUES) {
const uint8_t pconfigIndex = i + P052_QUERY1_CONFIG_POS;
uint8_t choice = PCONFIG(pconfigIndex);
ExtraTaskSettings.setTaskDeviceValueName(i, P052_data_struct::Plugin_052_valuename(choice, false));
} else {
ExtraTaskSettings.clearTaskDeviceValueName(i);
}
}
break;
}
case PLUGIN_GET_DEVICEVALUECOUNT:
{
event->Par1 = P052_NR_OUTPUT_VALUES;
success = true;
break;
}
case PLUGIN_GET_DEVICEVTYPE:
{
event->sensorType = static_cast<Sensor_VType>(PCONFIG(P052_SENSOR_TYPE_INDEX));
event->idx = P052_SENSOR_TYPE_INDEX;
success = true;
break;
}
case PLUGIN_GET_DEVICEGPIONAMES: {
serialHelper_getGpioNames(event);
break;
}
case PLUGIN_WEBFORM_SHOW_CONFIG:
{
string += serialHelper_getSerialTypeLabel(event);
success = true;
break;
}
case PLUGIN_SET_DEFAULTS:
{
PCONFIG(P052_SENSOR_TYPE_INDEX) = static_cast<int16_t>(Sensor_VType::SENSOR_TYPE_SINGLE);
PCONFIG(0) = 1; // "CO2"
for (uint8_t i = 1; i < VARS_PER_TASK; ++i) {
PCONFIG(i) = 0; // "Empty"
}
success = true;
break;
}
case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR:
{
const __FlashStringHelper *options[P052_NR_OUTPUT_OPTIONS];
for (uint8_t i = 0; i < P052_NR_OUTPUT_OPTIONS; ++i) {
options[i] = P052_data_struct::Plugin_052_valuename(i, true);
}
for (uint8_t i = 0; i < P052_NR_OUTPUT_VALUES; ++i) {
const uint8_t pconfigIndex = i + P052_QUERY1_CONFIG_POS;
sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, P052_NR_OUTPUT_OPTIONS, options);
}
break;
}
case PLUGIN_WEBFORM_LOAD: {
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->isInitialized()) {
addFormSubHeader(F("Device Information"));
{
int value = 0;
if (P052_data->modbus.detected_device_description.length() > 0) {
addRowLabel(F("Detected Device"));
addHtml(P052_data->modbus.detected_device_description);
}
if (P052_data->readInputRegister(P052_IR29_FW_REV, value)) {
addRowLabel(F("FW rev."));
addHtmlInt(value);
}
addRowLabel(F("Sensor ID"));
addHtml(formatToHex_decimal(P052_data->getSensorID()));
addRowLabel(F("Checksum (pass/fail/nodata)"));
{
uint32_t reads_pass, reads_crc_failed, reads_nodata;
P052_data->modbus.getStatistics(reads_pass, reads_crc_failed, reads_nodata);
addHtml(strformat(F("%d/%d/%d"), reads_pass, reads_crc_failed, reads_nodata));
}
bool hasFactorySettings = false;
bool enabledABC = false;
if (P052_data->readHoldingRegister(P052_HR19_METER_CONTROL, value)) {
hasFactorySettings = value == 255;
enabledABC = bitRead(value, 1) == 0;
addRowLabel(F("Using Factory Settings"));
addEnabled(hasFactorySettings);
if (!hasFactorySettings) {
// addRowLabel(F("HR19"));
// addHtmlInt(value);
addRowLabel(F("nRDY"));
addEnabled(bitRead(value, 0) == 0);
addRowLabel(F("ABC"));
addEnabled(enabledABC);
addRowLabel(F("Static IIR filter"));
addEnabled(bitRead(value, 2) == 0);
addRowLabel(F("Dynamic IIR filter"));
addEnabled(bitRead(value, 3) == 0);
addRowLabel(F("Pressure Compensation"));
addEnabled(bitRead(value, 4) == 0);
}
}
if (P052_data->readInputRegister(P052_IR7_MEASUREMENT_COUNT, value)) {
addRowLabel(F("Measurement Count"));
addHtmlInt(value);
}
if (P052_data->readInputRegister(P052_IR8_MEASUREMENT_CYCLE_TIME, value)) {
addRowLabel(F("Measurement Cycle time"));
addHtmlInt(value * 2);
addUnit('s');
}
if (P052_data->readInputRegister(P052_IR9_MEASURED_UNFILTERED_CO2, value)) {
addRowLabel(F("Unfiltered CO2"));
addHtmlInt(value);
}
if (P052_data->readHoldingRegister(P052_HR11_MEASUREMENT_MODE, value)) {
addRowLabel(F("Measurement Mode"));
addHtml(value == 0 ? F("Continuous") : F("Single Measurement"));
}
if (enabledABC) {
if (P052_data->readHoldingRegister(P052_HR14_ABC_PERIOD, value)) {
addRowLabel(F("ABC Period"));
addHtmlInt(value);
addUnit('h');
}
if (P052_data->readHoldingRegister(P052_HR5_ABC_TIME, value)) {
addRowLabel(F("Time Since ABC"));
addHtmlInt(value);
addUnit('h');
}
}
}
{
int value = 0;
/*
if (P052_data->readHoldingRegister(P052_HR11_MEASUREMENT_MODE, value)) {
// Disable selector for now, since single measurement not yet supported.
const __FlashStringHelper *options[2] = { F("Continuous"), F("Single Measurement") };
addFormSelector(F("Measurement Mode"), F("mode"), 2, options, nullptr, value);
}
*/
if (P052_data->readHoldingRegister(P052_HR12_MEASUREMENT_PERIOD, value)) {
addFormNumericBox(F("Measurement Period"), F("period"), value, 2, 65534);
addUnit('s');
}
if (P052_data->readHoldingRegister(P052_HR13_NR_OF_SAMPLES, value)) {
addFormNumericBox(F("Samples per measurement"), F("samp_meas"), value, 1, 1024);
}
}
{
// P052_ABC_PERIOD
}
}
/*
// ABC functionality disabled for now, due to a bug in the firmware.
// See https://github.com/letscontrolit/ESPEasy/issues/759
uint8_t choiceABCperiod = PCONFIG(4);
const __FlashStringHelper * optionsABCperiod[9] = { F("disable"), F("1 h"), F("12 h"), F("1
day"), F("2 days"), F("4 days"), F("7 days"), F("14 days"), F("30 days") };
addFormSelector(F("ABC period"), F("ABC_period"), 9, optionsABCperiod,
nullptr, choiceABCperiod);
*/
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE: {
// Save output selector parameters.
for (uint8_t i = 0; i < P052_NR_OUTPUT_VALUES; ++i) {
const uint8_t pconfigIndex = i + P052_QUERY1_CONFIG_POS;
const uint8_t choice = PCONFIG(pconfigIndex);
sensorTypeHelper_saveOutputSelector(event, pconfigIndex, i, P052_data_struct::Plugin_052_valuename(choice, false));
}
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->isInitialized()) {
bool changed = false;
uint16_t mode = getFormItemInt(F("mode"), 65535);
if (((mode == 0) || (mode == 1))) {
uint8_t errorcode;
int readVal = P052_data->modbus.readHoldingRegister(0x0A, errorcode);
if ((errorcode == 0) && (readVal != mode)) {
P052_data->modbus.writeMultipleRegisters(0x0A, mode);
delay(0);
changed = true;
}
}
uint16_t period = getFormItemInt(F("period"), 0);
if (period > 1) {
uint8_t errorcode;
int readVal = P052_data->modbus.readHoldingRegister(0x0B, errorcode);
if ((errorcode == 0) && (readVal != period)) {
P052_data->modbus.writeMultipleRegisters(0x0B, period);
delay(0);
changed = true;
}
}
uint16_t samp_meas = getFormItemInt(F("samp_meas"), 0);
if ((samp_meas > 0) && (samp_meas <= 1024)) {
uint8_t errorcode;
int readVal = P052_data->modbus.readHoldingRegister(0x0C, errorcode);
if ((errorcode == 0) && (readVal != samp_meas)) {
P052_data->modbus.writeMultipleRegisters(0x0C, samp_meas);
delay(0);
changed = true;
}
}
if (changed) {
// Restart sensor.
P052_data->modbus.writeMultipleRegisters(0x11, 0xFF);
// FIXME TD-er: Must leave the sensor to boot for a while.
delay(35);
}
}
/*
// ABC functionality disabled for now, due to a bug in the firmware.
// See https://github.com/letscontrolit/ESPEasy/issues/759
PCONFIG(4) = getFormItemInt(F("ABC_period"));
*/
success = true;
break;
}
case PLUGIN_INIT: {
const int16_t serial_rx = CONFIG_PIN1;
const int16_t serial_tx = CONFIG_PIN2;
const ESPEasySerialPort port = static_cast<ESPEasySerialPort>(CONFIG_PORT);
initPluginTaskData(event->TaskIndex, new (std::nothrow) P052_data_struct());
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr == P052_data) {
return success;
}
if (P052_data->init(port, serial_rx, serial_tx)) {
/*
// ABC functionality disabled for now, due to a bug in the firmware.
// See https://github.com/letscontrolit/ESPEasy/issues/759
const int periodInHours[9] = {0, 1, 12, (24*1), (24*2), (24*4), (24*7),
(24*14), (24*30) };
uint8_t choiceABCperiod = PCONFIG(1);
Plugin_052_setABCperiod(periodInHours[choiceABCperiod]);
*/
P052_data->modbus.setModbusTimeout(P052_MODBUS_TIMEOUT);
// P052_data->modbus.writeMultipleRegisters(0x09, 1); // Start Single Measurement
// P052_data->modbus.writeMultipleRegisters(0x0B, 16); // Measurement Period
// P052_data->modbus.writeMultipleRegisters(0x0C, 8); // Number of samples
success = true;
} else {
clearPluginTaskData(event->TaskIndex);
}
break;
}
case PLUGIN_EXIT: {
success = true;
break;
}
case PLUGIN_READ: {
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->isInitialized()) {
event->sensorType = static_cast<Sensor_VType>(PCONFIG(P052_SENSOR_TYPE_INDEX));
String log = F("Senseair: ");
String logPrefix;
for (int varnr = 0; varnr < P052_NR_OUTPUT_VALUES; ++varnr) {
uint8_t errorcode = 0;
float value = 0;
switch (PCONFIG(varnr)) {
case 1: {
value = P052_data->modbus.readInputRegister(P052_IR4_MEASURED_FILTERED_CO2, errorcode);
logPrefix = F("co2=");
break;
}
case 2: {
int temperatureX100 = P052_data->modbus.readInputRegister(P052_IR5_TEMPERATURE, errorcode);
if (errorcode == 2) {
// Exception code: Illegal Data Address
// SenseAir S8, not for other modules.
temperatureX100 = P052_data->modbus.read_RAM_EEPROM(
P052_CMD_READ_RAM, P052_RAM_ADDR_DET_TEMPERATURE, 2, errorcode);
}
if (temperatureX100 >= 32768) {
// 2-complement value.
temperatureX100 -= 65536;
}
value = static_cast<float>(temperatureX100) / 100.0f;
logPrefix = F("temp=");
break;
}
case 3: {
int rhX100 = P052_data->modbus.readInputRegister(P052_IR6_SPACE_HUMIDITY, errorcode);
value = static_cast<float>(rhX100) / 100.0f;
logPrefix = F("hum=");
break;
}
case 4: {
int status = P052_data->modbus.readInputRegister(P052_IR29_FW_REV, errorcode);
if (errorcode == 0) {
int relayStatus = (status >> 8) & 0x1;
UserVar.setFloat(event->TaskIndex, varnr, relayStatus);
log += F("relay status=");
log += relayStatus;
}
break;
}
case 5: {
int temperatureAdjustment = P052_data->modbus.readInputRegister(P052_IR11_MEASURED_CONCENTRATION_UNFILTERED, errorcode);
value = static_cast<float>(temperatureAdjustment);
logPrefix = F("temp adj=");
break;
}
case 7: {
const int errorWord = P052_data->modbus.readInputRegister(P052_IR_ERRORSTATUS, errorcode);
if (errorcode == 0) {
value = errorWord;
for (size_t i = 0; i < 9; i++) {
if (bitRead(errorWord, i)) {
log += F("err=");
log += i;
break;
}
}
} else {
value = -1;
log += F("err=");
log += -1;
}
break;
}
case 0:
default: {
UserVar.setFloat(event->TaskIndex, varnr, 0);
break;
}
}
if ((errorcode == 0) && (P052_data->modbus.getLastError() == 0)) {
success = true;
UserVar.setFloat(event->TaskIndex, varnr, value);
log += logPrefix;
log += value;
}
}
addLogMove(LOG_LEVEL_INFO, log);
break;
}
break;
}
case PLUGIN_WRITE:
{
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->plugin_write(event, string)) {
success = true;
}
break;
}
}
return success;
}
#endif // USES_P052
#include "_Plugin_Helper.h"
#ifdef USES_P052
# include "src/PluginStructs/P052_data_struct.h"
// #######################################################################################################
// ############################# Plugin 052: Senseair CO2 Sensors ########################################
// #######################################################################################################
/** Changelog:
* 2025-01-03 tonhuisman: Small code size reduction
* 2024-12-05 tonhuisman: Add support for MQTT AutoDiscovery for supported values
* 2024-12 tonhuisman: Start changelog
*/
/*
Plugin originally written by: Daniel Tedenljung
info__AT__tedenljungconsulting.com
Rewritten by: Mikael Trieb mikael__AT__triebconsulting.se
This plugin reads available values of Senseair Co2 Sensors.
Datasheet can be found here:
S8: http://www.senseair.com/products/oem-modules/senseair-s8/
K30: http://www.senseair.com/products/oem-modules/k30/
K70/tSENSE: http://www.senseair.com/products/wall-mount/tsense/
Circuit wiring
GPIO Setting 1 -> RX
GPIO Setting 2 -> TX
Use 1kOhm in serie on datapins!
*/
# define PLUGIN_052
# define PLUGIN_ID_052 52
# define PLUGIN_NAME_052 "Gases - CO2 Senseair"
boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string) {
boolean success = false;
switch (function) {
case PLUGIN_DEVICE_ADD: {
auto& dev = Device[++deviceCount];
dev.Number = PLUGIN_ID_052;
dev.Type = DEVICE_TYPE_SERIAL;
dev.VType = Sensor_VType::SENSOR_TYPE_SINGLE;
dev.FormulaOption = true;
dev.ValueCount = 1;
dev.SendDataOption = true;
dev.TimerOption = true;
dev.OutputDataType = Output_Data_type_t::Simple;
dev.ExitTaskBeforeSave = false;
dev.PluginStats = true;
break;
}
case PLUGIN_GET_DEVICENAME: {
string = F(PLUGIN_NAME_052);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES: {
for (uint8_t i = 0; i < VARS_PER_TASK; ++i) {
if (i < P052_NR_OUTPUT_VALUES) {
const uint8_t pconfigIndex = i + P052_QUERY1_CONFIG_POS;
uint8_t choice = PCONFIG(pconfigIndex);
ExtraTaskSettings.setTaskDeviceValueName(i, P052_data_struct::Plugin_052_valuename(choice, false));
} else {
ExtraTaskSettings.clearTaskDeviceValueName(i);
}
}
break;
}
# if FEATURE_MQTT_DISCOVER
case PLUGIN_GET_DISCOVERY_VTYPES:
{
for (uint8_t i = 0; i < P052_NR_OUTPUT_VALUES; ++i) {
event->ParN[i] = P052_data_struct::Plugin_052_valueVType(i);
}
success = true;
break;
}
# endif // if FEATURE_MQTT_DISCOVER
case PLUGIN_GET_DEVICEVALUECOUNT:
{
event->Par1 = P052_NR_OUTPUT_VALUES;
success = true;
break;
}
case PLUGIN_GET_DEVICEVTYPE:
{
event->sensorType = static_cast<Sensor_VType>(PCONFIG(P052_SENSOR_TYPE_INDEX));
event->idx = P052_SENSOR_TYPE_INDEX;
success = true;
break;
}
case PLUGIN_GET_DEVICEGPIONAMES: {
serialHelper_getGpioNames(event);
break;
}
case PLUGIN_WEBFORM_SHOW_CONFIG:
{
string += serialHelper_getSerialTypeLabel(event);
success = true;
break;
}
case PLUGIN_SET_DEFAULTS:
{
PCONFIG(P052_SENSOR_TYPE_INDEX) = static_cast<int16_t>(Sensor_VType::SENSOR_TYPE_SINGLE);
PCONFIG(0) = 1; // "CO2"
for (uint8_t i = 1; i < VARS_PER_TASK; ++i) {
PCONFIG(i) = 0; // "Empty"
}
success = true;
break;
}
case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR:
{
const __FlashStringHelper *options[P052_NR_OUTPUT_OPTIONS];
for (uint8_t i = 0; i < P052_NR_OUTPUT_OPTIONS; ++i) {
options[i] = P052_data_struct::Plugin_052_valuename(i, true);
}
for (uint8_t i = 0; i < P052_NR_OUTPUT_VALUES; ++i) {
const uint8_t pconfigIndex = i + P052_QUERY1_CONFIG_POS;
sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, P052_NR_OUTPUT_OPTIONS, options);
}
break;
}
case PLUGIN_WEBFORM_LOAD: {
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->isInitialized()) {
addFormSubHeader(F("Device Information"));
{
int value = 0;
if (P052_data->modbus.detected_device_description.length() > 0) {
addRowLabel(F("Detected Device"));
addHtml(P052_data->modbus.detected_device_description);
}
if (P052_data->readInputRegister(P052_IR29_FW_REV, value)) {
addRowLabel(F("FW rev."));
addHtmlInt(value);
}
addRowLabel(F("Sensor ID"));
addHtml(formatToHex_decimal(P052_data->getSensorID()));
addRowLabel(F("Checksum (pass/fail/nodata)"));
{
uint32_t reads_pass, reads_crc_failed, reads_nodata;
P052_data->modbus.getStatistics(reads_pass, reads_crc_failed, reads_nodata);
addHtml(strformat(F("%d/%d/%d"), reads_pass, reads_crc_failed, reads_nodata));
}
bool hasFactorySettings = false;
bool enabledABC = false;
if (P052_data->readHoldingRegister(P052_HR19_METER_CONTROL, value)) {
hasFactorySettings = value == 255;
enabledABC = bitRead(value, 1) == 0;
addRowLabel(F("Using Factory Settings"));
addEnabled(hasFactorySettings);
if (!hasFactorySettings) {
// addRowLabel(F("HR19"));
// addHtmlInt(value);
addRowLabel(F("nRDY"));
addEnabled(bitRead(value, 0) == 0);
addRowLabel(F("ABC"));
addEnabled(enabledABC);
addRowLabel(F("Static IIR filter"));
addEnabled(bitRead(value, 2) == 0);
addRowLabel(F("Dynamic IIR filter"));
addEnabled(bitRead(value, 3) == 0);
addRowLabel(F("Pressure Compensation"));
addEnabled(bitRead(value, 4) == 0);
}
}
if (P052_data->readInputRegister(P052_IR7_MEASUREMENT_COUNT, value)) {
addRowLabel(F("Measurement Count"));
addHtmlInt(value);
}
if (P052_data->readInputRegister(P052_IR8_MEASUREMENT_CYCLE_TIME, value)) {
addRowLabel(F("Measurement Cycle time"));
addHtmlInt(value * 2);
addUnit('s');
}
if (P052_data->readInputRegister(P052_IR9_MEASURED_UNFILTERED_CO2, value)) {
addRowLabel(F("Unfiltered CO2"));
addHtmlInt(value);
}
if (P052_data->readHoldingRegister(P052_HR11_MEASUREMENT_MODE, value)) {
addRowLabel(F("Measurement Mode"));
addHtml(value == 0 ? F("Continuous") : F("Single Measurement"));
}
if (enabledABC) {
if (P052_data->readHoldingRegister(P052_HR14_ABC_PERIOD, value)) {
addRowLabel(F("ABC Period"));
addHtmlInt(value);
addUnit('h');
}
if (P052_data->readHoldingRegister(P052_HR5_ABC_TIME, value)) {
addRowLabel(F("Time Since ABC"));
addHtmlInt(value);
addUnit('h');
}
}
}
{
int value = 0;
/*
if (P052_data->readHoldingRegister(P052_HR11_MEASUREMENT_MODE, value)) {
// Disable selector for now, since single measurement not yet supported.
const __FlashStringHelper *options[2] = { F("Continuous"), F("Single Measurement") };
addFormSelector(F("Measurement Mode"), F("mode"), 2, options, nullptr, value);
}
*/
if (P052_data->readHoldingRegister(P052_HR12_MEASUREMENT_PERIOD, value)) {
addFormNumericBox(F("Measurement Period"), F("period"), value, 2, 65534);
addUnit('s');
}
if (P052_data->readHoldingRegister(P052_HR13_NR_OF_SAMPLES, value)) {
addFormNumericBox(F("Samples per measurement"), F("samp_meas"), value, 1, 1024);
}
}
{
// P052_ABC_PERIOD
}
}
/*
// ABC functionality disabled for now, due to a bug in the firmware.
// See https://github.com/letscontrolit/ESPEasy/issues/759
uint8_t choiceABCperiod = PCONFIG(4);
const __FlashStringHelper * optionsABCperiod[9] = { F("disable"), F("1 h"), F("12 h"), F("1
day"), F("2 days"), F("4 days"), F("7 days"), F("14 days"), F("30 days") };
addFormSelector(F("ABC period"), F("ABC_period"), 9, optionsABCperiod,
nullptr, choiceABCperiod);
*/
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE: {
// Save output selector parameters.
for (uint8_t i = 0; i < P052_NR_OUTPUT_VALUES; ++i) {
const uint8_t pconfigIndex = i + P052_QUERY1_CONFIG_POS;
const uint8_t choice = PCONFIG(pconfigIndex);
sensorTypeHelper_saveOutputSelector(event, pconfigIndex, i, P052_data_struct::Plugin_052_valuename(choice, false));
}
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->isInitialized()) {
bool changed = false;
uint16_t mode = getFormItemInt(F("mode"), 65535);
if (((mode == 0) || (mode == 1))) {
uint8_t errorcode;
int readVal = P052_data->modbus.readHoldingRegister(0x0A, errorcode);
if ((errorcode == 0) && (readVal != mode)) {
P052_data->modbus.writeMultipleRegisters(0x0A, mode);
delay(0);
changed = true;
}
}
uint16_t period = getFormItemInt(F("period"), 0);
if (period > 1) {
uint8_t errorcode;
int readVal = P052_data->modbus.readHoldingRegister(0x0B, errorcode);
if ((errorcode == 0) && (readVal != period)) {
P052_data->modbus.writeMultipleRegisters(0x0B, period);
delay(0);
changed = true;
}
}
uint16_t samp_meas = getFormItemInt(F("samp_meas"), 0);
if ((samp_meas > 0) && (samp_meas <= 1024)) {
uint8_t errorcode;
int readVal = P052_data->modbus.readHoldingRegister(0x0C, errorcode);
if ((errorcode == 0) && (readVal != samp_meas)) {
P052_data->modbus.writeMultipleRegisters(0x0C, samp_meas);
delay(0);
changed = true;
}
}
if (changed) {
// Restart sensor.
P052_data->modbus.writeMultipleRegisters(0x11, 0xFF);
// FIXME TD-er: Must leave the sensor to boot for a while.
delay(35);
}
}
/*
// ABC functionality disabled for now, due to a bug in the firmware.
// See https://github.com/letscontrolit/ESPEasy/issues/759
PCONFIG(4) = getFormItemInt(F("ABC_period"));
*/
success = true;
break;
}
case PLUGIN_INIT: {
const int16_t serial_rx = CONFIG_PIN1;
const int16_t serial_tx = CONFIG_PIN2;
const ESPEasySerialPort port = static_cast<ESPEasySerialPort>(CONFIG_PORT);
initPluginTaskData(event->TaskIndex, new (std::nothrow) P052_data_struct());
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr == P052_data) {
return success;
}
if (P052_data->init(port, serial_rx, serial_tx)) {
/*
// ABC functionality disabled for now, due to a bug in the firmware.
// See https://github.com/letscontrolit/ESPEasy/issues/759
const int periodInHours[9] = {0, 1, 12, (24*1), (24*2), (24*4), (24*7),
(24*14), (24*30) };
uint8_t choiceABCperiod = PCONFIG(1);
Plugin_052_setABCperiod(periodInHours[choiceABCperiod]);
*/
P052_data->modbus.setModbusTimeout(P052_MODBUS_TIMEOUT);
// P052_data->modbus.writeMultipleRegisters(0x09, 1); // Start Single Measurement
// P052_data->modbus.writeMultipleRegisters(0x0B, 16); // Measurement Period
// P052_data->modbus.writeMultipleRegisters(0x0C, 8); // Number of samples
success = true;
} else {
clearPluginTaskData(event->TaskIndex);
}
break;
}
case PLUGIN_EXIT: {
success = true;
break;
}
case PLUGIN_READ: {
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->isInitialized()) {
event->sensorType = static_cast<Sensor_VType>(PCONFIG(P052_SENSOR_TYPE_INDEX));
String log = F("Senseair: ");
String logPrefix;
for (int varnr = 0; varnr < P052_NR_OUTPUT_VALUES; ++varnr) {
uint8_t errorcode = 0;
float value = 0;
switch (PCONFIG(varnr)) {
case 1: {
value = P052_data->modbus.readInputRegister(P052_IR4_MEASURED_FILTERED_CO2, errorcode);
logPrefix = F("co2=");
break;
}
case 2: {
int temperatureX100 = P052_data->modbus.readInputRegister(P052_IR5_TEMPERATURE, errorcode);
if (errorcode == 2) {
// Exception code: Illegal Data Address
// SenseAir S8, not for other modules.
temperatureX100 = P052_data->modbus.read_RAM_EEPROM(
P052_CMD_READ_RAM, P052_RAM_ADDR_DET_TEMPERATURE, 2, errorcode);
}
if (temperatureX100 >= 32768) {
// 2-complement value.
temperatureX100 -= 65536;
}
value = static_cast<float>(temperatureX100) / 100.0f;
logPrefix = F("temp=");
break;
}
case 3: {
int rhX100 = P052_data->modbus.readInputRegister(P052_IR6_SPACE_HUMIDITY, errorcode);
value = static_cast<float>(rhX100) / 100.0f;
logPrefix = F("hum=");
break;
}
case 4: {
int status = P052_data->modbus.readInputRegister(P052_IR29_FW_REV, errorcode);
if (errorcode == 0) {
int relayStatus = (status >> 8) & 0x1;
UserVar.setFloat(event->TaskIndex, varnr, relayStatus);
log += F("relay status=");
log += relayStatus;
}
break;
}
case 5: {
int temperatureAdjustment = P052_data->modbus.readInputRegister(P052_IR11_MEASURED_CONCENTRATION_UNFILTERED, errorcode);
value = static_cast<float>(temperatureAdjustment);
logPrefix = F("temp adj=");
break;
}
case 7: {
const int errorWord = P052_data->modbus.readInputRegister(P052_IR_ERRORSTATUS, errorcode);
if (errorcode == 0) {
value = errorWord;
for (size_t i = 0; i < 9; i++) {
if (bitRead(errorWord, i)) {
log += F("err=");
log += i;
break;
}
}
} else {
value = -1;
log += F("err=");
log += -1;
}
break;
}
case 0:
default: {
UserVar.setFloat(event->TaskIndex, varnr, 0);
break;
}
}
if ((errorcode == 0) && (P052_data->modbus.getLastError() == 0)) {
success = true;
UserVar.setFloat(event->TaskIndex, varnr, value);
log += logPrefix;
log += value;
}
}
addLogMove(LOG_LEVEL_INFO, log);
break;
}
break;
}
case PLUGIN_WRITE:
{
P052_data_struct *P052_data =
static_cast<P052_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P052_data) && P052_data->plugin_write(event, string)) {
success = true;
}
break;
}
}
return success;
}
#endif // USES_P052
@@ -48,6 +48,7 @@ void ControllerSettingsStruct::reset() {
VariousBits1.deduplicate = 0;
VariousBits1.useLocalSystemTime = 0;
VariousBits1.TLStype = 0;
VariousBits1.mqttAutoDiscovery = 0;
safe_strncpy(ClientID, F(CONTROLLER_DEFAULT_CLIENTID), sizeof(ClientID));
}
+58 -3
View File
@@ -69,6 +69,18 @@ uint8_t getValueCountFromSensorType(Sensor_VType sensorType)
case Sensor_VType::SENSOR_TYPE_UV_ONLY:
case Sensor_VType::SENSOR_TYPE_UV_INDEX_ONLY:
case Sensor_VType::SENSOR_TYPE_IR_ONLY:
case Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY:
case Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY:
case Sensor_VType::SENSOR_TYPE_CURRENT_ONLY:
case Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY:
case Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY:
case Sensor_VType::SENSOR_TYPE_APPRNT_POWER_USG_ONLY:
case Sensor_VType::SENSOR_TYPE_TVOC_ONLY:
case Sensor_VType::SENSOR_TYPE_BARO_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_RED_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_GREEN_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_BLUE_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_TEMP_ONLY:
return 1;
}
#ifndef BUILD_NO_DEBUG
@@ -112,22 +124,65 @@ const __FlashStringHelper* getSensorTypeLabel(Sensor_VType sensorType) {
case Sensor_VType::SENSOR_TYPE_NONE: return F("None");
case Sensor_VType::SENSOR_TYPE_NOT_SET: break;
// FIXME To be ignored?
// FIXME To be ignored?
#if FEATURE_MQTT_DISCOVER
case Sensor_VType::SENSOR_TYPE_ANALOG_ONLY: return F("Analog");
case Sensor_VType::SENSOR_TYPE_TEMP_ONLY: return F("Temp");
case Sensor_VType::SENSOR_TYPE_HUM_ONLY: return F("Hum");
case Sensor_VType::SENSOR_TYPE_LUX_ONLY: return F("Luc");
case Sensor_VType::SENSOR_TYPE_LUX_ONLY: return F("Lux");
case Sensor_VType::SENSOR_TYPE_DISTANCE_ONLY: return F("Distance");
case Sensor_VType::SENSOR_TYPE_DIRECTION_ONLY: return F("Direction");
case Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY: return F("Dust PM2.5");
case Sensor_VType::SENSOR_TYPE_DUSTPM1_0_ONLY: return F("Dust PM1.0");
case Sensor_VType::SENSOR_TYPE_DUSTPM10_ONLY: return F("Dust PM10");
case Sensor_VType::SENSOR_TYPE_MOISTURE_ONLY: return F("Moisture");
case Sensor_VType::SENSOR_TYPE_CO2_ONLY: return F("CO2");
case Sensor_VType::SENSOR_TYPE_CO2_ONLY: return F("(e)CO2");
case Sensor_VType::SENSOR_TYPE_GPS_ONLY: return F("GPS");
case Sensor_VType::SENSOR_TYPE_UV_ONLY: return F("UV");
case Sensor_VType::SENSOR_TYPE_UV_INDEX_ONLY: return F("UV Index");
case Sensor_VType::SENSOR_TYPE_IR_ONLY: return F("IR");
case Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY: return F("Weight");
case Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY: return F("Voltage");
case Sensor_VType::SENSOR_TYPE_CURRENT_ONLY: return F("Current");
case Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY: return F("Power Usage");
case Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY: return F("Power Factor");
case Sensor_VType::SENSOR_TYPE_APPRNT_POWER_USG_ONLY: return F("Apparent Power Usage");
case Sensor_VType::SENSOR_TYPE_TVOC_ONLY: return F("TVOC");
case Sensor_VType::SENSOR_TYPE_BARO_ONLY: return F("Baro");
case Sensor_VType::SENSOR_TYPE_COLOR_RED_ONLY: return F("Red");
case Sensor_VType::SENSOR_TYPE_COLOR_GREEN_ONLY: return F("Green");
case Sensor_VType::SENSOR_TYPE_COLOR_BLUE_ONLY: return F("Blue");
case Sensor_VType::SENSOR_TYPE_COLOR_TEMP_ONLY: return F("Color temperature");
#else // if FEATURE_MQTT_DISCOVER
case Sensor_VType::SENSOR_TYPE_ANALOG_ONLY:
case Sensor_VType::SENSOR_TYPE_TEMP_ONLY:
case Sensor_VType::SENSOR_TYPE_HUM_ONLY:
case Sensor_VType::SENSOR_TYPE_LUX_ONLY:
case Sensor_VType::SENSOR_TYPE_DISTANCE_ONLY:
case Sensor_VType::SENSOR_TYPE_DIRECTION_ONLY:
case Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY:
case Sensor_VType::SENSOR_TYPE_DUSTPM1_0_ONLY:
case Sensor_VType::SENSOR_TYPE_DUSTPM10_ONLY:
case Sensor_VType::SENSOR_TYPE_MOISTURE_ONLY:
case Sensor_VType::SENSOR_TYPE_CO2_ONLY:
case Sensor_VType::SENSOR_TYPE_GPS_ONLY:
case Sensor_VType::SENSOR_TYPE_UV_ONLY:
case Sensor_VType::SENSOR_TYPE_UV_INDEX_ONLY:
case Sensor_VType::SENSOR_TYPE_IR_ONLY:
case Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY:
case Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY:
case Sensor_VType::SENSOR_TYPE_CURRENT_ONLY:
case Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY:
case Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY:
case Sensor_VType::SENSOR_TYPE_APPRNT_POWER_USG_ONLY:
case Sensor_VType::SENSOR_TYPE_TVOC_ONLY:
case Sensor_VType::SENSOR_TYPE_BARO_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_RED_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_GREEN_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_BLUE_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_TEMP_ONLY:
break;
#endif // if FEATURE_MQTT_DISCOVER
}
return F("");
}
+12
View File
@@ -49,6 +49,18 @@ enum class Sensor_VType : uint8_t {
SENSOR_TYPE_UV_ONLY = 112,
SENSOR_TYPE_UV_INDEX_ONLY = 113,
SENSOR_TYPE_IR_ONLY = 114,
SENSOR_TYPE_WEIGHT_ONLY = 115,
SENSOR_TYPE_VOLTAGE_ONLY = 116,
SENSOR_TYPE_CURRENT_ONLY = 117,
SENSOR_TYPE_POWER_USG_ONLY = 118,
SENSOR_TYPE_POWER_FACT_ONLY = 119,
SENSOR_TYPE_APPRNT_POWER_USG_ONLY = 120,
SENSOR_TYPE_TVOC_ONLY = 121,
SENSOR_TYPE_BARO_ONLY = 122,
SENSOR_TYPE_COLOR_RED_ONLY = 123,
SENSOR_TYPE_COLOR_GREEN_ONLY = 124,
SENSOR_TYPE_COLOR_BLUE_ONLY = 125,
SENSOR_TYPE_COLOR_TEMP_ONLY = 126,
SENSOR_TYPE_NOT_SET = 255
};
+315 -116
View File
@@ -332,10 +332,14 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
// Translate Device[].VType to usable VType per value for MQTT AutoDiscovery
if (MQTT_DiscoveryGetDeviceVType(x, discoveryItems, valueCount)) {
const String hostName = Settings.getHostname();
const unsigned int groupId = Settings.TaskDeviceID[ControllerIndex][x] & 0x3FFF; // Remove opt. flag bits
const String hostName = Settings.getHostname();
const unsigned int groupId = Settings.TaskDeviceID[ControllerIndex][x] & 0x3FFF; // Remove opt. flag bits
const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(ControllerIndex);
const bool usesControllerIDX = validProtocolIndex(ProtocolIndex) &&
getProtocolStruct(ProtocolIndex).usesID;
// dev = device, ids = identifiers
const String deviceElement = groupId != 0 ?
const String deviceElement = groupId != 0 && !usesControllerIDX ?
strformat(F(",\"dev\":{\"name\":\"Group %u\",\"ids\":[\"group_%u\"]}"), groupId, groupId) :
strformat(F(",\"dev\":{\"name\":\"%s %s\",\"ids\":[\"%s_%s\"]}"),
hostName.c_str(), taskName.c_str(), hostName.c_str(), taskName.c_str());
@@ -349,20 +353,17 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
case Sensor_VType::SENSOR_TYPE_SWITCH:
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
const String valuename = getTaskValueName(x, v);
if (!valuename.isEmpty()) {
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publish_topic, &TempEvent, v, F("binary_sensor"));
const String discovery = MQTT_DiscoveryBuildValueTopic(discovery_topic, &TempEvent, v, F("binary_sensor"));
discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\","
"\"dev_cla\":\"switch\",\"cmd_t\":\"~/set\",\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
deviceElement.c_str());
success &= MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, x, v, taskName);
}
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("binary_sensor"),
F("switch"),
EMPTY_STRING, // No unit of measure
&TempEvent,
deviceElement,
success,
true, false);
}
break;
case Sensor_VType::SENSOR_TYPE_TEMP_HUM:
@@ -371,6 +372,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
case Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO:
case Sensor_VType::SENSOR_TYPE_TEMP_ONLY:
case Sensor_VType::SENSOR_TYPE_HUM_ONLY:
case Sensor_VType::SENSOR_TYPE_BARO_ONLY:
{
uint8_t v = discoveryItems[s].varIndex;
@@ -384,20 +386,17 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
}
for (v = fromV; v < maxV; ++v) {
const String valuename = getTaskValueName(x, v);
if (!valuename.isEmpty()) {
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publish_topic, &TempEvent, v, F("sensor"));
const String discovery = MQTT_DiscoveryBuildValueTopic(discovery_topic, &TempEvent, v, F("sensor"));
discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\","
"\"dev_cla\":\"temperature\",\"unit_of_meas\":\"°C\",\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
deviceElement.c_str());
success &= MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, x, v, taskName);
}
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("temperature"),
F("°C"),
&TempEvent,
deviceElement,
success,
false, false);
}
}
@@ -413,44 +412,39 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
}
for (v = fromV; v < maxV; ++v) {
const String valuename = getTaskValueName(x, v);
if (!valuename.isEmpty()) {
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publish_topic, &TempEvent, v, F("sensor"));
const String discovery = MQTT_DiscoveryBuildValueTopic(discovery_topic, &TempEvent, v, F("sensor"));
discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\","
"\"dev_cla\":\"humidity\",\"unit_of_meas\":\"%\",\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
deviceElement.c_str());
success &= MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, x, v, taskName);
}
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("humidity"),
F("%"),
&TempEvent,
deviceElement,
success,
false, false);
}
}
// Barometric pressure
if ((Sensor_VType::SENSOR_TYPE_TEMP_BARO == discoveryItems[s].VType) ||
(Sensor_VType::SENSOR_TYPE_TEMP_HUM_BARO == discoveryItems[s].VType) ||
(Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO == discoveryItems[s].VType)) {
(Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO == discoveryItems[s].VType) ||
(Sensor_VType::SENSOR_TYPE_BARO_ONLY == discoveryItems[s].VType)) {
if (Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO == discoveryItems[s].VType) {
v++; // Skip 2nd value = 'EMPTY'
}
const String valuename = getTaskValueName(x, v);
if (!valuename.isEmpty()) {
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publish_topic, &TempEvent, v, F("sensor"));
const String discovery = MQTT_DiscoveryBuildValueTopic(discovery_topic, &TempEvent, v, F("sensor"));
discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\","
"\"dev_cla\":\"pressure\",\"unit_of_meas\":\"hPa\",\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
deviceElement.c_str());
success &= MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, x, v, taskName);
}
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("pressure"),
F("hPa"),
&TempEvent,
deviceElement,
success,
false, false);
v++;
}
break;
@@ -458,53 +452,206 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn
case Sensor_VType::SENSOR_TYPE_DISTANCE_ONLY:
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
const String valuename = getTaskValueName(x, v);
if (!valuename.isEmpty()) {
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publish_topic, &TempEvent, v, F("sensor"));
const String discovery = MQTT_DiscoveryBuildValueTopic(discovery_topic, &TempEvent, v, F("sensor"));
discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\","
"\"dev_cla\":\"distance\",\"unit_of_meas\":\"cm\",\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
deviceElement.c_str());
success &= MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, x, v, taskName);
}
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("distance"),
F("cm"),
&TempEvent,
deviceElement,
success,
false, false);
}
break;
case Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY:
case Sensor_VType::SENSOR_TYPE_DUSTPM1_0_ONLY:
case Sensor_VType::SENSOR_TYPE_DUSTPM10_ONLY:
{
const __FlashStringHelper*dev = (Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY == discoveryItems[s].VType ? F("pm25") :
Sensor_VType::SENSOR_TYPE_DUSTPM1_0_ONLY == discoveryItems[s].VType ? F("pm1") :
F("pm10"));
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
const String valuename = getTaskValueName(x, v);
if (!valuename.isEmpty()) {
const String pm = (Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY == discoveryItems[2].VType ? F("pm25") :
Sensor_VType::SENSOR_TYPE_DUSTPM1_0_ONLY == discoveryItems[2].VType ? F("pm1") :
F("pm10"));
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publish_topic, &TempEvent, v, F("sensor"));
const String discovery = MQTT_DiscoveryBuildValueTopic(discovery_topic, &TempEvent, v, F("sensor"));
discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\","
"\"dev_cla\":\"%s\",\"unit_of_meas\":\"µg/m³\",\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
pm.c_str(),
deviceElement.c_str());
success &= MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, x, v, taskName);
}
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
dev,
F("µg/m³"),
&TempEvent,
deviceElement,
success,
false, false);
}
break;
}
case Sensor_VType::SENSOR_TYPE_CO2_ONLY:
case Sensor_VType::SENSOR_TYPE_TVOC_ONLY:
{
const __FlashStringHelper*dev = Sensor_VType::SENSOR_TYPE_CO2_ONLY == discoveryItems[s].VType ?
F("carbon_dioxide") : F("volatile_organic_compounds");
const __FlashStringHelper*uom = Sensor_VType::SENSOR_TYPE_CO2_ONLY == discoveryItems[s].VType ?
F("ppm") : F("ppd");
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
dev,
uom,
&TempEvent,
deviceElement,
success,
false, false);
}
break;
}
case Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY:
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("mdi:scale"), // Icon
F("kg"),
&TempEvent,
deviceElement,
success,
false, true);
}
break;
case Sensor_VType::SENSOR_TYPE_MOISTURE_ONLY:
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("mdi:cup-water"), // Icon
F("%"),
&TempEvent,
deviceElement,
success,
false, true);
}
break;
case Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY:
case Sensor_VType::SENSOR_TYPE_CURRENT_ONLY:
{
const __FlashStringHelper*dev = Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY == discoveryItems[s].VType ?
F("voltage") : F("current");
const __FlashStringHelper*uom = Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY == discoveryItems[s].VType ?
F("V") : F("A");
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
dev,
uom,
&TempEvent,
deviceElement,
success,
false, false);
}
break;
}
case Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY:
case Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY:
case Sensor_VType::SENSOR_TYPE_APPRNT_POWER_USG_ONLY:
{
const __FlashStringHelper*dev = Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY == discoveryItems[s].VType ?
F("power_factor") : F("power");
const __FlashStringHelper*uom = Sensor_VType::SENSOR_TYPE_POWER_FACT_ONLY == discoveryItems[s].VType ? F("Cos φ") :
Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY == discoveryItems[s].VType ? F("kWh") :
F("VA");
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
dev,
uom,
&TempEvent,
deviceElement,
success,
false, false);
}
break;
}
case Sensor_VType::SENSOR_TYPE_LUX_ONLY:
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("illuminance"),
F("lux"),
&TempEvent,
deviceElement,
success,
false, false);
}
break;
case Sensor_VType::SENSOR_TYPE_COLOR_RED_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_GREEN_ONLY:
case Sensor_VType::SENSOR_TYPE_COLOR_BLUE_ONLY:
{
const __FlashStringHelper*uom = Sensor_VType::SENSOR_TYPE_COLOR_RED_ONLY == discoveryItems[s].VType ? F("R") :
Sensor_VType::SENSOR_TYPE_COLOR_GREEN_ONLY == discoveryItems[s].VType ? F("G") :
F("B");
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("mdi:palette"),
uom,
&TempEvent,
deviceElement,
success,
false, true);
}
break;
}
case Sensor_VType::SENSOR_TYPE_COLOR_TEMP_ONLY:
for (uint8_t v = discoveryItems[s].varIndex; v < varCount; ++v) {
success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, taskName,
ControllerIndex,
publish_topic,
discovery_topic,
F("sensor"),
F("mdi:temperature-kelvin"),
F("K"),
&TempEvent,
deviceElement,
success,
false, true);
}
break;
case Sensor_VType::SENSOR_TYPE_WIND:
case Sensor_VType::SENSOR_TYPE_ANALOG_ONLY:
case Sensor_VType::SENSOR_TYPE_LUX_ONLY:
case Sensor_VType::SENSOR_TYPE_DIRECTION_ONLY:
case Sensor_VType::SENSOR_TYPE_MOISTURE_ONLY:
case Sensor_VType::SENSOR_TYPE_CO2_ONLY:
case Sensor_VType::SENSOR_TYPE_GPS_ONLY:
case Sensor_VType::SENSOR_TYPE_UV_ONLY:
case Sensor_VType::SENSOR_TYPE_UV_INDEX_ONLY:
@@ -591,7 +738,12 @@ bool MQTT_DiscoveryGetDeviceVType(taskIndex_t TaskIndex,
case 144: // Vindriktning / PM1006K
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY, valueCount, 0));
break;
case 47: // Soil Moisture
case 27: // INA219
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_CURRENT_ONLY, 1, 1));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY, 1, 2));
break;
case 47: // Soil Moisture
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_TEMP_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_MOISTURE_ONLY, 1, 1));
@@ -613,10 +765,18 @@ bool MQTT_DiscoveryGetDeviceVType(taskIndex_t TaskIndex,
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_DUSTPM10_ONLY, 1, 1));
break;
case 67: // HX711 Load cell
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY, valueCount, 0));
break;
case 74: // TSL2591
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_LUX_ONLY, 3, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_UV_ONLY, 1, 3));
break;
case 83: // SGP30 TVOC/eCO2
case 90: // CCS811 TVOC/eCO2
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_TVOC_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_CO2_ONLY, 1, 1));
break;
case 84: // VEML6070 (UV and Index only)
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_UV_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_UV_INDEX_ONLY, 1, 1));
@@ -657,6 +817,10 @@ bool MQTT_DiscoveryGetDeviceVType(taskIndex_t TaskIndex,
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_CO2_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_TEMP_HUM, 1, 1));
break;
case 151: // Honeywell pressure
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_BARO_ONLY, 1, 0));
discoveryItems.push_back(DiscoveryItem(Sensor_VType::SENSOR_TYPE_TEMP_ONLY, 1, 1));
break;
// Deliberately ignored/skipped for now:
case 3: // Pulse: Needs special handling/not usable?
@@ -669,47 +833,45 @@ bool MQTT_DiscoveryGetDeviceVType(taskIndex_t TaskIndex,
VType = Sensor_VType::SENSOR_TYPE_NONE;
break;
// To be reviewed/considered later:
// To be reviewed/considered later/custom/special handled:
case 8: // RFID Wiegand
case 17: // RFID PN532
case 27: // INA219
case 40: // RFID ID12
case 45: // MPU6050
case 50: // TCS34725 RGB: Needs special handling
case 52: // SensAir: Needs special handling
case 64: // APDS9960: Needs special handling
case 66: // VEML6040 RGB: Needs special handling
case 67: // HX711 Load cell
case 71: // Kamstrup Heat
case 76: // HLW8012
case 77: // CSE7766
case 76: // HLW8012: Needs special handling
case 77: // CSE7766: Needs special handling
case 78: // Eastron: Needs special handling
case 82: // GPS: Needs special handling
case 83: // SGP30 TVOC/eCO2
case 85: // AcuDC243
case 90: // CCS811 TVOC/eCO2
case 92: // DL-bus
case 85: // AcuDC243: Needs special handling
case 92: // DL-bus: Needs special handling
case 93: // Mitsubishi Heatpump
case 102: // PZEM004
case 103: // Atlas EZO
case 108: // DDS238
case 102: // PZEM004: Needs special handling
case 103: // Atlas EZO: Needs special handling
case 108: // DDS238: Needs special handling
case 111: // RFID RC522
case 112: // AS7265x: Needs special handling
case 115: // MAX1704x
case 117: // SCD30 CO2: Needs special handling
case 132: // INA3221
case 132: // INA3221: Needs special handling
case 142: // AS5600 Angle/rotation
case 145: // MQxxx gases
case 147: // SGP4x
case 151: // Honeywell pressure
case 159: // LD2410
case 163: // RadSens
case 164: // ENS160
case 164: // ENS160: Needs special handling
case 167: // Vindstyrka: Needs special handling
case 169: // AS3935 Lightning
case 176: // Victron VE.Direct: Needs user-configurable Sensor_VType + Unit_of_measure per value
// Reset
VType = Sensor_VType::SENSOR_TYPE_NONE;
break;
// Plugin ID numbers not listed in the above switch statement most likely have the correct Sensor_VType value set in DeviceStruct
}
// Use Device VType setting if not overridden or reset
@@ -792,20 +954,20 @@ String MQTT_DiscoveryBuildValueTopic(const String & topic,
bool MQTT_DiscoveryPublish(controllerIndex_t ControllerIndex,
const String & topic,
String & discoveryMessage,
const String & discoveryMessage,
taskIndex_t x,
uint8_t v,
const String & taskName) {
bool result = false;
// TODO Send each discovery message separate
// result = MQTTpublish(ControllerIndex, INVALID_TASK_INDEX, concat(topic, F("/config")).c_str(), discoveryMessage.c_str(), false);
// Send each discovery message separate
result = MQTTpublish(ControllerIndex, INVALID_TASK_INDEX, concat(topic, F("/config")).c_str(), discoveryMessage.c_str(), false);
# ifndef BUILD_NO_DEBUG
// FIXME change this log to DEBUG level
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO, strformat(F("MQTT : Discovery Task %d '%s': disc.topic: %s"),
x + 1, taskName.c_str(), concat(topic, F("/config")).c_str()));
addLog(LOG_LEVEL_INFO, strformat(F("MQTT : Discovery %d Task %d '%s': disc.topic: %s"),
result, x + 1, taskName.c_str(), concat(topic, F("/config")).c_str()));
addLog(LOG_LEVEL_INFO, strformat(F("MQTT : Discovery payload: %s"),
discoveryMessage.c_str()));
}
@@ -813,6 +975,43 @@ bool MQTT_DiscoveryPublish(controllerIndex_t ControllerIndex,
return result;
}
bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex,
uint8_t taskValue,
String taskName,
controllerIndex_t ControllerIndex,
String publishTopic,
String discoveryTopic,
const __FlashStringHelper*componentClass,
String deviceClass,
String unitOfMeasure,
struct EventStruct *event,
const String deviceElement,
bool success,
bool hasSet,
bool hasIcon) {
const String withSet = hasSet ? F(",\"cmd_t\":\"~/set\"") : EMPTY_STRING;
const String devOrIcon = hasIcon ? F("ic") : F("dev_cla");
const String withUoM = !unitOfMeasure.isEmpty() ?
strformat(F(",\"unit_of_meas\":\"%s\""), unitOfMeasure.c_str()) :
EMPTY_STRING;
const String valuename = getTaskValueName(taskIndex, taskValue);
if (!valuename.isEmpty()) {
const String uniqueId = MQTT_TaskValueUniqueName(taskName, valuename);
const String publish = MQTT_DiscoveryBuildValueTopic(publishTopic, event, taskValue, componentClass);
const String discovery = MQTT_DiscoveryBuildValueTopic(discoveryTopic, event, taskValue, componentClass);
const String discoveryMessage = strformat(F("{\"~\":\"%s\",\"name\":\"%s %s\",\"uniq_id\":\"%s\",\"schema\":\"basic\","
"\"%s\":\"%s\"%s%s,\"stat_t\":\"~\""
"%s}"), // deviceElement last
publish.c_str(), taskName.c_str(), valuename.c_str(), uniqueId.c_str(),
devOrIcon.c_str(), deviceClass.c_str(), withUoM.c_str(), withSet.c_str(),
deviceElement.c_str());
return MQTT_DiscoveryPublish(ControllerIndex, discovery, discoveryMessage, taskIndex, taskValue, taskName);
}
return success;
}
# endif // if FEATURE_MQTT_DISCOVER
#endif // if FEATURE_MQTT
+17 -1
View File
@@ -38,12 +38,28 @@ String MQTT_DiscoveryBuildValueTopic(const String & topic,
struct EventStruct *event,
uint8_t taskValueIndex,
const __FlashStringHelper*deviceClass);
bool MQTT_DiscoveryPublish(controllerIndex_t ControllerIndex,
const String & topic,
String & discoveryMessage,
const String & discoveryMessage,
taskIndex_t x,
uint8_t v,
const String & taskName);
bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex,
uint8_t taskValue,
String taskName,
controllerIndex_t ControllerIndex,
String publishTopic,
String discoveryTopic,
const __FlashStringHelper*componentClass,
String deviceClass,
String unitOfMeasure,
struct EventStruct *event,
const String deviceElement,
bool success,
bool hasSet,
bool hasIcon);
# endif // if FEATURE_MQTT_DISCOVER
#endif // if FEATURE_MQTT
#endif // ifndef CPLUGIN_HELPER_MQTT_H
File diff suppressed because it is too large Load Diff