mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-16 20:59:51 +03:00
Merge remote-tracking branch 'origin/dev'
This commit is contained in:
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server) {
|
||||
server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, std::bind(&WebAPIService::webAPIService, this, _1));
|
||||
}
|
||||
|
||||
// e.g. http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
|
||||
void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
||||
// must have device and cmd parameters
|
||||
if ((!request->hasParam(F_(device))) || (!request->hasParam(F_(cmd)))) {
|
||||
request->send(400, "text/plain", F("Invalid syntax"));
|
||||
return;
|
||||
}
|
||||
|
||||
// get device
|
||||
String device = request->getParam(F_(device))->value();
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(device.c_str());
|
||||
if (device_type == emsesp::EMSdevice::DeviceType::UNKNOWN) {
|
||||
request->send(400, "text/plain", F("Invalid device"));
|
||||
return;
|
||||
}
|
||||
|
||||
// get cmd, we know we have one
|
||||
String cmd = request->getParam(F_(cmd))->value();
|
||||
|
||||
String data;
|
||||
if (request->hasParam(F_(data))) {
|
||||
data = request->getParam(F_(data))->value();
|
||||
}
|
||||
|
||||
String id;
|
||||
if (request->hasParam(F_(id))) {
|
||||
id = request->getParam(F_(id))->value();
|
||||
}
|
||||
|
||||
if (id.isEmpty()) {
|
||||
id = "-1";
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool ok = false;
|
||||
|
||||
// execute the command
|
||||
if (data.isEmpty()) {
|
||||
ok = Command::call(device_type, cmd.c_str(), nullptr, id.toInt(), json); // command only
|
||||
} else {
|
||||
// we only allow commands with parameters if the API is enabled
|
||||
bool api_enabled;
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { api_enabled = settings.api_enabled; });
|
||||
if (api_enabled) {
|
||||
ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), json); // has cmd, data and id
|
||||
} else {
|
||||
request->send(401, "text/plain", F("Unauthorized"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ok && json.size()) {
|
||||
// send json output back to web
|
||||
doc.shrinkToFit();
|
||||
std::string buffer;
|
||||
serializeJsonPretty(doc, buffer);
|
||||
request->send(200, "text/plain;charset=utf-8", buffer.c_str());
|
||||
return;
|
||||
}
|
||||
request->send(200, "text/plain", ok ? F("OK") : F("Invalid"));
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebAPIService_h
|
||||
#define WebAPIService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#define EMSESP_API_SERVICE_PATH "/api"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class WebAPIService {
|
||||
public:
|
||||
WebAPIService(AsyncWebServer * server);
|
||||
|
||||
private:
|
||||
void webAPIService(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
188
src/command.cpp
188
src/command.cpp
@@ -30,24 +30,24 @@ std::vector<Command::CmdFunction> Command::cmdfunctions_;
|
||||
// id may be used to represent a heating circuit for example, it's optional
|
||||
// returns false if error or not found
|
||||
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
|
||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||
int8_t id_new = id;
|
||||
char cmd_new[20];
|
||||
int8_t id_new = id;
|
||||
char cmd_new[20] = {'\0'};
|
||||
strlcpy(cmd_new, cmd, 20);
|
||||
|
||||
check_command(cmd_new, cmd, id_new);
|
||||
auto cf = find_command(device_type, cmd_new);
|
||||
auto cf = find_command(device_type, cmd_new, id_new);
|
||||
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
|
||||
LOG_WARNING(F("Command %s on %s not found"), cmd, dname.c_str());
|
||||
LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
return false; // command not found, or requires a json
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||
if (value == nullptr) {
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s"), dname.c_str(), cmd);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s'"), dname.c_str(), cmd);
|
||||
} else if (id == -1) {
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s, value %s, id is default"), dname.c_str(), cmd, value);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||
} else {
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s, value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -58,20 +58,20 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
||||
// id may be used to represent a heating circuit for example
|
||||
// returns false if error or not found
|
||||
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) {
|
||||
int8_t id_new = id;
|
||||
char cmd_new[20];
|
||||
int8_t id_new = id;
|
||||
char cmd_new[20] = {'\0'};
|
||||
strlcpy(cmd_new, cmd, 20);
|
||||
|
||||
check_command(cmd_new, cmd, id_new);
|
||||
auto cf = find_command(device_type, cmd_new);
|
||||
auto cf = find_command(device_type, cmd_new, id_new);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||
if (value == nullptr) {
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s"), dname.c_str(), cmd);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s'"), dname.c_str(), cmd);
|
||||
} else if (id == -1) {
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s, value %s, id is default"), dname.c_str(), cmd, value);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||
} else {
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s, value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -81,6 +81,7 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress)
|
||||
if (cf == nullptr) {
|
||||
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type);
|
||||
}
|
||||
@@ -88,50 +89,78 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
||||
if (cf->cmdfunction_json_) {
|
||||
return ((cf->cmdfunction_json_)(value, id_new, json));
|
||||
} else {
|
||||
if (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0) {
|
||||
if ((device_type != EMSdevice::DeviceType::SYSTEM) && (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0)) {
|
||||
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type);
|
||||
}
|
||||
return ((cf->cmdfunction_)(value, id_new));
|
||||
}
|
||||
}
|
||||
|
||||
char * Command::check_command(char * out, const char * cmd, int8_t & id) {
|
||||
// strip prefixes, check, and find command
|
||||
Command::CmdFunction * Command::find_command(const uint8_t device_type, char * cmd, int8_t & id) {
|
||||
// no command for id0
|
||||
if (id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
// empty command is info with id0
|
||||
if (cmd[0] == '\0') {
|
||||
strcpy(cmd, "info");
|
||||
id = 0;
|
||||
}
|
||||
// convert cmd to lowercase
|
||||
strlcpy(out, cmd, 20);
|
||||
for (char * p = out; *p; p++) {
|
||||
for (char * p = cmd; *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
|
||||
//scan for prefix hc.
|
||||
// scan for prefix hc.
|
||||
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
|
||||
if ((strncmp(out, EMSdevice::tag_to_string(i).c_str(), 3) == 0) && (strlen(out) > 4)) {
|
||||
strcpy(out, &out[4]);
|
||||
const char * tag = EMSdevice::tag_to_string(i).c_str();
|
||||
uint8_t len = strlen(tag);
|
||||
if (strncmp(cmd, tag, len) == 0) {
|
||||
if (cmd[len] != '\0') {
|
||||
strcpy(cmd, &cmd[len + 1]);
|
||||
} else {
|
||||
strcpy(cmd, &cmd[len]);
|
||||
}
|
||||
id = 1 + i - DeviceValueTAG::TAG_HC1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//scan for prefix wwc.
|
||||
// scan for prefix wwc.
|
||||
for (uint8_t i = DeviceValueTAG::TAG_WWC1; i <= DeviceValueTAG::TAG_WWC4; i++) {
|
||||
if ((strncmp(out, EMSdevice::tag_to_string(i).c_str(), 4) == 0) && (strlen(out) > 5)) {
|
||||
strcpy(out, &out[5]);
|
||||
const char * tag = EMSdevice::tag_to_string(i).c_str();
|
||||
uint8_t len = strlen(tag);
|
||||
if (strncmp(cmd, tag, len) == 0) {
|
||||
if (cmd[len] != '\0') {
|
||||
strcpy(cmd, &cmd[len + 1]);
|
||||
} else {
|
||||
strcpy(cmd, &cmd[len]);
|
||||
}
|
||||
id = 8 + i - DeviceValueTAG::TAG_WWC1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
// empty command after processing prefix is info
|
||||
if (cmd[0] == '\0') {
|
||||
strlcpy(cmd, "info", 20);
|
||||
}
|
||||
|
||||
return find_command(device_type, cmd);
|
||||
}
|
||||
|
||||
|
||||
// add a command to the list, which does not return json
|
||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
|
||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flag) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, flag, cmd, cb, nullptr);
|
||||
// if the description is empty, it's hidden which means it will not show up in Web or Console as an available command
|
||||
bool hidden = (description == nullptr);
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, flag, cmd, cb, nullptr, description, hidden); // callback for json is nullptr
|
||||
|
||||
// see if we need to subscribe
|
||||
if (Mqtt::enabled()) {
|
||||
@@ -140,13 +169,15 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cm
|
||||
}
|
||||
|
||||
// add a command to the list, which does return json object as output
|
||||
void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb) {
|
||||
// flag is fixed
|
||||
// optional parameter hidden for commands that will not show up on the Console
|
||||
void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, bool hidden) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, MqttSubFlag::FLAG_NOSUB, cmd, nullptr, cb); // add command
|
||||
cmdfunctions_.emplace_back(device_type, MqttSubFlag::FLAG_NOSUB, cmd, nullptr, cb, description, hidden); // callback for json is included
|
||||
}
|
||||
|
||||
// see if a command exists for that device type
|
||||
@@ -172,17 +203,87 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
|
||||
return nullptr; // command not found
|
||||
}
|
||||
|
||||
// output list of all commands to console for a specific DeviceType
|
||||
void Command::show(uuid::console::Shell & shell, uint8_t device_type) {
|
||||
// list all commands for a specific device, output as json
|
||||
bool Command::list(const uint8_t device_type, JsonObject & json) {
|
||||
if (cmdfunctions_.empty()) {
|
||||
shell.println(F("No commands"));
|
||||
json["message"] = "no commands available";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create a list of commands, sort them
|
||||
std::list<std::string> sorted_cmds;
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if (cf.device_type_ == device_type) {
|
||||
shell.printf("%s ", uuid::read_flash_string(cf.cmd_).c_str());
|
||||
if ((cf.device_type_ == device_type) && !cf.hidden_) {
|
||||
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
||||
}
|
||||
}
|
||||
sorted_cmds.sort();
|
||||
|
||||
for (auto & cl : sorted_cmds) {
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.hidden_ && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
||||
json[cl] = cf.description_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// output list of all commands to console for a specific DeviceType
|
||||
void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbose) {
|
||||
if (cmdfunctions_.empty()) {
|
||||
shell.println(F("No commands available"));
|
||||
return;
|
||||
}
|
||||
|
||||
// create a list of commands, sort them
|
||||
std::list<std::string> sorted_cmds;
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.hidden_) {
|
||||
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
||||
}
|
||||
}
|
||||
sorted_cmds.sort();
|
||||
|
||||
// if not in verbose mode, just print them on a single line
|
||||
if (!verbose) {
|
||||
for (auto & cl : sorted_cmds) {
|
||||
shell.print(cl);
|
||||
shell.print(" ");
|
||||
}
|
||||
shell.println();
|
||||
return;
|
||||
}
|
||||
|
||||
// verbose mode
|
||||
shell.println();
|
||||
for (auto & cl : sorted_cmds) {
|
||||
// find and print the description
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.hidden_ && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
||||
uint8_t i = cl.length();
|
||||
shell.print(" ");
|
||||
if (cf.flag_ == FLAG_HC) {
|
||||
shell.print("[hc] ");
|
||||
i += 5;
|
||||
} else if (cf.flag_ == FLAG_WWC) {
|
||||
shell.print("[wwc] ");
|
||||
i += 6;
|
||||
}
|
||||
shell.print(cl);
|
||||
// pad with spaces
|
||||
while (i++ < 22) {
|
||||
shell.print(' ');
|
||||
}
|
||||
shell.print(COLOR_BRIGHT_CYAN);
|
||||
shell.print(uuid::read_flash_string(cf.description_));
|
||||
shell.print(COLOR_RESET);
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -234,24 +335,31 @@ void Command::show_devices(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
// output list of all commands to console
|
||||
// calls show with verbose mode set
|
||||
void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.println(F("Available commands per device: "));
|
||||
|
||||
// show system first
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM).c_str());
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM);
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
|
||||
// show sensor
|
||||
if (EMSESP::have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
|
||||
show(shell, EMSdevice::DeviceType::DALLASSENSOR);
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::DALLASSENSOR, true);
|
||||
}
|
||||
|
||||
// do this in the order of factory classes to keep a consistent order when displaying
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
if (Command::device_has_commands(device_class.first)) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
|
||||
show(shell, device_class.first);
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, device_class.first, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,13 +45,23 @@ class Command {
|
||||
const __FlashStringHelper * cmd_;
|
||||
cmdfunction_p cmdfunction_;
|
||||
cmdfunction_json_p cmdfunction_json_;
|
||||
const __FlashStringHelper * description_;
|
||||
bool hidden_; // if its command not to be shown on the Console
|
||||
|
||||
CmdFunction(const uint8_t device_type, const uint8_t flag, const __FlashStringHelper * cmd, cmdfunction_p cmdfunction, cmdfunction_json_p cmdfunction_json)
|
||||
CmdFunction(const uint8_t device_type,
|
||||
const uint8_t flag,
|
||||
const __FlashStringHelper * cmd,
|
||||
cmdfunction_p cmdfunction,
|
||||
cmdfunction_json_p cmdfunction_json,
|
||||
const __FlashStringHelper * description,
|
||||
bool hidden = false)
|
||||
: device_type_(device_type)
|
||||
, flag_(flag)
|
||||
, cmd_(cmd)
|
||||
, cmdfunction_(cmdfunction)
|
||||
, cmdfunction_json_(cmdfunction_json) {
|
||||
, cmdfunction_json_(cmdfunction_json)
|
||||
, description_(description)
|
||||
, hidden_(hidden) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,18 +69,21 @@ class Command {
|
||||
return cmdfunctions_;
|
||||
}
|
||||
|
||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json);
|
||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = 0);
|
||||
static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag = 0);
|
||||
static void add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb);
|
||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json);
|
||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = -1);
|
||||
static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flag = 0);
|
||||
static void
|
||||
add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, bool hidden = false);
|
||||
static void show_all(uuid::console::Shell & shell);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
||||
static char * check_command(char * out, const char * cmd, int8_t & id);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, char * cmd, int8_t & id);
|
||||
|
||||
static void show(uuid::console::Shell & shell, uint8_t device_type);
|
||||
static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose);
|
||||
static void show_devices(uuid::console::Shell & shell);
|
||||
static bool device_has_commands(const uint8_t device_type);
|
||||
|
||||
static bool list(const uint8_t device_type, JsonObject & json);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
|
||||
518
src/console.cpp
518
src/console.cpp
@@ -36,7 +36,9 @@ static std::shared_ptr<EMSESPShell> shell;
|
||||
std::vector<bool> EMSESPStreamConsole::ptys_;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::telnet::TelnetService telnet_([](Stream & stream, const IPAddress & addr, uint16_t port) -> std::shared_ptr<uuid::console::Shell> { return std::make_shared<EMSESPStreamConsole>(stream, addr, port); });
|
||||
uuid::telnet::TelnetService telnet_([](Stream & stream, const IPAddress & addr, uint16_t port) -> std::shared_ptr<uuid::console::Shell> {
|
||||
return std::make_shared<EMSESPStreamConsole>(stream, addr, port);
|
||||
});
|
||||
#endif
|
||||
|
||||
EMSESPShell::EMSESPShell()
|
||||
@@ -102,30 +104,42 @@ void EMSESPShell::add_console_commands() {
|
||||
// just in case, remove everything
|
||||
commands->remove_all_commands();
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
|
||||
shell.println();
|
||||
EMSESP::show_device_values(shell);
|
||||
EMSESP::show_sensor_values(shell);
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
|
||||
shell.println();
|
||||
EMSESP::show_device_values(shell);
|
||||
EMSESP::show_sensor_values(shell);
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show), F_(devices)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::show_devices(shell);
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(devices)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_devices(shell); });
|
||||
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show), F_(ems)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_ems(shell); });
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(ems)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_ems(shell); });
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show), F_(values)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::show_device_values(shell);
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(values)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::show_device_values(shell); });
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show), F_(mqtt)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Mqtt::show_mqtt(shell); });
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(mqtt)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Mqtt::show_mqtt(shell); });
|
||||
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show), F_(commands)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Command::show_all(shell);
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(commands)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Command::show_all(shell); });
|
||||
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
@@ -157,62 +171,75 @@ void EMSESPShell::add_console_commands() {
|
||||
};
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, flash_string_vector{F_(set), F_(tx_mode)}, flash_string_vector{F_(n_mandatory)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
|
||||
// save the tx_mode
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.tx_mode = tx_mode;
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(tx_mode)},
|
||||
flash_string_vector{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
|
||||
// save the tx_mode
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.tx_mode = tx_mode;
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, flash_string_vector{F_(scan), F_(devices)}, flash_string_vector{F_(deep_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
EMSESP::scan_devices();
|
||||
} else {
|
||||
shell.printfln(F("Performing a deep scan..."));
|
||||
EMSESP::clear_all_devices();
|
||||
std::vector<uint8_t> Device_Ids;
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(scan), F_(devices)},
|
||||
flash_string_vector{F_(deep_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
EMSESP::scan_devices();
|
||||
} else {
|
||||
shell.printfln(F("Performing a deep scan..."));
|
||||
EMSESP::clear_all_devices();
|
||||
std::vector<uint8_t> Device_Ids;
|
||||
|
||||
Device_Ids.push_back(0x08); // Boilers - 0x08
|
||||
Device_Ids.push_back(0x38); // HeatPump - 0x38
|
||||
Device_Ids.push_back(0x30); // Solar Module - 0x30
|
||||
Device_Ids.push_back(0x09); // Controllers - 0x09
|
||||
Device_Ids.push_back(0x02); // Connect - 0x02
|
||||
Device_Ids.push_back(0x48); // Gateway - 0x48
|
||||
Device_Ids.push_back(0x20); // Mixer Devices - 0x20
|
||||
Device_Ids.push_back(0x21); // Mixer Devices - 0x21
|
||||
Device_Ids.push_back(0x22); // Mixer Devices - 0x22
|
||||
Device_Ids.push_back(0x23); // Mixer Devices - 0x23
|
||||
Device_Ids.push_back(0x28); // Mixer Devices WW- 0x28
|
||||
Device_Ids.push_back(0x29); // Mixer Devices WW- 0x29
|
||||
Device_Ids.push_back(0x10); // Thermostats - 0x10
|
||||
Device_Ids.push_back(0x17); // Thermostats - 0x17
|
||||
Device_Ids.push_back(0x18); // Thermostat remote - 0x18
|
||||
Device_Ids.push_back(0x19); // Thermostat remote - 0x19
|
||||
Device_Ids.push_back(0x1A); // Thermostat remote - 0x1A
|
||||
Device_Ids.push_back(0x1B); // Thermostat remote - 0x1B
|
||||
Device_Ids.push_back(0x11); // Switches - 0x11
|
||||
Device_Ids.push_back(0x08); // Boilers - 0x08
|
||||
Device_Ids.push_back(0x38); // HeatPump - 0x38
|
||||
Device_Ids.push_back(0x30); // Solar Module - 0x30
|
||||
Device_Ids.push_back(0x09); // Controllers - 0x09
|
||||
Device_Ids.push_back(0x02); // Connect - 0x02
|
||||
Device_Ids.push_back(0x48); // Gateway - 0x48
|
||||
Device_Ids.push_back(0x20); // Mixer Devices - 0x20
|
||||
Device_Ids.push_back(0x21); // Mixer Devices - 0x21
|
||||
Device_Ids.push_back(0x22); // Mixer Devices - 0x22
|
||||
Device_Ids.push_back(0x23); // Mixer Devices - 0x23
|
||||
Device_Ids.push_back(0x28); // Mixer Devices WW- 0x28
|
||||
Device_Ids.push_back(0x29); // Mixer Devices WW- 0x29
|
||||
Device_Ids.push_back(0x10); // Thermostats - 0x10
|
||||
Device_Ids.push_back(0x17); // Thermostats - 0x17
|
||||
Device_Ids.push_back(0x18); // Thermostat remote - 0x18
|
||||
Device_Ids.push_back(0x19); // Thermostat remote - 0x19
|
||||
Device_Ids.push_back(0x1A); // Thermostat remote - 0x1A
|
||||
Device_Ids.push_back(0x1B); // Thermostat remote - 0x1B
|
||||
Device_Ids.push_back(0x11); // Switches - 0x11
|
||||
|
||||
// send the read command with Version command
|
||||
for (const uint8_t device_id : Device_Ids) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
// send the read command with Version command
|
||||
for (const uint8_t device_id : Device_Ids) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(set)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
char buffer[4];
|
||||
shell.printfln(F_(master_thermostat_fmt), settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, settings.master_thermostat));
|
||||
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
|
||||
});
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
char buffer[4];
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str()
|
||||
: Helpers::hextoa(buffer, settings.master_thermostat));
|
||||
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
|
||||
});
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
@@ -229,7 +256,7 @@ void EMSESPShell::add_console_commands() {
|
||||
uint16_t offset = Helpers::hextoint(arguments.back().c_str());
|
||||
EMSESP::send_read_request(type_id, device_id, offset, EMS_MAX_TELEGRAM_LENGTH);
|
||||
} else {
|
||||
// send with length to send immediatly and trigger publish read_id
|
||||
// send with length to send immediately and trigger publish read_id
|
||||
EMSESP::send_read_request(type_id, device_id, 0, EMS_MAX_TELEGRAM_LENGTH);
|
||||
}
|
||||
});
|
||||
@@ -245,18 +272,23 @@ void EMSESPShell::add_console_commands() {
|
||||
settings.master_thermostat = value;
|
||||
EMSESP::actual_master_thermostat(value); // set the internal value too
|
||||
char buffer[5];
|
||||
shell.printfln(F_(master_thermostat_fmt), !value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
|
||||
shell.printfln(F_(master_thermostat_fmt),
|
||||
!value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
});
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(set), F_(timeout)}, flash_string_vector{F_(n_mandatory)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint16_t value = Helpers::atoint(arguments.front().c_str());
|
||||
telnet_.initial_idle_timeout(value * 60);
|
||||
shell.printfln(F("Telnet timeout set to %d minutes"), value);
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set), F_(timeout)},
|
||||
flash_string_vector{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint16_t value = Helpers::atoint(arguments.front().c_str());
|
||||
telnet_.initial_idle_timeout(value * 60);
|
||||
shell.printfln(F("Telnet timeout set to %d minutes"), value);
|
||||
});
|
||||
#endif
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
@@ -341,19 +373,25 @@ void EMSESPShell::add_console_commands() {
|
||||
return;
|
||||
}
|
||||
|
||||
// validate the command
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
bool ok = false;
|
||||
// validate that a command is present
|
||||
if (arguments.size() < 2) {
|
||||
shell.print(F("Available commands are: "));
|
||||
Command::show(shell, device_type);
|
||||
// // no cmd specified, default to empty command
|
||||
// if (Command::call(device_type, "", "", -1, json)) {
|
||||
// serializeJsonPretty(doc, shell);
|
||||
// shell.println();
|
||||
// return;
|
||||
// }
|
||||
shell.print(F("Missing command. Available commands are: "));
|
||||
Command::show(shell, device_type, false); // non-verbose mode
|
||||
return;
|
||||
}
|
||||
|
||||
const char * cmd = arguments[1].c_str();
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
bool ok = false;
|
||||
if (arguments.size() == 2) {
|
||||
// no value specified, just the cmd
|
||||
ok = Command::call(device_type, cmd, nullptr, -1, json);
|
||||
@@ -371,14 +409,13 @@ void EMSESPShell::add_console_commands() {
|
||||
}
|
||||
|
||||
if (ok && json.size()) {
|
||||
doc.shrinkToFit();
|
||||
serializeJsonPretty(doc, shell);
|
||||
shell.println();
|
||||
return;
|
||||
} else if (!ok) {
|
||||
shell.println(F("Unknown command, value, or id."));
|
||||
shell.print(F("Available commands are: "));
|
||||
Command::show(shell, device_type);
|
||||
Command::show(shell, device_type, false); // non-verbose mode
|
||||
}
|
||||
},
|
||||
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
|
||||
@@ -444,27 +481,37 @@ void Console::enter_custom_context(Shell & shell, unsigned int context) {
|
||||
// each custom context has the common commands like log, help, exit, su etc
|
||||
void Console::load_standard_commands(unsigned int context) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("test")}, flash_string_vector{F_(name_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::run_test(shell, "default");
|
||||
} else {
|
||||
Test::run_test(shell, arguments.front());
|
||||
}
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F("test")},
|
||||
flash_string_vector{F_(name_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::run_test(shell, "default");
|
||||
} else {
|
||||
Test::run_test(shell, arguments.front());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("t")}, [](Shell & shell, const std::vector<std::string> & arguments) { Test::run_test(shell, "default"); });
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("t")}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Test::run_test(shell, "default");
|
||||
});
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(debug)}, flash_string_vector{F_(name_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::debug(shell, "default");
|
||||
} else {
|
||||
Test::debug(shell, arguments.front());
|
||||
}
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(debug)},
|
||||
flash_string_vector{F_(name_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::debug(shell, "default");
|
||||
} else {
|
||||
Test::debug(shell, arguments.front());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
@@ -493,44 +540,55 @@ void Console::load_standard_commands(unsigned int context) {
|
||||
}
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(shell.log_level()));
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> { return uuid::log::levels_lowercase(); });
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> {
|
||||
return uuid::log::levels_lowercase();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(help)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.print_all_available_commands();
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(help)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.print_all_available_commands();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(exit)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.stop();
|
||||
// shell.exit_context();
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(exit)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.stop();
|
||||
// shell.exit_context();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(su)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
auto become_admin = [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("su session opened on console"));
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
};
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(su)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
auto become_admin = [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("su session opened on console"));
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
};
|
||||
|
||||
if (shell.has_flags(CommandFlags::LOCAL)) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
uint64_t now = uuid::get_uptime_ms();
|
||||
if (shell.has_flags(CommandFlags::LOCAL)) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
uint64_t now = uuid::get_uptime_ms();
|
||||
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid su password on console"));
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid su password on console"));
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// console commands to add
|
||||
@@ -538,53 +596,67 @@ void Console::load_system_commands(unsigned int context) {
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(restart)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.restart(); });
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.restart();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(wifi), F_(reconnect)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.wifi_reconnect(); });
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.wifi_reconnect();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, flash_string_vector{F_(format)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||
EMSESP::system_.format(shell);
|
||||
} else {
|
||||
shell.println(F("incorrect password"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
EMSESPShell::commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(format)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||
EMSESP::system_.format(shell);
|
||||
} else {
|
||||
shell.println(F("incorrect password"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(passwd)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->update(
|
||||
[&](SecuritySettings & securitySettings) {
|
||||
securitySettings.jwtSecret = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.println(F("su password updated"));
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(passwd)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2),
|
||||
[password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
EMSESP::esp8266React.getSecuritySettingsService()->update(
|
||||
[&](SecuritySettings & securitySettings) {
|
||||
securitySettings.jwtSecret = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.println(F("su password updated"));
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(show), F_(system)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.show_system(shell);
|
||||
shell.println();
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(system)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.show_system(shell);
|
||||
shell.println();
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
@@ -614,55 +686,63 @@ void Console::load_system_commands(unsigned int context) {
|
||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(set), F_(wifi), F_(password)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
||||
networkSettings.password = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(board_profile)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, TLK110, LAN8720, CUSTOM)"));
|
||||
return;
|
||||
}
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
|
||||
EMSESP::system_.network_init(true);
|
||||
flash_string_vector{F_(set), F_(wifi), F_(password)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2),
|
||||
[password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation(
|
||||
[&](NetworkSettings & networkSettings) {
|
||||
networkSettings.password = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||
} else {
|
||||
shell.println(F("Passwords do not match"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(show), F_(users)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.show_users(shell);
|
||||
});
|
||||
|
||||
EMSESPShell::commands
|
||||
->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(board_profile)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, TLK110, LAN8720, CUSTOM)"));
|
||||
return;
|
||||
}
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
|
||||
EMSESP::system_.network_init(true);
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(show), F_(users)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::system_.show_users(shell);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -44,8 +44,6 @@ using uuid::log::Level;
|
||||
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
|
||||
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
|
||||
|
||||
#define MQTT_TOPIC(list_name) (__pstr__##list_name[0])
|
||||
|
||||
// clang-format off
|
||||
// strings stored 32 bit aligned on ESP8266/ESP32
|
||||
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal;
|
||||
|
||||
@@ -40,8 +40,17 @@ void DallasSensor::start() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bus_.begin(dallas_gpio_);
|
||||
#endif
|
||||
// API call
|
||||
Command::add_with_json(EMSdevice::DeviceType::DALLASSENSOR, F_(info), [&](const char * value, const int8_t id, JsonObject & json) { return command_info(value, id, json); });
|
||||
// API calls
|
||||
Command::add_with_json(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(info),
|
||||
[&](const char * value, const int8_t id, JsonObject & json) { return command_info(value, id, json); },
|
||||
F_(info_cmd));
|
||||
Command::add_with_json(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(commands),
|
||||
[&](const char * value, const int8_t id, JsonObject & json) { return command_commands(value, id, json); },
|
||||
F_(commands_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +134,7 @@ void DallasSensor::loop() {
|
||||
int16_t t;
|
||||
t = get_temperature_c(addr);
|
||||
if ((t >= -550) && (t <= 1250)) {
|
||||
sensorreads_++;
|
||||
// check if we have this sensor already
|
||||
bool found = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
@@ -270,11 +280,13 @@ const std::vector<DallasSensor::Sensor> DallasSensor::sensors() const {
|
||||
|
||||
// skip crc from id.
|
||||
DallasSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
: id_(((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16) | ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6])) {
|
||||
: id_(((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
| ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6])) {
|
||||
}
|
||||
|
||||
uint64_t DallasSensor::get_id(const uint8_t addr[]) {
|
||||
return (((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16) | ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6]));
|
||||
return (((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
| ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6]));
|
||||
}
|
||||
|
||||
uint64_t DallasSensor::Sensor::id() const {
|
||||
@@ -283,7 +295,13 @@ uint64_t DallasSensor::Sensor::id() const {
|
||||
|
||||
std::string DallasSensor::Sensor::to_string() const {
|
||||
std::string str(20, '\0');
|
||||
snprintf_P(&str[0], str.capacity() + 1, PSTR("%02X-%04X-%04X-%04X"), (unsigned int)(id_ >> 48) & 0xFF, (unsigned int)(id_ >> 32) & 0xFFFF, (unsigned int)(id_ >> 16) & 0xFFFF, (unsigned int)(id_)&0xFFFF);
|
||||
snprintf_P(&str[0],
|
||||
str.capacity() + 1,
|
||||
PSTR("%02X-%04X-%04X-%04X"),
|
||||
(unsigned int)(id_ >> 48) & 0xFF,
|
||||
(unsigned int)(id_ >> 32) & 0xFFFF,
|
||||
(unsigned int)(id_ >> 16) & 0xFFFF,
|
||||
(unsigned int)(id_)&0xFFFF);
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -296,6 +314,11 @@ bool DallasSensor::updated_values() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// list commands
|
||||
bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObject & json) {
|
||||
return Command::list(EMSdevice::DeviceType::DALLASSENSOR, json);
|
||||
}
|
||||
|
||||
// creates JSON doc from values
|
||||
// returns false if empty
|
||||
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":23.30},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":24.0}}
|
||||
@@ -308,22 +331,22 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
|
||||
for (const auto & sensor : sensors_) {
|
||||
char sensorID[10]; // sensor{1-n}
|
||||
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
|
||||
if (id == 0) {
|
||||
if (Mqtt::dallas_format() == Mqtt::Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) {
|
||||
json[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
json[sensorID] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
} else {
|
||||
if (id == -1) { // show number and id
|
||||
JsonObject dataSensor = json.createNestedObject(sensorID);
|
||||
dataSensor["id"] = sensor.to_string();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
} else { // show according to format
|
||||
if (Mqtt::dallas_format() == Mqtt::Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) {
|
||||
json[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
json[sensorID] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return (json.size() > 0);
|
||||
}
|
||||
|
||||
// send all dallas sensor values as a JSON package to MQTT
|
||||
@@ -414,7 +437,6 @@ void DallasSensor::publish_values(const bool force) {
|
||||
sensor_no++; // increment sensor count
|
||||
}
|
||||
|
||||
// doc.shrinkToFit();
|
||||
Mqtt::publish(F("dallassensor_data"), doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ class DallasSensor {
|
||||
|
||||
const std::vector<Sensor> sensors() const;
|
||||
|
||||
uint32_t reads() {
|
||||
return sensorreads_;
|
||||
}
|
||||
|
||||
uint32_t fails() {
|
||||
return sensorfails_;
|
||||
}
|
||||
@@ -113,6 +117,7 @@ class DallasSensor {
|
||||
uint64_t get_id(const uint8_t addr[]);
|
||||
|
||||
bool command_info(const char * value, const int8_t id, JsonObject & json);
|
||||
bool command_commands(const char * value, const int8_t id, JsonObject & json);
|
||||
|
||||
uint32_t last_activity_ = uuid::get_uptime();
|
||||
uint32_t last_publish_ = uuid::get_uptime();
|
||||
@@ -127,6 +132,7 @@ class DallasSensor {
|
||||
bool parasite_ = false;
|
||||
bool changed_ = false;
|
||||
uint32_t sensorfails_ = 0;
|
||||
uint32_t sensorreads_ = 0;
|
||||
int8_t scanretry_ = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,8 +76,8 @@
|
||||
#define EMSESP_DEFAULT_DALLAS_PARASITE false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_API_ENABLED
|
||||
#define EMSESP_DEFAULT_API_ENABLED false // turn off, because its insecure
|
||||
#ifndef EMSESP_DEFAULT_NOTOKEN_API
|
||||
#define EMSESP_DEFAULT_NOTOKEN_API false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
|
||||
@@ -152,4 +152,8 @@
|
||||
#define EMSESP_DEFAULT_SUBSCRIBE_FORMAT 0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SOLAR_MAXFLOW
|
||||
#define EMSESP_DEFAULT_SOLAR_MAXFLOW 30
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -85,7 +85,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor));
|
||||
}
|
||||
// MQTT commands for boiler topic
|
||||
register_device_value(TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_tapwarmwater_activated));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated));
|
||||
register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::ENUM, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
|
||||
|
||||
// add values
|
||||
@@ -95,8 +96,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
id_ = product_id;
|
||||
|
||||
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
|
||||
register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_burn_power));
|
||||
register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
|
||||
@@ -108,12 +109,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), FL_(sysPress), DeviceValueUOM::BAR);
|
||||
register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), FL_(boilTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), FL_(exhaustTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), FL_(flameCurr), DeviceValueUOM::UA);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::PUMP);
|
||||
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_heating_activated));
|
||||
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_heating_activated));
|
||||
register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, FL_(heatingTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heating_temp));
|
||||
register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, FL_(pumpModMax), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_pump));
|
||||
register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, FL_(pumpModMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_pump));
|
||||
@@ -126,7 +128,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, FL_(setBurnPow), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, FL_(burnStarts), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, FL_(burnStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, nullptr, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, nullptr, FL_(heatWorkMin), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, FL_(UBAuptime), DeviceValueUOM::MINUTES);
|
||||
@@ -135,7 +137,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, FL_(serviceCodeNumber), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, FL_(maintenanceMessage), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &maintenanceType_, DeviceValueType::ENUM, FL_(enum_off_time_date), FL_(maintenanceType), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenance));
|
||||
register_device_value(TAG_BOILER_DATA,
|
||||
&maintenanceType_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_off_time_date),
|
||||
FL_(maintenanceType),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_maintenance));
|
||||
register_device_value(TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, FL_(maintenanceTime), DeviceValueUOM::HOURS);
|
||||
// heatpump info
|
||||
if (model() == EMS_DEVICE_FLAG_HEATPUMP) {
|
||||
@@ -143,8 +151,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompHeating_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompHeating), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompCooling_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompCooling), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &upTimeCompWw_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompWw), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, FL_(heatingStarts), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, FL_(coolingStarts), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, FL_(heatingStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, FL_(coolingStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompHeating), DeviceValueUOM::KWH);
|
||||
@@ -172,54 +180,79 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
}
|
||||
|
||||
// warm water - boiler_data_ww topic
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, nullptr, FL_(wWSelTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, FL_(wWSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_warmwater_temp));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), FL_(wWType), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), FL_(wWComfort), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_mode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, FL_(wWFlowTempOffset), DeviceValueUOM::NONE, MAKE_CF_CB(set_wWFlowTempOffset));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, FL_(wWMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_warmwater_maxpower));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_pump));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, FL_(wWDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_disinfect_temp));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_mode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, FL_(wWCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wWCurFlow), DeviceValueUOM::LMIN);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, FL_(wWActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_activated));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, FL_(wWOneTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_onetime));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wWDisinfecting), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, FL_(wWCharging), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, FL_(wWRecharging), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, FL_(wWTempOK), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, FL_(wWActive), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, FL_(wWHeat), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wWSetPumpPower), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(mixerTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(tankMiddleTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, FL_(wWStarts), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, nullptr, FL_(wWStarts2), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, FL_(wWWorkM), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, nullptr, FL_(wWSelTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, FL_(wWSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_warmwater_temp));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), FL_(wWType), DeviceValueUOM::NONE);
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), FL_(wWComfort), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_mode));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, FL_(wWFlowTempOffset), DeviceValueUOM::NONE, MAKE_CF_CB(set_wWFlowTempOffset));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, FL_(wWMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_warmwater_maxpower));
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation_pump));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::ENUM, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_BOILER_DATA_WW,
|
||||
&wWDisinfectionTemp_,
|
||||
DeviceValueType::UINT,
|
||||
nullptr,
|
||||
FL_(wWDisinfectionTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_disinfect_temp));
|
||||
register_device_value(TAG_BOILER_DATA_WW,
|
||||
&wWCircMode_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_freq),
|
||||
FL_(wWCircMode),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_warmwater_circulation_mode));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, FL_(wWCirc), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wWCurFlow), DeviceValueUOM::LMIN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(
|
||||
TAG_BOILER_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, FL_(wWActivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_activated));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, FL_(wWOneTime), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_onetime));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wWDisinfecting), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, FL_(wWCharging), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, FL_(wWRecharging), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, FL_(wWTempOK), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, FL_(wWActive), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, FL_(wWHeat), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wWSetPumpPower), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(mixerTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(tankMiddleTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, FL_(wWStarts), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, nullptr, FL_(wWStarts2), DeviceValueUOM::NUM);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, FL_(wWWorkM), DeviceValueUOM::MINUTES);
|
||||
|
||||
// fetch some initial data
|
||||
EMSESP::send_read_request(0x10, device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x11, device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x15, device_id); // read maintenace data on start (only published on change)
|
||||
EMSESP::send_read_request(0x1C, device_id); // read maintenace status on start (only published on change)
|
||||
EMSESP::send_read_request(0x10,
|
||||
device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x11,
|
||||
device_id); // read last errorcode on start (only published on errors)
|
||||
EMSESP::send_read_request(0x15,
|
||||
device_id); // read maintenace data on start (only published on change)
|
||||
EMSESP::send_read_request(0x1C,
|
||||
device_id); // read maintenace status on start (only published on change)
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Boiler::publish_ha_config() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(boiler);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
doc["name"] = FJSON("ID");
|
||||
char name_s[40];
|
||||
snprintf_P(name_s, sizeof(name_s), PSTR("* %s Product ID"), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Boiler");
|
||||
@@ -231,7 +264,8 @@ bool Boiler::publish_ha_config() {
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/boiler/config"), Mqtt::base().c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
Mqtt::publish_ha(topic,
|
||||
doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -277,7 +311,6 @@ void Boiler::check_active(const bool force) {
|
||||
}
|
||||
|
||||
// 0x33
|
||||
// Boiler(0x08) -> Me(0x0B), UBAParameterWW(0x33), data: 08 FF 30 FB FF 28 FF 07 46 00 00
|
||||
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
||||
// has_update(telegram->read_bitvalue(wwEquipt_,0,3)); // 8=boiler has ww
|
||||
has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on
|
||||
@@ -288,7 +321,7 @@ void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on
|
||||
has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
|
||||
has_update(telegram->read_value(wWDisinfectionTemp_, 8));
|
||||
has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve
|
||||
has_update(telegram->read_bitvalue(wWChargeType_, 10, 0)); // 0 = charge pump, 0xff = 3-way valve
|
||||
|
||||
telegram->read_value(wWComfort_, 9);
|
||||
if (wWComfort_ == 0x00) {
|
||||
@@ -319,8 +352,9 @@ void Boiler::process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// warm water storage sensors (if present)
|
||||
// wWStorageTemp2 is also used by some brands as the boiler temperature - see https://github.com/emsesp/EMS-ESP/issues/206
|
||||
has_update(telegram->read_value(wWStorageTemp1_, 9)); // 0x8300 if not available
|
||||
has_update(telegram->read_value(wWStorageTemp2_, 11)); // 0x8000 if not available - this is boiler temp
|
||||
has_update(telegram->read_value(wWStorageTemp1_, 9)); // 0x8300 if not available
|
||||
has_update(telegram->read_value(wWStorageTemp2_,
|
||||
11)); // 0x8000 if not available - this is boiler temp
|
||||
|
||||
has_update(telegram->read_value(retTemp_, 13));
|
||||
has_update(telegram->read_value(flameCurr_, 15));
|
||||
@@ -456,7 +490,8 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(outdoorTemp_, 0));
|
||||
has_update(telegram->read_value(boilTemp_, 2));
|
||||
has_update(telegram->read_value(exhaustTemp_, 4));
|
||||
has_update(telegram->read_value(switchTemp_, 25)); // only if there is a mixer module present
|
||||
has_update(telegram->read_value(switchTemp_,
|
||||
25)); // only if there is a mixer module present
|
||||
has_update(telegram->read_value(heatingPumpMod_, 9));
|
||||
has_update(telegram->read_value(burnStarts_, 10, 3)); // force to 3 bytes
|
||||
has_update(telegram->read_value(burnWorkMin_, 13, 3)); // force to 3 bytes
|
||||
@@ -515,7 +550,8 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr<const Telegram> telegram)
|
||||
void Boiler::process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on
|
||||
has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes
|
||||
has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||
has_update(telegram->read_value(wWCircMode_,
|
||||
11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||
// has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9
|
||||
// has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9
|
||||
}
|
||||
@@ -588,13 +624,12 @@ void Boiler::process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram)
|
||||
}
|
||||
|
||||
// Heatpump power - type 0x48D
|
||||
void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram){
|
||||
void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hpPower_, 11));
|
||||
|
||||
}
|
||||
|
||||
// Heatpump outdoor unit - type 0x48F
|
||||
void Boiler::process_HpOutdoor(std::shared_ptr<const Telegram> telegram){
|
||||
void Boiler::process_HpOutdoor(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hpTc0_, 6));
|
||||
has_update(telegram->read_value(hpTc1_, 4));
|
||||
has_update(telegram->read_value(hpTc3_, 2));
|
||||
@@ -625,7 +660,8 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// UBASetPoint 0x1A
|
||||
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(setFlowTemp_, 0)); // boiler set temp from thermostat
|
||||
has_update(telegram->read_value(setFlowTemp_,
|
||||
0)); // boiler set temp from thermostat
|
||||
has_update(telegram->read_value(setBurnPow_, 1)); // max json power in %
|
||||
has_update(telegram->read_value(wWSetPumpPower_, 2)); // ww pump speed/power?
|
||||
}
|
||||
@@ -727,8 +763,9 @@ bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 6, v, EMS_TYPE_UBAParameterWWPlus);
|
||||
} else {
|
||||
// some boiler have it in 0x33, some in 0x35
|
||||
write_command(EMS_TYPE_UBAFlags, 3, v, 0x34); // for i9000, see #397
|
||||
write_command(EMS_TYPE_UBAParameterWW, 2, v, EMS_TYPE_UBAParameterWW); // read seltemp back
|
||||
write_command(EMS_TYPE_UBAFlags, 3, v, 0x34); // for i9000, see #397
|
||||
write_command(EMS_TYPE_UBAParameterWW, 2, v,
|
||||
EMS_TYPE_UBAParameterWW); // read seltemp back
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -769,11 +806,11 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
bool Boiler::set_burn_power(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set burner max. power: Invalid value"));
|
||||
LOG_WARNING(F("Set burner max power: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting burner max. power to %d %"), v);
|
||||
LOG_INFO(F("Setting burner max power to %d %"), v);
|
||||
write_command(EMS_TYPE_UBASetPoints, 1, v, EMS_TYPE_UBASetPoints);
|
||||
|
||||
return true;
|
||||
@@ -955,11 +992,11 @@ bool Boiler::set_hyst_off(const char * value, const int8_t id) {
|
||||
bool Boiler::set_burn_period(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
LOG_WARNING(F("Set burner min. period: Invalid value"));
|
||||
LOG_WARNING(F("Set burner min period: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting burner min. period to %d min"), v);
|
||||
LOG_INFO(F("Setting burner min period to %d min"), v);
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 10, v, EMS_TYPE_UBAParametersPlus);
|
||||
} else {
|
||||
@@ -1088,7 +1125,8 @@ bool Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
|
||||
|
||||
LOG_INFO(F("Setting warm water OneTime loading %s"), v ? "on" : "off");
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02), 0xE9); // not sure if this is in flags
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02),
|
||||
0xE9); // not sure if this is in flags
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02), 0x34);
|
||||
}
|
||||
@@ -1107,7 +1145,8 @@ bool Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
|
||||
|
||||
LOG_INFO(F("Setting warm water circulation %s"), v ? "on" : "off");
|
||||
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
|
||||
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02), 0xE9); // not sure if this is in flags
|
||||
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02),
|
||||
0xE9); // not sure if this is in flags
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02), 0x34);
|
||||
}
|
||||
@@ -1134,7 +1173,7 @@ bool Boiler::set_warmwater_circulation_pump(const char * value, const int8_t id)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the mode of circulation, 1x3min, ... 6x3min, continuos
|
||||
// Set the mode of circulation, 1x3min, ... 6x3min, continuous
|
||||
// true = on, false = off
|
||||
bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
@@ -1146,7 +1185,7 @@ bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id)
|
||||
if (v < 7) {
|
||||
LOG_INFO(F("Setting warm water circulation mode %dx3min"), v);
|
||||
} else if (v == 7) {
|
||||
LOG_INFO(F("Setting warm water circulation mode continuos"));
|
||||
LOG_INFO(F("Setting warm water circulation mode continuous"));
|
||||
} else {
|
||||
LOG_WARNING(F("Set warm water circulation mode: Invalid value"));
|
||||
return false;
|
||||
@@ -1164,7 +1203,7 @@ bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id)
|
||||
// Reset command
|
||||
// 0 & 1 Reset-Mode (Manual, others)
|
||||
// 8 reset maintenance message Hxx
|
||||
// 12 & 13 Reset that Error-memory
|
||||
// 12 & 13 Reset the Error-memory
|
||||
bool Boiler::set_reset(const char * value, const int8_t id) {
|
||||
uint8_t num;
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_reset))) {
|
||||
|
||||
@@ -44,13 +44,16 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
|
||||
bool Heatpump::publish_ha_config() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(heatpump);
|
||||
doc["ic"] = F_(iconheatpump);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
doc["name"] = FJSON("ID");
|
||||
char name_s[40];
|
||||
snprintf_P(name_s, sizeof(name_s), PSTR("* %s Product ID"), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Heat Pump");
|
||||
|
||||
@@ -60,7 +60,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::PUMP);
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES);
|
||||
} else {
|
||||
@@ -69,7 +69,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
uint8_t tag = TAG_WWC1 + hc_ - 1;
|
||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::PUMP);
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(wwTempStatus), DeviceValueUOM::NONE);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ bool Mixer::publish_ha_config() {
|
||||
snprintf_P(uniq_id, sizeof(uniq_id), PSTR("Mixer%02X"), device_id() - 0x20 + 1);
|
||||
doc["uniq_id"] = uniq_id;
|
||||
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
@@ -97,7 +99,14 @@ bool Mixer::publish_ha_config() {
|
||||
snprintf_P(name, sizeof(name), PSTR("Mixer %02X"), device_id() - 0x20 + 1);
|
||||
doc["name"] = name;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
char tpl[30];
|
||||
if (type_ == Type::HC) {
|
||||
snprintf_P(tpl, sizeof(tpl), PSTR("{{value_json.hc%d.id}}"), device_id() - 0x20 + 1);
|
||||
} else {
|
||||
snprintf_P(tpl, sizeof(tpl), PSTR("{{value_json.wwc%d.id}}"), device_id() - 0x28 + 1);
|
||||
}
|
||||
doc["val_tpl"] = tpl;
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Mixer");
|
||||
dev["sw"] = EMSESP_APP_VERSION;
|
||||
|
||||
@@ -30,7 +30,9 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
|
||||
// telegram handlers
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
register_telegram_type(0x0097, F("SM10Monitor"), true, MAKE_PF_CB(process_SM10Monitor));
|
||||
register_telegram_type(0x97, F("SM10Monitor"), false, MAKE_PF_CB(process_SM10Monitor));
|
||||
register_telegram_type(0x96, F("SM10Config"), true, MAKE_PF_CB(process_SM10Config));
|
||||
EMSESP::send_read_request(0x97, device_id);
|
||||
}
|
||||
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
|
||||
@@ -55,7 +57,7 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, MAKE_PF_CB(process_ISM1StatusMessage));
|
||||
register_telegram_type(0x0101, F("ISM1Set"), false, MAKE_PF_CB(process_ISM1Set));
|
||||
register_telegram_type(0x0101, F("ISM1Set"), true, MAKE_PF_CB(process_ISM1Set));
|
||||
}
|
||||
|
||||
// device values...
|
||||
@@ -64,12 +66,12 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
if (device_id == 0x2A) {
|
||||
register_device_value(TAG_NONE, &type_, DeviceValueType::TEXT, nullptr, FL_(type), DeviceValueUOM::NONE);
|
||||
strlcpy(type_, "warm water circuit", sizeof(type_));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::UINT, nullptr, FL_(wwTemp1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_3_, DeviceValueType::UINT, nullptr, FL_(wwTemp3), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_4_, DeviceValueType::UINT, nullptr, FL_(wwTemp4), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_5_, DeviceValueType::UINT, nullptr, FL_(wwTemp5), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_7_, DeviceValueType::UINT, nullptr, FL_(wwTemp7), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwPump_, DeviceValueType::UINT, nullptr, FL_(wwPump), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &wwTemp_1_, DeviceValueType::UINT, nullptr, FL_(wwTemp1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &wwTemp_3_, DeviceValueType::UINT, nullptr, FL_(wwTemp3), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &wwTemp_4_, DeviceValueType::UINT, nullptr, FL_(wwTemp4), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &wwTemp_5_, DeviceValueType::UINT, nullptr, FL_(wwTemp5), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &wwTemp_7_, DeviceValueType::UINT, nullptr, FL_(wwTemp7), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &wwPump_, DeviceValueType::UINT, nullptr, FL_(wwPump), DeviceValueUOM::DEGREES);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,35 +80,125 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
|
||||
register_device_value(TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(collectorTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, FL_(div10), FL_(tankBottomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), FL_(tank2BottomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), FL_(heatExchangerTemp), DeviceValueUOM::DEGREES);
|
||||
|
||||
register_device_value(TAG_NONE, &tankBottomMaxTemp_, DeviceValueType::UINT, nullptr, FL_(tankMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_SM100TankBottomMaxTemp));
|
||||
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, FL_(solarPumpModulation), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, FL_(cylinderPumpModulation), DeviceValueUOM::PERCENT);
|
||||
|
||||
register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::PUMP);
|
||||
register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE);
|
||||
|
||||
register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, FL_(pumpWorkTime), DeviceValueUOM::MINUTES);
|
||||
register_device_value(TAG_NONE, &tankMaxTemp_, DeviceValueType::UINT, nullptr, FL_(tankMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TankMaxTemp));
|
||||
|
||||
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||||
register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), FL_(energyTotal), DeviceValueUOM::KWH);
|
||||
register_device_value(TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, FL_(energyToday), DeviceValueUOM::WH);
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, FL_(solarPumpModulation), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_NONE, &solarPumpMinMod_, DeviceValueType::UINT, nullptr, FL_(pumpMinMod), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_PumpMinMod));
|
||||
register_device_value(
|
||||
TAG_NONE, &solarPumpTurnonDiff_, DeviceValueType::UINT, nullptr, FL_(solarPumpTurnonDiff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TurnonDiff));
|
||||
register_device_value(
|
||||
TAG_NONE, &solarPumpTurnoffDiff_, DeviceValueType::UINT, nullptr, FL_(solarPumpTurnoffDiff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TurnoffDiff));
|
||||
register_device_value(
|
||||
TAG_NONE, &collectorMaxTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMaxTemp));
|
||||
register_device_value(
|
||||
TAG_NONE, &collectorMinTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMinTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMinTemp));
|
||||
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &solarPower_, DeviceValueType::ULONG, nullptr, FL_(solarPower), DeviceValueUOM::W);
|
||||
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||||
register_device_value(TAG_NONE, &maxFlow_, DeviceValueType::UINT, FL_(div10), FL_(maxFlow), DeviceValueUOM::LMIN, MAKE_CF_CB(set_SM10MaxFlow));
|
||||
register_device_value(TAG_NONE, &wwMinTemp_, DeviceValueType::UINT, nullptr, FL_(wwMinTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwMinTemp));
|
||||
register_device_value(TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_solarEnabled));
|
||||
}
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||||
}
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
|
||||
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, FL_(solarPumpModulation), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_NONE, &solarPumpMinMod_, DeviceValueType::UINT, nullptr, FL_(pumpMinMod), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_PumpMinMod));
|
||||
register_device_value(
|
||||
TAG_NONE, &solarPumpTurnonDiff_, DeviceValueType::UINT, nullptr, FL_(solarPumpTurnonDiff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TurnonDiff));
|
||||
register_device_value(
|
||||
TAG_NONE, &solarPumpTurnoffDiff_, DeviceValueType::UINT, nullptr, FL_(solarPumpTurnoffDiff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TurnoffDiff));
|
||||
register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), FL_(tank2BottomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), FL_(heatExchangerTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, FL_(cylinderPumpModulation), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(
|
||||
TAG_NONE, &collectorMaxTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMaxTemp));
|
||||
register_device_value(
|
||||
TAG_NONE, &collectorMinTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMinTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMinTemp));
|
||||
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||||
register_device_value(TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, FL_(energyToday), DeviceValueUOM::WH);
|
||||
register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), FL_(energyTotal), DeviceValueUOM::KWH);
|
||||
|
||||
register_device_value(TAG_NONE,
|
||||
&heatTransferSystem_,
|
||||
DeviceValueType::BOOL,
|
||||
nullptr,
|
||||
FL_(heatTransferSystem),
|
||||
DeviceValueUOM::BOOLEAN,
|
||||
MAKE_CF_CB(set_heatTransferSystem));
|
||||
register_device_value(TAG_NONE, &externalTank_, DeviceValueType::BOOL, nullptr, FL_(externalTank), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_externalTank));
|
||||
register_device_value(
|
||||
TAG_NONE, &thermalDisinfect_, DeviceValueType::BOOL, nullptr, FL_(thermalDisinfect), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_thermalDisinfect));
|
||||
register_device_value(TAG_NONE, &heatMetering_, DeviceValueType::BOOL, nullptr, FL_(heatMetering), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_heatMetering));
|
||||
register_device_value(TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_solarEnabled));
|
||||
|
||||
// telegram 0x035A
|
||||
register_device_value(
|
||||
TAG_NONE, &solarPumpMode_, DeviceValueType::ENUM, FL_(enum_solarmode), FL_(solarPumpMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_solarMode));
|
||||
register_device_value(TAG_NONE,
|
||||
&solarPumpKick_,
|
||||
DeviceValueType::BOOL,
|
||||
nullptr,
|
||||
FL_(solarPumpKick),
|
||||
DeviceValueUOM::BOOLEAN,
|
||||
MAKE_CF_CB(set_solarPumpKick)); // pump kick for vacuum collector, 00=off
|
||||
register_device_value(TAG_NONE,
|
||||
&plainWaterMode_,
|
||||
DeviceValueType::BOOL,
|
||||
nullptr,
|
||||
FL_(plainWaterMode),
|
||||
DeviceValueUOM::BOOLEAN,
|
||||
MAKE_CF_CB(set_plainWaterMode)); // system does not use antifreeze, 00=off
|
||||
register_device_value(TAG_NONE,
|
||||
&doubleMatchFlow_,
|
||||
DeviceValueType::BOOL,
|
||||
nullptr,
|
||||
FL_(doubleMatchFlow),
|
||||
DeviceValueUOM::BOOLEAN,
|
||||
MAKE_CF_CB(set_doubleMatchFlow)); // double Match Flow, 00=off
|
||||
|
||||
// telegram 0x380
|
||||
register_device_value(TAG_NONE, &climateZone_, DeviceValueType::UINT, nullptr, FL_(climateZone), DeviceValueUOM::NONE, MAKE_CF_CB(set_climateZone)); // climate zone identifier
|
||||
register_device_value(TAG_NONE,
|
||||
&collector1Area_,
|
||||
DeviceValueType::USHORT,
|
||||
FL_(div10),
|
||||
FL_(collector1Area),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_collector1Area)); // Area of collector field 1
|
||||
register_device_value(TAG_NONE,
|
||||
&collector1Type_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_collectortype),
|
||||
FL_(collector1Type),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_collector1Type)); // Type of collector field 1, 01=flat, 02=vacuum
|
||||
}
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Solar::publish_ha_config() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["name"] = FJSON("ID");
|
||||
doc["uniq_id"] = F_(solar);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
char name_s[40];
|
||||
snprintf_P(name_s, sizeof(name_s), PSTR("* %s Product ID"), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Solar");
|
||||
@@ -123,13 +215,59 @@ bool Solar::publish_ha_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// SM10Monitor - type 0x96
|
||||
// Solar(0x30) -> All(0x00), (0x96), data: FF 18 19 0A 02 5A 27 0A 05 2D 1E 0F 64 28 0A
|
||||
void Solar::process_SM10Config(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(solarIsEnabled_, 0)); // FF on
|
||||
uint8_t colmax = collectorMaxTemp_ / 10;
|
||||
has_update(telegram->read_value(colmax, 3));
|
||||
collectorMaxTemp_ = colmax * 10;
|
||||
uint8_t colmin = collectorMinTemp_ / 10;
|
||||
has_update(telegram->read_value(colmin, 4));
|
||||
collectorMinTemp_ = colmin * 10;
|
||||
has_update(telegram->read_value(solarPumpMinMod_, 2));
|
||||
has_update(telegram->read_value(solarPumpTurnonDiff_, 7));
|
||||
has_update(telegram->read_value(solarPumpTurnoffDiff_, 8));
|
||||
has_update(telegram->read_value(tankMaxTemp_, 5));
|
||||
has_update(telegram->read_value(wwMinTemp_, 6));
|
||||
}
|
||||
|
||||
// SM10Monitor - type 0x97
|
||||
void Solar::process_SM10Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t solarpumpmod = solarPumpModulation_;
|
||||
|
||||
has_update(telegram->read_bitvalue(collectorShutdown_, 0, 3));
|
||||
// has_update(telegram->read_bitvalue(tankHeated_, 0, x)); // tank full, to be determined
|
||||
has_update(telegram->read_value(collectorTemp_, 2)); // collector temp from SM10, is *10
|
||||
has_update(telegram->read_value(tankBottomTemp_, 5)); // tank bottom temp from SM10, is *10
|
||||
has_update(telegram->read_value(solarPumpModulation_, 4)); // modulation solar pump
|
||||
has_update(telegram->read_bitvalue(solarPump_, 7, 1));
|
||||
has_update(telegram->read_value(pumpWorkTime_, 8, 3));
|
||||
|
||||
// mask out pump-boosts
|
||||
if (solarpumpmod == 0 && solarPumpModulation_ == 100) {
|
||||
solarPumpModulation_ = solarPumpMinMod_; // set to minimum
|
||||
}
|
||||
|
||||
if (!Helpers::hasValue(maxFlow_)) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { maxFlow_ = settings.solar_maxflow; });
|
||||
}
|
||||
|
||||
// solar publishes every minute, do not count reads by other devices
|
||||
if (telegram->dest == 0) {
|
||||
// water 4.184 J/gK, glycol ~2.6-2.8 J/gK, no aceotrope
|
||||
// solarPower_ = (collectorTemp_ - tankBottomTemp_) * solarPumpModulation_ * maxFlow_ * 10 / 1434; // water
|
||||
solarPower_ = (collectorTemp_ - tankBottomTemp_) * solarPumpModulation_ * maxFlow_ * 10 / 1665; //40% glycol@40°C
|
||||
if (energy.size() >= 60) {
|
||||
energy.pop_front();
|
||||
}
|
||||
energy.push_back(solarPower_);
|
||||
uint32_t sum = 0;
|
||||
for (auto e : energy) {
|
||||
sum += e;
|
||||
}
|
||||
energyLastHour_ = sum / 6; // counts in 0.1 Wh
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -150,10 +288,10 @@ void Solar::process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram)
|
||||
*/
|
||||
void Solar::process_SM100SolarCircuitConfig(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(collectorMaxTemp_, 0, 1));
|
||||
has_update(telegram->read_value(tankBottomMaxTemp_, 3, 1));
|
||||
has_update(telegram->read_value(tankMaxTemp_, 3, 1));
|
||||
has_update(telegram->read_value(collectorMinTemp_, 4, 1));
|
||||
has_update(telegram->read_value(solarPumpMode_, 5, 1));
|
||||
has_update(telegram->read_value(solarPumpMinRPM_, 6, 1));
|
||||
has_update(telegram->read_value(solarPumpMinMod_, 6, 1));
|
||||
has_update(telegram->read_value(solarPumpTurnoffDiff_, 7, 1));
|
||||
has_update(telegram->read_value(solarPumpTurnonDiff_, 8, 1));
|
||||
has_update(telegram->read_value(solarPumpKick_, 9, 1));
|
||||
@@ -262,11 +400,11 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(solarPumpModulation_, 9));
|
||||
|
||||
if (solarpumpmod == 0 && solarPumpModulation_ == 100) { // mask out boosts
|
||||
solarPumpModulation_ = 15; // set to minimum
|
||||
solarPumpModulation_ = solarPumpMinMod_; // set to minimum
|
||||
}
|
||||
|
||||
if (cylinderpumpmod == 0 && cylinderPumpModulation_ == 100) { // mask out boosts
|
||||
cylinderPumpModulation_ = 15; // set to minimum
|
||||
cylinderPumpModulation_ = solarPumpMinMod_; // set to minimum
|
||||
}
|
||||
has_update(telegram->read_bitvalue(tankHeated_, 3, 1)); // issue #422
|
||||
has_update(telegram->read_bitvalue(collectorShutdown_, 3, 0)); // collector shutdown
|
||||
@@ -317,12 +455,9 @@ void Solar::process_SM100Time(std::shared_ptr<const Telegram> telegram) {
|
||||
void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(collectorTemp_, 4)); // Collector Temperature
|
||||
has_update(telegram->read_value(tankBottomTemp_, 6)); // Temperature Bottom of Solar Boiler tank
|
||||
uint16_t Wh = 0xFFFF;
|
||||
uint16_t Wh = energyLastHour_ / 10;
|
||||
has_update(telegram->read_value(Wh, 2)); // Solar Energy produced in last hour only ushort, is not * 10
|
||||
|
||||
if (Wh != 0xFFFF) {
|
||||
energyLastHour_ = Wh * 10; // set to *10
|
||||
}
|
||||
energyLastHour_ = Wh * 10; // set to *10
|
||||
|
||||
has_update(telegram->read_bitvalue(solarPump_, 8, 0)); // PS1 Solar pump on (1) or off (0)
|
||||
has_update(telegram->read_value(pumpWorkTime_, 10, 3)); // force to 3 bytes
|
||||
@@ -334,21 +469,223 @@ void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram)
|
||||
* Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values
|
||||
*/
|
||||
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(setpoint_maxBottomTemp_, 6));
|
||||
has_update(telegram->read_value(tankMaxTemp_, 6));
|
||||
}
|
||||
|
||||
// set temperature for tank
|
||||
bool Solar::set_SM100TankBottomMaxTemp(const char * value, const int8_t id) {
|
||||
/*
|
||||
* Settings
|
||||
*/
|
||||
// collector shutdown temperature
|
||||
bool Solar::set_CollectorMaxTemp(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2number(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 3, (uint8_t)temperature / 10, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 0, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// write value
|
||||
// 90 30 FF 03 02 5A 59 B3
|
||||
// note: optionally add the validate to 0x035A which will pick up the adjusted tank1MaxTempCurrent_
|
||||
write_command(0x35A, 0x03, (uint8_t)temperature);
|
||||
// collector shutdown temperature
|
||||
bool Solar::set_CollectorMinTemp(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2number(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 4, (uint8_t)temperature / 10, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 4, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_TankMaxTemp(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2number(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 5, (uint8_t)temperature, 0x96);
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
write_command(0x101, 6, (uint8_t)temperature, 0x101);
|
||||
} else {
|
||||
write_command(0x35A, 3, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Solar::set_PumpMinMod(const char * value, const int8_t id) {
|
||||
int modulation;
|
||||
if (!Helpers::value2number(value, modulation)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x96, 2, (uint8_t)modulation, 0x96);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_wwMinTemp(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2number(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x96, 6, (uint8_t)temperature, 0x96);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_TurnoffDiff(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2number(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 8, (uint8_t)temperature, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 7, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_TurnonDiff(const char * value, const int8_t id) {
|
||||
int temperature;
|
||||
if (!Helpers::value2number(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 7, (uint8_t)temperature, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 8, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// external value to calculate energy
|
||||
bool Solar::set_SM10MaxFlow(const char * value, const int8_t id) {
|
||||
float flow;
|
||||
if (!Helpers::value2float(value, flow)) {
|
||||
return false;
|
||||
}
|
||||
maxFlow_ = (flow * 10);
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.solar_maxflow = maxFlow_;
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_heatTransferSystem(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x358, 5, v ? 0x01 : 0x00, 0x358);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_externalTank(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x358, 9, v ? 0x01 : 0x00, 0x358);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_thermalDisinfect(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x358, 10, v ? 0x01 : 0x00, 0x358);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_heatMetering(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x358, 14, v ? 0x01 : 0x00, 0x358);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_solarEnabled(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 0, v ? 0xFF : 0x00, 0x96);
|
||||
} else {
|
||||
write_command(0x358, 19, v ? 0x01 : 0x00, 0x358);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_solarMode(const char * value, const int8_t id) {
|
||||
uint8_t num;
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_solarmode))) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x35A, 5, num, 0x35A);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_solarPumpKick(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x35A, 9, v ? 0x01 : 0x00, 0x35A);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_plainWaterMode(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x35A, 10, v ? 0x01 : 0x00, 0x35A);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_doubleMatchFlow(const char * value, const int8_t id) {
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x35A, 11, v ? 0x01 : 0x00, 0x35A);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_climateZone(const char * value, const int8_t id) {
|
||||
int v = 0;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x380, 0, v, 0x380);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_collector1Area(const char * value, const int8_t id) {
|
||||
float v = 0;
|
||||
if (!Helpers::value2float(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x380, 3, (uint16_t)(v * 10), 0x380);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solar::set_collector1Type(const char * value, const int8_t id) {
|
||||
uint8_t num;
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_collectortype))) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x380, 5, num, 0x380);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ class Solar : public EMSdevice {
|
||||
uint8_t cylinderPumpModulation_; // PS5: modulation cylinder pump
|
||||
uint8_t solarPump_; // PS1: solar pump active
|
||||
uint8_t valveStatus_; // VS2: status 3-way valve for cylinder 2 (solar thermal system) with valve
|
||||
int16_t setpoint_maxBottomTemp_; // setpoint for maximum collector temp
|
||||
uint32_t energyLastHour_;
|
||||
uint32_t energyToday_;
|
||||
uint32_t energyTotal_;
|
||||
@@ -61,10 +60,10 @@ class Solar : public EMSdevice {
|
||||
|
||||
// telegram 0x035A
|
||||
uint8_t collectorMaxTemp_; // maximum allowed collectorTemp array 1
|
||||
uint8_t tankBottomMaxTemp_; // Current value for max tank temp
|
||||
uint8_t tankMaxTemp_; // Current value for max tank temp
|
||||
uint8_t collectorMinTemp_; // minimum allowed collectorTemp array 1
|
||||
uint8_t solarPumpMode_; // 00=off, 01=PWM, 02=10V
|
||||
uint8_t solarPumpMinRPM_; // minimum RPM setting, *5 %
|
||||
uint8_t solarPumpMinMod_; // minimum modulation setting, *5 %
|
||||
uint8_t solarPumpTurnoffDiff_; // solar pump turnoff collector/tank diff
|
||||
uint8_t solarPumpTurnonDiff_; // solar pump turnon collector/tank diff
|
||||
uint8_t solarPumpKick_; // pump kick for vacuum collector, 00=off
|
||||
@@ -86,10 +85,18 @@ class Solar : public EMSdevice {
|
||||
// SM100wwStatus - 0x07AA
|
||||
uint8_t wwPump_;
|
||||
|
||||
// SM10Config - 0x96
|
||||
uint8_t wwMinTemp_;
|
||||
uint8_t maxFlow_; // set this to caltulate power
|
||||
uint32_t solarPower_; // calculated from maxFlow
|
||||
|
||||
std::deque<uint16_t> energy;
|
||||
|
||||
char type_[20]; // Solar of WWC
|
||||
uint8_t id_;
|
||||
|
||||
void process_SM10Monitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM10Config(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100SolarCircuitConfig(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100ParamCfg(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -112,7 +119,28 @@ class Solar : public EMSdevice {
|
||||
void process_ISM1Set(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
|
||||
bool set_SM100TankBottomMaxTemp(const char * value, const int8_t id);
|
||||
bool set_CollectorMaxTemp(const char * value, const int8_t id);
|
||||
bool set_CollectorMinTemp(const char * value, const int8_t id);
|
||||
bool set_TankMaxTemp(const char * value, const int8_t id);
|
||||
bool set_PumpMinMod(const char * value, const int8_t id);
|
||||
bool set_wwMinTemp(const char * value, const int8_t id);
|
||||
bool set_TurnonDiff(const char * value, const int8_t id);
|
||||
bool set_TurnoffDiff(const char * value, const int8_t id);
|
||||
|
||||
bool set_SM10MaxFlow(const char * value, const int8_t id);
|
||||
// SM100
|
||||
bool set_heatTransferSystem(const char * value, const int8_t id);
|
||||
bool set_externalTank(const char * value, const int8_t id);
|
||||
bool set_thermalDisinfect(const char * value, const int8_t id);
|
||||
bool set_heatMetering(const char * value, const int8_t id);
|
||||
bool set_solarEnabled(const char * value, const int8_t id);
|
||||
bool set_solarMode(const char * value, const int8_t id);
|
||||
bool set_solarPumpKick(const char * value, const int8_t id);
|
||||
bool set_plainWaterMode(const char * value, const int8_t id);
|
||||
bool set_doubleMatchFlow(const char * value, const int8_t id);
|
||||
bool set_climateZone(const char * value, const int8_t id);
|
||||
bool set_collector1Area(const char * value, const int8_t id);
|
||||
bool set_collector1Type(const char * value, const int8_t id);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -35,7 +35,7 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage));
|
||||
|
||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, FL_(status), DeviceValueUOM::NONE);
|
||||
id_ = product_id;
|
||||
@@ -50,12 +50,16 @@ bool Switch::publish_ha_config() {
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(switch);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
doc["name"] = FJSON("ID");
|
||||
char name_s[40];
|
||||
snprintf_P(name_s, sizeof(name_s), PSTR("* %s Product ID"), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Switch");
|
||||
|
||||
@@ -38,7 +38,8 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
// or if its the master thermostat we defined
|
||||
// see https://github.com/emsesp/EMS-ESP/issues/362#issuecomment-629628161
|
||||
if ((master_thermostat == device_id)
|
||||
|| ((master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT) && (device_id < 0x19) && ((actual_master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT) || (device_id < actual_master_thermostat)))) {
|
||||
|| ((master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT) && (device_id < 0x19)
|
||||
&& ((actual_master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT) || (device_id < actual_master_thermostat)))) {
|
||||
EMSESP::actual_master_thermostat(device_id);
|
||||
actual_master_thermostat = device_id;
|
||||
reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
|
||||
@@ -196,12 +197,16 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
bool Thermostat::publish_ha_config() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(thermostat);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s"), Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
doc["name"] = FJSON("ID");
|
||||
char name_s[40];
|
||||
snprintf_P(name_s, sizeof(name_s), PSTR("* %s Product ID"), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Thermostat");
|
||||
@@ -340,7 +345,7 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
||||
|
||||
// now create the HA topics to send to MQTT for each sensor
|
||||
if (Mqtt::ha_enabled()) {
|
||||
register_mqtt_ha_config_hc(hc_num);
|
||||
publish_ha_config_hc(hc_num);
|
||||
}
|
||||
|
||||
// set the flag saying we want its data during the next auto fetch
|
||||
@@ -364,7 +369,7 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
||||
|
||||
// publish config topic for HA MQTT Discovery for each of the heating circuit
|
||||
// e.g. homeassistant/climate/ems-esp/thermostat_hc1/config
|
||||
void Thermostat::register_mqtt_ha_config_hc(uint8_t hc_num) {
|
||||
void Thermostat::publish_ha_config_hc(uint8_t hc_num) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
|
||||
char str1[20];
|
||||
@@ -622,6 +627,7 @@ void Thermostat::process_RC20Set(std::shared_ptr<const Telegram> telegram) {
|
||||
return;
|
||||
}
|
||||
has_update(telegram->read_value(hc->mode, 23));
|
||||
hc->hamode = hc->mode; // set special HA mode
|
||||
}
|
||||
|
||||
// type 0xAE - data from the RC20 thermostat (0x17) - not for RC20's
|
||||
@@ -647,6 +653,7 @@ void Thermostat::process_RC20Set_2(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hc->nighttemp, 1)); // is * 2,
|
||||
has_update(telegram->read_value(hc->daytemp, 2)); // is * 2,
|
||||
has_update(telegram->read_value(hc->mode, 3));
|
||||
hc->hamode = hc->mode; // set special HA mode
|
||||
has_update(telegram->read_value(hc->program, 11)); // 1 .. 9 predefined programs
|
||||
}
|
||||
|
||||
@@ -768,6 +775,7 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr<const Telegram> telegram
|
||||
|
||||
has_update(telegram->read_value(hc->modetype, 0)); // 1 = nofrost, 2 = eco, 3 = heat
|
||||
has_update(telegram->read_value(hc->mode, 1)); // 1 = manual, 2 = auto
|
||||
hc->hamode = hc->mode; // set special HA mode
|
||||
}
|
||||
|
||||
// type 0x02A5 - data from Worchester CRF200
|
||||
@@ -778,7 +786,8 @@ void Thermostat::process_CRFMonitor(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
has_update(telegram->read_value(hc->curr_roomTemp, 0)); // is * 10
|
||||
has_update(telegram->read_bitvalue(hc->modetype, 2, 0));
|
||||
has_update(telegram->read_bitvalue(hc->mode, 2, 4)); // bit 4, mode (auto=0 or off=1)
|
||||
has_update(telegram->read_bitvalue(hc->mode, 2, 4)); // bit 4, mode (auto=0 or off=1)
|
||||
hc->hamode = 2 - 2 * hc->mode; // set special HA mode
|
||||
has_update(telegram->read_value(hc->setpoint_roomTemp, 6, 1)); // is * 2, force as single byte
|
||||
has_update(telegram->read_value(hc->targetflowtemp, 4));
|
||||
}
|
||||
@@ -793,6 +802,7 @@ void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram)
|
||||
has_update(telegram->read_value(hc->curr_roomTemp, 0)); // is * 10
|
||||
has_update(telegram->read_bitvalue(hc->modetype, 10, 1));
|
||||
has_update(telegram->read_bitvalue(hc->mode, 10, 0)); // bit 1, mode (auto=1 or manual=0)
|
||||
hc->hamode = hc->mode + 1; // set special HA mode
|
||||
|
||||
// if manual, take the current setpoint temp at pos 6
|
||||
// if auto, take the next setpoint temp at pos 7
|
||||
@@ -936,6 +946,7 @@ void Thermostat::process_RC30Set(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
has_update(telegram->read_value(hc->mode, 23));
|
||||
hc->hamode = hc->mode; // set special HA mode
|
||||
}
|
||||
|
||||
// type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes
|
||||
@@ -985,6 +996,7 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hc->roominfluence, 4)); // is * 1
|
||||
has_update(telegram->read_value(hc->offsettemp, 6)); // is * 2
|
||||
has_update(telegram->read_value(hc->mode, 7)); // night, day, auto
|
||||
hc->hamode = hc->mode; // set special HA mode
|
||||
|
||||
has_update(telegram->read_value(hc->summertemp, 22)); // is * 1
|
||||
has_update(telegram->read_value(hc->nofrosttemp, 23)); // is * 1
|
||||
@@ -993,7 +1005,7 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(hc->control, 26)); // 0-off, 1-RC20 (remote), 2-RC35
|
||||
has_update(telegram->read_value(hc->controlmode, 33)); // 0-outdoortemp, 1-roomtemp
|
||||
has_update(telegram->read_value(hc->tempautotemp, 37));
|
||||
has_update(telegram->read_value(hc->noreducetemp, 38)); // outdoor temperature for no reduce
|
||||
has_update(telegram->read_value(hc->noreducetemp, 38)); // outdoor temperature for no reduce
|
||||
has_update(telegram->read_value(hc->minflowtemp, 16));
|
||||
if (hc->heatingtype == 3) {
|
||||
has_update(telegram->read_value(hc->designtemp, 36)); // is * 1
|
||||
@@ -2003,7 +2015,10 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
// if we know what to send and to where, go and do it
|
||||
if (offset != -1) {
|
||||
char s[10];
|
||||
LOG_INFO(F("Setting thermostat temperature to %s for heating circuit %d, mode %s"), Helpers::render_value(s, temperature, 2), hc->hc_num(), mode_tostring(mode).c_str());
|
||||
LOG_INFO(F("Setting thermostat temperature to %s for heating circuit %d, mode %s"),
|
||||
Helpers::render_value(s, temperature, 2),
|
||||
hc->hc_num(),
|
||||
mode_tostring(mode).c_str());
|
||||
|
||||
// add the write command to the Tx queue. value is *2
|
||||
// post validate is the corresponding monitor or set type IDs as they can differ per model
|
||||
@@ -2117,13 +2132,21 @@ void Thermostat::register_device_values() {
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &floordrystatus_, DeviceValueType::ENUM, FL_(enum_floordrystatus), FL_(floordrystatus), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp2_, DeviceValueType::SHORT, FL_(div10), FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &floordrytemp_, DeviceValueType::UINT, nullptr, FL_(floordrytemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemp));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTempLow_, DeviceValueType::UINT, nullptr, FL_(wwSetTempLow), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemplow));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwExtra1_, DeviceValueType::UINT, nullptr, FL_(wwExtra1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwExtra2_, DeviceValueType::UINT, nullptr, FL_(wwExtra2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaBuildingType_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_ibaBuildingType),
|
||||
FL_(ibaBuildingType),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_building));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||
register_device_value(
|
||||
TAG_THERMOSTAT_DATA, &wwSetTempLow_, DeviceValueType::UINT, nullptr, FL_(wwSetTempLow), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemplow));
|
||||
register_device_value(
|
||||
TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &wwExtra1_, DeviceValueType::UINT, nullptr, FL_(wwExtra1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &wwExtra2_, DeviceValueType::UINT, nullptr, FL_(wwExtra2), DeviceValueUOM::DEGREES);
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_RC20_N:
|
||||
case EMS_DEVICE_FLAG_RC20:
|
||||
@@ -2133,24 +2156,67 @@ void Thermostat::register_device_values() {
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE); // can't set datetime
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaMainDisplay_, DeviceValueType::ENUM, FL_(enum_ibaMainDisplay), FL_(ibaMainDisplay), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaLanguage_, DeviceValueType::ENUM, FL_(enum_ibaLanguage), FL_(ibaLanguage), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaClockOffset_, DeviceValueType::UINT, nullptr, FL_(ibaClockOffset), DeviceValueUOM::NONE); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_calinttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaMinExtTemperature_, DeviceValueType::INT, nullptr, FL_(ibaMinExtTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minexttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaClockOffset_,
|
||||
DeviceValueType::UINT,
|
||||
nullptr,
|
||||
FL_(ibaClockOffset),
|
||||
DeviceValueUOM::NONE); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaCalIntTemperature_,
|
||||
DeviceValueType::INT,
|
||||
FL_(div2),
|
||||
FL_(ibaCalIntTemperature),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_calinttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaMinExtTemperature_,
|
||||
DeviceValueType::INT,
|
||||
nullptr,
|
||||
FL_(ibaMinExtTemperature),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_minexttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaBuildingType_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_ibaBuildingType2),
|
||||
FL_(ibaBuildingType),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_building));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||
register_device_value(
|
||||
TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_RC35:
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_calinttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaMinExtTemperature_, DeviceValueType::INT, nullptr, FL_(ibaMinExtTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minexttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaCalIntTemperature_,
|
||||
DeviceValueType::INT,
|
||||
FL_(div2),
|
||||
FL_(ibaCalIntTemperature),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_calinttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaMinExtTemperature_,
|
||||
DeviceValueType::INT,
|
||||
nullptr,
|
||||
FL_(ibaMinExtTemperature),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_minexttemp));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor1_, DeviceValueType::USHORT, FL_(div10), FL_(tempsensor1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor2_, DeviceValueType::USHORT, FL_(div10), FL_(tempsensor2), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||
register_device_value(TAG_THERMOSTAT_DATA,
|
||||
&ibaBuildingType_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_ibaBuildingType2),
|
||||
FL_(ibaBuildingType),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_building));
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||
register_device_value(
|
||||
TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_JUNKERS:
|
||||
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
|
||||
@@ -2161,7 +2227,7 @@ void Thermostat::register_device_values() {
|
||||
}
|
||||
|
||||
// registers the values for a heating circuit
|
||||
void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::HeatingCircuit> hc) {
|
||||
void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCircuit> hc) {
|
||||
uint8_t model = hc->get_model();
|
||||
|
||||
// heating circuit
|
||||
@@ -2184,8 +2250,17 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
||||
if (has_flags(EMS_DEVICE_FLAG_NO_WRITE) || device_id() != EMSESP::actual_master_thermostat()) {
|
||||
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(setpoint_roomTemp), DeviceValueUOM::DEGREES);
|
||||
} else {
|
||||
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(setpoint_roomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
|
||||
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(temp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
|
||||
register_device_value(tag,
|
||||
&hc->setpoint_roomTemp,
|
||||
DeviceValueType::SHORT,
|
||||
setpoint_temp_divider,
|
||||
FL_(setpoint_roomTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_temp),
|
||||
5,
|
||||
29);
|
||||
register_device_value(
|
||||
tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(temp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
|
||||
}
|
||||
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, FL_(curr_roomTemp), DeviceValueUOM::DEGREES);
|
||||
|
||||
@@ -2209,8 +2284,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
||||
// manual & day = heat
|
||||
// night & off = off
|
||||
// everything else auto
|
||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_hamode), FL_(hamode), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &hc->hamode, DeviceValueType::ENUM, FL_(enum_hamode), FL_(hamode), DeviceValueUOM::NONE);
|
||||
}
|
||||
|
||||
switch (model) {
|
||||
case EMS_DEVICE_FLAG_RC100:
|
||||
case EMS_DEVICE_FLAG_RC300:
|
||||
@@ -2228,9 +2304,11 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
||||
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
|
||||
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summermode), DeviceValueUOM::NONE, MAKE_CF_CB(set_summermode));
|
||||
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
|
||||
register_device_value(
|
||||
tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summersetmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_summermode));
|
||||
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(
|
||||
tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
|
||||
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
||||
register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp));
|
||||
break;
|
||||
@@ -2260,8 +2338,8 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
||||
register_device_value(tag, &hc->holidaytemp, DeviceValueType::UINT, FL_(div2), FL_(holidaytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_holidaytemp));
|
||||
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp));
|
||||
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, FL_(holidaymode), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday));
|
||||
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::BOOLEAN);
|
||||
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, FL_(holidaymode), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_holiday));
|
||||
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
|
||||
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence));
|
||||
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
|
||||
@@ -2269,7 +2347,8 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
||||
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, nullptr, FL_(flowtempoffset), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowtempoffset));
|
||||
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(reducemode), DeviceValueUOM::NONE, MAKE_CF_CB(set_reducemode));
|
||||
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
|
||||
register_device_value(
|
||||
tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
|
||||
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
||||
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
||||
register_device_value(tag, &hc->pause, DeviceValueType::UINT, nullptr, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause));
|
||||
|
||||
@@ -39,6 +39,7 @@ class Thermostat : public EMSdevice {
|
||||
int16_t remotetemp; // for readback
|
||||
uint8_t tempautotemp;
|
||||
uint8_t mode;
|
||||
uint8_t hamode; // special mode for HA. See https://github.com/emsesp/EMS-ESP32/issues/66
|
||||
uint8_t modetype;
|
||||
uint8_t summermode;
|
||||
uint8_t holidaymode;
|
||||
@@ -148,7 +149,7 @@ class Thermostat : public EMSdevice {
|
||||
char dummychar_[5]; // for commands with no output
|
||||
|
||||
// Installation parameters
|
||||
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
|
||||
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
|
||||
uint8_t ibaLanguage_; // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian
|
||||
int8_t ibaCalIntTemperature_; // offset int. temperature sensor, by * 0.1 Kelvin (-5.0 to 5.0K)
|
||||
int8_t ibaMinExtTemperature_; // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1
|
||||
@@ -254,8 +255,8 @@ class Thermostat : public EMSdevice {
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> heating_circuit(std::shared_ptr<const Telegram> telegram);
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> heating_circuit(const uint8_t hc_num);
|
||||
|
||||
void register_mqtt_ha_config_hc(uint8_t hc_num);
|
||||
void register_device_values_hc(std::shared_ptr<emsesp::Thermostat::HeatingCircuit> hc);
|
||||
void publish_ha_config_hc(uint8_t hc_num);
|
||||
void register_device_values_hc(std::shared_ptr<Thermostat::HeatingCircuit> hc);
|
||||
|
||||
bool thermostat_ha_cmd(const char * message, uint8_t hc_num);
|
||||
|
||||
|
||||
@@ -34,7 +34,13 @@ static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__
|
||||
F_(minutes),
|
||||
F_(ua),
|
||||
F_(bar),
|
||||
F_(kw)
|
||||
F_(kw),
|
||||
F_(w),
|
||||
F_(kb),
|
||||
F_(seconds),
|
||||
F_(dbm),
|
||||
F_(num),
|
||||
F_(bool)
|
||||
|
||||
};
|
||||
|
||||
@@ -45,7 +51,7 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = {
|
||||
F_(tag_none), // ""
|
||||
F_(tag_heartbeat), // ""
|
||||
F_(tag_boiler_data), // ""
|
||||
F_(tag_device_data_ww), // "warm water"
|
||||
F_(tag_boiler_data_ww), // "ww"
|
||||
F_(tag_thermostat_data), // ""
|
||||
F_(tag_hc1), // "hc1"
|
||||
F_(tag_hc2), // "hc2"
|
||||
@@ -80,7 +86,7 @@ static const __FlashStringHelper * const DeviceValueTAG_mqtt[] PROGMEM = {
|
||||
F_(tag_none), // ""
|
||||
F_(heartbeat), // "heartbeat"
|
||||
F_(tag_boiler_data_mqtt), // ""
|
||||
F_(tag_device_data_ww_mqtt), // "ww"
|
||||
F_(tag_boiler_data_ww_mqtt), // "ww"
|
||||
F_(tag_thermostat_data), // ""
|
||||
F_(tag_hc1), // "hc1"
|
||||
F_(tag_hc2), // "hc2"
|
||||
@@ -118,7 +124,7 @@ const std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
|
||||
}
|
||||
|
||||
const std::string EMSdevice::uom_to_string(uint8_t uom) {
|
||||
if (uom == DeviceValueUOM::NONE || uom >= DeviceValueUOM::PUMP) {
|
||||
if (uom == DeviceValueUOM::NONE) {
|
||||
return std::string{};
|
||||
}
|
||||
return uuid::read_flash_string(DeviceValueUOM_s[uom - 1]); // offset by 1 to account for NONE
|
||||
@@ -298,7 +304,14 @@ std::string EMSdevice::to_string() const {
|
||||
if (brand_ == Brand::NO_BRAND) {
|
||||
snprintf_P(&str[0], str.capacity() + 1, PSTR("%s (DeviceID:0x%02X, ProductID:%d, Version:%s)"), name_.c_str(), device_id_, product_id_, version_.c_str());
|
||||
} else {
|
||||
snprintf_P(&str[0], str.capacity() + 1, PSTR("%s %s (DeviceID:0x%02X ProductID:%d, Version:%s)"), brand_to_string().c_str(), name_.c_str(), device_id_, product_id_, version_.c_str());
|
||||
snprintf_P(&str[0],
|
||||
str.capacity() + 1,
|
||||
PSTR("%s %s (DeviceID:0x%02X ProductID:%d, Version:%s)"),
|
||||
brand_to_string().c_str(),
|
||||
name_.c_str(),
|
||||
device_id_,
|
||||
product_id_,
|
||||
version_.c_str());
|
||||
}
|
||||
|
||||
return str;
|
||||
@@ -412,11 +425,6 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_
|
||||
Mqtt::subscribe(device_type_, topic, f);
|
||||
}
|
||||
|
||||
// add command to library
|
||||
// void EMSdevice::register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag) {
|
||||
// Command::add(device_type_, cmd, f, flag);
|
||||
// }
|
||||
|
||||
// register a callback function for a specific telegram type
|
||||
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) {
|
||||
telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, f);
|
||||
@@ -470,26 +478,46 @@ void EMSdevice::register_device_value(uint8_t tag,
|
||||
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom, 0, has_cmd, min, max);
|
||||
}
|
||||
|
||||
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f, int32_t min, uint32_t max) {
|
||||
// function with min and max values
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
cmdfunction_p f,
|
||||
int32_t min,
|
||||
uint32_t max) {
|
||||
register_device_value(tag, value_p, type, options, name[0], name[1], uom, (f != nullptr), min, max);
|
||||
if (f != nullptr) {
|
||||
if (tag >= TAG_HC1 && tag <= TAG_HC4) {
|
||||
Command::add(device_type_, name[0], f, FLAG_HC);
|
||||
Command::add(device_type_, name[0], f, name[1], FLAG_HC);
|
||||
} else if (tag >= TAG_WWC1 && tag <= TAG_WWC4) {
|
||||
Command::add(device_type_, name[0], f, name[1], FLAG_WWC);
|
||||
} else {
|
||||
Command::add(device_type_, name[0], f, 0);
|
||||
Command::add(device_type_, name[0], f, name[1], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f) {
|
||||
// function with no min and max values
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
cmdfunction_p f) {
|
||||
register_device_value(tag, value_p, type, options, name, uom, f, 0, 0);
|
||||
}
|
||||
|
||||
// void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max) {
|
||||
// register_device_value(tag, value_p, type, options, name, uom, nullptr, min, max);
|
||||
// }
|
||||
|
||||
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom) {
|
||||
// no command function
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom) {
|
||||
register_device_value(tag, value_p, type, options, name, uom, nullptr, 0, 0);
|
||||
}
|
||||
|
||||
@@ -509,7 +537,7 @@ std::string EMSdevice::get_value_uom(const char * key) {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if (dv.full_name != nullptr) {
|
||||
if (uuid::read_flash_string(dv.full_name) == p) {
|
||||
// ignore TIME since "minutes" is already included
|
||||
// ignore TIME since "minutes" is already added to the string value
|
||||
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
||||
break;
|
||||
}
|
||||
@@ -521,37 +549,34 @@ std::string EMSdevice::get_value_uom(const char * key) {
|
||||
return std::string{}; // not found
|
||||
}
|
||||
|
||||
// prepare array of device values, as 3 elements serialized (name, value, uom) in array to send to Web UI
|
||||
// returns number of elements
|
||||
bool EMSdevice::generate_values_json_web(JsonObject & json) {
|
||||
// prepare array of device values used for the WebUI
|
||||
// v = value, u=uom, n=name, c=cmd
|
||||
void EMSdevice::generate_values_json_web(JsonObject & json) {
|
||||
json["name"] = to_string_short();
|
||||
JsonArray data = json.createNestedArray("data");
|
||||
|
||||
uint8_t num_elements = 0;
|
||||
|
||||
for (const auto & dv : devicevalues_) {
|
||||
// ignore if full_name empty
|
||||
if (dv.full_name != nullptr) {
|
||||
JsonObject obj; // create the object, if needed
|
||||
|
||||
// handle Booleans (true, false)
|
||||
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
||||
// see if we have options for the bool's
|
||||
if (dv.options_size == 2) {
|
||||
data.add(*(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1]);
|
||||
} else {
|
||||
// always render booleans as on or off
|
||||
data.add(*(uint8_t *)(dv.value_p) ? F_(on) : F_(off));
|
||||
}
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = *(bool *)(dv.value_p);
|
||||
}
|
||||
|
||||
// handle TEXT strings
|
||||
else if ((dv.type == DeviceValueType::TEXT) && (Helpers::hasValue((char *)(dv.value_p)))) {
|
||||
data.add((char *)(dv.value_p));
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (char *)(dv.value_p);
|
||||
}
|
||||
|
||||
// handle ENUMs
|
||||
else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
|
||||
data.add(dv.options[*(uint8_t *)(dv.value_p)]);
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = dv.options[*(uint8_t *)(dv.value_p)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,81 +587,60 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
|
||||
// the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler
|
||||
uint8_t divider = (dv.options_size == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0;
|
||||
|
||||
// INT
|
||||
if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) {
|
||||
if (divider) {
|
||||
data.add(Helpers::round2(*(int8_t *)(dv.value_p), divider));
|
||||
} else {
|
||||
data.add(*(int8_t *)(dv.value_p));
|
||||
}
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (divider) ? Helpers::round2(*(int8_t *)(dv.value_p), divider) : *(int8_t *)(dv.value_p);
|
||||
} else if ((dv.type == DeviceValueType::UINT) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||
if (divider) {
|
||||
data.add(Helpers::round2(*(uint8_t *)(dv.value_p), divider));
|
||||
} else {
|
||||
data.add(*(uint8_t *)(dv.value_p));
|
||||
}
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (divider) ? Helpers::round2(*(uint8_t *)(dv.value_p), divider) : *(uint8_t *)(dv.value_p);
|
||||
} else if ((dv.type == DeviceValueType::SHORT) && Helpers::hasValue(*(int16_t *)(dv.value_p))) {
|
||||
if (divider) {
|
||||
data.add(Helpers::round2(*(int16_t *)(dv.value_p), divider));
|
||||
} else {
|
||||
data.add(*(int16_t *)(dv.value_p));
|
||||
}
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (divider) ? Helpers::round2(*(int16_t *)(dv.value_p), divider) : *(int16_t *)(dv.value_p);
|
||||
} else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
|
||||
if (divider) {
|
||||
data.add(Helpers::round2(*(uint16_t *)(dv.value_p), divider));
|
||||
} else {
|
||||
data.add(*(uint16_t *)(dv.value_p));
|
||||
}
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (divider) ? Helpers::round2(*(uint16_t *)(dv.value_p), divider) : *(uint16_t *)(dv.value_p);
|
||||
} else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
if (divider) {
|
||||
data.add(Helpers::round2(*(uint32_t *)(dv.value_p), divider));
|
||||
} else {
|
||||
data.add(*(uint32_t *)(dv.value_p));
|
||||
}
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (divider) ? Helpers::round2(*(uint32_t *)(dv.value_p), divider) : *(uint32_t *)(dv.value_p);
|
||||
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
uint32_t time_value = *(uint32_t *)(dv.value_p);
|
||||
time_value = (divider) ? time_value / divider : time_value; // sometimes we need to divide by 60
|
||||
char time_s[40];
|
||||
snprintf_P(time_s, 40, PSTR("%d days %d hours %d minutes"), (time_value / 1440), ((time_value % 1440) / 60), (time_value % 60));
|
||||
data.add(time_s);
|
||||
obj = data.createNestedObject();
|
||||
obj["v"] = (divider) ? time_value / divider : time_value; // sometimes we need to divide by 60
|
||||
}
|
||||
}
|
||||
|
||||
// check if we've added a data element by comparing the size
|
||||
// then add the remaining elements
|
||||
uint8_t sz = data.size();
|
||||
if (sz > num_elements) {
|
||||
// check if we've added a data element then add the remaining elements
|
||||
if (obj.containsKey("v")) {
|
||||
// add the unit of measure (uom)
|
||||
if (dv.uom == DeviceValueUOM::MINUTES) {
|
||||
data.add(nullptr); // use null for time/date
|
||||
} else {
|
||||
data.add(uom_to_string(dv.uom));
|
||||
}
|
||||
obj["u"] = dv.uom;
|
||||
|
||||
// add name, prefixing the tag if it exists
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
data.add(dv.full_name);
|
||||
// except if it's a BOILER which uses a tag to split the MQTT topics
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty() || device_type_ == DeviceType::BOILER) {
|
||||
obj["n"] = dv.full_name;
|
||||
} else {
|
||||
char name[50];
|
||||
snprintf_P(name, sizeof(name), "(%s) %s", tag_to_string(dv.tag).c_str(), uuid::read_flash_string(dv.full_name).c_str());
|
||||
data.add(name);
|
||||
obj["n"] = name;
|
||||
}
|
||||
|
||||
// add the name of the Command function if it exists
|
||||
if (dv.has_cmd) {
|
||||
data.add(dv.short_name);
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
obj["c"] = tag_to_string(dv.tag) + "/" + uuid::read_flash_string(dv.short_name);
|
||||
} else {
|
||||
obj["c"] = dv.short_name;
|
||||
}
|
||||
} else {
|
||||
data.add("");
|
||||
obj["c"] = "";
|
||||
}
|
||||
|
||||
num_elements = sz + 3; // increase count by 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (num_elements != 0);
|
||||
}
|
||||
|
||||
// builds json with specific device value information
|
||||
// e.g. http://ems-esp/api?device=thermostat&cmd=seltemp
|
||||
bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t id) {
|
||||
JsonObject json = root;
|
||||
int8_t tag = id;
|
||||
@@ -646,6 +650,8 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
tag = DeviceValueTAG::TAG_HC1 + id - 1;
|
||||
} else if (id >= 8 && id <= 11) {
|
||||
tag = DeviceValueTAG::TAG_WWC1 + id - 8;
|
||||
} else if (id != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// search device value with this tag
|
||||
@@ -657,10 +663,20 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
const char * max = "max";
|
||||
const char * value = "value";
|
||||
|
||||
json["name"] = dv.short_name;
|
||||
json["name"] = dv.short_name;
|
||||
// prefix tag if it's included
|
||||
if (dv.full_name != nullptr) {
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
json["fullname"] = tag_to_string(dv.tag) + " " + uuid::read_flash_string(dv.full_name);
|
||||
} else {
|
||||
json["fullname"] = dv.full_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag_to_mqtt(dv.tag).empty()) {
|
||||
json["circuit"] = tag_to_mqtt(dv.tag);
|
||||
}
|
||||
|
||||
switch (dv.type) {
|
||||
case DeviceValueType::ENUM: {
|
||||
if (Helpers::hasValue((uint8_t)(*(uint8_t *)(dv.value_p)))) {
|
||||
@@ -670,16 +686,17 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[value] = dv.options[*(uint8_t *)(dv.value_p)]; // text
|
||||
}
|
||||
}
|
||||
json[type] = F_(enum);
|
||||
uint8_t min_ = (uuid::read_flash_string(dv.options[0]) == "") ? 1 : 0;
|
||||
json[min] = min_;
|
||||
json[max] = dv.options_size - 1;
|
||||
json[type] = F_(enum);
|
||||
// uint8_t min_ = (uuid::read_flash_string(dv.options[0]) == "") ? 1 : 0;
|
||||
// json[min] = min_;
|
||||
// json[max] = dv.options_size - 1;
|
||||
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||
for (uint8_t i = min_; i < dv.options_size; i++) {
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
enum_.add(dv.options[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceValueType::USHORT:
|
||||
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint16_t *)(dv.value_p), divider);
|
||||
@@ -688,6 +705,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[min] = 0;
|
||||
json[max] = divider ? EMS_VALUE_USHORT_NOTSET / divider : EMS_VALUE_USHORT_NOTSET;
|
||||
break;
|
||||
|
||||
case DeviceValueType::UINT:
|
||||
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint8_t *)(dv.value_p), divider);
|
||||
@@ -700,6 +718,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[max] = divider ? EMS_VALUE_UINT_NOTSET / divider : EMS_VALUE_UINT_NOTSET;
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceValueType::SHORT:
|
||||
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(int16_t *)(dv.value_p), divider);
|
||||
@@ -708,6 +727,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[min] = divider ? -EMS_VALUE_SHORT_NOTSET / divider : -EMS_VALUE_SHORT_NOTSET;
|
||||
json[max] = divider ? EMS_VALUE_SHORT_NOTSET / divider : EMS_VALUE_SHORT_NOTSET;
|
||||
break;
|
||||
|
||||
case DeviceValueType::INT:
|
||||
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(int8_t *)(dv.value_p), divider);
|
||||
@@ -721,6 +741,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[max] = divider ? EMS_VALUE_INT_NOTSET / divider : EMS_VALUE_INT_NOTSET;
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceValueType::ULONG:
|
||||
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint32_t *)(dv.value_p), divider);
|
||||
@@ -729,42 +750,15 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[min] = 0;
|
||||
json[max] = divider ? EMS_VALUE_ULONG_NOTSET / divider : EMS_VALUE_ULONG_NOTSET;
|
||||
break;
|
||||
|
||||
case DeviceValueType::BOOL: {
|
||||
if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
||||
if (dv.options_size == 2) {
|
||||
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? dv.options[0] : dv.options[1];
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
|
||||
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(on) : F_(off);
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
|
||||
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(ON) : F_(OFF);
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
|
||||
} else {
|
||||
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
json[type] = F("boolean");
|
||||
json[min] = 0;
|
||||
json[max] = 1;
|
||||
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||
if (dv.options_size == 2) {
|
||||
enum_.add(dv.options[1]);
|
||||
enum_.add(dv.options[0]);
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
|
||||
enum_.add(F_(off));
|
||||
enum_.add(F_(on));
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
|
||||
enum_.add(F_(OFF));
|
||||
enum_.add(F_(ON));
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
enum_.add(false);
|
||||
enum_.add(true);
|
||||
} else {
|
||||
enum_.add(0);
|
||||
enum_.add(1);
|
||||
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
|
||||
}
|
||||
json[type] = F("boolean");
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceValueType::TIME:
|
||||
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
json[value] = (divider) ? *(uint32_t *)(dv.value_p) / divider : *(uint32_t *)(dv.value_p);
|
||||
@@ -773,28 +767,35 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
|
||||
json[min] = 0;
|
||||
json[max] = divider ? EMS_VALUE_ULONG_NOTSET / divider : EMS_VALUE_ULONG_NOTSET;
|
||||
break;
|
||||
|
||||
case DeviceValueType::TEXT:
|
||||
if (Helpers::hasValue((char *)(dv.value_p))) {
|
||||
json[value] = (char *)(dv.value_p);
|
||||
}
|
||||
json[type] = F_(text);
|
||||
break;
|
||||
|
||||
default:
|
||||
json[type] = F_(unknown);
|
||||
break;
|
||||
}
|
||||
if (dv.uom != DeviceValueUOM::NONE) {
|
||||
|
||||
// add uom if it's not a " " (single space)
|
||||
if ((dv.uom != DeviceValueUOM::NONE) && (dv.uom != DeviceValueUOM::NUM) && (dv.uom != DeviceValueUOM::BOOLEAN)) {
|
||||
json["unit"] = EMSdevice::uom_to_string(dv.uom);
|
||||
}
|
||||
|
||||
json["writeable"] = dv.has_cmd;
|
||||
// if we have individual limits, overwrite the common limits
|
||||
if (dv.min != 0 || dv.max != 0) {
|
||||
json[min] = dv.min;
|
||||
json[max] = dv.max;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -810,10 +811,11 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
|
||||
bool has_value = false;
|
||||
// only show if tag is either empty (TAG_NONE) or matches a value
|
||||
// and don't show if full_name is empty unless we're outputing for mqtt payloads
|
||||
// for nested we use all values, dont show command only (have_cmd and no fulname)
|
||||
if (((nested) || tag_filter == DeviceValueTAG::TAG_NONE || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !console) && !(dv.full_name == nullptr && dv.has_cmd)) {
|
||||
// for nested we use all values, dont show command only (have_cmd and no fullname)
|
||||
if (((nested) || tag_filter == DeviceValueTAG::TAG_NONE || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !console)
|
||||
&& !(dv.full_name == nullptr && dv.has_cmd)) {
|
||||
// we have a tag if it matches the filter given, and that the tag name is not empty/""
|
||||
bool have_tag = ((dv.tag != tag_filter) && !tag_to_string(dv.tag).empty());
|
||||
bool have_tag = ((dv.tag != tag_filter) && !tag_to_string(dv.tag).empty()) && (device_type_ != DeviceType::BOILER);
|
||||
|
||||
char name[80];
|
||||
if (console) {
|
||||
@@ -837,31 +839,26 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
|
||||
|
||||
// handle Booleans (true, false)
|
||||
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
||||
// see if we have options for the bool's
|
||||
if (dv.options_size == 2 && Mqtt::bool_format() != BOOL_FORMAT_10) {
|
||||
json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1];
|
||||
// see how to render the value depending on the setting
|
||||
// when in console mode we always use on and off
|
||||
if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) {
|
||||
// on or off as strings
|
||||
json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off);
|
||||
has_value = true;
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
|
||||
// on or off as strings
|
||||
json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF);
|
||||
has_value = true;
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
// true or false values (as real booleans, not strings)
|
||||
json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
|
||||
has_value = true;
|
||||
} else {
|
||||
// see how to render the value depending on the setting
|
||||
// when in console mode we always use on and off
|
||||
if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) {
|
||||
// on or off as strings
|
||||
json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off);
|
||||
has_value = true;
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
|
||||
// on or off as strings
|
||||
json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF);
|
||||
has_value = true;
|
||||
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
// true or false values (as real booleans, not strings)
|
||||
json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
|
||||
has_value = true;
|
||||
} else {
|
||||
// numerical 1 or 0
|
||||
json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
|
||||
has_value = true;
|
||||
}
|
||||
// numerical 1 or 0
|
||||
json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
|
||||
has_value = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// handle TEXT strings
|
||||
|
||||
@@ -30,17 +30,6 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// Home Assistant icons (https://materialdesignicons.com/)
|
||||
// MAKE_PSTR(icontemperature, "mdi:temperature-celsius")
|
||||
MAKE_PSTR(icontemperature, "mdi:coolant-temperature")
|
||||
MAKE_PSTR(iconpercent, "mdi:percent-outline")
|
||||
MAKE_PSTR(iconfire, "mdi:fire")
|
||||
MAKE_PSTR(iconfan, "mdi:fan")
|
||||
MAKE_PSTR(iconflame, "mdi:flash")
|
||||
MAKE_PSTR(iconvalve, "mdi:valve")
|
||||
MAKE_PSTR(iconpump, "mdi:pump")
|
||||
MAKE_PSTR(iconheatpump, "mdi:water-pump")
|
||||
|
||||
enum DeviceValueType : uint8_t {
|
||||
BOOL,
|
||||
INT,
|
||||
@@ -57,14 +46,50 @@ enum DeviceValueType : uint8_t {
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// uom - also used with HA
|
||||
// sequence is important!
|
||||
enum DeviceValueUOM : uint8_t { NONE = 0, DEGREES, PERCENT, LMIN, KWH, WH, HOURS, MINUTES, UA, BAR, KW, PUMP };
|
||||
enum DeviceValueUOM : uint8_t {
|
||||
|
||||
NONE = 0, // 0
|
||||
DEGREES, // 1
|
||||
PERCENT, // 2
|
||||
LMIN, // 3
|
||||
KWH, // 4
|
||||
WH, // 5
|
||||
HOURS, // 6
|
||||
MINUTES, // 7
|
||||
UA, // 8
|
||||
BAR, // 9
|
||||
KW, // 10
|
||||
W, // 11
|
||||
KB, // 12
|
||||
SECONDS, // 13
|
||||
DBM, // 14
|
||||
NUM, // 15
|
||||
BOOLEAN // 16
|
||||
|
||||
};
|
||||
|
||||
// Home Assistant icons (https://materialdesignicons.com)
|
||||
// the following are used with the UOMs (unit of measurements)
|
||||
MAKE_PSTR(icondegrees, "mdi:coolant-temperature") // DeviceValueUOM::DEGREES
|
||||
MAKE_PSTR(iconpercent, "mdi:percent-outline") // DeviceValueUOM::PERCENT
|
||||
MAKE_PSTR(icontime, "mdi:clock-outline") // DeviceValueUOM::SECONDS MINUTES & HOURS
|
||||
MAKE_PSTR(iconkb, "mdi:memory") // DeviceValueUOM::KB
|
||||
MAKE_PSTR(iconlmin, "mdi:water-boiler") // DeviceValueUOM::LMIN
|
||||
MAKE_PSTR(iconkwh, "mdi:transmission-tower") // DeviceValueUOM::KWH & WH
|
||||
MAKE_PSTR(iconua, "mdi:flash-circle") // DeviceValueUOM::UA
|
||||
MAKE_PSTR(iconbar, "mdi:gauge") // DeviceValueUOM::BAR
|
||||
MAKE_PSTR(iconkw, "mdi:omega") // DeviceValueUOM::KW & W
|
||||
MAKE_PSTR(icondbm, "mdi:wifi-strength-2") // DeviceValueUOM::DBM
|
||||
MAKE_PSTR(iconnum, "mdi:counter") // DeviceValueUOM::NUM
|
||||
|
||||
MAKE_PSTR(icondevice, "mdi:home-automation") // for devices in HA
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
enum DeviceValueTAG : uint8_t {
|
||||
TAG_NONE = 0, // wild card
|
||||
TAG_HEARTBEAT,
|
||||
TAG_BOILER_DATA,
|
||||
TAG_DEVICE_DATA_WW,
|
||||
TAG_BOILER_DATA_WW,
|
||||
TAG_THERMOSTAT_DATA,
|
||||
TAG_HC1,
|
||||
TAG_HC2,
|
||||
@@ -220,7 +245,7 @@ class EMSdevice {
|
||||
std::string get_value_uom(const char * key);
|
||||
bool get_value_info(JsonObject & root, const char * cmd, const int8_t id);
|
||||
bool generate_values_json(JsonObject & json, const uint8_t tag_filter, const bool nested, const bool console = false);
|
||||
bool generate_values_json_web(JsonObject & json);
|
||||
void generate_values_json_web(JsonObject & json);
|
||||
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
@@ -232,9 +257,28 @@ class EMSdevice {
|
||||
bool has_cmd,
|
||||
int32_t min,
|
||||
uint32_t max);
|
||||
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f, int32_t min, uint32_t max);
|
||||
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f);
|
||||
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom);
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
cmdfunction_p f,
|
||||
int32_t min,
|
||||
uint32_t max);
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
cmdfunction_p f);
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom);
|
||||
// void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max);
|
||||
|
||||
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
||||
|
||||
117
src/emsesp.cpp
117
src/emsesp.cpp
@@ -39,7 +39,8 @@ WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &
|
||||
|
||||
WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
WebDevicesService EMSESP::webDevicesService = WebDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
WebAPIService EMSESP::webAPIService = WebAPIService(&webServer);
|
||||
WebAPIService EMSESP::webAPIService = WebAPIService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
WebLogService EMSESP::webLogService = WebLogService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
|
||||
using DeviceFlags = EMSdevice;
|
||||
using DeviceType = EMSdevice::DeviceType;
|
||||
@@ -90,6 +91,15 @@ void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
}
|
||||
}
|
||||
|
||||
// for a specific EMS device type go and request data values
|
||||
void EMSESP::fetch_device_values_type(const uint8_t device_type) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if ((emsdevice) && (emsdevice->device_type() == device_type)) {
|
||||
emsdevice->fetch_values();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clears list of recognized devices
|
||||
void EMSESP::clear_all_devices() {
|
||||
// temporary removed: clearing the list causes a crash, the associated commands and mqtt should also be removed.
|
||||
@@ -288,7 +298,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
// show EMS device values to the shell console
|
||||
// this is the only time generate_values_json is called in verbose mode (set to true)
|
||||
// generate_values_json is called in verbose mode (set to true)
|
||||
void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
if (emsdevices.empty()) {
|
||||
shell.printfln(F("No EMS devices detected. Try using 'scan devices' from the ems menu."));
|
||||
@@ -314,8 +324,8 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
shell.printf(" %s: ", key);
|
||||
JsonVariant data = p.value();
|
||||
shell.print(COLOR_BRIGHT_GREEN);
|
||||
if (data.is<char *>()) {
|
||||
shell.print(data.as<char *>());
|
||||
if (data.is<const char *>()) {
|
||||
shell.print(data.as<const char *>());
|
||||
} else if (data.is<int>()) {
|
||||
shell.print(data.as<int>());
|
||||
} else if (data.is<float>()) {
|
||||
@@ -447,11 +457,13 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
|
||||
// if its a boiler, generate json for each group and publish it directly
|
||||
if (device_type == DeviceType::BOILER) {
|
||||
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA, false);
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA), json);
|
||||
json.clear();
|
||||
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_DEVICE_DATA_WW, false);
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_DEVICE_DATA_WW), json);
|
||||
if (emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA, false)) {
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA), json);
|
||||
}
|
||||
doc.clear();
|
||||
if (emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA_WW, false)) {
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA_WW), json);
|
||||
}
|
||||
need_publish = false;
|
||||
}
|
||||
|
||||
@@ -462,14 +474,16 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
if (nested) {
|
||||
need_publish |= emsdevice->generate_values_json(json, DeviceValueTAG::TAG_NONE, true); // nested
|
||||
} else {
|
||||
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_THERMOSTAT_DATA, false); // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
json.clear();
|
||||
if (emsdevice->generate_values_json(json, DeviceValueTAG::TAG_THERMOSTAT_DATA, false)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
}
|
||||
doc.clear();
|
||||
|
||||
for (uint8_t hc_tag = TAG_HC1; hc_tag <= DeviceValueTAG::TAG_HC4; hc_tag++) {
|
||||
emsdevice->generate_values_json(json, hc_tag, false); // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
json.clear();
|
||||
if (emsdevice->generate_values_json(json, hc_tag, false)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
need_publish = false;
|
||||
}
|
||||
@@ -482,9 +496,10 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
need_publish |= emsdevice->generate_values_json(json, DeviceValueTAG::TAG_NONE, true); // nested
|
||||
} else {
|
||||
for (uint8_t hc_tag = TAG_HC1; hc_tag <= DeviceValueTAG::TAG_WWC4; hc_tag++) {
|
||||
emsdevice->generate_values_json(json, hc_tag, false); // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
json.clear();
|
||||
if (emsdevice->generate_values_json(json, hc_tag, false)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
need_publish = false;
|
||||
}
|
||||
@@ -547,6 +562,7 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
|
||||
Mqtt::publish(F_(response), doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
// builds json with the detail of each value, for all specific device type or the dallas sensor
|
||||
bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice->device_type() == devicetype) {
|
||||
@@ -765,7 +781,8 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
read_next_ = false;
|
||||
} else if (watch() == WATCH_ON) {
|
||||
if ((watch_id_ == WATCH_ID_NONE) || (telegram->type_id == watch_id_) || ((watch_id_ < 0x80) && ((telegram->src == watch_id_) || (telegram->dest == watch_id_)))) {
|
||||
if ((watch_id_ == WATCH_ID_NONE) || (telegram->type_id == watch_id_)
|
||||
|| ((watch_id_ < 0x80) && ((telegram->src == watch_id_) || (telegram->dest == watch_id_)))) {
|
||||
LOG_NOTICE(pretty_telegram(telegram).c_str());
|
||||
} else if (!trace_raw_) {
|
||||
LOG_TRACE(pretty_telegram(telegram).c_str());
|
||||
@@ -806,7 +823,8 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
found = emsdevice->handle_telegram(telegram);
|
||||
// if we correctly processes the telegram follow up with sending it via MQTT if needed
|
||||
if (found && Mqtt::connected()) {
|
||||
if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update()) || (telegram->type_id == publish_id_ && telegram->dest == txservice_.ems_bus_id())) {
|
||||
if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update())
|
||||
|| (telegram->type_id == publish_id_ && telegram->dest == txservice_.ems_bus_id())) {
|
||||
if (telegram->type_id == publish_id_) {
|
||||
publish_id_ = 0;
|
||||
}
|
||||
@@ -824,7 +842,8 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
if (watch() == WATCH_UNKNOWN) {
|
||||
LOG_NOTICE(pretty_telegram(telegram).c_str());
|
||||
}
|
||||
if (first_scan_done_ && !knowndevice && (telegram->src != EMSbus::ems_bus_id()) && (telegram->src != 0x0B) && (telegram->src != 0x0C) && (telegram->src != 0x0D)) {
|
||||
if (first_scan_done_ && !knowndevice && (telegram->src != EMSbus::ems_bus_id()) && (telegram->src != 0x0B) && (telegram->src != 0x0C)
|
||||
&& (telegram->src != 0x0D)) {
|
||||
send_read_request(EMSdevice::EMS_TYPE_VERSION, telegram->src);
|
||||
}
|
||||
}
|
||||
@@ -920,7 +939,8 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
// sometimes boilers share the same product id as controllers
|
||||
// so only add boilers if the device_id is 0x08, which is fixed for EMS
|
||||
if (device.device_type == DeviceType::BOILER) {
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER || (device_id >= EMSdevice::EMS_DEVICE_ID_BOILER_1 && device_id <= EMSdevice::EMS_DEVICE_ID_BOILER_F)) {
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER
|
||||
|| (device_id >= EMSdevice::EMS_DEVICE_ID_BOILER_1 && device_id <= EMSdevice::EMS_DEVICE_ID_BOILER_F)) {
|
||||
device_p = &device;
|
||||
break;
|
||||
}
|
||||
@@ -936,7 +956,8 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
if (device_p == nullptr) {
|
||||
LOG_NOTICE(F("Unrecognized EMS device (device ID 0x%02X, product ID %d). Please report on GitHub."), device_id, product_id);
|
||||
std::string name("unknown");
|
||||
emsdevices.push_back(EMSFactory::add(DeviceType::GENERIC, device_id, product_id, version, name, DeviceFlags::EMS_DEVICE_FLAG_NONE, EMSdevice::Brand::NO_BRAND));
|
||||
emsdevices.push_back(
|
||||
EMSFactory::add(DeviceType::GENERIC, device_id, product_id, version, name, DeviceFlags::EMS_DEVICE_FLAG_NONE, EMSdevice::Brand::NO_BRAND));
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
@@ -949,32 +970,56 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
|
||||
fetch_device_values(device_id); // go and fetch its data
|
||||
|
||||
// add info command, but not for all devices
|
||||
// add info commands, but not for all devices
|
||||
if ((device_type == DeviceType::CONNECT) || (device_type == DeviceType::CONTROLLER) || (device_type == DeviceType::GATEWAY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Command::add_with_json(device_type, F_(info), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id); });
|
||||
Command::add_with_json(
|
||||
device_type,
|
||||
F_(info),
|
||||
[device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id, true); },
|
||||
F_(info_cmd));
|
||||
Command::add_with_json(
|
||||
device_type,
|
||||
F("info_short"),
|
||||
[device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id, false); },
|
||||
nullptr,
|
||||
true); // this command is hidden
|
||||
Command::add_with_json(
|
||||
device_type,
|
||||
F_(commands),
|
||||
[device_type](const char * value, const int8_t id, JsonObject & json) { return command_commands(device_type, json, id); },
|
||||
F_(commands_cmd));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// list all available commands, return as json
|
||||
bool EMSESP::command_commands(uint8_t device_type, JsonObject & json, const int8_t id) {
|
||||
return Command::list(device_type, json);
|
||||
}
|
||||
|
||||
// export all values to info command
|
||||
// value is ignored here
|
||||
bool EMSESP::command_info(uint8_t device_type, JsonObject & json, const int8_t id) {
|
||||
// info command always shows in verbose mode, so full names are displayed
|
||||
bool EMSESP::command_info(uint8_t device_type, JsonObject & json, const int8_t id, bool verbose) {
|
||||
bool has_value = false;
|
||||
uint8_t tag;
|
||||
if (id >= 1 && id <= 4) {
|
||||
tag = DeviceValueTAG::TAG_HC1 + id - 1;
|
||||
} else if (id >= 9 && id <= 10) {
|
||||
tag = DeviceValueTAG::TAG_WWC1 + id - 9;
|
||||
} else {
|
||||
} else if (id == -1 || id == 0) {
|
||||
tag = DeviceValueTAG::TAG_NONE;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type) && ((device_type != DeviceType::THERMOSTAT) || (emsdevice->device_id() == EMSESP::actual_master_thermostat()))) {
|
||||
has_value |= emsdevice->generate_values_json(json, tag, (id < 1), (id == -1)); // nested for id -1,0 & console for id -1
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)
|
||||
&& ((device_type != DeviceType::THERMOSTAT) || (emsdevice->device_id() == EMSESP::actual_master_thermostat()))) {
|
||||
has_value |= emsdevice->generate_values_json(json, tag, (id < 1), verbose && (id == -1)); // nested for id -1,0 & console for id -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,7 +1032,12 @@ void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const
|
||||
}
|
||||
|
||||
// sends write request
|
||||
void EMSESP::send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) {
|
||||
void EMSESP::send_write_request(const uint16_t type_id,
|
||||
const uint8_t dest,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validate_typeid) {
|
||||
txservice_.add(Telegram::Operation::TX_WRITE, dest, type_id, offset, message_data, message_length, validate_typeid, true);
|
||||
}
|
||||
|
||||
@@ -1018,6 +1068,8 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
// get_uptime is only updated once per loop, does not give the right time
|
||||
LOG_TRACE(F("[UART_DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
|
||||
#endif
|
||||
// add to RxQueue for log/watch
|
||||
rxservice_.add(data, length);
|
||||
return; // it's an echo
|
||||
}
|
||||
|
||||
@@ -1053,7 +1105,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
tx_successful = true;
|
||||
// if telegram is longer read next part with offset + 25 for ems+
|
||||
if (length == 32) {
|
||||
if (txservice_.read_next_tx() == read_id_) {
|
||||
if (txservice_.read_next_tx(data[3]) == read_id_) {
|
||||
read_next_ = true;
|
||||
}
|
||||
}
|
||||
@@ -1158,11 +1210,12 @@ void EMSESP::start() {
|
||||
|
||||
// main loop calling all services
|
||||
void EMSESP::loop() {
|
||||
esp8266React.loop(); // web
|
||||
esp8266React.loop(); // web services
|
||||
system_.loop(); // does LED and checks system health, and syslog service
|
||||
|
||||
// if we're doing an OTA upload, skip MQTT and EMS
|
||||
if (!system_.upload_status()) {
|
||||
webLogService.loop(); // log in Web UI
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
shower_.loop(); // check for shower on/off
|
||||
dallassensor_.loop(); // read dallas sensor temperatures
|
||||
|
||||
25
src/emsesp.h
25
src/emsesp.h
@@ -35,10 +35,11 @@
|
||||
|
||||
#include <ESP8266React.h>
|
||||
|
||||
#include "WebStatusService.h"
|
||||
#include "WebDevicesService.h"
|
||||
#include "WebSettingsService.h"
|
||||
#include "WebAPIService.h"
|
||||
#include "web/WebStatusService.h"
|
||||
#include "web/WebDevicesService.h"
|
||||
#include "web/WebSettingsService.h"
|
||||
#include "web/WebAPIService.h"
|
||||
#include "web/WebLogService.h"
|
||||
|
||||
#include "emsdevice.h"
|
||||
#include "emsfactory.h"
|
||||
@@ -93,7 +94,12 @@ class EMSESP {
|
||||
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
||||
static void send_write_request(const uint16_t type_id,
|
||||
const uint8_t dest,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validate_typeid);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||
|
||||
@@ -126,6 +132,10 @@ class EMSESP {
|
||||
return (!(dallassensor_.sensors().empty()));
|
||||
}
|
||||
|
||||
static uint32_t sensor_reads() {
|
||||
return dallassensor_.reads();
|
||||
}
|
||||
|
||||
static uint32_t sensor_fails() {
|
||||
return dallassensor_.fails();
|
||||
}
|
||||
@@ -173,6 +183,7 @@ class EMSESP {
|
||||
}
|
||||
|
||||
static void fetch_device_values(const uint8_t device_id = 0);
|
||||
static void fetch_device_values_type(const uint8_t device_type);
|
||||
|
||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
||||
static void scan_devices();
|
||||
@@ -195,6 +206,7 @@ class EMSESP {
|
||||
static WebStatusService webStatusService;
|
||||
static WebDevicesService webDevicesService;
|
||||
static WebAPIService webAPIService;
|
||||
static WebLogService webLogService;
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
|
||||
@@ -209,7 +221,8 @@ class EMSESP {
|
||||
static void process_version(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_response(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_all_loop();
|
||||
static bool command_info(uint8_t device_type, JsonObject & json, const int8_t id);
|
||||
static bool command_info(uint8_t device_type, JsonObject & json, const int8_t id, bool verbose = true);
|
||||
static bool command_commands(uint8_t device_type, JsonObject & json, const int8_t id);
|
||||
|
||||
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
|
||||
static uint32_t last_fetch_;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "version.h"
|
||||
#include "default_settings.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
// forward declarators
|
||||
// used to bind EMS-ESP functions to external frameworks
|
||||
namespace emsesp {
|
||||
|
||||
@@ -478,7 +478,8 @@ bool Helpers::value2enum(const char * v, uint8_t & value, const __FlashStringHel
|
||||
std::string str = toLower(v);
|
||||
for (value = 0; strs[value]; value++) {
|
||||
std::string str1 = toLower(uuid::read_flash_string(strs[value]));
|
||||
if ((str1 == uuid::read_flash_string(F_(off)) && str == "false") || (str1 == uuid::read_flash_string(F_(on)) && str == "true") || (str == str1) || (v[0] == ('0' + value) && v[1] == '\0')) {
|
||||
if ((str1 == uuid::read_flash_string(F_(off)) && str == "false") || (str1 == uuid::read_flash_string(F_(on)) && str == "true") || (str == str1)
|
||||
|| (v[0] == ('0' + value) && v[1] == '\0')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
200
src/locale_EN.h
200
src/locale_EN.h
@@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/* spell-checker: disable */
|
||||
|
||||
// common words
|
||||
MAKE_PSTR_WORD(debug)
|
||||
MAKE_PSTR_WORD(exit)
|
||||
@@ -80,6 +82,7 @@ MAKE_PSTR_WORD(command)
|
||||
MAKE_PSTR_WORD(commands)
|
||||
MAKE_PSTR_WORD(info)
|
||||
MAKE_PSTR_WORD(settings)
|
||||
MAKE_PSTR_WORD(value)
|
||||
|
||||
// devices
|
||||
MAKE_PSTR_WORD(boiler)
|
||||
@@ -94,6 +97,7 @@ MAKE_PSTR_WORD(heatpump)
|
||||
MAKE_PSTR_WORD(generic)
|
||||
MAKE_PSTR_WORD(dallassensor)
|
||||
MAKE_PSTR_WORD(unknown)
|
||||
MAKE_PSTR_WORD(Dallassensor)
|
||||
|
||||
// format strings
|
||||
MAKE_PSTR(EMSESP, "EMS-ESP")
|
||||
@@ -110,7 +114,7 @@ MAKE_PSTR(tx_mode_fmt, "Tx mode: %d")
|
||||
MAKE_PSTR(bus_id_fmt, "Bus ID: %02X")
|
||||
MAKE_PSTR(log_level_fmt, "Log level: %s")
|
||||
|
||||
//strings
|
||||
// strings
|
||||
MAKE_PSTR(cmd_optional, "[cmd]")
|
||||
MAKE_PSTR(ha_optional, "[ha]")
|
||||
MAKE_PSTR(deep_optional, "[deep]")
|
||||
@@ -135,6 +139,10 @@ MAKE_PSTR(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_PSTR(password_prompt, "Password: ")
|
||||
MAKE_PSTR(unset, "<unset>")
|
||||
|
||||
// command descriptions
|
||||
MAKE_PSTR(info_cmd, "list all values")
|
||||
MAKE_PSTR(commands_cmd, "list all commands")
|
||||
|
||||
MAKE_PSTR_WORD(number)
|
||||
MAKE_PSTR_WORD(enum)
|
||||
MAKE_PSTR_WORD(text)
|
||||
@@ -150,7 +158,7 @@ MAKE_PSTR_LIST(div100, F_(100))
|
||||
MAKE_PSTR_LIST(div60, F_(60))
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// uom - also used with HA
|
||||
// uom - also used with HA see https://github.com/home-assistant/core/blob/d7ac4bd65379e11461c7ce0893d3533d8d8b8cbf/homeassistant/const.py#L384
|
||||
MAKE_PSTR(percent, "%")
|
||||
MAKE_PSTR(degrees, "°C")
|
||||
MAKE_PSTR(kwh, "kWh")
|
||||
@@ -161,13 +169,20 @@ MAKE_PSTR(hours, "hours")
|
||||
MAKE_PSTR(ua, "uA")
|
||||
MAKE_PSTR(lmin, "l/min")
|
||||
MAKE_PSTR(kw, "kW")
|
||||
MAKE_PSTR(w, "W")
|
||||
MAKE_PSTR(kb, "KB")
|
||||
MAKE_PSTR(seconds, "seconds")
|
||||
MAKE_PSTR(dbm, "dBm")
|
||||
MAKE_PSTR(num, " ") // this is hack so HA renders numbers correctly
|
||||
MAKE_PSTR(bool, " ") // this is hack so HA renders numbers correctly
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
// use empty string if want to suppress showing tags
|
||||
// tags must not have spaces
|
||||
MAKE_PSTR(tag_none, "")
|
||||
MAKE_PSTR(tag_heartbeat, "")
|
||||
MAKE_PSTR(tag_boiler_data, "")
|
||||
MAKE_PSTR(tag_device_data_ww, "warm water")
|
||||
MAKE_PSTR(tag_boiler_data_ww, "ww")
|
||||
MAKE_PSTR(tag_thermostat_data, "")
|
||||
MAKE_PSTR(tag_hc1, "hc1")
|
||||
MAKE_PSTR(tag_hc2, "hc2")
|
||||
@@ -195,9 +210,8 @@ MAKE_PSTR(tag_hs15, "hs15")
|
||||
MAKE_PSTR(tag_hs16, "hs16")
|
||||
|
||||
// MQTT topic names
|
||||
// MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
|
||||
MAKE_PSTR(tag_boiler_data_mqtt, "")
|
||||
MAKE_PSTR(tag_device_data_ww_mqtt, "ww")
|
||||
MAKE_PSTR(tag_boiler_data_ww_mqtt, "ww")
|
||||
|
||||
// boiler
|
||||
MAKE_PSTR_WORD(time)
|
||||
@@ -208,7 +222,7 @@ MAKE_PSTR_WORD(3x3min)
|
||||
MAKE_PSTR_WORD(4x3min)
|
||||
MAKE_PSTR_WORD(5x3min)
|
||||
MAKE_PSTR_WORD(6x3min)
|
||||
MAKE_PSTR_WORD(continuos)
|
||||
MAKE_PSTR_WORD(continuous)
|
||||
MAKE_PSTR(3wayvalve, "3-way valve")
|
||||
MAKE_PSTR(chargepump, "charge pump")
|
||||
MAKE_PSTR_WORD(hot)
|
||||
@@ -223,8 +237,8 @@ MAKE_PSTR_WORD(error)
|
||||
|
||||
// boiler lists
|
||||
MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date))
|
||||
MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuos))
|
||||
MAKE_PSTR_LIST(enum_charge, F_(3wayvalve), F_(chargepump))
|
||||
MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuous))
|
||||
MAKE_PSTR_LIST(enum_charge, F_(chargepump), F_(3wayvalve))
|
||||
MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent))
|
||||
MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer))
|
||||
MAKE_PSTR_LIST(enum_reset, F_(maintenance), F_(error))
|
||||
@@ -283,7 +297,16 @@ MAKE_PSTR(functioning_mode, "functioning mode")
|
||||
MAKE_PSTR(smoke_temperature, "smoke temperature")
|
||||
|
||||
// thermostat lists
|
||||
MAKE_PSTR_LIST(enum_ibaMainDisplay, F_(internal_temperature), F_(internal_setpoint), F_(external_temperature), F_(burner_temperature), F_(ww_temperature), F_(functioning_mode), F_(time), F_(date), F_(smoke_temperature))
|
||||
MAKE_PSTR_LIST(enum_ibaMainDisplay,
|
||||
F_(internal_temperature),
|
||||
F_(internal_setpoint),
|
||||
F_(external_temperature),
|
||||
F_(burner_temperature),
|
||||
F_(ww_temperature),
|
||||
F_(functioning_mode),
|
||||
F_(time),
|
||||
F_(date),
|
||||
F_(smoke_temperature))
|
||||
MAKE_PSTR_LIST(enum_ibaLanguage, F_(german), F_(dutch), F_(french), F_(italian))
|
||||
MAKE_PSTR_LIST(enum_floordrystatus, F_(off), F_(start), F_(heat), F_(hold), F_(cool), F_(end))
|
||||
MAKE_PSTR_LIST(enum_ibaBuildingType, F_(blank), F_(light), F_(medium), F_(heavy)) // RC300
|
||||
@@ -295,11 +318,11 @@ MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor))
|
||||
MAKE_PSTR_LIST(enum_summermode, F_(summer), F_(auto), F_(winter))
|
||||
|
||||
MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode4, F_(blank), F_(manual), F_(auto), F_(holiday))
|
||||
MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off))
|
||||
MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto)) // RC100, RC300, RC310
|
||||
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto)) // RC20
|
||||
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30
|
||||
MAKE_PSTR_LIST(enum_mode4, F_(blank), F_(manual), F_(auto), F_(holiday)) // JUNKERS
|
||||
MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off)) // CRF
|
||||
|
||||
MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort))
|
||||
MAKE_PSTR_LIST(enum_modetype2, F_(day))
|
||||
@@ -316,19 +339,21 @@ MAKE_PSTR_LIST(enum_control, F_(off), F_(rc20), F_(rc3x))
|
||||
|
||||
MAKE_PSTR_LIST(enum_hamode, F_(off), F_(heat), F_(auto), F_(heat), F_(off), F_(heat), F_(auto), F_(auto), F_(auto), F_(auto))
|
||||
|
||||
/*
|
||||
* MQTT topics and full text for values and commands
|
||||
*/
|
||||
// solar list
|
||||
MAKE_PSTR_LIST(enum_solarmode, F_(constant), F("pwm"), F("analog"))
|
||||
MAKE_PSTR_LIST(enum_collectortype, F_(blank), F("flat"), F("vacuum"))
|
||||
|
||||
// MQTT topics and full text for values and commands
|
||||
MAKE_PSTR(homeassistant, "homeassistant/")
|
||||
|
||||
// id for all devices
|
||||
// id used to store the device ID, goes into MQTT payload
|
||||
// empty full name to prevent being shown in web or console
|
||||
MAKE_PSTR_LIST(ID, F_(id))
|
||||
|
||||
// Boiler
|
||||
// extra commands, no output
|
||||
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"))
|
||||
MAKE_PSTR_LIST(reset, F("reset"))
|
||||
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"), F("turn on/off DHW by going into maintenance mode"))
|
||||
MAKE_PSTR_LIST(reset, F("reset"), F("reset messages"))
|
||||
|
||||
// single mqtt topics
|
||||
MAKE_PSTR_WORD(heating_active)
|
||||
@@ -347,7 +372,7 @@ MAKE_PSTR_LIST(curFlowTemp, F("curflowtemp"), F("current flow temperature"))
|
||||
MAKE_PSTR_LIST(retTemp, F("rettemp"), F("return temperature"))
|
||||
MAKE_PSTR_LIST(switchTemp, F("switchtemp"), F("mixing switch temperature"))
|
||||
MAKE_PSTR_LIST(sysPress, F("syspress"), F("system pressure"))
|
||||
MAKE_PSTR_LIST(boilTemp, F("boiltemp"), F("max boiler temperature"))
|
||||
MAKE_PSTR_LIST(boilTemp, F("boiltemp"), F("boiler temperature"))
|
||||
MAKE_PSTR_LIST(exhaustTemp, F("exhausttemp"), F("exhaust temperature"))
|
||||
MAKE_PSTR_LIST(burnGas, F("burngas"), F("gas"))
|
||||
MAKE_PSTR_LIST(flameCurr, F("flamecurr"), F("flame current"))
|
||||
@@ -367,7 +392,7 @@ MAKE_PSTR_LIST(boilHystOff, F("boilhystoff"), F("hysteresis off temperature"))
|
||||
MAKE_PSTR_LIST(setFlowTemp, F("setflowtemp"), F("set flow temperature"))
|
||||
MAKE_PSTR_LIST(setBurnPow, F("setburnpow"), F("burner set power"))
|
||||
MAKE_PSTR_LIST(curBurnPow, F("curburnpow"), F("burner current power"))
|
||||
MAKE_PSTR_LIST(burnStarts, F("burnstarts"), F("burner # starts"))
|
||||
MAKE_PSTR_LIST(burnStarts, F("burnstarts"), F("# burner starts"))
|
||||
MAKE_PSTR_LIST(burnWorkMin, F("burnworkmin"), F("total burner operating time"))
|
||||
MAKE_PSTR_LIST(heatWorkMin, F("heatworkmin"), F("total heat operating time"))
|
||||
MAKE_PSTR_LIST(UBAuptime, F("ubauptime"), F("total UBA operating time"))
|
||||
@@ -378,7 +403,6 @@ MAKE_PSTR_LIST(maintenanceMessage, F("maintenancemessage"), F("maintenance messa
|
||||
MAKE_PSTR_LIST(maintenanceDate, F("maintenancedate"), F("maintenance set date"))
|
||||
MAKE_PSTR_LIST(maintenanceType, F_(maintenance), F("maintenance scheduled"))
|
||||
MAKE_PSTR_LIST(maintenanceTime, F("maintenancetime"), F("maintenance set time"))
|
||||
|
||||
MAKE_PSTR_LIST(upTimeControl, F("uptimecontrol"), F("operating time total heat"))
|
||||
MAKE_PSTR_LIST(upTimeCompHeating, F("uptimecompheating"), F("operating time compressor heating"))
|
||||
MAKE_PSTR_LIST(upTimeCompCooling, F("uptimecompcooling"), F("operating time compressor cooling"))
|
||||
@@ -400,7 +424,7 @@ MAKE_PSTR_LIST(auxElecHeatNrgConsWW, F("auxelecheatnrgconsww"), F("auxiliary ele
|
||||
|
||||
MAKE_PSTR_LIST(hpPower, F("hppower"), F("heatpump power"))
|
||||
MAKE_PSTR_LIST(hpTc0, F("hptc0"), F("water temperature condenser inlet (TC0)"))
|
||||
MAKE_PSTR_LIST(hpTc1, F("hptc1"), F("water temperture condenser output (TC1)"))
|
||||
MAKE_PSTR_LIST(hpTc1, F("hptc1"), F("water temperature condenser output (TC1)"))
|
||||
MAKE_PSTR_LIST(hpTc3, F("hptc3"), F("condenser temperature (TC3)"))
|
||||
MAKE_PSTR_LIST(hpTr3, F("hptr3"), F("refrigerant temperature liquid side (condenser output) (TR3)"))
|
||||
MAKE_PSTR_LIST(hpTr4, F("hptr4"), F("evaporator inlet temperature (TR4)"))
|
||||
@@ -411,39 +435,39 @@ MAKE_PSTR_LIST(hpTl2, F("hptl2"), F("air inlet temperature (TL2)"))
|
||||
MAKE_PSTR_LIST(hpPl1, F("hppl1"), F("low pressure side temperature (PL1)"))
|
||||
MAKE_PSTR_LIST(hpPh1, F("hpph1"), F("high pressure side temperature (PH1)"))
|
||||
|
||||
MAKE_PSTR_LIST(wWSelTemp, F("wwseltemp"), F("selected temperature"))
|
||||
MAKE_PSTR_LIST(wWSetTemp, F("wwsettemp"), F("set temperature"))
|
||||
MAKE_PSTR_LIST(wWType, F("wwtype"), F("type"))
|
||||
MAKE_PSTR_LIST(wWComfort, F("wwcomfort"), F("comfort"))
|
||||
MAKE_PSTR_LIST(wWFlowTempOffset, F("wwflowtempoffset"), F("flow temperature offset"))
|
||||
MAKE_PSTR_LIST(wWMaxPower, F("wwmaxpower"), F("max power"))
|
||||
MAKE_PSTR_LIST(wWCircPump, F("wwcircpump"), F("circulation pump available"))
|
||||
MAKE_PSTR_LIST(wWChargeType, F("wwchargetype"), F("charging type"))
|
||||
MAKE_PSTR_LIST(wWDisinfectionTemp, F("wwdisinfectiontemp"), F("disinfection temperature"))
|
||||
MAKE_PSTR_LIST(wWCircMode, F("wwcircmode"), F("circulation pump freq"))
|
||||
MAKE_PSTR_LIST(wWCirc, F("wwcirc"), F("circulation active"))
|
||||
MAKE_PSTR_LIST(wWCurTemp, F("wwcurtemp"), F("current intern temperature"))
|
||||
MAKE_PSTR_LIST(wWCurTemp2, F("wwcurtemp2"), F("current extern temperature"))
|
||||
MAKE_PSTR_LIST(wWCurFlow, F("wwcurflow"), F("current tap water flow"))
|
||||
MAKE_PSTR_LIST(wWStorageTemp1, F("wwstoragetemp1"), F("storage intern temperature"))
|
||||
MAKE_PSTR_LIST(wWStorageTemp2, F("wwstoragetemp2"), F("storage extern temperature"))
|
||||
MAKE_PSTR_LIST(wWActivated, F("wwactivated"), F("activated"))
|
||||
MAKE_PSTR_LIST(wWOneTime, F("wwonetime"), F("one time charging"))
|
||||
MAKE_PSTR_LIST(wWDisinfecting, F("wwdisinfecting"), F("disinfecting"))
|
||||
MAKE_PSTR_LIST(wWCharging, F("wwcharging"), F("charging"))
|
||||
MAKE_PSTR_LIST(wWRecharging, F("wwrecharging"), F("recharging"))
|
||||
MAKE_PSTR_LIST(wWTempOK, F("wwtempok"), F("temperature ok"))
|
||||
MAKE_PSTR_LIST(wWActive, F("wwactive"), F("active"))
|
||||
MAKE_PSTR_LIST(wWHeat, F("wwheat"), F("heating"))
|
||||
MAKE_PSTR_LIST(wWSetPumpPower, F("wwsetpumppower"), F("pump set power"))
|
||||
MAKE_PSTR_LIST(mixerTemp, F("mixertemp"), F("mixer temperature"))
|
||||
MAKE_PSTR_LIST(tankMiddleTemp, F("tankmiddletemp"), F("tank middle temperature (TS3)"))
|
||||
MAKE_PSTR_LIST(wWStarts, F("wwstarts"), F("# starts"))
|
||||
MAKE_PSTR_LIST(wWStarts2, F("wwstarts2"), F("# control starts"))
|
||||
MAKE_PSTR_LIST(wWWorkM, F("wwworkm"), F("active time"))
|
||||
MAKE_PSTR_LIST(wWSelTemp, F("wwseltemp"), F("ww selected temperature"))
|
||||
MAKE_PSTR_LIST(wWSetTemp, F("wwsettemp"), F("ww set temperature"))
|
||||
MAKE_PSTR_LIST(wWType, F("wwtype"), F("ww type"))
|
||||
MAKE_PSTR_LIST(wWComfort, F("wwcomfort"), F("ww comfort"))
|
||||
MAKE_PSTR_LIST(wWFlowTempOffset, F("wwflowtempoffset"), F("ww flow temperature offset"))
|
||||
MAKE_PSTR_LIST(wWMaxPower, F("wwmaxpower"), F("ww max power"))
|
||||
MAKE_PSTR_LIST(wWCircPump, F("wwcircpump"), F("ww circulation pump available"))
|
||||
MAKE_PSTR_LIST(wWChargeType, F("wwchargetype"), F("ww charging type"))
|
||||
MAKE_PSTR_LIST(wWDisinfectionTemp, F("wwdisinfectiontemp"), F("ww disinfection temperature"))
|
||||
MAKE_PSTR_LIST(wWCircMode, F("wwcircmode"), F("ww circulation pump frequency"))
|
||||
MAKE_PSTR_LIST(wWCirc, F("wwcirc"), F("ww circulation active"))
|
||||
MAKE_PSTR_LIST(wWCurTemp, F("wwcurtemp"), F("ww current intern temperature"))
|
||||
MAKE_PSTR_LIST(wWCurTemp2, F("wwcurtemp2"), F("ww current extern temperature"))
|
||||
MAKE_PSTR_LIST(wWCurFlow, F("wwcurflow"), F("ww current tap water flow"))
|
||||
MAKE_PSTR_LIST(wWStorageTemp1, F("wwstoragetemp1"), F("ww storage intern temperature"))
|
||||
MAKE_PSTR_LIST(wWStorageTemp2, F("wwstoragetemp2"), F("ww storage extern temperature"))
|
||||
MAKE_PSTR_LIST(wWActivated, F("wwactivated"), F("ww activated"))
|
||||
MAKE_PSTR_LIST(wWOneTime, F("wwonetime"), F("ww one time charging"))
|
||||
MAKE_PSTR_LIST(wWDisinfecting, F("wwdisinfecting"), F("ww disinfecting"))
|
||||
MAKE_PSTR_LIST(wWCharging, F("wwcharging"), F("ww charging"))
|
||||
MAKE_PSTR_LIST(wWRecharging, F("wwrecharging"), F("ww recharging"))
|
||||
MAKE_PSTR_LIST(wWTempOK, F("wwtempok"), F("ww temperature ok"))
|
||||
MAKE_PSTR_LIST(wWActive, F("wwactive"), F("ww active"))
|
||||
MAKE_PSTR_LIST(wWHeat, F("wwheat"), F("ww heating"))
|
||||
MAKE_PSTR_LIST(wWSetPumpPower, F("wwsetpumppower"), F("ww set pump power"))
|
||||
MAKE_PSTR_LIST(mixerTemp, F("mixertemp"), F("ww mixer temperature"))
|
||||
MAKE_PSTR_LIST(tankMiddleTemp, F("tankmiddletemp"), F("ww tank middle temperature (TS3)"))
|
||||
MAKE_PSTR_LIST(wWStarts, F("wwstarts"), F("ww # starts"))
|
||||
MAKE_PSTR_LIST(wWStarts2, F("wwstarts2"), F("ww # control starts"))
|
||||
MAKE_PSTR_LIST(wWWorkM, F("wwworkm"), F("ww active time"))
|
||||
|
||||
// thermostat
|
||||
// extra commands, no long name, does not show on web
|
||||
// extra commands, with no long name so they don't show up in WebUI
|
||||
MAKE_PSTR_LIST(switchtime, F("switchtime"))
|
||||
MAKE_PSTR_LIST(temp, F("temp"))
|
||||
MAKE_PSTR_LIST(hatemp, F("hatemp"))
|
||||
@@ -452,34 +476,27 @@ MAKE_PSTR_LIST(hamode, F("hamode"))
|
||||
// mqtt values / commands
|
||||
MAKE_PSTR_LIST(dateTime, F("datetime"), F("date/time"))
|
||||
MAKE_PSTR_LIST(errorCode, F("errorcode"), F("error code"))
|
||||
|
||||
MAKE_PSTR_LIST(ibaMainDisplay, F("display"), F("display"))
|
||||
MAKE_PSTR_LIST(ibaLanguage, F("language"), F("language"))
|
||||
MAKE_PSTR_LIST(ibaClockOffset, F("clockoffset"), F("clock offset"))
|
||||
|
||||
MAKE_PSTR_LIST(ibaBuildingType, F("building"), F("building"))
|
||||
MAKE_PSTR_LIST(ibaCalIntTemperature, F("intoffset"), F("offset internal temperature"))
|
||||
MAKE_PSTR_LIST(ibaMinExtTemperature, F("minexttemp"), F("min external temperature"))
|
||||
MAKE_PSTR_LIST(ibaMinExtTemperature, F("minexttemp"), F("minimal external temperature"))
|
||||
MAKE_PSTR_LIST(tempsensor1, F("inttemp1"), F("temperature sensor 1"))
|
||||
MAKE_PSTR_LIST(tempsensor2, F("inttemp2"), F("temperature sensor 2"))
|
||||
MAKE_PSTR_LIST(dampedoutdoortemp, F("dampedoutdoortemp"), F("damped outdoor temperature"))
|
||||
|
||||
MAKE_PSTR_LIST(floordrystatus, F("floordry"), F("floor drying"))
|
||||
MAKE_PSTR_LIST(dampedoutdoortemp2, F("dampedoutdoortemp"), F("damped outdoor temperature"))
|
||||
MAKE_PSTR_LIST(floordrytemp, F("floordrytemp"), F("floor drying temperature"))
|
||||
|
||||
MAKE_PSTR_LIST(wwMode, F("wwmode"), F("mode"))
|
||||
MAKE_PSTR_LIST(wwSetTemp, F("wwsettemp"), F("set temperature"))
|
||||
MAKE_PSTR_LIST(wwSetTempLow, F("wwsettemplow"), F("set temperature low"))
|
||||
MAKE_PSTR_LIST(wwExtra1, F("wwextra1"), F("circuit 1 extra"))
|
||||
MAKE_PSTR_LIST(wwExtra2, F("wwextra2"), F("circuit 2 extra"))
|
||||
|
||||
MAKE_PSTR_LIST(wwMode, F("wwmode"), F("ww mode"))
|
||||
MAKE_PSTR_LIST(wwSetTemp, F("wwsettemp"), F("ww set temperature"))
|
||||
MAKE_PSTR_LIST(wwSetTempLow, F("wwsettemplow"), F("ww set low temperature"))
|
||||
MAKE_PSTR_LIST(wwExtra1, F("wwextra1"), F("ww circuit 1 extra"))
|
||||
MAKE_PSTR_LIST(wwExtra2, F("wwextra2"), F("ww circuit 2 extra"))
|
||||
MAKE_PSTR_LIST(setpoint_roomTemp, F("seltemp"), F("selected room temperature"))
|
||||
MAKE_PSTR_LIST(curr_roomTemp, F("currtemp"), F("current room temperature"))
|
||||
|
||||
MAKE_PSTR_LIST(mode, F("mode"), F("mode"))
|
||||
MAKE_PSTR_LIST(modetype, F("modetype"), F("mode type"))
|
||||
|
||||
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("day temperature"))
|
||||
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("heat temperature"))
|
||||
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("night temperature"))
|
||||
@@ -496,7 +513,7 @@ MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("room influence"))
|
||||
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("nofrost temperature"))
|
||||
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("target flow temperature"))
|
||||
MAKE_PSTR_LIST(heatingtype, F("heatingtype"), F("heating type"))
|
||||
MAKE_PSTR_LIST(summersetmode, F("summersetmode"), F("summer set mode"))
|
||||
MAKE_PSTR_LIST(summersetmode, F("summersetmode"), F("set summer mode"))
|
||||
MAKE_PSTR_LIST(controlmode, F("controlmode"), F("control mode"))
|
||||
MAKE_PSTR_LIST(control, F("control"), F("control device"))
|
||||
MAKE_PSTR_LIST(program, F("program"), F("program"))
|
||||
@@ -521,10 +538,9 @@ MAKE_PSTR_LIST(flowTempHc, F("flowtemphc"), F("flow temperature in assigned hc (
|
||||
MAKE_PSTR_LIST(pumpStatus, F("pumpstatus"), F("pump status in assigned hc (PC1)"))
|
||||
MAKE_PSTR_LIST(mixerStatus, F("valvestatus"), F("mixing valve actuator in assigned hc (VC1)"))
|
||||
MAKE_PSTR_LIST(flowTempVf, F("flowtempvf"), F("flow temperature in header (T0/Vf)"))
|
||||
|
||||
MAKE_PSTR_LIST(wwPumpStatus, F("pumpstatus"), F("pump status in assigned wwc (PC1)"))
|
||||
MAKE_PSTR_LIST(wwTempStatus, F("wwtempstatus"), F("temperature switch in assigned wwc (MC1)"))
|
||||
MAKE_PSTR_LIST(wwTemp, F("wwtemp"), F("current warm water temperature"))
|
||||
MAKE_PSTR_LIST(wwTemp, F("wwtemp"), F("ww current temperature"))
|
||||
|
||||
// solar
|
||||
MAKE_PSTR_LIST(type, F("type"), F("type"))
|
||||
@@ -532,28 +548,48 @@ MAKE_PSTR_LIST(collectorTemp, F("collectortemp"), F("collector temperature (TS1)
|
||||
MAKE_PSTR_LIST(tankBottomTemp, F("tankbottomtemp"), F("tank bottom temperature (TS2)"))
|
||||
MAKE_PSTR_LIST(tank2BottomTemp, F("tank2bottomtemp"), F("second tank bottom temperature (TS5)"))
|
||||
MAKE_PSTR_LIST(heatExchangerTemp, F("heatexchangertemp"), F("heat exchanger temperature (TS6)"))
|
||||
|
||||
MAKE_PSTR_LIST(collectorMaxTemp, F("collectormaxtemp"), F("maximum collector temperature"))
|
||||
MAKE_PSTR_LIST(collectorMinTemp, F("collectormintemp"), F("minimum collector temperature"))
|
||||
MAKE_PSTR_LIST(tankMaxTemp, F("tankmaxtemp"), F("maximum tank temperature"))
|
||||
MAKE_PSTR_LIST(solarPumpModulation, F("solarpumpmodulation"), F("pump modulation (PS1)"))
|
||||
MAKE_PSTR_LIST(cylinderPumpModulation, F("cylinderpumpmodulation"), F("cylinder pump modulation (PS5)"))
|
||||
|
||||
MAKE_PSTR_LIST(solarPump, F("solarpump"), F("pump (PS1)"))
|
||||
MAKE_PSTR_LIST(valveStatus, F("valvestatus"), F("valve status"))
|
||||
MAKE_PSTR_LIST(tankHeated, F("tankheated"), F("tank heated"))
|
||||
MAKE_PSTR_LIST(collectorShutdown, F("collectorshutdown"), F("collector shutdown"))
|
||||
|
||||
MAKE_PSTR_LIST(pumpWorkTime, F("pumpWorktime"), F("pump working time"))
|
||||
|
||||
MAKE_PSTR_LIST(pumpWorkTime, F("pumpworktime"), F("pump working time"))
|
||||
MAKE_PSTR_LIST(energyLastHour, F("energylasthour"), F("energy last hour"))
|
||||
MAKE_PSTR_LIST(energyTotal, F("energytotal"), F("energy total"))
|
||||
MAKE_PSTR_LIST(energyToday, F("energytoday"), F("energy today"))
|
||||
MAKE_PSTR_LIST(wwTemp1, F("wwtemp1"), F("ww temperature 1"))
|
||||
MAKE_PSTR_LIST(wwTemp3, F("wwtemp3"), F("ww temperature 3"))
|
||||
MAKE_PSTR_LIST(wwTemp4, F("wwtemp4"), F("ww temperature 4"))
|
||||
MAKE_PSTR_LIST(wwTemp5, F("wwtemp5"), F("ww temperature 5"))
|
||||
MAKE_PSTR_LIST(wwTemp7, F("wwtemp7"), F("ww temperature 7"))
|
||||
MAKE_PSTR_LIST(wwPump, F("wwpump"), F("ww pump"))
|
||||
MAKE_PSTR_LIST(wwMinTemp, F("wwmintemp"), F("ww minimum temperature"))
|
||||
MAKE_PSTR_LIST(pumpMinMod, F("pumpminmod"), F("minimum pump modulation"))
|
||||
MAKE_PSTR_LIST(maxFlow, F("maxflow"), F("maximum solar flow"))
|
||||
MAKE_PSTR_LIST(solarPower, F("solarpower"), F("actual solar power"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnonDiff, F("turnondiff"), F("pump turn on difference"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnoffDiff, F("turnoffdiff"), F("pump turn off difference"))
|
||||
|
||||
MAKE_PSTR_LIST(wwTemp1, F("wwtemp1"), F("temperature 1"))
|
||||
MAKE_PSTR_LIST(wwTemp3, F("wwtemp3"), F("temperature 3"))
|
||||
MAKE_PSTR_LIST(wwTemp4, F("wwtemp4"), F("temperature 4"))
|
||||
MAKE_PSTR_LIST(wwTemp5, F("wwtemp5"), F("temperature 5"))
|
||||
MAKE_PSTR_LIST(wwTemp7, F("wwtemp7"), F("temperature 7"))
|
||||
MAKE_PSTR_LIST(wwPump, F("wwpump"), F("pump"))
|
||||
// Solar SM100
|
||||
MAKE_PSTR_LIST(heatTransferSystem, F("heattransfersystem"), F("heattransfer system"))
|
||||
MAKE_PSTR_LIST(externalTank, F("externaltank"), F("external tank"))
|
||||
MAKE_PSTR_LIST(thermalDisinfect, F("thermaldisinfect"), F("thermal disinfection"))
|
||||
MAKE_PSTR_LIST(heatMetering, F("heatmetering"), F("heatmetering"))
|
||||
|
||||
// telegram 0x035A
|
||||
MAKE_PSTR_LIST(solarPumpMode, F("solarpumpmode"), F("solar pump mode"))
|
||||
MAKE_PSTR_LIST(solarPumpKick, F("pumpkick"), F("pumpkick"))
|
||||
MAKE_PSTR_LIST(plainWaterMode, F("plainwatermode"), F("plain water mode"))
|
||||
MAKE_PSTR_LIST(doubleMatchFlow, F("doublematchflow"), F("doublematchflow"))
|
||||
|
||||
// telegram 0x380
|
||||
MAKE_PSTR_LIST(climateZone, F("climatezone"), F("climate zone"))
|
||||
MAKE_PSTR_LIST(collector1Area, F("collector1area"), F("collector 1 area"))
|
||||
MAKE_PSTR_LIST(collector1Type, F("collector1type"), F("collector 1 type"))
|
||||
|
||||
// switch
|
||||
MAKE_PSTR_LIST(activated, F("activated"), F("activated"))
|
||||
|
||||
173
src/mqtt.cpp
173
src/mqtt.cpp
@@ -228,12 +228,27 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
}
|
||||
for (const auto & cf : Command::commands()) {
|
||||
if (subscribe_format_ == 2 && cf.flag_ == MqttSubFlag::FLAG_HC) {
|
||||
shell.printfln(F(" %s/%s/hc1/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc2/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc3/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc4/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc1/%s"),
|
||||
mqtt_base_.c_str(),
|
||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||
uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc2/%s"),
|
||||
mqtt_base_.c_str(),
|
||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||
uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc3/%s"),
|
||||
mqtt_base_.c_str(),
|
||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||
uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/hc4/%s"),
|
||||
mqtt_base_.c_str(),
|
||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||
uuid::read_flash_string(cf.cmd_).c_str());
|
||||
} else if (subscribe_format_ && cf.flag_ != MqttSubFlag::FLAG_NOSUB) {
|
||||
shell.printfln(F(" %s/%s/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), uuid::read_flash_string(cf.cmd_).c_str());
|
||||
shell.printfln(F(" %s/%s/%s"),
|
||||
mqtt_base_.c_str(),
|
||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||
uuid::read_flash_string(cf.cmd_).c_str());
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
@@ -265,7 +280,12 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
shell.printfln(F(" [%02d] (Pub) topic=%s payload=%s (pid %d)"), message.id_, topic, content->payload.c_str(), message.packet_id_);
|
||||
}
|
||||
} else {
|
||||
shell.printfln(F(" [%02d] (Pub) topic=%s payload=%s (pid %d, retry #%d)"), message.id_, topic, content->payload.c_str(), message.packet_id_, message.retry_count_);
|
||||
shell.printfln(F(" [%02d] (Pub) topic=%s payload=%s (pid %d, retry #%d)"),
|
||||
message.id_,
|
||||
topic,
|
||||
content->payload.c_str(),
|
||||
message.packet_id_,
|
||||
message.retry_count_);
|
||||
}
|
||||
} else {
|
||||
// Subscribe messages
|
||||
@@ -327,15 +347,9 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
||||
return; // invalid topic name
|
||||
}
|
||||
cmd_only++; // skip the /
|
||||
int8_t id = -1;
|
||||
// check for hcx/ prefix, commented out, this is now in command::call
|
||||
// if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') {
|
||||
// id = cmd_only[2] - '0';
|
||||
// cmd_only += 4;
|
||||
// }
|
||||
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s, id = %d"), mf.device_type_, topic, cmd_only, message, id);
|
||||
if (!Command::call(mf.device_type_, cmd_only, message, id)) {
|
||||
LOG_ERROR(F("No matching cmd (%s) in topic %s, id %d, or invalid data"), cmd_only, topic, id);
|
||||
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s), mf.device_type_, topic, cmd_only, message);
|
||||
if (!Command::call(mf.device_type_, cmd_only, message)) {
|
||||
LOG_ERROR(F("No matching cmd (%s) in topic %s, or invalid data"), cmd_only, topic);
|
||||
Mqtt::publish(F_(response), "unknown");
|
||||
}
|
||||
return;
|
||||
@@ -367,8 +381,8 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
||||
bool cmd_known = false;
|
||||
JsonVariant data = doc["data"];
|
||||
|
||||
if (data.is<char *>()) {
|
||||
cmd_known = Command::call(mf.device_type_, command, data.as<char *>(), n);
|
||||
if (data.is<const char *>()) {
|
||||
cmd_known = Command::call(mf.device_type_, command, data.as<const char *>(), n);
|
||||
} else if (data.is<int>()) {
|
||||
char data_str[10];
|
||||
cmd_known = Command::call(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as<int>()), n);
|
||||
@@ -400,7 +414,10 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
||||
|
||||
// print all the topics related to a specific device type
|
||||
void Mqtt::show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type) {
|
||||
if (std::count_if(mqtt_subfunctions_.cbegin(), mqtt_subfunctions_.cend(), [=](MQTTSubFunction const & mqtt_subfunction) { return device_type == mqtt_subfunction.device_type_; }) == 0) {
|
||||
if (std::count_if(mqtt_subfunctions_.cbegin(),
|
||||
mqtt_subfunctions_.cend(),
|
||||
[=](MQTTSubFunction const & mqtt_subfunction) { return device_type == mqtt_subfunction.device_type_; })
|
||||
== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -626,7 +643,7 @@ void Mqtt::on_connect() {
|
||||
doc["ip"] = WiFi.localIP().toString();
|
||||
}
|
||||
#endif
|
||||
publish(F_(info), doc.as<JsonObject>());
|
||||
publish(F_(info), doc.as<JsonObject>()); // topic called "info"
|
||||
|
||||
// create the EMS-ESP device in HA, which is MQTT retained
|
||||
if (ha_enabled()) {
|
||||
@@ -670,6 +687,7 @@ void Mqtt::ha_status() {
|
||||
// doc["json_attr_t"] = FJSON("~/heartbeat"); // store also as HA attributes
|
||||
doc["stat_t"] = FJSON("~/heartbeat");
|
||||
doc["name"] = FJSON("EMS-ESP status");
|
||||
doc["ic"] = F_(icondevice);
|
||||
doc["val_tpl"] = FJSON("{{value_json['status']}}");
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
@@ -684,18 +702,36 @@ void Mqtt::ha_status() {
|
||||
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/system/config"), mqtt_base_.c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
// create the sensors
|
||||
// must match the MQTT payload keys
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("WiFi strength"), EMSdevice::DeviceType::SYSTEM, F("rssi"));
|
||||
// create the sensors - must match the MQTT payload keys
|
||||
if (!EMSESP::system_.ethernet_connected()) {
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("WiFi RSSI"), EMSdevice::DeviceType::SYSTEM, F("rssi"), DeviceValueUOM::DBM);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("WiFi strength"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("wifistrength"),
|
||||
DeviceValueUOM::PERCENT);
|
||||
}
|
||||
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime"), EMSdevice::DeviceType::SYSTEM, F("uptime"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime (sec)"), EMSdevice::DeviceType::SYSTEM, F("uptime_sec"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# MQTT fails"), EMSdevice::DeviceType::SYSTEM, F("mqttfails"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx received"), EMSdevice::DeviceType::SYSTEM, F("rxreceived"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx reads"), EMSdevice::DeviceType::SYSTEM, F("txread"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx writes"), EMSdevice::DeviceType::SYSTEM, F("txwrite"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"));
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("Uptime (sec)"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("uptime_sec"),
|
||||
DeviceValueUOM::SECONDS);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"), DeviceValueUOM::KB);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# MQTT fails"), EMSdevice::DeviceType::SYSTEM, F("mqttfails"), DeviceValueUOM::NUM);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("# Rx received"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("rxreceived"),
|
||||
DeviceValueUOM::NUM);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"), DeviceValueUOM::NUM);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx reads"), EMSdevice::DeviceType::SYSTEM, F("txread"), DeviceValueUOM::NUM);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx writes"), EMSdevice::DeviceType::SYSTEM, F("txwrite"), DeviceValueUOM::NUM);
|
||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"), DeviceValueUOM::NUM);
|
||||
}
|
||||
|
||||
// add sub or pub task to the queue.
|
||||
@@ -851,7 +887,13 @@ void Mqtt::process_queue() {
|
||||
|
||||
// else try and publish it
|
||||
uint16_t packet_id = mqttClient_->publish(topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_);
|
||||
LOG_DEBUG(F("Publishing topic %s (#%02d, retain=%d, try#%d, size %d, pid %d)"), topic, mqtt_message.id_, message->retain, mqtt_message.retry_count_ + 1, message->payload.size(), packet_id);
|
||||
LOG_DEBUG(F("Publishing topic %s (#%02d, retain=%d, try#%d, size %d, pid %d)"),
|
||||
topic,
|
||||
mqtt_message.id_,
|
||||
message->retain,
|
||||
mqtt_message.retry_count_ + 1,
|
||||
message->payload.size(),
|
||||
packet_id);
|
||||
|
||||
if (packet_id == 0) {
|
||||
// it failed. if we retried n times, give up. remove from queue
|
||||
@@ -883,11 +925,6 @@ void Mqtt::process_queue() {
|
||||
|
||||
// HA config for a sensor and binary_sensor entity
|
||||
// entity must match the key/value pair in the *_data topic
|
||||
// e.g. homeassistant/sensor/ems-esp32/thermostat_hc1_seltemp/config
|
||||
// with:
|
||||
// {"uniq_id":"thermostat_hc1_seltemp","stat_t":"ems-esp32/thermostat_data","name":"Thermostat hc1 Setpoint room temperature",
|
||||
// "val_tpl":"{{value_json.hc1.seltemp}}","unit_of_meas":"°C","ic":"mdi:temperature-celsius","dev":{"ids":["ems-esp-thermostat"]}}
|
||||
//
|
||||
// note: some string copying here into chars, it looks messy but does help with heap fragmentation issues
|
||||
void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice::DeviceValueType
|
||||
uint8_t tag, // EMSdevice::DeviceValueTAG
|
||||
@@ -900,10 +937,23 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
||||
return;
|
||||
}
|
||||
|
||||
// bool have_tag = !EMSdevice::tag_to_string(tag).empty() && (device_type != EMSdevice::DeviceType::BOILER); // ignore boiler
|
||||
bool have_tag = !EMSdevice::tag_to_string(tag).empty();
|
||||
|
||||
// nested_format is 1 if nested, otherwise 2 for single topics
|
||||
bool is_nested;
|
||||
if (device_type == EMSdevice::DeviceType::BOILER) {
|
||||
is_nested = false; // boiler never uses nested
|
||||
} else {
|
||||
is_nested = (nested_format_ == 1);
|
||||
}
|
||||
|
||||
char device_name[50];
|
||||
strlcpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
|
||||
|
||||
bool have_tag = !EMSdevice::tag_to_string(tag).empty() && (device_type != EMSdevice::DeviceType::BOILER); // ignore boiler
|
||||
bool is_nested = (nested_format_ == 1) || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested
|
||||
doc["~"] = mqtt_base_;
|
||||
|
||||
// create entity by add the tag if present, seperating with a .
|
||||
char new_entity[50];
|
||||
@@ -913,10 +963,6 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
||||
snprintf_P(new_entity, sizeof(new_entity), PSTR("%s"), uuid::read_flash_string(entity).c_str());
|
||||
}
|
||||
|
||||
// device name
|
||||
char device_name[50];
|
||||
strlcpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
|
||||
|
||||
// build unique identifier which will be used in the topic
|
||||
// and replacing all . with _ as not to break HA
|
||||
std::string uniq(50, '\0');
|
||||
@@ -924,14 +970,12 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
||||
std::replace(uniq.begin(), uniq.end(), '.', '_');
|
||||
doc["uniq_id"] = uniq;
|
||||
|
||||
doc["~"] = mqtt_base_;
|
||||
|
||||
// state topic
|
||||
char stat_t[MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("~/%s"), tag_to_topic(device_type, tag).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
// name
|
||||
// name = <device> <tag> <name>
|
||||
char new_name[80];
|
||||
if (have_tag) {
|
||||
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s %s"), device_name, EMSdevice::tag_to_string(tag).c_str(), uuid::read_flash_string(name).c_str());
|
||||
@@ -942,6 +986,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
||||
doc["name"] = new_name;
|
||||
|
||||
// value template
|
||||
// if its nested mqtt format then use the appended entity name, otherwise take the original
|
||||
char val_tpl[50];
|
||||
if (is_nested) {
|
||||
snprintf_P(val_tpl, sizeof(val_tpl), PSTR("{{value_json.%s}}"), new_entity);
|
||||
@@ -966,18 +1011,47 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
||||
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
|
||||
|
||||
// unit of measure and map the HA icon
|
||||
if (uom != DeviceValueUOM::NONE && uom != DeviceValueUOM::PUMP) {
|
||||
if (uom != DeviceValueUOM::NONE) {
|
||||
doc["unit_of_meas"] = EMSdevice::uom_to_string(uom);
|
||||
}
|
||||
|
||||
switch (uom) {
|
||||
case DeviceValueUOM::DEGREES:
|
||||
doc["ic"] = F_(icontemperature);
|
||||
doc["ic"] = F_(icondegrees);
|
||||
break;
|
||||
case DeviceValueUOM::PERCENT:
|
||||
doc["ic"] = F_(iconpercent);
|
||||
break;
|
||||
case DeviceValueUOM::PUMP:
|
||||
doc["ic"] = F_(iconpump);
|
||||
case DeviceValueUOM::SECONDS:
|
||||
case DeviceValueUOM::MINUTES:
|
||||
case DeviceValueUOM::HOURS:
|
||||
doc["ic"] = F_(icontime);
|
||||
break;
|
||||
case DeviceValueUOM::KB:
|
||||
doc["ic"] = F_(iconkb);
|
||||
break;
|
||||
case DeviceValueUOM::LMIN:
|
||||
doc["ic"] = F_(iconlmin);
|
||||
break;
|
||||
case DeviceValueUOM::WH:
|
||||
case DeviceValueUOM::KWH:
|
||||
doc["ic"] = F_(iconkwh);
|
||||
break;
|
||||
case DeviceValueUOM::UA:
|
||||
doc["ic"] = F_(iconua);
|
||||
break;
|
||||
case DeviceValueUOM::BAR:
|
||||
doc["ic"] = F_(iconbar);
|
||||
break;
|
||||
case DeviceValueUOM::W:
|
||||
case DeviceValueUOM::KW:
|
||||
doc["ic"] = F_(iconkw);
|
||||
break;
|
||||
case DeviceValueUOM::DBM:
|
||||
doc["ic"] = F_(icondbm);
|
||||
break;
|
||||
case DeviceValueUOM::NUM:
|
||||
doc["ic"] = F_(iconnum);
|
||||
break;
|
||||
case DeviceValueUOM::NONE:
|
||||
default:
|
||||
@@ -997,7 +1071,6 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
||||
ids.add(ha_device);
|
||||
}
|
||||
|
||||
doc.shrinkToFit();
|
||||
publish_ha(topic, doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
|
||||
11
src/mqtt.h
11
src/mqtt.h
@@ -106,7 +106,12 @@ class Mqtt {
|
||||
static void publish_ha(const std::string & topic, const JsonObject & payload);
|
||||
static void publish_ha(const __FlashStringHelper * topic, const JsonObject & payload);
|
||||
|
||||
static void publish_mqtt_ha_sensor(uint8_t type, uint8_t tag, const __FlashStringHelper * name, const uint8_t device_type, const __FlashStringHelper * entity, const uint8_t uom = 0);
|
||||
static void publish_mqtt_ha_sensor(uint8_t type,
|
||||
uint8_t tag,
|
||||
const __FlashStringHelper * name,
|
||||
const uint8_t device_type,
|
||||
const __FlashStringHelper * entity,
|
||||
const uint8_t uom = 0);
|
||||
static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t tag = 0);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
||||
@@ -140,6 +145,10 @@ class Mqtt {
|
||||
return mqtt_base_;
|
||||
}
|
||||
|
||||
static uint16_t publish_count() {
|
||||
return mqtt_message_id_;
|
||||
}
|
||||
|
||||
static uint32_t publish_fails() {
|
||||
return mqtt_publish_fails_;
|
||||
}
|
||||
|
||||
126
src/system.cpp
126
src/system.cpp
@@ -63,35 +63,55 @@ bool System::command_send(const char * value, const int8_t id) {
|
||||
|
||||
// fetch device values
|
||||
bool System::command_fetch(const char * value, const int8_t id) {
|
||||
LOG_INFO(F("Requesting data from EMS devices"));
|
||||
EMSESP::fetch_device_values();
|
||||
std::string value_s(14, '\0');
|
||||
if (Helpers::value2string(value, value_s)) {
|
||||
if (value_s == "all") {
|
||||
LOG_INFO(F("Requesting data from EMS devices"));
|
||||
EMSESP::fetch_device_values();
|
||||
return true;
|
||||
} else if (value_s == "boiler") {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::BOILER);
|
||||
return true;
|
||||
} else if (value_s == "thermostat") {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::THERMOSTAT);
|
||||
return true;
|
||||
} else if (value_s == "solar") {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::SOLAR);
|
||||
return true;
|
||||
} else if (value_s == "mixer") {
|
||||
EMSESP::fetch_device_values_type(EMSdevice::DeviceType::MIXER);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::fetch_device_values(); // default if no name or id is given
|
||||
return true;
|
||||
}
|
||||
|
||||
// mqtt publish
|
||||
bool System::command_publish(const char * value, const int8_t id) {
|
||||
std::string ha(14, '\0');
|
||||
if (Helpers::value2string(value, ha)) {
|
||||
if (ha == "ha") {
|
||||
std::string value_s(14, '\0');
|
||||
if (Helpers::value2string(value, value_s)) {
|
||||
if (value_s == "ha") {
|
||||
EMSESP::publish_all(true); // includes HA
|
||||
LOG_INFO(F("Publishing all data to MQTT, including HA configs"));
|
||||
return true;
|
||||
} else if (ha == "boiler") {
|
||||
} else if (value_s == "boiler") {
|
||||
EMSESP::publish_device_values(EMSdevice::DeviceType::BOILER);
|
||||
return true;
|
||||
} else if (ha == "thermostat") {
|
||||
} else if (value_s == "thermostat") {
|
||||
EMSESP::publish_device_values(EMSdevice::DeviceType::THERMOSTAT);
|
||||
return true;
|
||||
} else if (ha == "solar") {
|
||||
} else if (value_s == "solar") {
|
||||
EMSESP::publish_device_values(EMSdevice::DeviceType::SOLAR);
|
||||
return true;
|
||||
} else if (ha == "mixer") {
|
||||
} else if (value_s == "mixer") {
|
||||
EMSESP::publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||
return true;
|
||||
} else if (ha == "other") {
|
||||
} else if (value_s == "other") {
|
||||
EMSESP::publish_other_values();
|
||||
return true;
|
||||
} else if (ha == "dallassensor") {
|
||||
} else if (value_s == "dallassensor") {
|
||||
EMSESP::publish_sensor_values(true);
|
||||
return true;
|
||||
}
|
||||
@@ -393,7 +413,6 @@ void System::loop() {
|
||||
send_heartbeat();
|
||||
}
|
||||
|
||||
/*
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#if defined(EMSESP_DEBUG)
|
||||
static uint32_t last_memcheck_ = 0;
|
||||
@@ -403,7 +422,6 @@ void System::loop() {
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
*/
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -419,14 +437,6 @@ void System::show_mem(const char * note) {
|
||||
|
||||
// create the json for heartbeat
|
||||
bool System::heartbeat_json(JsonObject & doc) {
|
||||
int8_t rssi;
|
||||
if (!ethernet_connected_) {
|
||||
rssi = wifi_quality();
|
||||
if (rssi == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ems_status = EMSESP::bus_status();
|
||||
if (ems_status == EMSESP::BUS_STATUS_TX_ERRORS) {
|
||||
doc["status"] = FJSON("txerror");
|
||||
@@ -436,12 +446,9 @@ bool System::heartbeat_json(JsonObject & doc) {
|
||||
doc["status"] = FJSON("disconnected");
|
||||
}
|
||||
|
||||
if (!ethernet_connected_) {
|
||||
doc["rssi"] = rssi;
|
||||
}
|
||||
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
doc["uptime_sec"] = uuid::get_uptime_sec();
|
||||
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
|
||||
doc["uptime_sec"] = uuid::get_uptime_sec();
|
||||
doc["rxreceived"] = EMSESP::rxservice_.telegram_count();
|
||||
doc["rxfails"] = EMSESP::rxservice_.telegram_error_count();
|
||||
doc["txread"] = EMSESP::txservice_.telegram_read_count();
|
||||
@@ -453,15 +460,24 @@ bool System::heartbeat_json(JsonObject & doc) {
|
||||
if (EMSESP::dallas_enabled()) {
|
||||
doc["dallasfails"] = EMSESP::sensor_fails();
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
doc["freemem"] = ESP.getFreeHeap();
|
||||
doc["freemem"] = ESP.getFreeHeap() / 1000L; // kilobytes
|
||||
#endif
|
||||
|
||||
if (analog_enabled_) {
|
||||
doc["adc"] = analog_;
|
||||
}
|
||||
|
||||
return (doc.size() > 0);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!ethernet_connected_) {
|
||||
int8_t rssi = WiFi.RSSI();
|
||||
doc["rssi"] = rssi;
|
||||
doc["wifistrength"] = wifi_quality(rssi);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// send periodic MQTT message with system information
|
||||
@@ -599,14 +615,15 @@ void System::system_check() {
|
||||
// these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
|
||||
// no individual subscribe for pin command because id is needed
|
||||
void System::commands_init() {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, MqttSubFlag::FLAG_NOSUB);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch);
|
||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
|
||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set GPIO"), MqttSubFlag::FLAG_NOSUB);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"));
|
||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status"));
|
||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings"));
|
||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands"));
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run tests"));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -628,19 +645,12 @@ void System::led_monitor() {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the quality (Received Signal Strength Indicator) of the WiFi network as a %. Or -1 if disconnected.
|
||||
// Return the quality (Received Signal Strength Indicator) of the WiFi network as a %
|
||||
// High quality: 90% ~= -55dBm
|
||||
// Medium quality: 50% ~= -75dBm
|
||||
// Low quality: 30% ~= -85dBm
|
||||
// Unusable quality: 8% ~= -96dBm
|
||||
int8_t System::wifi_quality() {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
return 100;
|
||||
#else
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
return -1;
|
||||
}
|
||||
int32_t dBm = WiFi.RSSI();
|
||||
int8_t System::wifi_quality(int8_t dBm) {
|
||||
if (dBm <= -100) {
|
||||
return 0;
|
||||
}
|
||||
@@ -649,7 +659,6 @@ int8_t System::wifi_quality() {
|
||||
return 100;
|
||||
}
|
||||
return 2 * (dBm + 100);
|
||||
#endif
|
||||
}
|
||||
|
||||
// print users to console
|
||||
@@ -693,7 +702,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.printfln(F("WiFi: Connected"));
|
||||
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
|
||||
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
|
||||
shell.printfln(F("RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality());
|
||||
shell.printfln(F("RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
||||
shell.printfln(F("MAC address: %s"), WiFi.macAddress().c_str());
|
||||
shell.printfln(F("Hostname: %s"), WiFi.getHostname());
|
||||
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||
@@ -757,6 +766,11 @@ bool System::check_upgrade() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// list commands
|
||||
bool System::command_commands(const char * value, const int8_t id, JsonObject & json) {
|
||||
return Command::list(EMSdevice::DeviceType::SYSTEM, json);
|
||||
}
|
||||
|
||||
// export all settings to JSON text
|
||||
// e.g. http://ems-esp/api?device=system&cmd=settings
|
||||
// value and id are ignored
|
||||
@@ -767,8 +781,8 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
||||
node["version"] = EMSESP_APP_VERSION;
|
||||
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
||||
node = json.createNestedObject("WIFI");
|
||||
node["ssid"] = settings.ssid;
|
||||
node = json.createNestedObject("WIFI");
|
||||
// node["ssid"] = settings.ssid; // commented out - people don't like others to see this
|
||||
node["hostname"] = settings.hostname;
|
||||
node["static_ip_config"] = settings.staticIPConfig;
|
||||
JsonUtils::writeIP(node, "local_ip", settings.localIP);
|
||||
@@ -833,6 +847,7 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
node = json.createNestedObject("Settings");
|
||||
node["tx_mode"] = settings.tx_mode;
|
||||
node["tx_delay"] = settings.tx_delay;
|
||||
node["ems_bus_id"] = settings.ems_bus_id;
|
||||
node["syslog_enabled"] = settings.syslog_enabled;
|
||||
node["syslog_level"] = settings.syslog_level;
|
||||
@@ -848,7 +863,7 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
||||
node["dallas_parasite"] = settings.dallas_parasite;
|
||||
node["led_gpio"] = settings.led_gpio;
|
||||
node["hide_led"] = settings.hide_led;
|
||||
node["api_enabled"] = settings.api_enabled;
|
||||
node["notoken_api"] = settings.notoken_api;
|
||||
node["analog_enabled"] = settings.analog_enabled;
|
||||
node["pbutton_gpio"] = settings.pbutton_gpio;
|
||||
node["board_profile"] = settings.board_profile;
|
||||
@@ -860,11 +875,6 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
||||
// export status information including some basic settings
|
||||
// e.g. http://ems-esp/api?device=system&cmd=info
|
||||
bool System::command_info(const char * value, const int8_t id, JsonObject & json) {
|
||||
|
||||
if (id == 0) {
|
||||
return EMSESP::system_.heartbeat_json(json);
|
||||
}
|
||||
|
||||
JsonObject node;
|
||||
|
||||
node = json.createNestedObject("System");
|
||||
@@ -872,7 +882,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
|
||||
node["version"] = EMSESP_APP_VERSION;
|
||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["freemem"] = ESP.getFreeHeap();
|
||||
node["freemem"] = ESP.getFreeHeap() / 1000L; // kilobytes
|
||||
#endif
|
||||
|
||||
node = json.createNestedObject("Status");
|
||||
@@ -900,10 +910,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
|
||||
node["rx line quality"] = EMSESP::rxservice_.quality();
|
||||
node["tx line quality"] = EMSESP::txservice_.quality();
|
||||
if (Mqtt::enabled()) {
|
||||
node["#MQTT publishes"] = Mqtt::publish_count();
|
||||
node["#MQTT publish fails"] = Mqtt::publish_fails();
|
||||
}
|
||||
if (EMSESP::dallas_enabled()) {
|
||||
node["#dallas sensors"] = EMSESP::sensor_devices().size();
|
||||
node["#dallas reads"] = EMSESP::sensor_reads();
|
||||
node["#dallas fails"] = EMSESP::sensor_fails();
|
||||
}
|
||||
}
|
||||
@@ -923,8 +935,8 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
|
||||
}
|
||||
if (EMSESP::sensor_devices().size()) {
|
||||
JsonObject obj = devices2.createNestedObject();
|
||||
obj["type"] = F("Dallassensor");
|
||||
obj["name"] = F("Dallassensor");
|
||||
obj["type"] = F_(Dallassensor);
|
||||
obj["name"] = F_(Dallassensor);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -54,6 +54,7 @@ class System {
|
||||
static bool command_fetch(const char * value, const int8_t id);
|
||||
static bool command_info(const char * value, const int8_t id, JsonObject & json);
|
||||
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
|
||||
static bool command_commands(const char * value, const int8_t id, JsonObject & json);
|
||||
#if defined(EMSESP_DEBUG)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
@@ -138,7 +139,7 @@ class System {
|
||||
void system_check();
|
||||
void measure_analog();
|
||||
|
||||
int8_t wifi_quality();
|
||||
int8_t wifi_quality(int8_t dBm);
|
||||
|
||||
bool system_healthy_ = false;
|
||||
uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly
|
||||
|
||||
@@ -22,15 +22,19 @@
|
||||
namespace emsesp {
|
||||
|
||||
// CRC lookup table with poly 12 for faster checking
|
||||
const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38,
|
||||
0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72,
|
||||
0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC,
|
||||
0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBE, 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6,
|
||||
0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE, 0x19, 0x1B, 0x1D, 0x1F, 0x11, 0x13, 0x15, 0x17, 0x09, 0x0B, 0x0D, 0x0F, 0x01, 0x03, 0x05, 0x07, 0x39,
|
||||
0x3B, 0x3D, 0x3F, 0x31, 0x33, 0x35, 0x37, 0x29, 0x2B, 0x2D, 0x2F, 0x21, 0x23, 0x25, 0x27, 0x59, 0x5B, 0x5D, 0x5F, 0x51, 0x53, 0x55, 0x57, 0x49, 0x4B, 0x4D, 0x4F, 0x41, 0x43,
|
||||
0x45, 0x47, 0x79, 0x7B, 0x7D, 0x7F, 0x71, 0x73, 0x75, 0x77, 0x69, 0x6B, 0x6D, 0x6F, 0x61, 0x63, 0x65, 0x67, 0x99, 0x9B, 0x9D, 0x9F, 0x91, 0x93, 0x95, 0x97, 0x89, 0x8B, 0x8D,
|
||||
0x8F, 0x81, 0x83, 0x85, 0x87, 0xB9, 0xBB, 0xBD, 0xBF, 0xB1, 0xB3, 0xB5, 0xB7, 0xA9, 0xAB, 0xAD, 0xAF, 0xA1, 0xA3, 0xA5, 0xA7, 0xD9, 0xDB, 0xDD, 0xDF, 0xD1, 0xD3, 0xD5, 0xD7,
|
||||
0xC9, 0xCB, 0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xE1, 0xE3, 0xE5, 0xE7};
|
||||
const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, 0x26,
|
||||
0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E,
|
||||
0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76,
|
||||
0x78, 0x7A, 0x7C, 0x7E, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E,
|
||||
0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBE, 0xC0, 0xC2, 0xC4, 0xC6,
|
||||
0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
|
||||
0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE, 0x19, 0x1B, 0x1D, 0x1F, 0x11, 0x13, 0x15, 0x17, 0x09, 0x0B, 0x0D, 0x0F,
|
||||
0x01, 0x03, 0x05, 0x07, 0x39, 0x3B, 0x3D, 0x3F, 0x31, 0x33, 0x35, 0x37, 0x29, 0x2B, 0x2D, 0x2F, 0x21, 0x23, 0x25, 0x27,
|
||||
0x59, 0x5B, 0x5D, 0x5F, 0x51, 0x53, 0x55, 0x57, 0x49, 0x4B, 0x4D, 0x4F, 0x41, 0x43, 0x45, 0x47, 0x79, 0x7B, 0x7D, 0x7F,
|
||||
0x71, 0x73, 0x75, 0x77, 0x69, 0x6B, 0x6D, 0x6F, 0x61, 0x63, 0x65, 0x67, 0x99, 0x9B, 0x9D, 0x9F, 0x91, 0x93, 0x95, 0x97,
|
||||
0x89, 0x8B, 0x8D, 0x8F, 0x81, 0x83, 0x85, 0x87, 0xB9, 0xBB, 0xBD, 0xBF, 0xB1, 0xB3, 0xB5, 0xB7, 0xA9, 0xAB, 0xAD, 0xAF,
|
||||
0xA1, 0xA3, 0xA5, 0xA7, 0xD9, 0xDB, 0xDD, 0xDF, 0xD1, 0xD3, 0xD5, 0xD7, 0xC9, 0xCB, 0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7,
|
||||
0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xE1, 0xE3, 0xE5, 0xE7};
|
||||
|
||||
uint32_t EMSbus::last_bus_activity_ = 0; // timestamp of last time a valid Rx came in
|
||||
bool EMSbus::bus_connected_ = false; // start assuming the bus hasn't been connected
|
||||
@@ -55,7 +59,13 @@ uint8_t EMSbus::calculate_crc(const uint8_t * data, const uint8_t length) {
|
||||
|
||||
// creates a telegram object
|
||||
// stores header in separate member objects and the rest in the message_data block
|
||||
Telegram::Telegram(const uint8_t operation, const uint8_t src, const uint8_t dest, const uint16_t type_id, const uint8_t offset, const uint8_t * data, const uint8_t message_length)
|
||||
Telegram::Telegram(const uint8_t operation,
|
||||
const uint8_t src,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
const uint8_t * data,
|
||||
const uint8_t message_length)
|
||||
: operation(operation)
|
||||
, src(src)
|
||||
, dest(dest)
|
||||
@@ -192,7 +202,8 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
// if we're watching and "raw" print out actual telegram as bytes to the console
|
||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) {
|
||||
uint16_t trace_watch_id = EMSESP::watch_id();
|
||||
if ((trace_watch_id == WATCH_ID_NONE) || (type_id == trace_watch_id) || ((trace_watch_id < 0x80) && ((src == trace_watch_id) || (dest == trace_watch_id)))) {
|
||||
if ((trace_watch_id == WATCH_ID_NONE) || (type_id == trace_watch_id)
|
||||
|| ((trace_watch_id < 0x80) && ((src == trace_watch_id) || (dest == trace_watch_id)))) {
|
||||
LOG_NOTICE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
|
||||
} else if (EMSESP::trace_raw()) {
|
||||
LOG_TRACE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
|
||||
@@ -349,7 +360,10 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
|
||||
length++; // add one since we want to now include the CRC
|
||||
|
||||
LOG_DEBUG(F("Sending %s Tx [#%d], telegram: %s"), (telegram->operation == Telegram::Operation::TX_WRITE) ? F("write") : F("read"), tx_telegram.id_, Helpers::data_to_hex(telegram_raw, length).c_str());
|
||||
LOG_DEBUG(F("Sending %s Tx [#%d], telegram: %s"),
|
||||
(telegram->operation == Telegram::Operation::TX_WRITE) ? F("write") : F("read"),
|
||||
tx_telegram.id_,
|
||||
Helpers::data_to_hex(telegram_raw, length).c_str());
|
||||
|
||||
set_post_send_query(tx_telegram.validateid_);
|
||||
// send the telegram to the UART Tx
|
||||
@@ -389,7 +403,14 @@ void TxService::send_telegram(const uint8_t * data, const uint8_t length) {
|
||||
}
|
||||
*/
|
||||
|
||||
void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validateid, const bool front) {
|
||||
void TxService::add(const uint8_t operation,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validateid,
|
||||
const bool front) {
|
||||
auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
@@ -546,7 +567,10 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
reset_retry_count(); // give up
|
||||
increment_telegram_fail_count(); // another Tx fail
|
||||
|
||||
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), MAXIMUM_TX_RETRIES), telegram_last_->to_string().c_str();
|
||||
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"),
|
||||
(operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"),
|
||||
MAXIMUM_TX_RETRIES),
|
||||
telegram_last_->to_string().c_str();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -566,9 +590,12 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram_last_), true, get_post_send_query());
|
||||
}
|
||||
|
||||
uint16_t TxService::read_next_tx() {
|
||||
uint16_t TxService::read_next_tx(uint8_t offset) {
|
||||
// add to the top/front of the queue
|
||||
uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
|
||||
if (telegram_last_->offset != offset) {
|
||||
return 0;
|
||||
}
|
||||
add(Telegram::Operation::TX_READ, telegram_last_->dest, telegram_last_->type_id, telegram_last_->offset + 25, message_data, 1, 0, true);
|
||||
return telegram_last_->type_id;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,13 @@ namespace emsesp {
|
||||
|
||||
class Telegram {
|
||||
public:
|
||||
Telegram(const uint8_t operation, const uint8_t src, const uint8_t dest, const uint16_t type_id, const uint8_t offset, const uint8_t * message_data, const uint8_t message_length);
|
||||
Telegram(const uint8_t operation,
|
||||
const uint8_t src,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
const uint8_t * message_data,
|
||||
const uint8_t message_length);
|
||||
~Telegram() = default;
|
||||
|
||||
const uint8_t operation; // is Operation mode
|
||||
@@ -262,7 +268,14 @@ class TxService : public EMSbus {
|
||||
|
||||
void start();
|
||||
void send();
|
||||
void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validateid, const bool front = false);
|
||||
void add(const uint8_t operation,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validateid,
|
||||
const bool front = false);
|
||||
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false);
|
||||
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
|
||||
void send_raw(const char * telegram_data);
|
||||
@@ -270,7 +283,7 @@ class TxService : public EMSbus {
|
||||
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
|
||||
bool is_last_tx(const uint8_t src, const uint8_t dest) const;
|
||||
uint16_t post_send_query();
|
||||
uint16_t read_next_tx();
|
||||
uint16_t read_next_tx(uint8_t offset);
|
||||
|
||||
uint8_t retry_count() const {
|
||||
return retry_count_;
|
||||
|
||||
@@ -37,7 +37,8 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
|
||||
// add some data
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A, 0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
|
||||
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
||||
@@ -46,12 +47,13 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
|
||||
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(command, "general2") == 0) {
|
||||
if (strcmp(command, "310") == 0) {
|
||||
EMSESP::logger().info(F("Testing GB072/RC310..."));
|
||||
|
||||
add_device(0x08, 123); // GB072
|
||||
@@ -59,7 +61,8 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
|
||||
// add some data
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A, 0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
|
||||
uart_telegram({0x08, 0x90, 0x33, 0x00, 0x23, 0x24});
|
||||
@@ -68,7 +71,11 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
|
||||
// Thermostat 0x2A5 for HC1
|
||||
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54, 03, 03, 01, 01, 0x54, 02, 0xA8, 00, 00, 0x11, 01, 03, 0xFF, 0xFF, 00});
|
||||
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54,
|
||||
03, 03, 01, 01, 0x54, 02, 0xA8, 00, 00, 0x11, 01, 03, 0xFF, 0xFF, 00});
|
||||
|
||||
// RC300WWmode2(0x31D), data: 00 00 09 07
|
||||
uart_telegram({0x10, 00, 0xFF, 00, 02, 0x1D, 00, 00, 0x09, 0x07});
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -123,7 +130,8 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A, 0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
@@ -150,9 +158,11 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
add_device(0x30, 163); // SM100
|
||||
|
||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||
uart_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
uart_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
|
||||
uart_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
uart_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
|
||||
uart_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||
|
||||
@@ -320,35 +330,45 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
run_test("gateway");
|
||||
}
|
||||
|
||||
if (command == "310") {
|
||||
shell.printfln(F("Testing RC310..."));
|
||||
run_test("310");
|
||||
shell.invoke_command("show devices");
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
}
|
||||
|
||||
if (command == "web") {
|
||||
shell.printfln(F("Testing Web..."));
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
// test call
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
(void)emsesp::Command::call(EMSdevice::DeviceType::BOILER, "info", nullptr, -1, json);
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Serial.print(COLOR_YELLOW);
|
||||
if (json.size() != 0) {
|
||||
serializeJson(doc, Serial);
|
||||
}
|
||||
shell.println();
|
||||
Serial.print(COLOR_RESET);
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc(8000); // some absurb high number
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
doc.clear();
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
Command::call(emsdevice->device_type(), "info", nullptr, -1, json);
|
||||
|
||||
Serial.print(COLOR_YELLOW);
|
||||
if (json.size() != 0) {
|
||||
serializeJson(doc, Serial);
|
||||
}
|
||||
shell.println();
|
||||
Serial.print(COLOR_RESET);
|
||||
|
||||
|
||||
doc.clear();
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
emsdevice->generate_values_json_web(root);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Serial.print(COLOR_BRIGHT_MAGENTA);
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(COLOR_RESET);
|
||||
Serial.println();
|
||||
Serial.print("** memoryUsage=");
|
||||
Serial.print(doc.memoryUsage());
|
||||
@@ -357,10 +377,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
Serial.print(" measureJson=");
|
||||
Serial.print(measureJson(doc));
|
||||
Serial.println(" **");
|
||||
Serial.print(COLOR_RESET);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -477,7 +496,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
add_device(0x18, 202); // Bosch TC100 - https://github.com/emsesp/EMS-ESP/issues/474
|
||||
|
||||
// 0x0A
|
||||
uart_telegram({0x98, 0x0B, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
uart_telegram({0x98, 0x0B, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
}
|
||||
|
||||
if (command == "solar") {
|
||||
@@ -507,9 +527,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
|
||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
|
||||
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||
|
||||
@@ -588,7 +610,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
|
||||
// RCPLUSStatusMessage_HC1(0x01A5)
|
||||
// 98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 (no CRC)
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
uart_telegram("98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03"); // without CRC
|
||||
uart_telegram_withCRC("98 00 FF 00 01 A6 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 6B"); // with CRC
|
||||
@@ -617,7 +640,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
|
||||
// fake telegrams. length includes CRC
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A, 0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
|
||||
uart_telegram({0x08, 0x97, 0x33, 0x00, 0x23, 0x24});
|
||||
@@ -643,10 +667,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
|
||||
// Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00
|
||||
// 0x1A5 test ems+
|
||||
uart_telegram({0x10, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xD7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x84, 0x01, 0x01, 0x03, 0x01, 0x84, 0x01, 0xF1, 0x00, 0x00, 0x11, 0x01, 0x00, 0x08, 0x63, 0x00});
|
||||
uart_telegram({0x10, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xD7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x84,
|
||||
0x01, 0x01, 0x03, 0x01, 0x84, 0x01, 0xF1, 0x00, 0x00, 0x11, 0x01, 0x00, 0x08, 0x63, 0x00});
|
||||
|
||||
// setting temp from 21.5 to 19.9C
|
||||
uart_telegram({0x10, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xC7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x84, 0x01, 0x01, 0x03, 0x01, 0x84, 0x01, 0xF1, 0x00, 0x00, 0x11, 0x01, 0x00, 0x08, 0x63, 0x00});
|
||||
uart_telegram({0x10, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xC7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x84,
|
||||
0x01, 0x01, 0x03, 0x01, 0x84, 0x01, 0xF1, 0x00, 0x00, 0x11, 0x01, 0x00, 0x08, 0x63, 0x00});
|
||||
|
||||
// Thermostat -> Boiler, UBAFlags(0x35), telegram: 17 08 35 00 11 00 (#data=2)
|
||||
uart_telegram({0x17, 0x08, 0x35, 0x00, 0x11, 0x00});
|
||||
@@ -655,10 +681,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
uart_telegram({0x17, 0x08, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
|
||||
// Thermostat -> Me, RC20Set(0xA8), telegram: 17 0B A8 00 01 00 FF F6 01 06 00 01 0D 01 00 FF FF 01 02 02 02 00 00 05 1F 05 1F 02 0E 00 FF (#data=27)
|
||||
uart_telegram({0x17, 0x0B, 0xA8, 0x00, 0x01, 0x00, 0xFF, 0xF6, 0x01, 0x06, 0x00, 0x01, 0x0D, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x05, 0x1F, 0x05, 0x1F, 0x02, 0x0E, 0x00, 0xFF});
|
||||
uart_telegram({0x17, 0x0B, 0xA8, 0x00, 0x01, 0x00, 0xFF, 0xF6, 0x01, 0x06, 0x00, 0x01, 0x0D, 0x01, 0x00, 0xFF,
|
||||
0xFF, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x05, 0x1F, 0x05, 0x1F, 0x02, 0x0E, 0x00, 0xFF});
|
||||
|
||||
// Boiler(0x08) -> All(0x00), UBAMonitorWW(0x34), data: 36 01 A5 80 00 21 00 00 01 00 01 3E 8D 03 77 91 00 80 00
|
||||
uart_telegram({0x08, 0x00, 0x34, 0x00, 0x36, 0x01, 0xA5, 0x80, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x01, 0x3E, 0x8D, 0x03, 0x77, 0x91, 0x00, 0x80, 0x00});
|
||||
uart_telegram(
|
||||
{0x08, 0x00, 0x34, 0x00, 0x36, 0x01, 0xA5, 0x80, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x01, 0x3E, 0x8D, 0x03, 0x77, 0x91, 0x00, 0x80, 0x00});
|
||||
|
||||
// test 0x2A - DHWStatus3
|
||||
uart_telegram({0x88, 00, 0x2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xD2, 00, 00, 0x80, 00, 00, 01, 0x9D, 0x80, 0x00, 0x02, 0x79, 00});
|
||||
@@ -686,7 +714,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
EMSESP::send_write_request(0x91, 0x17, 0x00, t18, sizeof(t18), 0x00);
|
||||
|
||||
// TX - send EMS+
|
||||
const uint8_t t13[] = {0x90, 0x0B, 0xFF, 00, 01, 0xBA, 00, 0x2E, 0x2A, 0x26, 0x1E, 0x03, 00, 0xFF, 0xFF, 05, 0x2A, 01, 0xE1, 0x20, 0x01, 0x0F, 05, 0x2A};
|
||||
const uint8_t t13[] = {0x90, 0x0B, 0xFF, 00, 01, 0xBA, 00, 0x2E, 0x2A, 0x26, 0x1E, 0x03,
|
||||
00, 0xFF, 0xFF, 05, 0x2A, 01, 0xE1, 0x20, 0x01, 0x0F, 05, 0x2A};
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_RAW, t13, sizeof(t13), 0);
|
||||
|
||||
// EMS+ Junkers read request
|
||||
@@ -791,7 +820,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
add_device(0x18, 157); // Bosch CR100 - https://github.com/emsesp/EMS-ESP/issues/355
|
||||
|
||||
// RCPLUSStatusMessage_HC1(0x01A5) - HC1
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
uart_telegram("98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03"); // without CRC
|
||||
uart_telegram_withCRC("98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 13"); // with CRC
|
||||
|
||||
@@ -858,7 +888,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
if (command == "rx2") {
|
||||
shell.printfln(F("Testing rx2..."));
|
||||
|
||||
uart_telegram({0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B, 0x73, 0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF});
|
||||
uart_telegram({0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B,
|
||||
0x73, 0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF});
|
||||
}
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP/issues/380#issuecomment-633663007
|
||||
@@ -897,6 +928,23 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
}
|
||||
|
||||
if (command == "api") {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
shell.printfln(F("Testing RESTful API..."));
|
||||
Mqtt::ha_enabled(false);
|
||||
run_test("general");
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/thermostat/seltemp");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
|
||||
request.url("/api/boiler/syspress");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// simulates a telegram in the Rx queue, but without the CRC which is added automatically
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define EMSESP_TEST_H
|
||||
|
||||
#include "emsesp.h"
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -35,8 +36,9 @@ namespace emsesp {
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
|
||||
// #define EMSESP_DEBUG_DEFAULT "ha"
|
||||
// #define EMSESP_DEBUG_DEFAULT "board_profile"
|
||||
#define EMSESP_DEBUG_DEFAULT "shower_alert"
|
||||
|
||||
// #define EMSESP_DEBUG_DEFAULT "shower_alert"
|
||||
// #define EMSESP_DEBUG_DEFAULT "310"
|
||||
#define EMSESP_DEBUG_DEFAULT "api"
|
||||
|
||||
class Test {
|
||||
public:
|
||||
|
||||
@@ -102,10 +102,10 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
|
||||
|
||||
uart_param_config(EMSUART_UART, &uart_config);
|
||||
uart_set_pin(EMSUART_UART, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
EMS_UART.int_ena.val = 0; // disable all intr.
|
||||
EMS_UART.int_clr.val = 0xFFFFFFFF; // clear all intr. flags
|
||||
EMS_UART.idle_conf.tx_brk_num = 10; // breaklength 10 bit
|
||||
drop_next_rx_ = true;
|
||||
EMS_UART.int_ena.val = 0; // disable all intr.
|
||||
EMS_UART.int_clr.val = 0xFFFFFFFF; // clear all intr. flags
|
||||
EMS_UART.idle_conf.tx_brk_num = 10; // breaklength 10 bit
|
||||
drop_next_rx_ = true;
|
||||
// EMS_UART.idle_conf.rx_idle_thrhd = 256;
|
||||
// EMS_UART.auto_baud.glitch_filt = 192;
|
||||
#if (EMSUART_UART != UART_NUM_2)
|
||||
@@ -140,7 +140,6 @@ void EMSuart::restart() {
|
||||
EMS_UART.int_ena.brk_det = 1; // activate only break
|
||||
EMS_UART.conf0.txd_brk = (tx_mode_ == EMS_TXMODE_HW) ? 1 : 0;
|
||||
portEXIT_CRITICAL(&mux_);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.1.0"
|
||||
#define EMSESP_PLATFORM "ESP32"
|
||||
#define EMSESP_APP_VERSION "3.1.1b8"
|
||||
|
||||
304
src/web/WebAPIService.cpp
Normal file
304
src/web/WebAPIService.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// SUrlParser from https://github.com/Mad-ness/simple-url-parser
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS
|
||||
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS
|
||||
server->addHandler(&_apiHandler);
|
||||
}
|
||||
|
||||
// GET /{device}
|
||||
// GET /{device}/{name}
|
||||
// GET /device={device}?cmd={name}?data={value}[?id={hc}
|
||||
void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
|
||||
std::string device("");
|
||||
std::string cmd("");
|
||||
int id = -1;
|
||||
std::string value("");
|
||||
|
||||
parse(request, device, cmd, id, value); // pass it defaults
|
||||
}
|
||||
|
||||
// For POSTS with an optional JSON body
|
||||
// HTTP_POST | HTTP_PUT | HTTP_PATCH
|
||||
// POST/PUT /{device}[/{hc}][/{name}]
|
||||
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
// extra the params from the json body
|
||||
if (not json.is<JsonObject>()) {
|
||||
webAPIService_get(request);
|
||||
return;
|
||||
}
|
||||
|
||||
// extract values from the json
|
||||
// these will be used as default values
|
||||
auto && body = json.as<JsonObject>();
|
||||
|
||||
std::string device = body["name"].as<std::string>(); // note this was called device in the v2
|
||||
std::string cmd = body["cmd"].as<std::string>();
|
||||
int id = -1;
|
||||
if (body.containsKey("id")) {
|
||||
id = body["id"];
|
||||
} else if (body.containsKey("hc")) {
|
||||
id = body["hc"];
|
||||
} else {
|
||||
id = -1;
|
||||
}
|
||||
|
||||
// make sure we have a value. There must always be a value
|
||||
if (!body.containsKey(F_(value))) {
|
||||
send_message_response(request, 400, "Problems parsing JSON"); // Bad Request
|
||||
return;
|
||||
}
|
||||
std::string value = body["value"].as<std::string>(); // always convert value to string
|
||||
|
||||
// now parse the URL. The URL is always leading and will overwrite anything provided in the json body
|
||||
parse(request, device, cmd, id, value); // pass it defaults
|
||||
}
|
||||
|
||||
// parse the URL looking for query or path parameters
|
||||
// reporting back any errors
|
||||
void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_s, std::string & cmd_s, int id, std::string & value_s) {
|
||||
// parse URL for the path names
|
||||
SUrlParser p;
|
||||
|
||||
p.parse(request->url().c_str());
|
||||
|
||||
// remove the /api from the path
|
||||
if (p.paths().front() == "api") {
|
||||
p.paths().erase(p.paths().begin());
|
||||
} else {
|
||||
return; // bad URL
|
||||
}
|
||||
|
||||
uint8_t device_type;
|
||||
int8_t id_n = -1; // default hc
|
||||
|
||||
// check for query parameters first
|
||||
// /device={device}?cmd={name}?data={value}[?id={hc}
|
||||
if (p.paths().size() == 0) {
|
||||
// get the device
|
||||
if (request->hasParam(F_(device))) {
|
||||
device_s = request->getParam(F_(device))->value().c_str();
|
||||
}
|
||||
|
||||
// get cmd
|
||||
if (request->hasParam(F_(cmd))) {
|
||||
cmd_s = request->getParam(F_(cmd))->value().c_str();
|
||||
}
|
||||
|
||||
// get data, which is optional. This is now replaced with the name 'value' in JSON body
|
||||
if (request->hasParam(F_(data))) {
|
||||
value_s = request->getParam(F_(data))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(value))) {
|
||||
value_s = request->getParam(F_(value))->value().c_str();
|
||||
}
|
||||
|
||||
// get id (or hc), which is optional
|
||||
if (request->hasParam(F_(id))) {
|
||||
id_n = Helpers::atoint(request->getParam(F_(id))->value().c_str());
|
||||
}
|
||||
if (request->hasParam("hc")) {
|
||||
id_n = Helpers::atoint(request->getParam("hc")->value().c_str());
|
||||
}
|
||||
} else {
|
||||
// parse paths and json data
|
||||
// /{device}[/{hc}][/{name}]
|
||||
// first param must be a valid device, which includes "system"
|
||||
device_s = p.paths().front();
|
||||
|
||||
auto num_paths = p.paths().size();
|
||||
if (num_paths == 1) {
|
||||
// if there are no more paths parameters, default to 'info'
|
||||
// cmd_s = "info_short";
|
||||
// check empty command in Command::find_command and set the default there!
|
||||
} else if (num_paths == 2) {
|
||||
cmd_s = p.paths()[1];
|
||||
} else if (num_paths > 2) {
|
||||
// check in Command::find_command makes prefix to TAG
|
||||
cmd_s = p.paths()[1] + "/" + p.paths()[2];
|
||||
}
|
||||
}
|
||||
// now go and validate everything
|
||||
|
||||
// device check
|
||||
if (device_s.empty()) {
|
||||
send_message_response(request, 422, "Missing device"); // Unprocessable Entity
|
||||
return;
|
||||
}
|
||||
device_type = EMSdevice::device_name_2_device_type(device_s.c_str());
|
||||
if (device_type == EMSdevice::DeviceType::UNKNOWN) {
|
||||
send_message_response(request, 422, "Invalid call"); // Unprocessable Entity
|
||||
return;
|
||||
}
|
||||
|
||||
// check that we have permissions first. We require authenticating on 1 or more of these conditions:
|
||||
// 1. any HTTP POSTs or PUTs
|
||||
// 2. a HTTP GET which has a 'data' parameter which is not empty (to keep v2 compatibility)
|
||||
auto method = request->method();
|
||||
bool have_data = !value_s.empty();
|
||||
bool admin_allowed;
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
admin_allowed = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
||||
});
|
||||
|
||||
if ((method != HTTP_GET) || ((method == HTTP_GET) && have_data)) {
|
||||
if (!admin_allowed) {
|
||||
send_message_response(request, 401, "Bad credentials"); // Unauthorized
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// now we have all the parameters go and execute the command
|
||||
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject json = response->getRoot();
|
||||
|
||||
bool ok = Command::call(device_type, cmd_s.c_str(), (have_data ? value_s.c_str() : nullptr), id_n, json);
|
||||
|
||||
// check for errors
|
||||
if (!ok) {
|
||||
send_message_response(request, 400, "Problems parsing elements"); // Bad Request
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json.size()) {
|
||||
send_message_response(request, 200, "OK"); // OK
|
||||
return;
|
||||
}
|
||||
|
||||
// send the json that came back from the command call
|
||||
response->setLength();
|
||||
request->send(response); // send json response
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Serial.print(COLOR_YELLOW);
|
||||
if (json.size() != 0) {
|
||||
serializeJsonPretty(json, Serial);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print(COLOR_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
// send a HTTP error back, with optional JSON body data
|
||||
void WebAPIService::send_message_response(AsyncWebServerRequest * request, uint16_t error_code, const char * error_message) {
|
||||
if (error_message == nullptr) {
|
||||
AsyncWebServerResponse * response = request->beginResponse(error_code); // just send the code
|
||||
request->send(response);
|
||||
} else {
|
||||
// build a return message and send it
|
||||
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
JsonObject json = response->getRoot();
|
||||
json["message"] = error_message;
|
||||
response->setCode(error_code);
|
||||
response->setLength();
|
||||
response->setContentType("application/json");
|
||||
request->send(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract only the path component from the passed URI and normalized it.
|
||||
* Ex. //one/two////three/// becomes /one/two/three
|
||||
*/
|
||||
std::string SUrlParser::path() {
|
||||
std::string s = "/"; // set up the beginning slash
|
||||
for (std::string & f : m_folders) {
|
||||
s += f;
|
||||
s += "/";
|
||||
}
|
||||
s.pop_back(); // deleting last letter, that is slash '/'
|
||||
return std::string(s);
|
||||
}
|
||||
|
||||
SUrlParser::SUrlParser(const char * uri) {
|
||||
parse(uri);
|
||||
}
|
||||
|
||||
bool SUrlParser::parse(const char * uri) {
|
||||
m_folders.clear();
|
||||
m_keysvalues.clear();
|
||||
enum Type { begin, folder, param, value };
|
||||
std::string s;
|
||||
|
||||
const char * c = uri;
|
||||
enum Type t = Type::begin;
|
||||
std::string last_param;
|
||||
|
||||
if (c != NULL || *c != '\0') {
|
||||
do {
|
||||
if (*c == '/') {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
t = Type::folder;
|
||||
} else if (*c == '?' && (t == Type::folder || t == Type::begin)) {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
t = Type::param;
|
||||
} else if (*c == '=' && (t == Type::param || t == Type::begin)) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
s.clear();
|
||||
t = Type::value;
|
||||
} else if (*c == '&' && (t == Type::value || t == Type::param || t == Type::begin)) {
|
||||
if (t == Type::value) {
|
||||
m_keysvalues[last_param] = s;
|
||||
} else if ((t == Type::param || t == Type::begin) && (s.length() > 0)) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
t = Type::param;
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() > 0) {
|
||||
if (t == Type::value) {
|
||||
m_keysvalues[last_param] = s;
|
||||
} else if (t == Type::folder || t == Type::begin) {
|
||||
m_folders.push_back(s);
|
||||
} else if (t == Type::param) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() == 0) {
|
||||
if (t == Type::param && last_param.length() > 0) {
|
||||
m_keysvalues[last_param] = "";
|
||||
}
|
||||
s.clear();
|
||||
} else {
|
||||
s += *c;
|
||||
}
|
||||
} while (*c++ != '\0');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
76
src/web/WebAPIService.h
Normal file
76
src/web/WebAPIService.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebAPIService_h
|
||||
#define WebAPIService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define EMSESP_API_SERVICE_PATH "/api"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
typedef std::unordered_map<std::string, std::string> KeyValueMap_t;
|
||||
typedef std::vector<std::string> Folder_t;
|
||||
|
||||
class SUrlParser {
|
||||
private:
|
||||
KeyValueMap_t m_keysvalues;
|
||||
Folder_t m_folders;
|
||||
|
||||
public:
|
||||
SUrlParser(){};
|
||||
SUrlParser(const char * url);
|
||||
|
||||
bool parse(const char * url);
|
||||
|
||||
Folder_t & paths() {
|
||||
return m_folders;
|
||||
};
|
||||
|
||||
KeyValueMap_t & params() {
|
||||
return m_keysvalues;
|
||||
};
|
||||
|
||||
std::string path();
|
||||
};
|
||||
|
||||
class WebAPIService {
|
||||
public:
|
||||
WebAPIService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json); // for POSTs
|
||||
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
AsyncCallbackJsonWebHandler _apiHandler; // for POSTs
|
||||
|
||||
void parse(AsyncWebServerRequest * request, std::string & device, std::string & cmd, int id, std::string & value);
|
||||
void send_message_response(AsyncWebServerRequest * request, uint16_t error_code, const char * error_message = nullptr);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -23,10 +23,16 @@ namespace emsesp {
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) {
|
||||
server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
server->on(EMSESP_DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
_device_dataHandler.setMethod(HTTP_POST);
|
||||
_device_dataHandler.setMaxContentLength(256);
|
||||
@@ -77,9 +83,10 @@ void WebDevicesService::all_devices(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
// The unique_id is the unique record ID from the Web table to identify which device to load
|
||||
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
||||
void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
@@ -102,28 +109,21 @@ void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant
|
||||
|
||||
// takes a command and its data value from a specific Device, from the Web
|
||||
void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
// only issue commands if the API is enabled
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
if (!settings.api_enabled) {
|
||||
request->send(403); // forbidden error
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject dv = json["devicevalue"];
|
||||
uint8_t id = json["id"];
|
||||
|
||||
// using the unique ID from the web find the real device type
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->unique_id() == dv["id"].as<int>()) {
|
||||
const char * cmd = dv["cmd"];
|
||||
if (emsdevice->unique_id() == id) {
|
||||
const char * cmd = dv["c"];
|
||||
uint8_t device_type = emsdevice->device_type();
|
||||
bool ok = false;
|
||||
char s[10];
|
||||
// the data could be in any format, but we need string
|
||||
JsonVariant data = dv["data"];
|
||||
if (data.is<char *>()) {
|
||||
JsonVariant data = dv["v"];
|
||||
if (data.is<const char *>()) {
|
||||
ok = Command::call(device_type, cmd, data.as<const char *>());
|
||||
} else if (data.is<int>()) {
|
||||
ok = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0));
|
||||
@@ -133,16 +133,17 @@ void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant
|
||||
ok = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false");
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
request->send(200);
|
||||
}
|
||||
return; // found device, quit
|
||||
// send "Write command sent to device" or "Write command failed"
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request->send(204); // no content error
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(204); // Write command failed
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -29,7 +29,6 @@
|
||||
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
||||
#define WRITE_VALUE_SERVICE_PATH "/rest/writeValue"
|
||||
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class WebDevicesService {
|
||||
171
src/web/WebLogService.cpp
Normal file
171
src/web/WebLogService.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _events(EVENT_SOURCE_LOG_PATH)
|
||||
, _setLevel(LOG_SETTINGS_PATH, std::bind(&WebLogService::setLevel, this, _1, _2), 256) { // for POSTS
|
||||
|
||||
_events.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
server->addHandler(&_events);
|
||||
server->on(EVENT_SOURCE_LOG_PATH, HTTP_GET, std::bind(&WebLogService::forbidden, this, _1));
|
||||
|
||||
// for bring back the whole log
|
||||
server->on(FETCH_LOG_PATH, HTTP_GET, std::bind(&WebLogService::fetchLog, this, _1));
|
||||
|
||||
// get when page is loaded
|
||||
server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getLevel, this, _1));
|
||||
|
||||
// for setting a level
|
||||
server->addHandler(&_setLevel);
|
||||
|
||||
// start event source service
|
||||
start();
|
||||
}
|
||||
|
||||
void WebLogService::forbidden(AsyncWebServerRequest * request) {
|
||||
request->send(403);
|
||||
}
|
||||
|
||||
void WebLogService::start() {
|
||||
uuid::log::Logger::register_handler(this, uuid::log::Level::INFO); // default is INFO
|
||||
}
|
||||
|
||||
uuid::log::Level WebLogService::log_level() const {
|
||||
return uuid::log::Logger::get_log_level(this);
|
||||
}
|
||||
|
||||
void WebLogService::log_level(uuid::log::Level level) {
|
||||
uuid::log::Logger::register_handler(this, level);
|
||||
}
|
||||
|
||||
size_t WebLogService::maximum_log_messages() const {
|
||||
return maximum_log_messages_;
|
||||
}
|
||||
|
||||
void WebLogService::maximum_log_messages(size_t count) {
|
||||
maximum_log_messages_ = std::max((size_t)1, count);
|
||||
while (log_messages_.size() > maximum_log_messages_) {
|
||||
log_messages_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
WebLogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
|
||||
: id_(id)
|
||||
, content_(std::move(content)) {
|
||||
}
|
||||
|
||||
void WebLogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
||||
if (log_messages_.size() >= maximum_log_messages_) {
|
||||
log_messages_.pop_front();
|
||||
}
|
||||
log_messages_.emplace_back(log_message_id_++, std::move(message));
|
||||
}
|
||||
|
||||
void WebLogService::loop() {
|
||||
if (!_events.count() || log_messages_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// put a small delay in
|
||||
const uint64_t now = uuid::get_uptime_ms();
|
||||
if (now < last_transmit_ || now - last_transmit_ < 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
// see if we've advanced
|
||||
if (log_messages_.back().id_ > log_message_id_tail_) {
|
||||
transmit(log_messages_.back());
|
||||
log_message_id_tail_ = log_messages_.back().id_;
|
||||
last_transmit_ = uuid::get_uptime_ms();
|
||||
}
|
||||
}
|
||||
|
||||
// send to web eventsource
|
||||
void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_SMALL);
|
||||
JsonObject logEvent = jsonDocument.to<JsonObject>();
|
||||
logEvent["t"] = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3);
|
||||
logEvent["l"] = message.content_->level;
|
||||
logEvent["n"] = message.content_->name;
|
||||
logEvent["m"] = message.content_->text;
|
||||
|
||||
size_t len = measureJson(jsonDocument);
|
||||
char * buffer = new char[len + 1];
|
||||
if (buffer) {
|
||||
serializeJson(jsonDocument, buffer, len + 1);
|
||||
_events.send(buffer, "message", millis());
|
||||
}
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
// send the current log buffer to the API
|
||||
void WebLogService::fetchLog(AsyncWebServerRequest * request) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); // 8kb buffer
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray log = root.createNestedArray("events");
|
||||
|
||||
for (const auto & msg : log_messages_) {
|
||||
JsonObject logEvent = log.createNestedObject();
|
||||
auto message = std::move(msg);
|
||||
logEvent["t"] = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3);
|
||||
logEvent["l"] = message.content_->level;
|
||||
logEvent["n"] = message.content_->name;
|
||||
logEvent["m"] = message.content_->text;
|
||||
}
|
||||
|
||||
log_message_id_tail_ = log_messages_.back().id_;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// sets the level after a POST
|
||||
void WebLogService::setLevel(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (not json.is<JsonObject>()) {
|
||||
return;
|
||||
}
|
||||
auto && body = json.as<JsonObject>();
|
||||
uuid::log::Level level = body["level"];
|
||||
log_level(level);
|
||||
|
||||
// send the value back
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
JsonObject root = response->getRoot();
|
||||
root["level"] = log_level();
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// return the current log level after a GET
|
||||
void WebLogService::getLevel(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
JsonObject root = response->getRoot();
|
||||
root["level"] = log_level();
|
||||
root["max_messages"] = maximum_log_messages();
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
80
src/web/WebLogService.h
Normal file
80
src/web/WebLogService.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebLogService_h
|
||||
#define WebLogService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
#define EVENT_SOURCE_LOG_PATH "/es/log"
|
||||
#define FETCH_LOG_PATH "/rest/fetchLog"
|
||||
#define LOG_SETTINGS_PATH "/rest/logSettings"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class WebLogService : public uuid::log::Handler {
|
||||
public:
|
||||
static constexpr size_t MAX_LOG_MESSAGES = 30;
|
||||
|
||||
WebLogService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
void start();
|
||||
uuid::log::Level log_level() const;
|
||||
void log_level(uuid::log::Level level);
|
||||
size_t maximum_log_messages() const;
|
||||
void maximum_log_messages(size_t count);
|
||||
void loop();
|
||||
|
||||
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
||||
|
||||
private:
|
||||
AsyncEventSource _events;
|
||||
|
||||
class QueuedLogMessage {
|
||||
public:
|
||||
QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content);
|
||||
~QueuedLogMessage() = default;
|
||||
|
||||
unsigned long id_; // Sequential identifier for this log message
|
||||
struct timeval time_; // Time message was received
|
||||
const std::shared_ptr<const uuid::log::Message> content_; // Log message content
|
||||
};
|
||||
|
||||
void forbidden(AsyncWebServerRequest * request);
|
||||
void transmit(const QueuedLogMessage & message);
|
||||
void fetchLog(AsyncWebServerRequest * request);
|
||||
void getLevel(AsyncWebServerRequest * request);
|
||||
|
||||
void setLevel(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
AsyncCallbackJsonWebHandler _setLevel; // for POSTs
|
||||
|
||||
uint64_t last_transmit_ = 0; // Last transmit time
|
||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; // Maximum number of log messages to buffer before they are output
|
||||
unsigned long log_message_id_ = 0; // The next identifier to use for queued log messages
|
||||
unsigned long log_message_id_tail_ = 0; // last event shown on the screen after fetch
|
||||
std::list<QueuedLogMessage> log_messages_; // Queued log messages, in the order they were received
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -27,7 +27,8 @@ using namespace std::placeholders; // for `_1` etc
|
||||
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(WebSettings::read, WebSettings::update, this, fs, EMSESP_SETTINGS_FILE)
|
||||
, _boardProfileHandler(EMSESP_BOARD_PROFILE_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebSettingsService::board_profile, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
, _boardProfileHandler(EMSESP_BOARD_PROFILE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebSettingsService::board_profile, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
_boardProfileHandler.setMethod(HTTP_POST);
|
||||
_boardProfileHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_boardProfileHandler);
|
||||
@@ -54,9 +55,10 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
root["dallas_parasite"] = settings.dallas_parasite;
|
||||
root["led_gpio"] = settings.led_gpio;
|
||||
root["hide_led"] = settings.hide_led;
|
||||
root["api_enabled"] = settings.api_enabled;
|
||||
root["notoken_api"] = settings.notoken_api;
|
||||
root["analog_enabled"] = settings.analog_enabled;
|
||||
root["pbutton_gpio"] = settings.pbutton_gpio;
|
||||
root["solar_maxflow"] = settings.solar_maxflow;
|
||||
root["board_profile"] = settings.board_profile;
|
||||
}
|
||||
|
||||
@@ -168,7 +170,8 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT;
|
||||
|
||||
// doesn't need any follow-up actions
|
||||
settings.api_enabled = root["api_enabled"] | EMSESP_DEFAULT_API_ENABLED;
|
||||
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
|
||||
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
|
||||
#include "default_settings.h"
|
||||
#include "../default_settings.h"
|
||||
|
||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
|
||||
@@ -50,9 +50,10 @@ class WebSettings {
|
||||
bool dallas_parasite;
|
||||
uint8_t led_gpio;
|
||||
bool hide_led;
|
||||
bool api_enabled;
|
||||
bool notoken_api;
|
||||
bool analog_enabled;
|
||||
uint8_t pbutton_gpio;
|
||||
uint8_t solar_maxflow;
|
||||
String board_profile;
|
||||
|
||||
static void read(WebSettings & settings, JsonObject & root);
|
||||
@@ -24,7 +24,9 @@ namespace emsesp {
|
||||
|
||||
WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
// rest endpoint for web page
|
||||
server->on(EMSESP_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebStatusService::webStatusService, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
server->on(EMSESP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebStatusService::webStatusService, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
WiFi.onEvent(std::bind(&WebStatusService::WiFiEvent, this, _1, _2));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user