gridPowerDelta

This commit is contained in:
Patrick Haßel 2025-09-03 08:50:01 +02:00
parent c4d30371cf
commit 0eb51d8ad6
4 changed files with 136 additions and 21 deletions

View File

@ -85,7 +85,7 @@ public:
this->onCount = onCount_; this->onCount = onCount_;
} }
void loop() { virtual void loop() {
const auto status = get(); const auto status = get();
const auto ageMillis = millis() - stateMillis; const auto ageMillis = millis() - stateMillis;
if (status) { if (status) {

View File

@ -11,36 +11,55 @@ class Relay final : public Output {
String topicFallback; String topicFallback;
const uint8_t index;
String topic; String topic;
const uint8_t index; long gridPowerDeltaOnThreshold = 0;
long gridPowerDeltaOnDelay = 0;
long gridPowerDeltaOffThreshold = 0;
long gridPowerDeltaOffDelay = 0;
unsigned long gridPowerDeltaMillis = 0;
public: public:
Relay(const uint8_t index, const String &topic, const char *name, const uint8_t pin, const bool inverted, const bool logState) : Output(name, pin, inverted, logState), nameFallback(name), topicFallback(topic), topic(topic), index(index) { Relay(const uint8_t index, const String &topic, const char *name, const uint8_t pin, const bool inverted, const bool logState) : Output(name, pin, inverted, logState), nameFallback(name), topicFallback(topic), index(index), topic(topic) {
// //
} }
void setup() override { void setup() override {
Output::setup(); Output::setup();
Output::setName(configRead(path("name"), nameFallback)); Output::setName(configRead(path("name"), nameFallback));
topic = configRead(path("topic"), topicFallback);
Output::setInitial(configRead(path("initial"), INITIAL_OFF)); Output::setInitial(configRead(path("initial"), INITIAL_OFF));
Output::setOnMillis(configRead(path("onMillis"), 0L)); Output::setOnMillis(configRead(path("onMillis"), 0L));
Output::setOffMillis(configRead(path("offMillis"), 0L)); Output::setOffMillis(configRead(path("offMillis"), 0L));
topic = configRead(path("topic"), topicFallback);
gridPowerDeltaOnThreshold = configRead(path("autoOnThreshold"), -400L);
gridPowerDeltaOnDelay = configRead(path("autoOnDelay"), 30000L);
gridPowerDeltaOffThreshold = configRead(path("autoOffThreshold"), 100L);
gridPowerDeltaOffDelay = configRead(path("autoOffDelay"), 30000L);
_applyInitial(); _applyInitial();
} }
void loop() override {
Output::loop();
doGridPowerDelta();
}
void setName(const String &value) override { void setName(const String &value) override {
Output::setName(value); Output::setName(value);
configWrite(path("name"), nameFallback, value); configWrite(path("name"), nameFallback, value);
} }
void setTopic(const String &value) {
topic = value;
configWrite(path("topic"), topicFallback, value);
}
void setInitial(const Initial value) override { void setInitial(const Initial value) override {
Output::setInitial(value); Output::setInitial(value);
configWrite(path("initial"), INITIAL_OFF, value); configWrite(path("initial"), INITIAL_OFF, value);
@ -56,23 +75,46 @@ public:
configWrite(path("offMillis"), 0L, value); configWrite(path("offMillis"), 0L, value);
} }
void setTopic(const String &value) {
topic = value;
configWrite(path("topic"), topicFallback, value);
}
void setAutoOnThreshold(const long value) {
gridPowerDeltaOnThreshold = value;
configWrite(path("autoOnThreshold"), 0L, value);
}
void setAutoOnDelay(const long value) {
gridPowerDeltaOnDelay = value;
configWrite(path("autoOnDelay"), 0L, value);
}
void setAutoOffThreshold(const long value) {
gridPowerDeltaOffThreshold = value;
configWrite(path("autoOffThreshold"), 0L, value);
}
void setAutoOffDelay(const long value) {
gridPowerDeltaOffDelay = value;
configWrite(path("autoOffDelay"), 0L, value);
}
void json(const JsonObject json) const { void json(const JsonObject json) const {
json["name"] = name; json["name"] = name;
json["topic"] = topic;
json["state"] = get(); json["state"] = get();
json["stateAgeMillis"] = millis() - stateMillis; json["stateAgeMillis"] = millis() - stateMillis;
json["initial"] = initialToString(initial); json["initial"] = initialToString(initial);
json["onCount"] = onCount; json["onCount"] = onCount;
json["onMillis"] = onMillis; json["onMillis"] = onMillis;
json["offMillis"] = offMillis; json["offMillis"] = offMillis;
}
private: json["topic"] = topic;
String path(const char *name) const { json["autoOnThreshold"] = gridPowerDeltaOnThreshold;
char path[64]; json["autoOnDelay"] = gridPowerDeltaOnDelay;
snprintf(path, sizeof(path), "/relay%d/%s", index, name); json["autoOffThreshold"] = gridPowerDeltaOffThreshold;
return String(path); json["autoOffDelay"] = gridPowerDeltaOffDelay;
} }
protected: protected:
@ -83,6 +125,49 @@ protected:
mqttPublish(topic, doc); mqttPublish(topic, doc);
} }
private:
String path(const char *name) const {
char path[64];
snprintf(path, sizeof(path), "/relay%d/%s", index, name);
return String(path);
}
void doGridPowerDelta() {
const auto age = millis() - gridPowerDeltaMillis;
if (get()) {
if (gridPowerDelta > gridPowerDeltaOffThreshold) {
if (gridPowerDeltaMillis == 0) {
Serial.printf("[%s] CONSUMING TOO MUCH: Preparing to power OFF...\n", name.c_str());
gridPowerDeltaMillis = max(1UL, millis());
} else {
if (age > gridPowerDeltaOffDelay) {
gridPowerDeltaMillis = 0;
set(false);
}
}
} else if (gridPowerDeltaMillis > 0) {
Serial.printf("[%s] Powering off CANCELED!\n", name.c_str());
gridPowerDeltaMillis = 0;
}
} else {
if (gridPowerDelta < gridPowerDeltaOnThreshold) {
if (gridPowerDeltaMillis == 0) {
Serial.printf("[%s] PRODUCING TOO MUCH: Preparing to power ON...\n", name.c_str());
gridPowerDeltaMillis = max(1UL, millis());
} else {
if (age > gridPowerDeltaOnDelay) {
gridPowerDeltaMillis = 0;
set(true);
}
}
} else if (gridPowerDeltaMillis > 0) {
Serial.printf("[%s] Powering on CANCELED!\n", name.c_str());
gridPowerDeltaMillis = 0;
}
}
}
}; };
#endif #endif

View File

@ -5,6 +5,8 @@
#include "PubSubClient.h" #include "PubSubClient.h"
const String GRID_POWER_DELTA_TOPIC = "electricity/grid/power/signed/w";
WiFiClient wifiClient; WiFiClient wifiClient;
PubSubClient client(wifiClient); PubSubClient client(wifiClient);
@ -13,10 +15,32 @@ bool mqttShouldConnect = false;
unsigned long mqttLast = 0; unsigned long mqttLast = 0;
unsigned long warningLast = 0;
void mqttSetup() { void mqttSetup() {
mqttShouldConnect = true; mqttShouldConnect = true;
} }
double gridPowerDelta = NAN;
double gridPowerDeltaMillis = 0;
void mqttReceive(const char *topic, const uint8_t *bytes, const unsigned int length) {
if (length >= 100) {
Serial.println("[MQTT] Inbound buffer overflow");
return;
}
char string[100];
memcpy(string, bytes, length);
const auto payload = String(string);
if (GRID_POWER_DELTA_TOPIC == topic) {
gridPowerDelta = payload.toDouble();
gridPowerDeltaMillis = millis();
} else {
Serial.printf("[MQTT] Received unexpected topic: %s\n", topic);
}
}
void mqttLoop() { void mqttLoop() {
if (client.loop()) { if (client.loop()) {
if (!mqttShouldConnect) { if (!mqttShouldConnect) {
@ -24,20 +48,22 @@ void mqttLoop() {
Serial.println("[MQTT] Stopped."); Serial.println("[MQTT] Stopped.");
} }
} else if (mqttShouldConnect) { } else if (mqttShouldConnect) {
const auto host = configRead("/mqtt/host", "", false); const auto host = configRead("/mqtt/host", "10.0.0.50", false);
if (host == "") { if (host == "") {
return; return;
} }
if (mqttLast == 0 || millis() - mqttLast >= 3000) { if (mqttLast == 0 || millis() - mqttLast >= 3000) {
const auto id = configRead("/mqtt/id", "test", false); const auto id = configRead("/mqtt/id", "test");
const auto user = configRead("/mqtt/user", "", false); const auto user = configRead("/mqtt/user", "");
const auto pass = configRead("/mqtt/pass", "", false, true); const auto pass = configRead("/mqtt/pass", "", true, true);
const auto port = configRead("/mqtt/port", 1883L, false); const auto port = configRead("/mqtt/port", 1883L);
mqttLast = max(1UL, millis()); mqttLast = max(1UL, millis());
client.setServer(host.c_str(), port); client.setServer(host.c_str(), port);
Serial.printf("[MQTT] Connecting: %s:%ld\n", host.c_str(), port); Serial.printf("[MQTT] Connecting: %s:%ld\n", host.c_str(), port);
if (client.connect(id.c_str(), user.c_str(), pass.c_str())) { if (client.connect(id.c_str(), user.c_str(), pass.c_str())) {
Serial.printf("[MQTT] Connected.\n"); Serial.printf("[MQTT] Connected.\n");
client.subscribe(GRID_POWER_DELTA_TOPIC.c_str());
client.setCallback(mqttReceive);
} else { } else {
Serial.printf("[MQTT] Failed to connect.\n"); Serial.printf("[MQTT] Failed to connect.\n");
} }

View File

@ -3,6 +3,10 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
extern double gridPowerDelta;
extern double gridPowerDeltaMillis;
void mqttSetup(); void mqttSetup();
void mqttLoop(); void mqttLoop();