mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 08:49:52 +03:00
mqtt/commands for scheduler
This commit is contained in:
@@ -128,7 +128,8 @@ void AnalogSensor::reload() {
|
||||
[&](const char * value, const int8_t id) { return command_setvalue(value, sensor.gpio); },
|
||||
sensor.type == AnalogType::COUNTER ? FL_(counter)
|
||||
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
|
||||
: FL_(pwm));
|
||||
: FL_(pwm),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -145,6 +145,33 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
data = input["value"];
|
||||
}
|
||||
|
||||
// check if data is entity like device/hc/name/value
|
||||
if (data.is<const char *>()) {
|
||||
const char * d = data.as<const char *>();
|
||||
if (strlen(d)) {
|
||||
char * device_end = strchr(d, '/');
|
||||
if (device_end != nullptr) {
|
||||
char device_s[15] = {'\0'};
|
||||
const char * device_p = device_s;
|
||||
const char * data_p = nullptr;
|
||||
strlcpy(device_s, d, device_end - d + 1);
|
||||
data_p = device_end + 1;
|
||||
int8_t id_d = -1;
|
||||
data_p = parse_command_string(data_p, id_d);
|
||||
char data_s[50];
|
||||
strcpy(data_s, data_p);
|
||||
strcat(data_s, "/value");
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(device_p);
|
||||
if (CommandRet::OK == Command::call(device_type, data_s, "", true, id_d, output)) {
|
||||
if (output.containsKey("api_data")) {
|
||||
data = output["api_data"];
|
||||
}
|
||||
}
|
||||
output.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call the command based on the type
|
||||
uint8_t return_code = CommandRet::ERROR;
|
||||
if (data.is<const char *>()) {
|
||||
@@ -204,11 +231,13 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
id = command[2] - '0';
|
||||
command += 3;
|
||||
} else if (!strncmp(lowerCmd, "wwc", 3) && command[3] == '1' && command[4] == '0') {
|
||||
id = DeviceValueTAG::TAG_WWC10 - DeviceValueTAG::TAG_HC1 + 1; //18; } else if (!strncmp(lowerCmd, "wwc", 3) && command[3] >= '1' && command[3] <= '9') {
|
||||
id = command[3] - '0' + 8;
|
||||
id = DeviceValueTAG::TAG_WWC10 - DeviceValueTAG::TAG_HC1 + 1; //18;
|
||||
command += 5;
|
||||
} else if (!strncmp(lowerCmd, "wwc", 3) && command[3] >= '1' && command[3] <= '9') {
|
||||
id = command[3] - '1' + DeviceValueTAG::TAG_WWC1 - DeviceValueTAG::TAG_HC1 + 1; //9;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') {
|
||||
id = command[3] - '1' + DeviceValueTAG::TAG_WWC1 - DeviceValueTAG::TAG_HC1 + 1; //9;
|
||||
id = command[3] - '0' + 10;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "id", 2) && command[2] >= '1' && command[2] <= '9') {
|
||||
id = command[2] - '0';
|
||||
@@ -499,6 +528,10 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
||||
return true; // we always have System
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::SCHEDULER) {
|
||||
return EMSESP::webSchedulerService.has_commands();
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::DALLASSENSOR) {
|
||||
return (EMSESP::dallassensor_.have_sensors());
|
||||
}
|
||||
@@ -555,6 +588,13 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
|
||||
// show scheduler
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SCHEDULER));
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SCHEDULER, true);
|
||||
|
||||
// show sensors
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
|
||||
@@ -105,6 +105,8 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
|
||||
switch (device_type) {
|
||||
case DeviceType::SYSTEM:
|
||||
return F_(system);
|
||||
case DeviceType::SCHEDULER:
|
||||
return F_(scheduler);
|
||||
case DeviceType::BOILER:
|
||||
return F_(boiler);
|
||||
case DeviceType::THERMOSTAT:
|
||||
@@ -194,6 +196,9 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(system))) {
|
||||
return DeviceType::SYSTEM;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(scheduler))) {
|
||||
return DeviceType::SCHEDULER;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(heatpump))) {
|
||||
return DeviceType::HEATPUMP;
|
||||
}
|
||||
@@ -225,10 +230,6 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
return DeviceType::HEATSOURCE;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(heatsource))) {
|
||||
return DeviceType::HEATSOURCE;
|
||||
}
|
||||
|
||||
return DeviceType::UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -550,10 +551,6 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignore) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add the device entity
|
||||
devicevalues_.emplace_back(
|
||||
device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, custom_fullname, uom, has_cmd, min, max, state);
|
||||
|
||||
@@ -322,6 +322,7 @@ class EMSdevice {
|
||||
SYSTEM = 0, // this is us (EMS-ESP)
|
||||
DALLASSENSOR, // for internal dallas sensors
|
||||
ANALOGSENSOR, // for internal analog sensors
|
||||
SCHEDULER,
|
||||
BOILER,
|
||||
THERMOSTAT,
|
||||
MIXER,
|
||||
|
||||
@@ -478,7 +478,8 @@ void EMSESP::publish_all(bool force) {
|
||||
publish_device_values(EMSdevice::DeviceType::THERMOSTAT);
|
||||
publish_device_values(EMSdevice::DeviceType::SOLAR);
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||
publish_other_values(); // switch and heat pump, ...
|
||||
publish_other_values(); // switch and heat pump, ...
|
||||
webSchedulerService.publish();
|
||||
publish_sensor_values(true); // includes dallas and analog sensors
|
||||
system_.send_heartbeat();
|
||||
}
|
||||
@@ -510,6 +511,7 @@ void EMSESP::publish_all_loop() {
|
||||
break;
|
||||
case 5:
|
||||
publish_other_values(); // switch and heat pump
|
||||
webSchedulerService.publish(true);
|
||||
break;
|
||||
case 6:
|
||||
publish_sensor_values(true, true);
|
||||
|
||||
@@ -97,6 +97,7 @@ MAKE_WORD(dallassensor)
|
||||
MAKE_WORD(alert)
|
||||
MAKE_WORD(pump)
|
||||
MAKE_WORD(heatsource)
|
||||
MAKE_WORD(scheduler)
|
||||
|
||||
// brands
|
||||
MAKE_WORD_CUSTOM(bosch, "Bosch")
|
||||
|
||||
@@ -61,6 +61,7 @@ MAKE_TRANSLATION(restart_cmd, "restart EMS-ESP", "Neustart", "", "", "", "", "",
|
||||
MAKE_TRANSLATION(watch_cmd, "watch incoming telegrams", "Watch auf eingehende Telegramme", "", "", "", "", "", "Gelen telegramları ")
|
||||
MAKE_TRANSLATION(publish_cmd, "publish all to MQTT", "Publiziere MQTT", "", "", "", "", "", "Hepsini MQTTye gönder")
|
||||
MAKE_TRANSLATION(system_info_cmd, "show system status", "Zeige System-Status", "", "", "", "", "", "Sistem Durumunu Göster")
|
||||
MAKE_TRANSLATION(schedule_cmd, "enable schedule item", "Aktiviere Zeitplan", "", "", "", "", "", "")
|
||||
|
||||
// tags
|
||||
MAKE_TRANSLATION(tag_boiler_data_ww, "dhw", "WW", "dhw", "VV", "CWU", "dhw", "ecs", "SKS")
|
||||
|
||||
@@ -182,6 +182,7 @@ void Mqtt::loop() {
|
||||
if (publish_time_other_ && (currentMillis - last_publish_other_ > publish_time_other_)) {
|
||||
last_publish_other_ = (currentMillis / publish_time_other_) * publish_time_other_;
|
||||
EMSESP::publish_other_values(); // switch and heatpump
|
||||
EMSESP::webSchedulerService.publish();
|
||||
} else
|
||||
|
||||
if (publish_time_sensor_ && (currentMillis - last_publish_sensor_ > publish_time_sensor_)) {
|
||||
|
||||
@@ -64,6 +64,9 @@ StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webSche
|
||||
Serial.println(COLOR_RESET);
|
||||
#endif
|
||||
|
||||
for (ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||
Command::erase_command(EMSdevice::DeviceType::SCHEDULER, scheduleItem.description.c_str());
|
||||
}
|
||||
webScheduler.scheduleItems.clear();
|
||||
|
||||
if (root["schedule"].is<JsonArray>()) {
|
||||
@@ -84,11 +87,146 @@ StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webSche
|
||||
si.retry_cnt = 0xFF; // no starup retries
|
||||
|
||||
webScheduler.scheduleItems.push_back(si); // add to list
|
||||
if (!webScheduler.scheduleItems.back().description.empty()) {
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::SCHEDULER,
|
||||
webScheduler.scheduleItems.back().description.c_str(),
|
||||
[webScheduler](const char * value, const int8_t id) {
|
||||
return EMSESP::webSchedulerService.command_setvalue(value, webScheduler.scheduleItems.back().description);
|
||||
},
|
||||
FL_(schedule_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
EMSESP::webSchedulerService.publish(true);
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
// set active by api command
|
||||
bool WebSchedulerService::command_setvalue(const char * value, const std::string name) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.description == name) {
|
||||
if (scheduleItem.active == v) {
|
||||
return true;
|
||||
}
|
||||
scheduleItem.active = v;
|
||||
publish_single(name.c_str(), v);
|
||||
if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
publish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebSchedulerService::publish_single(const char * name, const bool state) {
|
||||
if (!Mqtt::publish_single() || name == nullptr || name[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", F_(scheduler), name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(scheduler), "_data", name);
|
||||
}
|
||||
char payload[12];
|
||||
Mqtt::queue_publish(topic, Helpers::render_boolean(payload, state));
|
||||
}
|
||||
|
||||
// publish to Mqtt
|
||||
void WebSchedulerService::publish(const bool force) {
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (Mqtt::publish_single() && force) {
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
publish_single(scheduleItem.description.c_str(), scheduleItem.active);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.description.empty() && !doc.containsKey(scheduleItem.description)) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc[scheduleItem.description] = scheduleItem.active;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc[scheduleItem.description] = scheduleItem.active ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
doc[scheduleItem.description] = Helpers::render_boolean(result, scheduleItem.active);
|
||||
}
|
||||
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && force) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/scheduler_data", Mqtt::base().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", scheduleItem.description.c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
snprintf(uniq_s, sizeof(uniq_s), "scheduler_%s", scheduleItem.description.c_str());
|
||||
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
config["name"] = scheduleItem.description.c_str();
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "switch/%s/scheduler_%s/config", Mqtt::basename().c_str(), scheduleItem.description.c_str());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/scheduler/%s", Mqtt::basename().c_str(), scheduleItem.description.c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
config["pl_on"] = true;
|
||||
config["pl_off"] = false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
config["pl_on"] = 1;
|
||||
config["pl_off"] = 0;
|
||||
} else {
|
||||
char result[12];
|
||||
config["pl_on"] = Helpers::render_boolean(result, true);
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
}
|
||||
Mqtt::queue_publish("scheduler_data", doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
bool WebSchedulerService::has_commands() {
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return false;
|
||||
}
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.description.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// execute scheduled command
|
||||
bool WebSchedulerService::command(const char * cmd, const char * data) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_input;
|
||||
|
||||
@@ -54,6 +54,10 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
void publish_single(const char * name, const bool state);
|
||||
void publish(const bool force = false);
|
||||
bool has_commands();
|
||||
bool command_setvalue(const char * value, const std::string name);
|
||||
|
||||
private:
|
||||
bool command(const char * cmd, const char * data);
|
||||
|
||||
Reference in New Issue
Block a user