added set time via ntp as command and retrieve all cron jobs
This commit is contained in:
@@ -232,7 +232,7 @@ private:
|
||||
m_mqttLoopTime = mqtt["loopTime"].as<uint16_t>();
|
||||
m_mqttKeepalive = mqtt["keepalive"].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)
|
||||
{
|
||||
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 = {
|
||||
{"commands", "etcontroller/hw/commands"}};
|
||||
std::map<const std::string, std::string> m_mqttPublish = {
|
||||
{"cronjobs", "etcontroller/hw/cronjobs"},
|
||||
{"answers", "etcontroller/hw/answers"},
|
||||
{"heatpump", "etcontroller/hw/heatpump"},
|
||||
{"temperatures", "etcontroller/hw/temperatures"},
|
||||
|
||||
@@ -100,8 +100,30 @@ namespace commands
|
||||
response["cmd"] = "getCronJob";
|
||||
auto &cron = Cron::getInstance(dev);
|
||||
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;
|
||||
if (eventName.empty() || !cron.getEvent(eventName, event))
|
||||
if (!cron.getEvent(eventName, event))
|
||||
{
|
||||
LOG_ERROR("getCronJob failed to get job [", eventName.c_str(), "]");
|
||||
response["values"]["status"] = "invalid";
|
||||
@@ -176,12 +198,12 @@ namespace commands
|
||||
LOG_ERROR("setHPlimit invalid level", level.c_str());
|
||||
return response;
|
||||
}
|
||||
for (auto k : c_hpLimitsMap)
|
||||
for (const auto [lvl, ro] : c_hpLimitsMap)
|
||||
{
|
||||
if (level == k.first && level != "UNLIMITED")
|
||||
dev.io.digitalOutWrite(k.second, true);
|
||||
if (level == lvl && level != "UNLIMITED")
|
||||
dev.io.digitalOutWrite(ro, true);
|
||||
else
|
||||
dev.io.digitalOutWrite(k.second, false);
|
||||
dev.io.digitalOutWrite(ro, false);
|
||||
}
|
||||
LOG_INFO("setHPlimit -> level", level.c_str());
|
||||
return response;
|
||||
@@ -200,19 +222,19 @@ namespace commands
|
||||
LOG_ERROR("setHeating incorrect paramaters");
|
||||
return response;
|
||||
}
|
||||
for (auto v : c_heatingValveMap)
|
||||
for (const auto [lvl, ro] : c_heatingValveMap)
|
||||
{
|
||||
if (params[v.first].isNull())
|
||||
if (params[lvl].isNull())
|
||||
continue;
|
||||
if (params[v.first] == "ON")
|
||||
if (params[lvl] == "ON")
|
||||
{
|
||||
dev.io.digitalOutWrite(v.second, true);
|
||||
LOG_INFO("setHeating -> ", v.first.c_str(), "ON");
|
||||
dev.io.digitalOutWrite(ro, true);
|
||||
LOG_INFO("setHeating -> ", lvl.c_str(), "ON");
|
||||
}
|
||||
else if (params[v.first] == "OFF")
|
||||
else if (params[lvl] == "OFF")
|
||||
{
|
||||
dev.io.digitalOutWrite(v.second, false);
|
||||
LOG_INFO("setHeating -> ", v.first.c_str(), "OFF");
|
||||
dev.io.digitalOutWrite(ro, false);
|
||||
LOG_INFO("setHeating -> ", lvl.c_str(), "OFF");
|
||||
}
|
||||
else
|
||||
LOG_ERROR("setHeating invalid valve state");
|
||||
@@ -342,6 +364,33 @@ namespace commands
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
const ArduinoJson::JsonDocument Commands::setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||
{
|
||||
ArduinoJson::JsonDocument response;
|
||||
response["cmd"] = "setTimeNTP";
|
||||
auto ð = 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 //
|
||||
|
||||
@@ -350,8 +399,8 @@ namespace commands
|
||||
const ArduinoJson::JsonDocument Commands::getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||
{
|
||||
ArduinoJson::JsonDocument response;
|
||||
const auto pinfo = dev.seneca.getAll();
|
||||
response["cmd"] = "getHPpower";
|
||||
const auto pinfo = dev.seneca.getAll();
|
||||
auto values = response["values"].to<JsonObject>();
|
||||
values["power"] = pinfo.pAct;
|
||||
values["current"] = pinfo.a;
|
||||
@@ -424,8 +473,9 @@ namespace commands
|
||||
auto rtcOk = rtc.readDatetime(rtcTime);
|
||||
auto rtcTimeTm = drivers::PCF85063::datetime2tm(rtcTime);
|
||||
|
||||
if (!rtcOk || !ntpOk) {
|
||||
response["value"]["status"] = "unable to get time";
|
||||
if (!rtcOk || !ntpOk)
|
||||
{
|
||||
response["values"]["status"] = "unable to get time";
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -435,9 +485,11 @@ namespace commands
|
||||
auto timeDiff = std::chrono::duration_cast<std::chrono::seconds>(ntpTimePoint - rtcTimePoint);
|
||||
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";
|
||||
|
||||
LOG_INFO("getTimeDrift -> RTC is [", (uint32_t)timeDiff.count(), "] sec, [", std::string(direction).c_str(), "] NTP time");
|
||||
|
||||
return response;
|
||||
}
|
||||
// GETTERS //
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace commands
|
||||
static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||
static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||
static const ArduinoJson::JsonDocument setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||
static const ArduinoJson::JsonDocument setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||
|
||||
// GETTERS //
|
||||
static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||
@@ -92,7 +93,7 @@ namespace commands
|
||||
static const ArduinoJson::JsonDocument getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||
};
|
||||
|
||||
static const std::map<const std::string, Command> commandMap = {
|
||||
static const std::map<const std::string, Command> s_commandMap = {
|
||||
{"setConfig", Commands::setConfig},
|
||||
{"getConfig", Commands::getConfig},
|
||||
|
||||
@@ -108,7 +109,9 @@ namespace commands
|
||||
|
||||
{"getHPpower", Commands::getHPpower},
|
||||
{"setHeating", Commands::setHeating},
|
||||
|
||||
{"getTimeDrift", Commands::getTimeDrift},
|
||||
{"setTimeNTP", Commands::setTimeNTP},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 cmd = action["cmd"].as<std::string>();
|
||||
const auto params = action["params"];
|
||||
if (!commands::commandMap.contains(cmd))
|
||||
if (!commands::s_commandMap.contains(cmd))
|
||||
{
|
||||
LOG_ERROR("Cron unknown command [", cmd.c_str(), "]");
|
||||
return false;
|
||||
@@ -153,9 +153,14 @@ const bool Cron::delEvent(const std::string &name)
|
||||
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)
|
||||
{
|
||||
cron.processEvents();
|
||||
@@ -168,7 +173,7 @@ void Cron::startCron()
|
||||
if (!m_cronTaskHandle)
|
||||
{
|
||||
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
|
||||
// otherwise time tracking is lost
|
||||
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;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <PCF85063_Driver.h>
|
||||
|
||||
#include <commands.h>
|
||||
#include <mqtt.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <croncpp.h>
|
||||
@@ -16,6 +17,8 @@ class Cron
|
||||
{
|
||||
public: // eventName cronExpression nextExec command parameters
|
||||
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:
|
||||
static Cron &getInstance(const devices_t &dev)
|
||||
@@ -30,11 +33,17 @@ private:
|
||||
Cron &operator=(const Cron &) = delete;
|
||||
|
||||
public:
|
||||
void setResponseCallback(CronCallback &cb)
|
||||
{
|
||||
m_callback = cb;
|
||||
}
|
||||
|
||||
const bool loadEvents();
|
||||
const bool storeEvents();
|
||||
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 delEvent(const std::string &name);
|
||||
const CronEventMap &getAllEvents();
|
||||
|
||||
void startCron();
|
||||
void stopCron();
|
||||
@@ -42,7 +51,8 @@ public:
|
||||
|
||||
private:
|
||||
const devices_t &m_dev;
|
||||
CronCallback m_callback;
|
||||
CronEventMap m_cronMap;
|
||||
TaskHandle_t m_cronTaskHandle;
|
||||
std::map<std::string, CronEvent> m_cronMap;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
34
src/main.cpp
34
src/main.cpp
@@ -55,13 +55,7 @@ void loop()
|
||||
auto mqtt = MQTTwrapper();
|
||||
//////////////// MQTT ////////////////
|
||||
|
||||
//////////////// CRONJOB ////////////////
|
||||
auto &cron = Cron::getInstance(devices);
|
||||
cron.loadEvents();
|
||||
cron.startCron();
|
||||
//////////////// CRONJOB ////////////////
|
||||
|
||||
//////////////// MQTT ////////////////
|
||||
//////////////// MQTT //////////////
|
||||
/////////////// CALLBACK //////////////
|
||||
std::function<void(const ArduinoJson::JsonDocument &)> commandsCallback =
|
||||
[&mqtt, &devices](const ArduinoJson::JsonDocument &doc)
|
||||
@@ -73,10 +67,10 @@ void loop()
|
||||
}
|
||||
const std::string cmd = doc["cmd"].as<std::string>();
|
||||
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)
|
||||
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())
|
||||
return;
|
||||
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 ////////////////
|
||||
/////////////// CALLBACK ////////////////
|
||||
Network.onEvent(
|
||||
@@ -113,7 +123,7 @@ void loop()
|
||||
LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str());
|
||||
break;
|
||||
}
|
||||
delay(100);
|
||||
delay(250);
|
||||
}
|
||||
while (mqttRetries++ < conf.m_mqttRetries)
|
||||
{
|
||||
@@ -124,7 +134,7 @@ void loop()
|
||||
mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback);
|
||||
break;
|
||||
}
|
||||
delay(100);
|
||||
delay(250);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -170,7 +180,7 @@ void loop()
|
||||
{
|
||||
LOG_WARN("RESTART!");
|
||||
buzzer.beep(450, NOTE_D);
|
||||
delay(100);
|
||||
delay(450);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user