OpenDTU-old/src/MqttHandleVedirect.cpp
2024-05-31 20:16:29 +02:00

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 &currentData,
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
}