144 lines
6.2 KiB
C++
144 lines
6.2 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2022 Helge Erbe and others
|
|
*/
|
|
#include "VictronMppt.h"
|
|
#include "MqttHandleVedirect.h"
|
|
#include "MqttSettings.h"
|
|
#include "MessageOutput.h"
|
|
|
|
|
|
|
|
|
|
MqttHandleVedirectClass MqttHandleVedirect;
|
|
|
|
// #define MQTTHANDLEVEDIRECT_DEBUG
|
|
|
|
void MqttHandleVedirectClass::init(Scheduler& scheduler)
|
|
{
|
|
scheduler.addTask(_loopTask);
|
|
_loopTask.setCallback([this] { loop(); });
|
|
_loopTask.setIterations(TASK_FOREVER);
|
|
_loopTask.enable();
|
|
|
|
// initially force a full publish
|
|
this->forceUpdate();
|
|
}
|
|
|
|
void MqttHandleVedirectClass::forceUpdate()
|
|
{
|
|
// initially force a full publish
|
|
_nextPublishUpdatesOnly = 0;
|
|
_nextPublishFull = 1;
|
|
}
|
|
|
|
|
|
void MqttHandleVedirectClass::loop()
|
|
{
|
|
CONFIG_T& config = Configuration.get();
|
|
|
|
if (!MqttSettings.getConnected() || !config.Vedirect.Enabled) {
|
|
return;
|
|
}
|
|
|
|
if ((millis() >= _nextPublishFull) || (millis() >= _nextPublishUpdatesOnly)) {
|
|
// determine if this cycle should publish full values or updates only
|
|
if (_nextPublishFull <= _nextPublishUpdatesOnly) {
|
|
_PublishFull = true;
|
|
} else {
|
|
_PublishFull = !config.Vedirect.UpdatesOnly;
|
|
}
|
|
|
|
#ifdef MQTTHANDLEVEDIRECT_DEBUG
|
|
MessageOutput.printf("\r\n\r\nMqttHandleVedirectClass::loop millis %lu _nextPublishUpdatesOnly %u _nextPublishFull %u\r\n", millis(), _nextPublishUpdatesOnly, _nextPublishFull);
|
|
if (_PublishFull) {
|
|
MessageOutput.println("MqttHandleVedirectClass::loop publish full");
|
|
} else {
|
|
MessageOutput.println("MqttHandleVedirectClass::loop publish updates only");
|
|
}
|
|
#endif
|
|
|
|
for (int idx = 0; idx < VictronMppt.controllerAmount(); ++idx) {
|
|
std::optional<VeDirectMpptController::data_t> optMpptData = VictronMppt.getData(idx);
|
|
if (!optMpptData.has_value()) { continue; }
|
|
|
|
auto const& kvFrame = _kvFrames[optMpptData->serialNr_SER];
|
|
publish_mppt_data(*optMpptData, kvFrame);
|
|
if (!_PublishFull) {
|
|
_kvFrames[optMpptData->serialNr_SER] = *optMpptData;
|
|
}
|
|
}
|
|
|
|
// now calculate next points of time to publish
|
|
_nextPublishUpdatesOnly = millis() + (config.Mqtt.PublishInterval * 1000);
|
|
|
|
if (_PublishFull) {
|
|
// when Home Assistant MQTT-Auto-Discovery is active,
|
|
// and "enable expiration" is active, all values must be published at
|
|
// least once before the announced expiry interval is reached
|
|
if ((config.Vedirect.UpdatesOnly) && (config.Mqtt.Hass.Enabled) && (config.Mqtt.Hass.Expire)) {
|
|
_nextPublishFull = millis() + (((config.Mqtt.PublishInterval * 3) - 1) * 1000);
|
|
|
|
#ifdef MQTTHANDLEVEDIRECT_DEBUG
|
|
uint32_t _tmpNextFullSeconds = (config.Mqtt_PublishInterval * 3) - 1;
|
|
MessageOutput.printf("MqttHandleVedirectClass::loop _tmpNextFullSeconds %u - _nextPublishFull %u \r\n", _tmpNextFullSeconds, _nextPublishFull);
|
|
#endif
|
|
|
|
} else {
|
|
// no future publish full needed
|
|
_nextPublishFull = UINT32_MAX;
|
|
}
|
|
}
|
|
|
|
#ifdef MQTTHANDLEVEDIRECT_DEBUG
|
|
MessageOutput.printf("MqttHandleVedirectClass::loop _nextPublishUpdatesOnly %u _nextPublishFull %u\r\n", _nextPublishUpdatesOnly, _nextPublishFull);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void MqttHandleVedirectClass::publish_mppt_data(const VeDirectMpptController::data_t ¤tData,
|
|
const VeDirectMpptController::data_t &previousData) const {
|
|
String value;
|
|
String topic = "victron/";
|
|
topic.concat(currentData.serialNr_SER);
|
|
topic.concat("/");
|
|
|
|
#define PUBLISH(sm, t, val) \
|
|
if (_PublishFull || currentData.sm != previousData.sm) { \
|
|
MqttSettings.publish(topic + t, String(val)); \
|
|
}
|
|
|
|
PUBLISH(productID_PID, "PID", currentData.getPidAsString().data());
|
|
PUBLISH(serialNr_SER, "SER", currentData.serialNr_SER);
|
|
PUBLISH(firmwareVer_FW, "FW", currentData.firmwareVer_FW);
|
|
PUBLISH(loadOutputState_LOAD, "LOAD", (currentData.loadOutputState_LOAD ? "ON" : "OFF"));
|
|
PUBLISH(currentState_CS, "CS", currentData.getCsAsString().data());
|
|
PUBLISH(errorCode_ERR, "ERR", currentData.getErrAsString().data());
|
|
PUBLISH(offReason_OR, "OR", currentData.getOrAsString().data());
|
|
PUBLISH(stateOfTracker_MPPT, "MPPT", currentData.getMpptAsString().data());
|
|
PUBLISH(daySequenceNr_HSDS, "HSDS", currentData.daySequenceNr_HSDS);
|
|
PUBLISH(batteryVoltage_V_mV, "V", currentData.batteryVoltage_V_mV / 1000.0);
|
|
PUBLISH(batteryCurrent_I_mA, "I", currentData.batteryCurrent_I_mA / 1000.0);
|
|
PUBLISH(batteryOutputPower_W, "P", currentData.batteryOutputPower_W);
|
|
PUBLISH(panelVoltage_VPV_mV, "VPV", currentData.panelVoltage_VPV_mV / 1000.0);
|
|
PUBLISH(panelCurrent_mA, "IPV", currentData.panelCurrent_mA / 1000.0);
|
|
PUBLISH(panelPower_PPV_W, "PPV", currentData.panelPower_PPV_W);
|
|
PUBLISH(mpptEfficiency_Percent, "E", currentData.mpptEfficiency_Percent);
|
|
PUBLISH(yieldTotal_H19_Wh, "H19", currentData.yieldTotal_H19_Wh / 1000.0);
|
|
PUBLISH(yieldToday_H20_Wh, "H20", currentData.yieldToday_H20_Wh / 1000.0);
|
|
PUBLISH(maxPowerToday_H21_W, "H21", currentData.maxPowerToday_H21_W);
|
|
PUBLISH(yieldYesterday_H22_Wh, "H22", currentData.yieldYesterday_H22_Wh / 1000.0);
|
|
PUBLISH(maxPowerYesterday_H23_W, "H23", currentData.maxPowerYesterday_H23_W);
|
|
#undef PUBLILSH
|
|
|
|
#define PUBLISH_OPT(sm, t, val) \
|
|
if (currentData.sm.first != 0 && (_PublishFull || currentData.sm.second != previousData.sm.second)) { \
|
|
MqttSettings.publish(topic + t, String(val)); \
|
|
}
|
|
|
|
PUBLISH_OPT(NetworkTotalDcInputPowerMilliWatts, "NetworkTotalDcInputPower", currentData.NetworkTotalDcInputPowerMilliWatts.second / 1000.0);
|
|
PUBLISH_OPT(MpptTemperatureMilliCelsius, "MpptTemperature", currentData.MpptTemperatureMilliCelsius.second / 1000.0);
|
|
PUBLISH_OPT(SmartBatterySenseTemperatureMilliCelsius, "SmartBatterySenseTemperature", currentData.SmartBatterySenseTemperatureMilliCelsius.second / 1000.0);
|
|
#undef PUBLILSH_OPT
|
|
}
|