added set time via ntp as command and retrieve all cron jobs

This commit is contained in:
Emanuele Trabattoni
2025-07-30 15:24:11 +02:00
parent 581eca124e
commit 1110648978
6 changed files with 128 additions and 43 deletions

View File

@@ -232,7 +232,7 @@ private:
m_mqttLoopTime = mqtt["loopTime"].as<uint16_t>(); m_mqttLoopTime = mqtt["loopTime"].as<uint16_t>();
m_mqttKeepalive = mqtt["keepalive"].as<uint8_t>(); m_mqttKeepalive = mqtt["keepalive"].as<uint8_t>();
m_mqttRetries = mqtt["retries"].as<uint8_t>(); m_mqttRetries = mqtt["retries"].as<uint8_t>();
auto subscribe = mqtt["subsribe"].as<ArduinoJson::JsonObject>(); auto subscribe = mqtt["subscribe"].as<ArduinoJson::JsonObject>();
for (auto v : subscribe) for (auto v : subscribe)
{ {
m_mqttSubscribe[v.key().c_str()] = v.value().as<std::string>(); m_mqttSubscribe[v.key().c_str()] = v.value().as<std::string>();
@@ -287,6 +287,7 @@ public:
std::map<const std::string, std::string> m_mqttSubscribe = { std::map<const std::string, std::string> m_mqttSubscribe = {
{"commands", "etcontroller/hw/commands"}}; {"commands", "etcontroller/hw/commands"}};
std::map<const std::string, std::string> m_mqttPublish = { std::map<const std::string, std::string> m_mqttPublish = {
{"cronjobs", "etcontroller/hw/cronjobs"},
{"answers", "etcontroller/hw/answers"}, {"answers", "etcontroller/hw/answers"},
{"heatpump", "etcontroller/hw/heatpump"}, {"heatpump", "etcontroller/hw/heatpump"},
{"temperatures", "etcontroller/hw/temperatures"}, {"temperatures", "etcontroller/hw/temperatures"},

View File

@@ -100,8 +100,30 @@ namespace commands
response["cmd"] = "getCronJob"; response["cmd"] = "getCronJob";
auto &cron = Cron::getInstance(dev); auto &cron = Cron::getInstance(dev);
auto eventName = params["name"].as<std::string>(); auto eventName = params["name"].as<std::string>();
if (eventName.empty())
{
LOG_ERROR("getCronJob empty job name");
response["values"]["status"] = "invalid";
return response;
}
if (eventName == "all")
{
const auto &eventMap = cron.getAllEvents();
uint8_t eventNum(0);
for (const auto &[name, event] : eventMap)
{
const auto cmd = std::get<0>(event);
response["values"]["name"] = cmd;
eventNum++;
}
LOG_INFO("getCronJob got [", eventNum, "] events");
return response;
}
Cron::CronEvent event; Cron::CronEvent event;
if (eventName.empty() || !cron.getEvent(eventName, event)) if (!cron.getEvent(eventName, event))
{ {
LOG_ERROR("getCronJob failed to get job [", eventName.c_str(), "]"); LOG_ERROR("getCronJob failed to get job [", eventName.c_str(), "]");
response["values"]["status"] = "invalid"; response["values"]["status"] = "invalid";
@@ -176,12 +198,12 @@ namespace commands
LOG_ERROR("setHPlimit invalid level", level.c_str()); LOG_ERROR("setHPlimit invalid level", level.c_str());
return response; return response;
} }
for (auto k : c_hpLimitsMap) for (const auto [lvl, ro] : c_hpLimitsMap)
{ {
if (level == k.first && level != "UNLIMITED") if (level == lvl && level != "UNLIMITED")
dev.io.digitalOutWrite(k.second, true); dev.io.digitalOutWrite(ro, true);
else else
dev.io.digitalOutWrite(k.second, false); dev.io.digitalOutWrite(ro, false);
} }
LOG_INFO("setHPlimit -> level", level.c_str()); LOG_INFO("setHPlimit -> level", level.c_str());
return response; return response;
@@ -200,19 +222,19 @@ namespace commands
LOG_ERROR("setHeating incorrect paramaters"); LOG_ERROR("setHeating incorrect paramaters");
return response; return response;
} }
for (auto v : c_heatingValveMap) for (const auto [lvl, ro] : c_heatingValveMap)
{ {
if (params[v.first].isNull()) if (params[lvl].isNull())
continue; continue;
if (params[v.first] == "ON") if (params[lvl] == "ON")
{ {
dev.io.digitalOutWrite(v.second, true); dev.io.digitalOutWrite(ro, true);
LOG_INFO("setHeating -> ", v.first.c_str(), "ON"); LOG_INFO("setHeating -> ", lvl.c_str(), "ON");
} }
else if (params[v.first] == "OFF") else if (params[lvl] == "OFF")
{ {
dev.io.digitalOutWrite(v.second, false); dev.io.digitalOutWrite(ro, false);
LOG_INFO("setHeating -> ", v.first.c_str(), "OFF"); LOG_INFO("setHeating -> ", lvl.c_str(), "OFF");
} }
else else
LOG_ERROR("setHeating invalid valve state"); LOG_ERROR("setHeating invalid valve state");
@@ -342,6 +364,33 @@ namespace commands
} }
return response; return response;
} }
const ArduinoJson::JsonDocument Commands::setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{
ArduinoJson::JsonDocument response;
response["cmd"] = "setTimeNTP";
auto &eth = dev.eth;
auto &rtc = dev.rtc;
time_t ntpTime;
auto ntpOk = eth.getNtpTime(ntpTime);
drivers::PCF85063::datetime_t rtcTime(drivers::PCF85063::fromEpoch(ntpTime));
auto rtcOk = rtc.setDatetime(rtcTime);
if (!rtcOk || !ntpOk)
{
response["values"]["status"] = "unable to get time";
return response;
}
response["values"]["status"] = "valid";
response["status"]["time"] = rtc.getTimeStr();
LOG_INFO("setTimeNTP -> RTC is [", response["status"]["time"].as<std::string>().c_str(), "]");
return response;
}
// SETTERS // // SETTERS //
// SETTERS // // SETTERS //
@@ -350,8 +399,8 @@ namespace commands
const ArduinoJson::JsonDocument Commands::getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument &params) const ArduinoJson::JsonDocument Commands::getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument &params)
{ {
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
const auto pinfo = dev.seneca.getAll();
response["cmd"] = "getHPpower"; response["cmd"] = "getHPpower";
const auto pinfo = dev.seneca.getAll();
auto values = response["values"].to<JsonObject>(); auto values = response["values"].to<JsonObject>();
values["power"] = pinfo.pAct; values["power"] = pinfo.pAct;
values["current"] = pinfo.a; values["current"] = pinfo.a;
@@ -414,8 +463,8 @@ namespace commands
{ {
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
response["cmd"] = "getTimeDrift"; response["cmd"] = "getTimeDrift";
auto& eth= dev.eth; auto &eth = dev.eth;
auto& rtc = dev.rtc; auto &rtc = dev.rtc;
time_t ntpTime; time_t ntpTime;
auto ntpOk = eth.getNtpTime(ntpTime); auto ntpOk = eth.getNtpTime(ntpTime);
@@ -424,8 +473,9 @@ namespace commands
auto rtcOk = rtc.readDatetime(rtcTime); auto rtcOk = rtc.readDatetime(rtcTime);
auto rtcTimeTm = drivers::PCF85063::datetime2tm(rtcTime); auto rtcTimeTm = drivers::PCF85063::datetime2tm(rtcTime);
if (!rtcOk || !ntpOk) { if (!rtcOk || !ntpOk)
response["value"]["status"] = "unable to get time"; {
response["values"]["status"] = "unable to get time";
return response; return response;
} }
@@ -433,11 +483,13 @@ namespace commands
auto rtcTimePoint = std::chrono::system_clock::from_time_t(std::mktime(&rtcTimeTm)); auto rtcTimePoint = std::chrono::system_clock::from_time_t(std::mktime(&rtcTimeTm));
auto timeDiff = std::chrono::duration_cast<std::chrono::seconds>(ntpTimePoint - rtcTimePoint); auto timeDiff = std::chrono::duration_cast<std::chrono::seconds>(ntpTimePoint - rtcTimePoint);
auto direction = timeDiff.count() >= 0 ? "BEYOND" : "AHEAD"; auto direction = timeDiff.count() >= 0 ? "BEYOND" : "AHEAD";
response["values"]["drift"] = timeDiff.count(); response["values"]["drift"] = (uint32_t)timeDiff.count();
response["values"]["direction"] = "RTC is [" + std::string(direction) + "] NTP time"; response["values"]["direction"] = "RTC is [" + std::string(direction) + "] NTP time";
LOG_INFO("getTimeDrift -> RTC is [", (uint32_t)timeDiff.count(), "] sec, [", std::string(direction).c_str(), "] NTP time");
return response; return response;
} }
// GETTERS // // GETTERS //

View File

@@ -79,6 +79,7 @@ namespace commands
static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument &params);
static const ArduinoJson::JsonDocument setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument &params);
// GETTERS // // GETTERS //
static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument &params);
@@ -92,7 +93,7 @@ namespace commands
static const ArduinoJson::JsonDocument getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument &params); static const ArduinoJson::JsonDocument getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument &params);
}; };
static const std::map<const std::string, Command> commandMap = { static const std::map<const std::string, Command> s_commandMap = {
{"setConfig", Commands::setConfig}, {"setConfig", Commands::setConfig},
{"getConfig", Commands::getConfig}, {"getConfig", Commands::getConfig},
@@ -108,7 +109,9 @@ namespace commands
{"getHPpower", Commands::getHPpower}, {"getHPpower", Commands::getHPpower},
{"setHeating", Commands::setHeating}, {"setHeating", Commands::setHeating},
{"getTimeDrift", Commands::getTimeDrift}, {"getTimeDrift", Commands::getTimeDrift},
{"setTimeNTP", Commands::setTimeNTP},
}; };
} }

View File

@@ -102,7 +102,7 @@ const bool Cron::addEvent(const std::string &name, const std::string &expr, cons
const auto eventExpr(cron::make_cron(expr)); const auto eventExpr(cron::make_cron(expr));
const auto cmd = action["cmd"].as<std::string>(); const auto cmd = action["cmd"].as<std::string>();
const auto params = action["params"]; const auto params = action["params"];
if (!commands::commandMap.contains(cmd)) if (!commands::s_commandMap.contains(cmd))
{ {
LOG_ERROR("Cron unknown command [", cmd.c_str(), "]"); LOG_ERROR("Cron unknown command [", cmd.c_str(), "]");
return false; return false;
@@ -153,9 +153,14 @@ const bool Cron::delEvent(const std::string &name)
return true; return true;
} }
void cronLoop(void *dev) const Cron::CronEventMap &Cron::getAllEvents()
{ {
auto &cron = Cron::getInstance(*(devices_t *)dev); return m_cronMap;
}
void cronLoop(void *cronPtr)
{
auto &cron = *(Cron *)cronPtr;
while (true) while (true)
{ {
cron.processEvents(); cron.processEvents();
@@ -168,7 +173,7 @@ void Cron::startCron()
if (!m_cronTaskHandle) if (!m_cronTaskHandle)
{ {
LOG_INFO("Cron starting loop"); LOG_INFO("Cron starting loop");
xTaskCreate(cronLoop, "cronLoop", STACK_DEPTH, (void *)&m_dev, PRIORITY, &m_cronTaskHandle); xTaskCreate(cronLoop, "cronLoop", STACK_DEPTH, this, PRIORITY, &m_cronTaskHandle);
} }
} }
@@ -217,7 +222,11 @@ const bool Cron::processEvents()
next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed
// otherwise time tracking is lost // otherwise time tracking is lost
LOG_INFO("Cron running event [", eventName.c_str(), "] next execution time [", drivers::PCF85063::tm2str(next).c_str(), "]"); LOG_INFO("Cron running event [", eventName.c_str(), "] next execution time [", drivers::PCF85063::tm2str(next).c_str(), "]");
commands::commandMap.at(cmd)(m_dev, cmdParams); // here the magic happens auto resp = commands::s_commandMap.at(cmd)(m_dev, cmdParams); // here the magic happens
if (m_callback)
{
m_callback(resp);
}
} }
} }
return true; return true;

View File

@@ -8,14 +8,17 @@
#include <PCF85063_Driver.h> #include <PCF85063_Driver.h>
#include <commands.h> #include <commands.h>
#include <mqtt.h>
#include <filesystem> #include <filesystem>
#include <croncpp.h> #include <croncpp.h>
class Cron class Cron
{ {
public: // eventName cronExpression nextExec command parameters public: // eventName cronExpression nextExec command parameters
using CronEvent = std::tuple<std::string, cron::cronexpr, std::tm, ArduinoJson::JsonDocument>; using CronEvent = std::tuple<std::string, cron::cronexpr, std::tm, ArduinoJson::JsonDocument>;
using CronEventMap = std::map<std::string, CronEvent>;
using CronCallback = std::function<void(const ArduinoJson::JsonDocument &)>;
public: public:
static Cron &getInstance(const devices_t &dev) static Cron &getInstance(const devices_t &dev)
@@ -30,11 +33,17 @@ private:
Cron &operator=(const Cron &) = delete; Cron &operator=(const Cron &) = delete;
public: public:
void setResponseCallback(CronCallback &cb)
{
m_callback = cb;
}
const bool loadEvents(); const bool loadEvents();
const bool storeEvents(); const bool storeEvents();
const bool addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action); const bool addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action);
const bool getEvent(const std::string &name, CronEvent &event); const bool getEvent(const std::string &name, CronEvent &event);
const bool delEvent(const std::string &name); const bool delEvent(const std::string &name);
const CronEventMap &getAllEvents();
void startCron(); void startCron();
void stopCron(); void stopCron();
@@ -42,7 +51,8 @@ public:
private: private:
const devices_t &m_dev; const devices_t &m_dev;
CronCallback m_callback;
CronEventMap m_cronMap;
TaskHandle_t m_cronTaskHandle; TaskHandle_t m_cronTaskHandle;
std::map<std::string, CronEvent> m_cronMap;
std::mutex m_mutex; std::mutex m_mutex;
}; };

View File

@@ -55,13 +55,7 @@ void loop()
auto mqtt = MQTTwrapper(); auto mqtt = MQTTwrapper();
//////////////// MQTT //////////////// //////////////// MQTT ////////////////
//////////////// CRONJOB //////////////// //////////////// MQTT //////////////
auto &cron = Cron::getInstance(devices);
cron.loadEvents();
cron.startCron();
//////////////// CRONJOB ////////////////
//////////////// MQTT ////////////////
/////////////// CALLBACK ////////////// /////////////// CALLBACK //////////////
std::function<void(const ArduinoJson::JsonDocument &)> commandsCallback = std::function<void(const ArduinoJson::JsonDocument &)> commandsCallback =
[&mqtt, &devices](const ArduinoJson::JsonDocument &doc) [&mqtt, &devices](const ArduinoJson::JsonDocument &doc)
@@ -73,10 +67,10 @@ void loop()
} }
const std::string cmd = doc["cmd"].as<std::string>(); const std::string cmd = doc["cmd"].as<std::string>();
const ArduinoJson::JsonDocument params = doc["params"]; const ArduinoJson::JsonDocument params = doc["params"];
if (commands::commandMap.contains(cmd)) if (commands::s_commandMap.contains(cmd))
{ // call command from command map in this same thread (the MQTT thread) { // call command from command map in this same thread (the MQTT thread)
LOG_INFO("Executing command", cmd.c_str()); LOG_INFO("Executing command", cmd.c_str());
const auto answer = std::move(commands::commandMap.at(cmd)(devices, params)); // here the magic happens const auto answer = std::move(commands::s_commandMap.at(cmd)(devices, params)); // here the magic happens
if (answer.isNull()) if (answer.isNull())
return; return;
mqtt.publish(conf.m_mqttPublish["answers"], answer); mqtt.publish(conf.m_mqttPublish["answers"], answer);
@@ -87,6 +81,22 @@ void loop()
} }
}; };
///////////// CRONJOB //////////////
/////////////// CALLBACK //////////////
Cron::CronCallback cronCallback = [&mqtt](const ArduinoJson::JsonDocument &resp)
{
if (resp.isNull())
return;
mqtt.publish(conf.m_mqttPublish["cronjobs"], resp);
};
//////////////// CRONJOB ////////////////
auto &cron = Cron::getInstance(devices);
cron.setResponseCallback(cronCallback);
cron.loadEvents();
cron.startCron();
//////////////// CRONJOB ////////////////
//////////////// NETWORK //////////////// //////////////// NETWORK ////////////////
/////////////// CALLBACK //////////////// /////////////// CALLBACK ////////////////
Network.onEvent( Network.onEvent(
@@ -113,7 +123,7 @@ void loop()
LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str()); LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str());
break; break;
} }
delay(100); delay(250);
} }
while (mqttRetries++ < conf.m_mqttRetries) while (mqttRetries++ < conf.m_mqttRetries)
{ {
@@ -124,7 +134,7 @@ void loop()
mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback); mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback);
break; break;
} }
delay(100); delay(250);
} }
}); });
@@ -170,7 +180,7 @@ void loop()
{ {
LOG_WARN("RESTART!"); LOG_WARN("RESTART!");
buzzer.beep(450, NOTE_D); buzzer.beep(450, NOTE_D);
delay(100); delay(450);
esp_restart(); esp_restart();
} }