Merge pull request #4110 from itCarl/usermod_battery_mqtt_update

Update usermod "Battery" improved MQTT support
This commit is contained in:
Blaž Kristan 2024-08-20 12:51:02 +02:00 committed by GitHub
commit d11ca2e159
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 114 additions and 47 deletions

View File

@ -131,6 +131,11 @@ Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.6
## 📝 Change Log ## 📝 Change Log
2024-08-19
- Improved MQTT support
- Added battery percentage & battery voltage as MQTT topic
2024-05-11 2024-05-11
- Documentation updated - Documentation updated

View File

@ -50,6 +50,7 @@ class UsermodBattery : public Usermod
// //
bool initDone = false; bool initDone = false;
bool initializing = true; bool initializing = true;
bool HomeAssistantDiscovery = false;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
@ -59,6 +60,7 @@ class UsermodBattery : public Usermod
static const char _preset[]; static const char _preset[];
static const char _duration[]; static const char _duration[];
static const char _init[]; static const char _init[];
static const char _haDiscovery[];
/** /**
* Helper for rounding floating point values * Helper for rounding floating point values
@ -69,6 +71,17 @@ class UsermodBattery : public Usermod
return (float)(nx / 100); return (float)(nx / 100);
} }
/**
* Helper for converting a string to lowercase
*/
String stringToLower(String str)
{
for(int i = 0; i < str.length(); i++)
if(str[i] >= 'A' && str[i] <= 'Z')
str[i] += 32;
return str;
}
/** /**
* Turn off all leds * Turn off all leds
*/ */
@ -115,6 +128,55 @@ class UsermodBattery : public Usermod
#endif #endif
} }
#ifndef WLED_DISABLE_MQTT
void addMqttSensor(const String &name, const String &type, const String &topic, const String &deviceClass, const String &unitOfMeasurement = "", const bool &isDiagnostic = false)
{
StaticJsonDocument<600> doc;
char uid[128], json_str[1024], buf[128];
doc[F("name")] = name;
doc[F("stat_t")] = topic;
sprintf_P(uid, PSTR("%s_%s_%s"), escapedMac.c_str(), stringToLower(name).c_str(), type);
doc[F("uniq_id")] = uid;
doc[F("dev_cla")] = deviceClass;
doc[F("exp_aft")] = 1800;
if(type == "binary_sensor") {
doc[F("pl_on")] = "on";
doc[F("pl_off")] = "off";
}
if(unitOfMeasurement != "")
doc[F("unit_of_measurement")] = unitOfMeasurement;
if(isDiagnostic)
doc[F("entity_category")] = "diagnostic";
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("ids")] = String(F("wled-sensor-")) + mqttClientID;
device[F("mf")] = F(WLED_BRAND);
device[F("mdl")] = F(WLED_PRODUCT_NAME);
device[F("sw")] = versionString;
sprintf_P(buf, PSTR("homeassistant/%s/%s/%s/config"), type, mqttClientID, uid);
DEBUG_PRINTLN(buf);
size_t payload_size = serializeJson(doc, json_str);
DEBUG_PRINTLN(json_str);
mqtt->publish(buf, 0, true, json_str, payload_size);
}
void publishMqtt(const char* topic, const char* state)
{
if (WLED_MQTT_CONNECTED) {
char buf[128];
snprintf_P(buf, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
mqtt->publish(buf, 0, false, state);
}
}
#endif
public: public:
//Functions called by WLED //Functions called by WLED
@ -223,13 +285,8 @@ class UsermodBattery : public Usermod
turnOff(); turnOff();
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
// SmartHome stuff publishMqtt("battery", String(bat->getLevel(), 0).c_str());
// still don't know much about MQTT and/or HA publishMqtt("voltage", String(bat->getVoltage()).c_str());
if (WLED_MQTT_CONNECTED) {
char buf[64]; // buffer for snprintf()
snprintf_P(buf, 63, PSTR("%s/voltage"), mqttDeviceTopic);
mqtt->publish(buf, 0, false, String(bat->getVoltage()).c_str());
}
#endif #endif
} }
@ -288,6 +345,7 @@ class UsermodBattery : public Usermod
battery[F("calibration")] = bat->getCalibration(); battery[F("calibration")] = bat->getCalibration();
battery[F("voltage-multiplier")] = bat->getVoltageMultiplier(); battery[F("voltage-multiplier")] = bat->getVoltageMultiplier();
battery[FPSTR(_readInterval)] = readingInterval; battery[FPSTR(_readInterval)] = readingInterval;
battery[FPSTR(_haDiscovery)] = HomeAssistantDiscovery;
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
ao[FPSTR(_enabled)] = autoOffEnabled; ao[FPSTR(_enabled)] = autoOffEnabled;
@ -307,8 +365,8 @@ class UsermodBattery : public Usermod
getJsonValue(battery[F("max-voltage")], cfg.maxVoltage); getJsonValue(battery[F("max-voltage")], cfg.maxVoltage);
getJsonValue(battery[F("calibration")], cfg.calibration); getJsonValue(battery[F("calibration")], cfg.calibration);
getJsonValue(battery[F("voltage-multiplier")], cfg.voltageMultiplier); getJsonValue(battery[F("voltage-multiplier")], cfg.voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
setHomeAssistantDiscovery(battery[FPSTR(_haDiscovery)] | HomeAssistantDiscovery);
JsonObject ao = battery[F("auto-off")]; JsonObject ao = battery[F("auto-off")];
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled); setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
@ -420,17 +478,18 @@ class UsermodBattery : public Usermod
void appendConfigData() void appendConfigData()
{ {
// Total: 462 Bytes // Total: 462 Bytes
oappend(SET_F("td=addDropdown('Battery', 'type');")); // 35 Bytes oappend(SET_F("td=addDropdown('Battery','type');")); // 34 Bytes
oappend(SET_F("addOption(td, 'Unkown', '0');")); // 30 Bytes oappend(SET_F("addOption(td,'Unkown','0');")); // 28 Bytes
oappend(SET_F("addOption(td, 'LiPo', '1');")); // 28 Bytes oappend(SET_F("addOption(td,'LiPo','1');")); // 26 Bytes
oappend(SET_F("addOption(td, 'LiOn', '2');")); // 28 Bytes oappend(SET_F("addOption(td,'LiOn','2');")); // 26 Bytes
oappend(SET_F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes oappend(SET_F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes
oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');")); // 40 Bytes oappend(SET_F("addInfo('Battery:min-voltage',1,'v');")); // 38 Bytes
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');")); // 40 Bytes oappend(SET_F("addInfo('Battery:max-voltage',1,'v');")); // 38 Bytes
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');")); // 38 Bytes oappend(SET_F("addInfo('Battery:interval',1,'ms');")); // 36 Bytes
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');")); // 47 Bytes oappend(SET_F("addInfo('Battery:HA-discovery',1,'');")); // 38 Bytes
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');")); // 48 Bytes oappend(SET_F("addInfo('Battery:auto-off:threshold',1,'%');")); // 45 Bytes
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');")); // 47 Bytes oappend(SET_F("addInfo('Battery:indicator:threshold',1,'%');")); // 46 Bytes
oappend(SET_F("addInfo('Battery:indicator:duration',1,'s');")); // 45 Bytes
// this option list would exeed the oappend() buffer // this option list would exeed the oappend() buffer
// a list of all presets to select one from // a list of all presets to select one from
@ -478,12 +537,12 @@ class UsermodBattery : public Usermod
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
newBatteryPin = battery[F("pin")] | newBatteryPin; newBatteryPin = battery[F("pin")] | newBatteryPin;
#endif #endif
// calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
setMinBatteryVoltage(battery[F("min-voltage")] | bat->getMinVoltage()); setMinBatteryVoltage(battery[F("min-voltage")] | bat->getMinVoltage());
setMaxBatteryVoltage(battery[F("max-voltage")] | bat->getMaxVoltage()); setMaxBatteryVoltage(battery[F("max-voltage")] | bat->getMaxVoltage());
setCalibration(battery[F("calibration")] | bat->getCalibration()); setCalibration(battery[F("calibration")] | bat->getCalibration());
setVoltageMultiplier(battery[F("voltage-multiplier")] | bat->getVoltageMultiplier()); setVoltageMultiplier(battery[F("voltage-multiplier")] | bat->getVoltageMultiplier());
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
setHomeAssistantDiscovery(battery[FPSTR(_haDiscovery)] | HomeAssistantDiscovery);
getUsermodConfigFromJsonObject(battery); getUsermodConfigFromJsonObject(battery);
@ -513,38 +572,24 @@ class UsermodBattery : public Usermod
return !battery[FPSTR(_readInterval)].isNull(); return !battery[FPSTR(_readInterval)].isNull();
} }
/** #ifndef WLED_DISABLE_MQTT
* TBD: Generate a preset sample for low power indication void onMqttConnect(bool sessionPresent)
* a button on the config page would be cool, currently not possible
*/
void generateExamplePreset()
{ {
// StaticJsonDocument<300> j; // Home Assistant Autodiscovery
// JsonObject preset = j.createNestedObject(); if (!HomeAssistantDiscovery)
// preset["mainseg"] = 0; return;
// JsonArray seg = preset.createNestedArray("seg");
// JsonObject seg0 = seg.createNestedObject();
// seg0["id"] = 0;
// seg0["start"] = 0;
// seg0["stop"] = 60;
// seg0["grp"] = 0;
// seg0["spc"] = 0;
// seg0["on"] = true;
// seg0["bri"] = 255;
// JsonArray col0 = seg0.createNestedArray("col"); // battery percentage
// JsonArray col00 = col0.createNestedArray(); char mqttBatteryTopic[128];
// col00.add(255); snprintf_P(mqttBatteryTopic, 127, PSTR("%s/battery"), mqttDeviceTopic);
// col00.add(0); this->addMqttSensor(F("Battery"), "sensor", mqttBatteryTopic, "battery", "%", true);
// col00.add(0);
// seg0["fx"] = 1; // voltage
// seg0["sx"] = 128; char mqttVoltageTopic[128];
// seg0["ix"] = 128; snprintf_P(mqttVoltageTopic, 127, PSTR("%s/voltage"), mqttDeviceTopic);
this->addMqttSensor(F("Voltage"), "sensor", mqttVoltageTopic, "voltage", "V", true);
// savePreset(199, "Low power Indicator", preset);
} }
#endif
/* /*
* *
@ -785,6 +830,22 @@ class UsermodBattery : public Usermod
{ {
return lowPowerIndicationDone; return lowPowerIndicationDone;
} }
/**
* Set Home Assistant auto discovery
*/
void setHomeAssistantDiscovery(bool enable)
{
HomeAssistantDiscovery = enable;
}
/**
* Get Home Assistant auto discovery
*/
bool getHomeAssistantDiscovery()
{
return HomeAssistantDiscovery;
}
}; };
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
@ -795,3 +856,4 @@ const char UsermodBattery::_threshold[] PROGMEM = "threshold";
const char UsermodBattery::_preset[] PROGMEM = "preset"; const char UsermodBattery::_preset[] PROGMEM = "preset";
const char UsermodBattery::_duration[] PROGMEM = "duration"; const char UsermodBattery::_duration[] PROGMEM = "duration";
const char UsermodBattery::_init[] PROGMEM = "init"; const char UsermodBattery::_init[] PROGMEM = "init";
const char UsermodBattery::_haDiscovery[] PROGMEM = "HA-discovery";