mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
Merge remote-tracking branch 'origin/tech-upgrade' into dev
This commit is contained in:
@@ -68,7 +68,7 @@ void AnalogSensor::reload() {
|
||||
analog_enabled_ = true; // for local offline testing
|
||||
#endif
|
||||
for (auto sensor : sensors_) {
|
||||
remove_ha_topic(sensor.gpio());
|
||||
remove_ha_topic(sensor.type(), sensor.gpio());
|
||||
sensor.ha_registered = false;
|
||||
}
|
||||
if (!analog_enabled_) {
|
||||
@@ -333,7 +333,7 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset,
|
||||
|
||||
// if the sensor exists and we're using HA, delete the old HA record
|
||||
if (found_sensor && Mqtt::ha_enabled()) {
|
||||
remove_ha_topic(gpio); // the GPIO
|
||||
remove_ha_topic(type, gpio); // the GPIO
|
||||
}
|
||||
|
||||
// we didn't find it, it's new, so create and store it
|
||||
@@ -379,21 +379,35 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(analogsensor), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles
|
||||
}
|
||||
}
|
||||
|
||||
// send empty config topic to remove the entry from HA
|
||||
void AnalogSensor::remove_ha_topic(const uint8_t gpio) const {
|
||||
void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
#ifdef EMSESP_DEBUG
|
||||
|
||||
LOG_DEBUG("Removing HA config for analog sensor GPIO %02d", gpio);
|
||||
#endif
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
Mqtt::publish_ha(topic);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (type == AnalogType::DIGITAL_OUT && gpio != 25 && gpio != 26) {
|
||||
#else
|
||||
if (type == AnalogType::DIGITAL_OUT) {
|
||||
#endif
|
||||
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
} else if (type == AnalogType::DIGITAL_OUT) { // DAC
|
||||
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
} else if (type >= AnalogType::PWM_0) {
|
||||
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
} else if (type == AnalogType::DIGITAL_IN) {
|
||||
snprintf(topic, sizeof(topic), "binary-sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
}
|
||||
Mqtt::queue_remove_topic(topic);
|
||||
}
|
||||
|
||||
// send all sensor values as a JSON package to MQTT
|
||||
@@ -447,13 +461,16 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); // use base path
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char str[50];
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(str, sizeof(str), "{{value_json['%02d'].value}}", sensor.gpio());
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%02d'].value", sensor.gpio());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%02d'] is defined", sensor.gpio());
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "{{value_json['%s']}", sensor.name().c_str());
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
config["val_tpl"] = str;
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
@@ -462,31 +479,85 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio());
|
||||
}
|
||||
|
||||
config["object_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
|
||||
snprintf(str, sizeof(str), "%s", sensor.name().c_str());
|
||||
config["name"] = str;
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s", sensor.name().c_str());
|
||||
config["name"] = name;
|
||||
|
||||
if (sensor.uom() != DeviceValueUOM::NONE) {
|
||||
config["unit_of_meas"] = EMSdevice::uom_to_string(sensor.uom());
|
||||
}
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
// Set commands for some analog types
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT && sensor.gpio() != 25 && sensor.gpio() != 26) {
|
||||
#else
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
#endif
|
||||
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
config["pl_on"] = true;
|
||||
config["pl_off"] = false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
config["pl_on"] = 1;
|
||||
config["pl_off"] = 0;
|
||||
} else {
|
||||
char result[12];
|
||||
config["pl_on"] = Helpers::render_boolean(result, true);
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { // DAC
|
||||
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
config["min"] = 0;
|
||||
config["max"] = 255;
|
||||
config["mode"] = "box"; // auto, slider or box
|
||||
config["step"] = 1;
|
||||
} else if (sensor.type() >= AnalogType::PWM_0) {
|
||||
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
config["min"] = 0;
|
||||
config["max"] = 100;
|
||||
config["mode"] = "box"; // auto, slider or box
|
||||
config["step"] = 0.1;
|
||||
} else if (sensor.type() == AnalogType::COUNTER) {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
config["stat_cla"] = "total_increasing";
|
||||
// config["mode"] = "box"; // auto, slider or box
|
||||
// config["step"] = sensor.factor();
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
snprintf(topic, sizeof(topic), "binary-sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
config["stat_cla"] = "measurement";
|
||||
}
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
|
||||
sensor.ha_registered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::publish("analogsensor_data", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("analogsensor_data", doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
// called from emsesp.cpp, similar to the emsdevice->get_value_info
|
||||
@@ -689,7 +760,7 @@ bool AnalogSensor::command_commands(const char * value, const int8_t id, JsonObj
|
||||
}
|
||||
|
||||
// hard coded tests
|
||||
#ifdef EMSESP_DEBUG
|
||||
#ifdef EMSESP_TEST
|
||||
void AnalogSensor::test() {
|
||||
sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC);
|
||||
sensors_.back().set_value(12.4);
|
||||
@@ -699,4 +770,4 @@ void AnalogSensor::test() {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -160,7 +160,7 @@ class AnalogSensor {
|
||||
bool update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type);
|
||||
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id) const;
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
void test();
|
||||
#endif
|
||||
|
||||
@@ -170,7 +170,7 @@ class AnalogSensor {
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void remove_ha_topic(const uint8_t id) const;
|
||||
void remove_ha_topic(const int8_t type, const uint8_t id) const;
|
||||
bool command_setvalue(const char * value, const int8_t gpio);
|
||||
void measure();
|
||||
bool command_info(const char * value, const int8_t id, JsonObject & output) const;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -52,12 +52,6 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_USE_SERIAL)
|
||||
// Serial.println(p.path().c_str()); // dump paths, for debugging
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// re-calculate new path
|
||||
// if there is only a path (URL) and no body then error!
|
||||
size_t num_paths = p.paths().size();
|
||||
@@ -210,13 +204,11 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
id = command[2] - '0';
|
||||
command += 3;
|
||||
} else if (!strncmp(lowerCmd, "wwc", 3) && command[3] == '1' && command[4] == '0') {
|
||||
id = DeviceValueTAG::TAG_WWC10 - DeviceValueTAG::TAG_HC1 + 1; //18;
|
||||
command += 5;
|
||||
} else if (!strncmp(lowerCmd, "wwc", 3) && command[3] >= '1' && command[3] <= '9') {
|
||||
id = command[3] - '1' + DeviceValueTAG::TAG_WWC1 - DeviceValueTAG::TAG_HC1 + 1; //9;
|
||||
id = DeviceValueTAG::TAG_WWC10 - DeviceValueTAG::TAG_HC1 + 1; //18; } else if (!strncmp(lowerCmd, "wwc", 3) && command[3] >= '1' && command[3] <= '9') {
|
||||
id = command[3] - '0' + 8;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') {
|
||||
id = command[3] - '0' + 10;
|
||||
id = command[3] - '1' + DeviceValueTAG::TAG_WWC1 - DeviceValueTAG::TAG_HC1 + 1; //9;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "id", 2) && command[2] >= '1' && command[2] <= '9') {
|
||||
id = command[2] - '0';
|
||||
@@ -231,11 +223,14 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
id = command[2] - '1' + DeviceValueTAG::TAG_HS1 - DeviceValueTAG::TAG_HC1 + 1; //20;
|
||||
command += 3;
|
||||
}
|
||||
|
||||
// remove separator
|
||||
if (command[0] == '/' || command[0] == '.' || command[0] == '_') {
|
||||
command++;
|
||||
}
|
||||
|
||||
free(lowerCmd);
|
||||
|
||||
// return null for empty command
|
||||
if (command[0] == '\0') {
|
||||
return nullptr;
|
||||
@@ -273,9 +268,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
|
||||
// except for system commands as this is a special device without any queryable entities (device values)
|
||||
if ((device_type > EMSdevice::DeviceType::SYSTEM) && (!value || !strlen(value))) {
|
||||
if (!cf || !cf->cmdfunction_json_) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Calling %s command '%s' to retrieve attributes", dname, cmd);
|
||||
#endif
|
||||
LOG_DEBUG("Calling %s command '%s' to retrieve attributes", dname, cmd);
|
||||
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
|
||||
}
|
||||
}
|
||||
@@ -350,7 +343,8 @@ void Command::add(const uint8_t device_type, const uint8_t device_id, const char
|
||||
cmdfunctions_.emplace_back(device_type, device_id, flags, cmd, cb, nullptr, description); // callback for json is nullptr
|
||||
}
|
||||
|
||||
// same for system/dallas/analog devices with device_id 0
|
||||
// add a command with no json output
|
||||
// system/dallas/analog devices uses device_id 0
|
||||
void Command::add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
||||
add(device_type, 0, cmd, cb, description, flags);
|
||||
}
|
||||
@@ -415,7 +409,9 @@ bool Command::list(const uint8_t device_type, JsonObject & output) {
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == std::string(cf.cmd_))) {
|
||||
if (cf.has_flags(CommandFlag::MQTT_SUB_FLAG_WW)) {
|
||||
output[cl] = EMSdevice::tag_to_string(DeviceValueTAG::TAG_DEVICE_DATA_WW) + " " + Helpers::translated_word(cf.description_);
|
||||
char s[100];
|
||||
snprintf(s, sizeof(s), "%s %s", EMSdevice::tag_to_string(DeviceValueTAG::TAG_DEVICE_DATA_WW), Helpers::translated_word(cf.description_));
|
||||
output[cl] = s;
|
||||
} else {
|
||||
output[cl] = Helpers::translated_word(cf.description_);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
30
src/common.h
30
src/common.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -19,11 +19,16 @@
|
||||
#ifndef EMSESP_COMMON_H
|
||||
#define EMSESP_COMMON_H
|
||||
|
||||
// logging
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::log::Level;
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#define LOG_DEBUG(...) logger_.debug(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#define LOG_INFO(...) logger_.info(__VA_ARGS__)
|
||||
#define LOG_TRACE(...) logger_.trace(__VA_ARGS__)
|
||||
#define LOG_NOTICE(...) logger_.notice(__VA_ARGS__)
|
||||
@@ -41,13 +46,26 @@ using string_vector = std::vector<const char *>;
|
||||
// clang-format off
|
||||
|
||||
#define FPSTR(pstr_pointer) pstr_pointer
|
||||
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
|
||||
#define MAKE_PSTR_WORD(string_name) MAKE_PSTR(string_name, #string_name)
|
||||
#define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
|
||||
#define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name)
|
||||
|
||||
#define F_(string_name) (__pstr__##string_name)
|
||||
#define FL_(list_name) (__pstr__L_##list_name)
|
||||
#define MAKE_PSTR_LIST(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#define MAKE_PSTR_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
// In testing just take one language (en) to save on Flash space
|
||||
#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
|
||||
#else
|
||||
#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#endif
|
||||
|
||||
#define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// fixed strings, no translations
|
||||
#define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// with translations
|
||||
#define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
855
src/console.cpp
855
src/console.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -26,85 +26,47 @@
|
||||
#include "system.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
using uuid::console::Commands;
|
||||
using uuid::console::Shell;
|
||||
|
||||
static constexpr uint32_t INVALID_PASSWORD_DELAY_MS = 2000;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using LogLevel = ::uuid::log::Level;
|
||||
using LogFacility = ::uuid::log::Facility;
|
||||
|
||||
#ifdef LOCAL
|
||||
#undef LOCAL
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum CommandFlags : uint8_t { USER = 0, ADMIN = (1 << 0), LOCAL = (1 << 1) };
|
||||
|
||||
enum ShellContext : uint8_t {
|
||||
MAIN = 0,
|
||||
SYSTEM,
|
||||
};
|
||||
enum ShellContext : uint8_t { MAIN = 0, SYSTEM, END };
|
||||
|
||||
class EMSESPShell : virtual public uuid::console::Shell {
|
||||
class EMSESP;
|
||||
|
||||
class EMSESPShell : public uuid::console::Shell {
|
||||
public:
|
||||
~EMSESPShell() override = default;
|
||||
|
||||
virtual std::string console_name() = 0;
|
||||
|
||||
static std::shared_ptr<uuid::console::Commands> commands;
|
||||
static std::shared_ptr<EMSESPShell> shell;
|
||||
static void main_help_function(Shell & shell, const std::vector<std::string> & arguments);
|
||||
static void main_exit_function(Shell & shell, const std::vector<std::string> & arguments);
|
||||
|
||||
EMSESP & emsesp_;
|
||||
|
||||
protected:
|
||||
EMSESPShell();
|
||||
EMSESPShell(EMSESP & emsesp, Stream & stream, unsigned int context, unsigned int flags);
|
||||
|
||||
static std::shared_ptr<uuid::console::Commands> commands_;
|
||||
|
||||
// our custom functions for Shell
|
||||
void started() override;
|
||||
void stopped() override;
|
||||
void display_banner() override;
|
||||
std::string hostname_text() override;
|
||||
std::string context_text() override;
|
||||
std::string prompt_suffix() override;
|
||||
void end_of_transmission() override;
|
||||
|
||||
|
||||
private:
|
||||
void add_console_commands();
|
||||
bool console_commands_loaded_ = false; // set to true when the initial commands are loaded
|
||||
std::string console_hostname_;
|
||||
};
|
||||
|
||||
class EMSESPStreamConsole : public uuid::console::StreamConsole, public EMSESPShell {
|
||||
public:
|
||||
EMSESPStreamConsole(Stream & stream, bool local);
|
||||
EMSESPStreamConsole(Stream & stream, const IPAddress & addr, uint16_t port);
|
||||
~EMSESPStreamConsole() override;
|
||||
|
||||
std::string console_name() override;
|
||||
|
||||
private:
|
||||
static std::vector<bool> ptys_;
|
||||
|
||||
std::string name_;
|
||||
size_t pty_;
|
||||
IPAddress addr_;
|
||||
uint16_t port_;
|
||||
};
|
||||
|
||||
class Console {
|
||||
public:
|
||||
void loop();
|
||||
void start_serial();
|
||||
void start_telnet();
|
||||
|
||||
uuid::log::Level log_level();
|
||||
|
||||
static void load_standard_commands(unsigned int context);
|
||||
static void load_system_commands(unsigned int context);
|
||||
|
||||
private:
|
||||
bool telnet_enabled_ = false; // telnet is default off
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
50
src/console_stream.h
Normal file
50
src/console_stream.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* mcu-app - Microcontroller application framework
|
||||
* Copyright 2022 Simon Arlott
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <uuid/console.h>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPConsole : public EMSESPShell {
|
||||
public:
|
||||
EMSESPConsole(EMSESP & emsesp, Stream & stream, bool local);
|
||||
#ifndef ENV_NATIVE
|
||||
EMSESPConsole(EMSESP & emsesp, Stream & stream, const IPAddress & addr, uint16_t port);
|
||||
#endif
|
||||
~EMSESPConsole() override;
|
||||
|
||||
std::string console_name();
|
||||
|
||||
private:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static std::vector<bool> ptys_;
|
||||
#endif
|
||||
|
||||
std::string name_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
size_t pty_;
|
||||
IPAddress addr_;
|
||||
uint16_t port_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -91,7 +91,7 @@ void DallasSensor::loop() {
|
||||
if (state_ == State::IDLE) {
|
||||
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG("[DEBUG] Read sensor temperature");
|
||||
LOG_DEBUG("Read sensor temperature");
|
||||
#endif
|
||||
if (bus_.reset() || parasite_) {
|
||||
YIELD;
|
||||
@@ -214,7 +214,7 @@ void DallasSensor::loop() {
|
||||
// LOG_DEBUG("Adding %d dallas sensor(s) from first scan", firstscan_);
|
||||
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
|
||||
scancnt_ = SCAN_START;
|
||||
sensors_.clear(); // restart scaning and clear to get correct numbering
|
||||
sensors_.clear(); // restart scanning and clear to get correct numbering
|
||||
}
|
||||
state_ = State::IDLE;
|
||||
}
|
||||
@@ -439,7 +439,7 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
|
||||
|
||||
// publish a single sensor to MQTT
|
||||
void DallasSensor::publish_sensor(const Sensor & sensor) {
|
||||
if (Mqtt::publish_single()) {
|
||||
if (Mqtt::enabled() && Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", (F_(dallassensor)), sensor.name().c_str());
|
||||
@@ -447,7 +447,7 @@ void DallasSensor::publish_sensor(const Sensor & sensor) {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", (F_(dallassensor)), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,19 +456,21 @@ void DallasSensor::remove_ha_topic(const std::string & id) {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("Removing HA config for temperature sensor ID %s", id.c_str());
|
||||
#endif
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = id;
|
||||
std::replace(sensorid.begin(), sensorid.end(), '-', '_');
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
Mqtt::publish_ha(topic);
|
||||
Mqtt::queue_remove_topic(topic);
|
||||
}
|
||||
|
||||
// send all dallas sensor values as a JSON package to MQTT
|
||||
void DallasSensor::publish_values(const bool force) {
|
||||
if (!Mqtt::enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
|
||||
if (num_sensors == 0) {
|
||||
@@ -514,13 +516,16 @@ void DallasSensor::publish_values(const bool force) {
|
||||
|
||||
config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
|
||||
char str[50];
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(str, sizeof(str), "{{value_json['%s'].temp}}", sensor.id().c_str());
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s'].temp", sensor.id().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined", sensor.id().c_str());
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "{{value_json['%s']}}", sensor.name().c_str());
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
config["val_tpl"] = str;
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
@@ -529,16 +534,20 @@ void DallasSensor::publish_values(const bool force) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "dallassensor_%s", sensor.id().c_str());
|
||||
}
|
||||
|
||||
config["object_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id/obj_id
|
||||
|
||||
snprintf(str, sizeof(str), "%s", sensor.name().c_str());
|
||||
config["name"] = str;
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s", sensor.name().c_str());
|
||||
config["name"] = name;
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = sensor.id();
|
||||
@@ -546,14 +555,14 @@ void DallasSensor::publish_values(const bool force) {
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
|
||||
sensor.ha_registered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::publish("dallassensor_data", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("dallassensor_data", doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
|
||||
@@ -596,9 +605,7 @@ bool DallasSensor::Sensor::apply_customization() {
|
||||
auto sensors = settings.sensorCustomizations;
|
||||
if (!sensors.empty()) {
|
||||
for (const auto & sensor : sensors) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("Loading customization for dallas sensor %s", sensor.id.c_str());
|
||||
#endif
|
||||
if (id_ == sensor.id) {
|
||||
set_name(sensor.name);
|
||||
set_offset(sensor.offset);
|
||||
@@ -613,7 +620,7 @@ bool DallasSensor::Sensor::apply_customization() {
|
||||
}
|
||||
|
||||
// hard coded tests
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
void DallasSensor::test() {
|
||||
// add 2 dallas sensors
|
||||
uint8_t addr[ADDR_LEN] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
@@ -630,4 +637,4 @@ void DallasSensor::test() {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -111,7 +111,7 @@ class DallasSensor {
|
||||
|
||||
bool update(const std::string & id, const std::string & name, int16_t offset);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
void test();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -169,6 +169,10 @@
|
||||
#define EMSESP_DEFAULT_DISCOVERY_PREFIX "homeassistant"
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DISCOVERY_TYPE
|
||||
#define EMSESP_DEFAULT_DISCOVERY_TYPE 0 // HA
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE
|
||||
#define EMSESP_DEFAULT_PUBLISH_SINGLE false
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -175,4 +175,4 @@
|
||||
// Generic - 0x40 or other with no product-id and no version
|
||||
{0, DeviceType::GENERIC, "unknown", DeviceFlags::EMS_DEVICE_FLAG_NONE}
|
||||
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -27,7 +27,6 @@ uuid::log::Logger Boiler::logger_{F_(boiler), uuid::log::Facility::CONSOLE};
|
||||
Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// register values for master boiler/cascade module
|
||||
// reserve_telegram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
|
||||
|
||||
// the telegram handlers...
|
||||
// common for all boilers
|
||||
@@ -76,8 +75,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0x4A2, "HpInput", true, MAKE_PF_CB(process_HpInput));
|
||||
register_telegram_type(0x485, "HpCooling", true, MAKE_PF_CB(process_HpCooling));
|
||||
register_telegram_type(0x486, "HpInConfig", true, MAKE_PF_CB(process_HpInConfig));
|
||||
register_telegram_type(0x492, "HpHeaterConfig", true, MAKE_PF_CB(process_HpHeaterConfig));
|
||||
|
||||
register_telegram_type(0x492, "HpHeaterConfig", true, MAKE_PF_CB(process_HpHeaterConfig));
|
||||
register_telegram_type(0x488, "HPValve", true, MAKE_PF_CB(process_HpValve));
|
||||
register_telegram_type(0x484, "HPSilentMode", true, MAKE_PF_CB(process_HpSilentMode));
|
||||
register_telegram_type(0x48B, "HPPumps", true, MAKE_PF_CB(process_HpPumps));
|
||||
@@ -848,7 +847,7 @@ void Boiler::check_active(const bool force) {
|
||||
if (heatingActive_ != val || force) {
|
||||
heatingActive_ = val;
|
||||
char s[12];
|
||||
Mqtt::publish(F_(heating_active), Helpers::render_boolean(s, b));
|
||||
Mqtt::queue_publish(F_(heating_active), Helpers::render_boolean(s, b));
|
||||
}
|
||||
|
||||
// check if we can use tapactivated in flow systems
|
||||
@@ -872,7 +871,7 @@ void Boiler::check_active(const bool force) {
|
||||
if (tapwaterActive_ != val || force) {
|
||||
tapwaterActive_ = val;
|
||||
char s[12];
|
||||
Mqtt::publish(F_(tapwater_active), Helpers::render_boolean(s, b));
|
||||
Mqtt::queue_publish(F_(tapwater_active), Helpers::render_boolean(s, b));
|
||||
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
|
||||
}
|
||||
}
|
||||
@@ -1456,7 +1455,8 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
|
||||
snprintf(&code[3], sizeof(code) - 3, "(%d) %02d.%02d.%04d %02d:%02d - now", codeNo, start_day, start_month, start_year, start_hour, start_min);
|
||||
}
|
||||
} else { // no clock, the uptime is stored https://github.com/emsesp/EMS-ESP32/issues/121
|
||||
uint32_t starttime, endtime;
|
||||
uint32_t starttime = 0;
|
||||
uint32_t endtime = 0;
|
||||
telegram->read_value(starttime, 11, 3);
|
||||
telegram->read_value(endtime, 16, 3);
|
||||
snprintf(&code[3], sizeof(code) - 3, "(%d) @uptime %d - %d min", codeNo, starttime, endtime);
|
||||
@@ -1712,6 +1712,7 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
|
||||
// no write/verify if there is no change, see https://github.com/emsesp/EMS-ESP32/issues/654
|
||||
if (v == selFlowTemp_) {
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, (uint8_t *)&v, 1, 0, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -92,8 +92,8 @@ class Boiler : public EMSdevice {
|
||||
uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3)
|
||||
uint16_t wwSolarTemp_;
|
||||
uint8_t wwAlternatingOper_; // alternating operation on/off
|
||||
uint8_t wwAltOpPrioHeat_; // alternating operation, prioritise heat time
|
||||
uint8_t wwAltOpPrioWw_; // alternating operation, prioritise dhw time
|
||||
uint8_t wwAltOpPrioHeat_; // alternating operation, prioritize heat time
|
||||
uint8_t wwAltOpPrioWw_; // alternating operation, prioritize dhw time
|
||||
|
||||
// main
|
||||
uint8_t reset_; // for reset command
|
||||
@@ -389,6 +389,14 @@ class Boiler : public EMSdevice {
|
||||
bool set_pvCooling(const char * value, const int8_t id);
|
||||
bool set_hpCircPumpWw(const char * value, const int8_t id);
|
||||
|
||||
bool set_auxLimit(const char * value, const int8_t id);
|
||||
inline bool set_auxMaxLimit(const char * value, const int8_t id) {
|
||||
return set_auxLimit(value, 14);
|
||||
}
|
||||
inline bool set_auxLimitStart(const char * value, const int8_t id) {
|
||||
return set_auxLimit(value, 15);
|
||||
}
|
||||
|
||||
bool set_auxLimit(const char * value, const int8_t id);
|
||||
inline bool set_auxMaxLimit(const char * value, const int8_t id) {
|
||||
return set_auxLimit(value, 14);
|
||||
@@ -460,4 +468,4 @@ class Boiler : public EMSdevice {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -32,14 +32,25 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
|
||||
register_telegram_type(0x999, "HPFunctionTest", true, MAKE_PF_CB(process_HPFunctionTest));
|
||||
register_telegram_type(0x9A0, "HPTemperature", false, MAKE_PF_CB(process_HPTemperature));
|
||||
register_telegram_type(0x99B, "HPFlowTemp", false, MAKE_PF_CB(process_HPFlowTemp));
|
||||
register_telegram_type(0x99C, "HPComp", false, MAKE_PF_CB(process_HPComp));
|
||||
|
||||
// device values
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &airHumidity_, DeviceValueType::UINT, FL_(airHumidity), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dewTemperature_, DeviceValueType::UINT, FL_(dewTemperature), DeviceValueUOM::DEGREES);
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &flowTemp_, DeviceValueType::UINT, FL_(curFlowTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &retTemp_, DeviceValueType::UINT, FL_(retTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &sysRetTemp_, DeviceValueType::UINT, FL_(sysRetTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&flowTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(curFlowTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &retTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(retTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&sysRetTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(sysRetTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTa4_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTa4), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTr1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTr1), DeviceValueUOM::DEGREES);
|
||||
@@ -51,6 +62,9 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpJr0_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPl1), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpJr1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPh1), DeviceValueUOM::DEGREES);
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingPumpMod_, DeviceValueType::UINT, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpCompSpd_, DeviceValueType::UINT, FL_(hpCompSpd), DeviceValueUOM::PERCENT);
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&controlStrategy_,
|
||||
DeviceValueType::ENUM,
|
||||
@@ -177,6 +191,7 @@ void Heatpump::process_HPFlowTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, flowTemp_, 4);
|
||||
has_update(telegram, retTemp_, 6);
|
||||
has_update(telegram, sysRetTemp_, 14);
|
||||
has_update(telegram, heatingPumpMod_, 19);
|
||||
}
|
||||
|
||||
// 0x0998 HPSettings
|
||||
@@ -193,6 +208,14 @@ void Heatpump::process_HPSettings(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, switchOverTemp_, 14);
|
||||
}
|
||||
|
||||
// 0x099C HPComp
|
||||
// Broadcast (0x099C), data: 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 76 00 00
|
||||
// data: 00 2B 00 03 04 13 00 00 00 00 00 02 02 02 (offset 24)
|
||||
void Heatpump::process_HPComp(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, hpCompSpd_, 51);
|
||||
}
|
||||
|
||||
// 0x999 HPFunctionTest
|
||||
void Heatpump::process_HPFunctionTest(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, airPurgeMode_, 0);
|
||||
has_update(telegram, heatPumpOutput_, 2);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -41,6 +41,8 @@ class Heatpump : public EMSdevice {
|
||||
uint8_t energyPriceEl_;
|
||||
uint8_t energyPricePV_;
|
||||
int8_t switchOverTemp_;
|
||||
uint8_t heatingPumpMod_;
|
||||
uint8_t hpCompSpd_;
|
||||
|
||||
// Function test
|
||||
uint8_t airPurgeMode_;
|
||||
@@ -50,20 +52,20 @@ class Heatpump : public EMSdevice {
|
||||
uint8_t heatDrainPan_;
|
||||
uint8_t heatCable_;
|
||||
|
||||
// HM200 temperature
|
||||
int16_t flowTemp_;
|
||||
int16_t retTemp_;
|
||||
int16_t sysRetTemp_;
|
||||
int16_t hpTc3_; // condenser temp.
|
||||
int16_t hpTr1_; // compressor temp.
|
||||
int16_t hpTr3_; // cond. temp. heating
|
||||
int16_t hpTr4_; // cond. temp. clg
|
||||
int16_t hpTr5_; // suction line temp.
|
||||
int16_t hpTr6_; // hot gas temp.
|
||||
int16_t hpTl2_; // inlet air temperature
|
||||
int16_t hpTa4_; // drain pan temp.
|
||||
int16_t hpJr0_; // low pressure sensor
|
||||
int16_t hpJr1_; // high pressure sensor
|
||||
// HM200 temperatures
|
||||
int16_t flowTemp_; // TH1
|
||||
int16_t retTemp_; // TH2
|
||||
int16_t sysRetTemp_; // TH3
|
||||
int16_t hpTc3_; // condenser temp.
|
||||
int16_t hpTr1_; // compressor temp.
|
||||
int16_t hpTr3_; // cond. temp. heating
|
||||
int16_t hpTr4_; // cond. temp. clg
|
||||
int16_t hpTr5_; // suction line temp.
|
||||
int16_t hpTr6_; // hot gas temp.
|
||||
int16_t hpTl2_; // inlet air temperature
|
||||
int16_t hpTa4_; // drain pan temp.
|
||||
int16_t hpJr0_; // low pressure sensor
|
||||
int16_t hpJr1_; // high pressure sensor
|
||||
|
||||
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -71,6 +73,8 @@ class Heatpump : public EMSdevice {
|
||||
void process_HPFunctionTest(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPTemperature(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPFlowTemp(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPComp(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
bool set_controlStrategy(const char * value, const int8_t id);
|
||||
bool set_lowNoiseMode(const char * value, const int8_t id);
|
||||
bool set_lowNoiseStart(const char * value, const int8_t id);
|
||||
@@ -80,7 +84,6 @@ class Heatpump : public EMSdevice {
|
||||
bool set_energyPriceEl(const char * value, const int8_t id);
|
||||
bool set_energyPricePV(const char * value, const int8_t id);
|
||||
bool set_switchOverTemp(const char * value, const int8_t id);
|
||||
|
||||
bool set_airPurgeMode(const char * value, const int8_t id);
|
||||
bool set_heatPumpOutput(const char * value, const int8_t id);
|
||||
bool set_coolingCircuit(const char * value, const int8_t id);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -317,6 +317,7 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylPumpMod_, DeviceValueType::UINT, FL_(cylPumpMod), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &valveStatus_, DeviceValueType::BOOL, FL_(valveStatus), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vs1Status_, DeviceValueType::BOOL, FL_(vs1Status), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylHeated_, DeviceValueType::BOOL, FL_(cylHeated), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &collectorShutdown_, DeviceValueType::BOOL, FL_(collectorShutdown), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
@@ -820,6 +821,7 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
|
||||
* byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3)
|
||||
*/
|
||||
void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
|
||||
has_bitupdate(telegram, vs1Status_, 0, 2); // on if bit 2 set
|
||||
has_bitupdate(telegram, valveStatus_, 4, 2); // on if bit 2 set
|
||||
has_bitupdate(telegram, solarPump_, 10, 2); // on if bit 2 set
|
||||
has_bitupdate(telegram, solarPump2_, 1, 2); // on if bit 2 set
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -45,6 +45,7 @@ class Solar : public EMSdevice {
|
||||
uint8_t solarPump2Mod_; // PS4: modulation solar pump
|
||||
uint8_t m1Valve_; // M1: heat assistance valve
|
||||
uint8_t m1Power_; // M1: heat assistance valve
|
||||
uint8_t vs1Status_; // VS1: status
|
||||
|
||||
// 0x363 heat counter
|
||||
uint16_t heatCntFlowTemp_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -216,9 +216,7 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const ui
|
||||
return heating_circuit;
|
||||
}
|
||||
}
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("Heating circuit not fond on device 0x%02X", device_id());
|
||||
#endif
|
||||
return nullptr; // not found
|
||||
}
|
||||
|
||||
@@ -398,9 +396,9 @@ void Thermostat::add_ha_climate(std::shared_ptr<HeatingCircuit> hc) const {
|
||||
|
||||
if (Helpers::hasValue(hc->selTemp) && is_readable(&hc->selTemp)) {
|
||||
if (Helpers::hasValue(hc->roomTemp) && is_readable(&hc->roomTemp)) {
|
||||
hc->climate = 1;
|
||||
hc->climate = 1; // use roomTemp as we have a sensor
|
||||
} else {
|
||||
hc->climate = 0;
|
||||
hc->climate = 0; // use selTemp, as there is no sensor present in the thermostat
|
||||
}
|
||||
} else {
|
||||
hc->climate = EMS_VALUE_UINT_NOTSET;
|
||||
@@ -1192,10 +1190,9 @@ void Thermostat::process_RC30Temp(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes
|
||||
void Thermostat::process_RC35Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
// exit if the 15th byte (second from last) is 0x00, which I think is calculated flow setpoint temperature
|
||||
// with weather controlled RC35s this value is >=5, otherwise can be zero and our setpoint temps will be incorrect
|
||||
// see https://github.com/emsesp/EMS-ESP/issues/373#issuecomment-627907301
|
||||
if (telegram->offset > 0 || telegram->message_length < 15) {
|
||||
// Check if heatingciruit is active, see https://github.com/emsesp/EMS-ESP32/issues/786
|
||||
// some RC30_N have only 13 byte, use byte 0 for active detection.
|
||||
if (telegram->offset > 0 || telegram->message_data[0] == 0x00) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3299,9 +3296,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
write_command(set_typeid, offset, (uint8_t)(temperature * (float)factor), validate_typeid);
|
||||
return true;
|
||||
}
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("temperature mode %d not found", mode);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3344,7 +3339,6 @@ void Thermostat::register_device_values() {
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &errorCode_, DeviceValueType::STRING, FL_(errorCode), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &lastCode_, DeviceValueType::STRING, FL_(lastCode), DeviceValueUOM::NONE);
|
||||
|
||||
|
||||
switch (this->model()) {
|
||||
case EMS_DEVICE_FLAG_RC100:
|
||||
case EMS_DEVICE_FLAG_RC300:
|
||||
@@ -4000,7 +3994,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
register_device_value(tag, &hc->selTemp, DeviceValueType::SHORT, seltemp_divider, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 0, 30);
|
||||
}
|
||||
register_device_value(tag, &hc->roomTemp, DeviceValueType::SHORT, roomtemp_divider, FL_(roomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(climate), DeviceValueUOM::NONE, nullptr, 5, 30);
|
||||
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(haclimate), DeviceValueUOM::NONE, nullptr, 5, 30);
|
||||
|
||||
switch (model) {
|
||||
case EMS_DEVICE_FLAG_RC10:
|
||||
@@ -4294,4 +4288,4 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -555,4 +555,4 @@ class Thermostat : public EMSdevice {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -52,16 +52,18 @@ bool EMSdevice::has_entities() const {
|
||||
return found;
|
||||
}
|
||||
|
||||
std::string EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
|
||||
return (translate ? Helpers::translated_word(DeviceValue::DeviceValueTAG_s[tag]) : DeviceValue::DeviceValueTAG_s[tag][0]);
|
||||
// return translated tag name based on tag id
|
||||
const char * EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
|
||||
uint8_t tag_n = tag > DeviceValue::NUM_TAGS ? 0 : tag;
|
||||
return (translate ? Helpers::translated_word(DeviceValue::DeviceValueTAG_s[tag_n]) : DeviceValue::DeviceValueTAG_s[tag_n][0]);
|
||||
}
|
||||
|
||||
std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
|
||||
return (DeviceValue::DeviceValueTAG_mqtt[tag]);
|
||||
const char * EMSdevice::tag_to_mqtt(uint8_t tag) {
|
||||
return (DeviceValue::DeviceValueTAG_mqtt[tag > DeviceValue::NUM_TAGS ? 0 : tag]);
|
||||
}
|
||||
|
||||
// convert UOM to a string - translating only for hours/minutes/seconds
|
||||
std::string EMSdevice::uom_to_string(uint8_t uom) {
|
||||
// convert UOM to a char string - translating only for hours/minutes/seconds
|
||||
const char * EMSdevice::uom_to_string(uint8_t uom) {
|
||||
switch (uom) {
|
||||
case DeviceValueUOM::DEGREES:
|
||||
case DeviceValueUOM::DEGREES_R:
|
||||
@@ -77,24 +79,24 @@ std::string EMSdevice::uom_to_string(uint8_t uom) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string EMSdevice::brand_to_string() {
|
||||
const char * EMSdevice::brand_to_char() {
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
return "Bosch";
|
||||
return F_(bosch);
|
||||
case EMSdevice::Brand::JUNKERS:
|
||||
return "Junkers";
|
||||
return F_(junkers);
|
||||
case EMSdevice::Brand::BUDERUS:
|
||||
return "Buderus";
|
||||
return F_(buderus);
|
||||
case EMSdevice::Brand::NEFIT:
|
||||
return "Nefit";
|
||||
return F_(nefit);
|
||||
case EMSdevice::Brand::SIEGER:
|
||||
return "Sieger";
|
||||
return F_(sieger);
|
||||
case EMSdevice::Brand::WORCESTER:
|
||||
return "Worcester";
|
||||
return F_(worcester);
|
||||
case EMSdevice::Brand::IVT:
|
||||
return "IVT";
|
||||
return F_(ivt);
|
||||
default:
|
||||
return "";
|
||||
return F_(no_brand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +225,10 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
return DeviceType::HEATSOURCE;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(heatsource))) {
|
||||
return DeviceType::HEATSOURCE;
|
||||
}
|
||||
|
||||
return DeviceType::UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -264,8 +270,8 @@ const std::string EMSdevice::to_string() {
|
||||
return std::string(name_) + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
return brand_to_string() + " " + name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_
|
||||
+ ")";
|
||||
return std::string(brand_to_char()) + " " + name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_)
|
||||
+ ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
// returns out brand + device name
|
||||
@@ -275,12 +281,14 @@ const std::string EMSdevice::to_string_short() {
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + name_;
|
||||
}
|
||||
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + brand_to_string() + " " + name_;
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + brand_to_char() + " " + name_;
|
||||
}
|
||||
|
||||
// for each telegram that has the fetch value set (true) do a read request
|
||||
void EMSdevice::fetch_values() {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Fetching values for deviceID 0x%02X", device_id());
|
||||
#endif
|
||||
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
if (tf.fetch_) {
|
||||
@@ -291,7 +299,9 @@ void EMSdevice::fetch_values() {
|
||||
|
||||
// toggle on/off automatic fetch for a telegramID
|
||||
void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Toggling fetch for deviceID 0x%02X, telegramID 0x%02X to %d", device_id(), telegram_id, toggle);
|
||||
#endif
|
||||
|
||||
for (auto & tf : telegram_functions_) {
|
||||
if (tf.telegram_type_id_ == telegram_id) {
|
||||
@@ -311,7 +321,7 @@ bool EMSdevice::is_fetch(uint16_t telegram_id) const {
|
||||
}
|
||||
|
||||
// check for a tag to create a nest
|
||||
bool EMSdevice::has_tag(const uint8_t tag) const {
|
||||
bool EMSdevice::has_tags(const uint8_t tag) const {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if (dv.tag == tag && tag >= DeviceValueTAG::TAG_HC1) {
|
||||
return true;
|
||||
@@ -339,8 +349,9 @@ void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.type != DeviceValueType::CMD && !fullname.empty()) {
|
||||
// if we have a tag prefix it
|
||||
char key[50];
|
||||
if (!EMSdevice::tag_to_mqtt(dv.tag).empty()) {
|
||||
snprintf(key, sizeof(key), "%s.%s", EMSdevice::tag_to_mqtt(dv.tag).c_str(), dv.short_name);
|
||||
auto tag_s = EMSdevice::tag_to_mqtt(dv.tag);
|
||||
if (strlen(tag_s)) {
|
||||
snprintf(key, sizeof(key), "%s.%s", tag_s, dv.short_name);
|
||||
} else {
|
||||
snprintf(key, sizeof(key), "%s", dv.short_name);
|
||||
}
|
||||
@@ -351,7 +362,7 @@ void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
details.add(fullname);
|
||||
|
||||
// add uom
|
||||
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
|
||||
if (dv.uom != DeviceValueUOM::NONE) {
|
||||
details.add(EMSdevice::uom_to_string(dv.uom));
|
||||
}
|
||||
}
|
||||
@@ -454,48 +465,37 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const ch
|
||||
}
|
||||
|
||||
// add to device value library, also know now as a "device entity"
|
||||
// arguments are:
|
||||
// tag: to be used to group mqtt together, either as separate topics as a nested object
|
||||
// value_p: pointer to the value from the .h file
|
||||
// type: one of DeviceValueType
|
||||
// options: options for enum, which are translated as a list of lists
|
||||
// options_single: list of names
|
||||
// numeric_operator: to divide or multiply, see DeviceValueNumOps::
|
||||
// short_name: used in MQTT as keys
|
||||
// fullname: used in Web and Console unless empty (nullptr) - can be translated
|
||||
// uom: unit of measure from DeviceValueUOM
|
||||
// has_cmd: true if this is an associated command
|
||||
// min: min allowed value
|
||||
// max: max allowed value
|
||||
void EMSdevice::add_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const ** options,
|
||||
const char * const * options_single,
|
||||
int8_t numeric_operator,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
void EMSdevice::add_device_value(uint8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
|
||||
void * value_p, // pointer to the value from the .h file
|
||||
uint8_t type, // one of DeviceValueType
|
||||
const char * const ** options, // options for enum, which are translated as a list of lists
|
||||
const char * const * options_single, // list of names
|
||||
int8_t numeric_operator, // to divide or multiply, see DeviceValueNumOps::
|
||||
const char * const * name, // list of names, including shortname and translations
|
||||
uint8_t uom, // unit of measure from DeviceValueUOM
|
||||
const cmd_function_p f, // command function pointer
|
||||
int16_t min, // min allowed value
|
||||
uint16_t max // max allowed value
|
||||
) {
|
||||
// initialize the device value depending on it's type
|
||||
// ignoring DeviceValueType::CMD and DeviceValueType::TIME
|
||||
|
||||
if (type == DeviceValueType::STRING) {
|
||||
*(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later
|
||||
} else if (type == DeviceValueType::INT) {
|
||||
*(int8_t *)(value_p) = EMS_VALUE_DEFAULT_INT;
|
||||
*(int8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_INT_DUMMY : EMS_VALUE_DEFAULT_INT;
|
||||
} else if (type == DeviceValueType::UINT) {
|
||||
*(uint8_t *)(value_p) = EMS_VALUE_DEFAULT_UINT;
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_UINT_DUMMY : EMS_VALUE_DEFAULT_UINT;
|
||||
} else if (type == DeviceValueType::SHORT) {
|
||||
*(int16_t *)(value_p) = EMS_VALUE_DEFAULT_SHORT;
|
||||
*(int16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_SHORT_DUMMY : EMS_VALUE_DEFAULT_SHORT;
|
||||
} else if (type == DeviceValueType::USHORT) {
|
||||
*(uint16_t *)(value_p) = EMS_VALUE_DEFAULT_USHORT;
|
||||
*(uint16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_USHORT_DUMMY : EMS_VALUE_DEFAULT_USHORT;
|
||||
} else if ((type == DeviceValueType::ULONG) || (type == DeviceValueType::TIME)) {
|
||||
*(uint32_t *)(value_p) = EMS_VALUE_DEFAULT_ULONG;
|
||||
*(uint32_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_ULONG_DUMMY : EMS_VALUE_DEFAULT_ULONG;
|
||||
} else if (type == DeviceValueType::BOOL) {
|
||||
*(int8_t *)(value_p) = EMS_VALUE_DEFAULT_BOOL; // bool is uint8_t, but other initial value
|
||||
*(int8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_BOOL_DUMMY : EMS_VALUE_DEFAULT_BOOL; // bool is uint8_t, but other initial value
|
||||
} else if (type == DeviceValueType::ENUM) {
|
||||
*(uint8_t *)(value_p) = EMS_VALUE_DEFAULT_ENUM; // enums behave as uint8_t
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_ENUM_DUMMY : EMS_VALUE_DEFAULT_ENUM; // enums behave as uint8_t
|
||||
}
|
||||
|
||||
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
||||
@@ -507,7 +507,7 @@ void EMSdevice::add_device_value(uint8_t tag,
|
||||
// get fullname, getting translation if it exists
|
||||
const char * const * fullname;
|
||||
if (Helpers::count_items(name) == 1) {
|
||||
fullname = nullptr; // no translations available, use empty to prevent crash
|
||||
fullname = nullptr; // no translations available, use empty
|
||||
} else {
|
||||
fullname = &name[1]; // translations start at index 1
|
||||
}
|
||||
@@ -516,7 +516,13 @@ void EMSdevice::add_device_value(uint8_t tag,
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||
if ((entityCustomization.product_id == product_id()) && (entityCustomization.device_id == device_id())) {
|
||||
std::string entity = tag < DeviceValueTAG::TAG_HC1 ? short_name : tag_to_mqtt(tag) + "/" + short_name;
|
||||
char entity[70];
|
||||
if (tag < DeviceValueTAG::TAG_HC1) {
|
||||
strncpy(entity, short_name, sizeof(entity));
|
||||
} else {
|
||||
snprintf(entity, sizeof(entity), "%s/%s", tag_to_mqtt(tag), short_name);
|
||||
}
|
||||
|
||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||
// if there is an appended custom name, strip it to get the true entity name
|
||||
// and extract the new custom name
|
||||
@@ -544,6 +550,10 @@ void EMSdevice::add_device_value(uint8_t tag,
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignore) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add the device entity
|
||||
devicevalues_.emplace_back(
|
||||
device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, custom_fullname, uom, has_cmd, min, max, state);
|
||||
@@ -705,12 +715,12 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", device_type_2_device_name(device_type_), tag_to_mqtt(dv.tag).c_str(), dv.short_name);
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", device_type_2_device_name(device_type_), tag_to_mqtt(dv.tag), dv.short_name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", device_type_2_device_name(device_type_), (dv.short_name));
|
||||
}
|
||||
} else if (Mqtt::is_nested() && dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), tag_to_mqtt(dv.tag).c_str(), dv.short_name);
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), tag_to_mqtt(dv.tag), dv.short_name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), dv.short_name);
|
||||
}
|
||||
@@ -766,7 +776,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
}
|
||||
|
||||
if (payload[0] != '\0') {
|
||||
Mqtt::publish(topic, payload);
|
||||
Mqtt::queue_publish(topic, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,7 +790,7 @@ std::string EMSdevice::get_value_uom(const char * key) const {
|
||||
strlcpy(new_key, key, sizeof(new_key));
|
||||
char * key_p = new_key;
|
||||
|
||||
for (uint8_t i = 0; i < DeviceValue::tag_count; i++) {
|
||||
for (uint8_t i = 0; i < DeviceValue::NUM_TAGS; i++) {
|
||||
auto tag = Helpers::translated_word(DeviceValue::DeviceValueTAG_s[i]);
|
||||
if (tag) {
|
||||
std::string key2 = key; // copy string to a std::string so we can use the find function
|
||||
@@ -871,17 +881,19 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string
|
||||
|
||||
// add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
obj["id"] = mask + fullname;
|
||||
} else {
|
||||
if (dv.has_tag()) {
|
||||
obj["id"] = mask + tag_to_string(dv.tag) + " " + fullname;
|
||||
} else {
|
||||
obj["id"] = mask + fullname;
|
||||
}
|
||||
|
||||
// add commands and options
|
||||
if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
|
||||
// add the name of the Command function
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
obj["c"] = tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
char c_s[50];
|
||||
snprintf(c_s, sizeof(c_s), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||
obj["c"] = c_s;
|
||||
} else {
|
||||
obj["c"] = dv.short_name;
|
||||
}
|
||||
@@ -976,7 +988,9 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
|
||||
// id holds the shortname and must always have a value for the WebUI table to work
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
obj["id"] = tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
char id_s[50];
|
||||
snprintf(id_s, sizeof(id_s), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||
obj["id"] = id_s;
|
||||
} else {
|
||||
obj["id"] = dv.short_name;
|
||||
}
|
||||
@@ -986,12 +1000,12 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
auto fullname = Helpers::translated_word(dv.fullname);
|
||||
if (dv.type != DeviceValueType::CMD) {
|
||||
if (fullname) {
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
obj["n"] = fullname;
|
||||
} else {
|
||||
if (dv.has_tag()) {
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), fullname);
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname);
|
||||
obj["n"] = name;
|
||||
} else {
|
||||
obj["n"] = fullname;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1018,9 +1032,10 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||
if ((entityCustomization.device_id == device_id())) {
|
||||
if (entityCustomization.device_id == device_id()) {
|
||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||
if (mask & 0x80) {
|
||||
@@ -1038,7 +1053,8 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
|
||||
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (dv.tag == tag && dv.short_name == FL_(climate[0])) {
|
||||
// TODO check if pointer reference is safe. strcpy better?
|
||||
if (dv.tag == tag && dv.short_name == FL_(haclimate[0])) {
|
||||
if (dv.min != min || dv.max != max) {
|
||||
dv.min = min;
|
||||
dv.max = max;
|
||||
@@ -1054,7 +1070,12 @@ void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
|
||||
// returns true if the entity has a mask set (not 0 the default)
|
||||
void EMSdevice::setCustomEntity(const std::string & entity_id) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
char entity_name[70];
|
||||
if (dv.tag < DeviceValueTAG::TAG_HC1) {
|
||||
strncpy(entity_name, dv.short_name, sizeof(entity_name));
|
||||
} else {
|
||||
snprintf(entity_name, sizeof(entity_name), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||
}
|
||||
|
||||
// extra shortname
|
||||
auto custom_name_pos = entity_id.find('|');
|
||||
@@ -1101,9 +1122,18 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
|
||||
// populate a string vector with entities that have masks set or have a custom name
|
||||
void EMSdevice::getCustomEntities(std::vector<std::string> & entity_ids) {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
uint8_t mask = dv.state >> 4;
|
||||
bool is_set = false;
|
||||
char name[100];
|
||||
name[0] = '\0';
|
||||
if (dv.has_tag()) {
|
||||
// prefix tag
|
||||
strcpy(name, tag_to_mqtt(dv.tag));
|
||||
strcat(name, "/");
|
||||
}
|
||||
strcat(name, dv.short_name);
|
||||
std::string entity_name = name;
|
||||
|
||||
uint8_t mask = dv.state >> 4;
|
||||
bool is_set = false;
|
||||
for (auto & eid : entity_ids) {
|
||||
if (DeviceValue::get_name(eid) == entity_name) {
|
||||
is_set = true;
|
||||
@@ -1120,7 +1150,7 @@ void EMSdevice::getCustomEntities(std::vector<std::string> & entity_ids) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// dumps all entity values in native English
|
||||
// the code is intended to run only once standalone, outside the ESP32 so not optimized for memory efficiency
|
||||
// pipe symbols (|) are escaped so they can be converted to Markdown in the Wiki
|
||||
@@ -1228,7 +1258,6 @@ void EMSdevice::dump_value_info() {
|
||||
char entityid[500];
|
||||
char entity_name[100];
|
||||
|
||||
|
||||
for (uint8_t count = 0; count < 2; count++) {
|
||||
if (count) {
|
||||
// new name, comes as last
|
||||
@@ -1247,7 +1276,7 @@ void EMSdevice::dump_value_info() {
|
||||
sizeof(entity_with_tag),
|
||||
"%s_%s_%s",
|
||||
device_type_2_device_name(device_type_),
|
||||
EMSdevice::tag_to_mqtt(dv.tag).c_str(),
|
||||
EMSdevice::tag_to_mqtt(dv.tag),
|
||||
entity_name);
|
||||
} else {
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%s_%s", device_type_2_device_name(device_type_), entity_name);
|
||||
@@ -1325,14 +1354,16 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
|
||||
auto fullname = dv.get_fullname();
|
||||
if (!fullname.empty()) {
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
json["fullname"] = fullname;
|
||||
if (dv.has_tag()) {
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname.c_str());
|
||||
json["name"] = name;
|
||||
} else {
|
||||
json["fullname"] = tag_to_string(dv.tag) + " " + fullname;
|
||||
json["fullname"] = fullname;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag_to_mqtt(dv.tag).empty()) {
|
||||
if (dv.tag != DeviceValueTAG::TAG_NONE) {
|
||||
json["circuit"] = tag_to_mqtt(dv.tag);
|
||||
}
|
||||
|
||||
@@ -1444,7 +1475,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
}
|
||||
|
||||
// add uom if it's not a " " (single space)
|
||||
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
|
||||
if (dv.uom != DeviceValueUOM::NONE) {
|
||||
json["uom"] = uom_to_string(dv.uom);
|
||||
}
|
||||
|
||||
@@ -1460,7 +1491,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
// if we're filtering on an attribute, go find it
|
||||
if (attribute_s) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("[DEBUG] Attribute '%s'", attribute_s);
|
||||
EMSESP::logger().debug("Attribute '%s'", attribute_s);
|
||||
#endif
|
||||
if (json.containsKey(attribute_s)) {
|
||||
JsonVariant data = json[attribute_s];
|
||||
@@ -1468,10 +1499,6 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
output["api_data"] = data;
|
||||
return true;
|
||||
} else {
|
||||
char error[100];
|
||||
snprintf(error, sizeof(error), "cannot find attribute %s in entity %s", attribute_s, command_s);
|
||||
output.clear();
|
||||
output["message"] = error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1492,7 +1519,7 @@ void EMSdevice::publish_all_values() {
|
||||
|
||||
// For each value in the device create the json object pair and add it to given json
|
||||
// return false if empty
|
||||
// this is used to create both the MQTT payloads, Console messages and Web API calls
|
||||
// this is used to create the MQTT payloads, Console messages and Web API calls
|
||||
bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, const bool nested, const uint8_t output_target) {
|
||||
bool has_values = false; // to see if we've added a value. it's faster than doing a json.size() at the end
|
||||
uint8_t old_tag = 255; // NAN
|
||||
@@ -1501,11 +1528,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
for (auto & dv : devicevalues_) {
|
||||
// check if it exists, there is a value for the entity. Set the flag to ACTIVE
|
||||
// not that this will override any previously removed states
|
||||
if (dv.hasValue()) {
|
||||
dv.add_state(DeviceValueState::DV_ACTIVE);
|
||||
} else {
|
||||
dv.remove_state(DeviceValueState::DV_ACTIVE);
|
||||
}
|
||||
(dv.hasValue()) ? dv.add_state(DeviceValueState::DV_ACTIVE) : dv.remove_state(DeviceValueState::DV_ACTIVE);
|
||||
|
||||
auto fullname = dv.get_fullname();
|
||||
|
||||
@@ -1519,16 +1542,22 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
has_values = true; // flagged if we actually have data
|
||||
|
||||
// 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) && dv.has_tag());
|
||||
|
||||
// create the name for the JSON key
|
||||
char name[80];
|
||||
|
||||
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
if (have_tag) {
|
||||
snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), fullname.c_str()); // prefix the tag
|
||||
char short_name[20];
|
||||
if (output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
snprintf(short_name, sizeof(short_name), " (%s)", dv.short_name);
|
||||
} else {
|
||||
strlcpy(name, fullname.c_str(), sizeof(name)); // use full name
|
||||
strcpy(short_name, "");
|
||||
}
|
||||
if (have_tag) {
|
||||
snprintf(name, sizeof(name), "%s %s%s", tag_to_string(dv.tag), fullname.c_str(), short_name); // prefix the tag
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "%s%s", fullname.c_str(), short_name);
|
||||
}
|
||||
} else {
|
||||
strlcpy(name, (dv.short_name), sizeof(name)); // use short name
|
||||
@@ -1613,6 +1642,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
json[name] = time_value;
|
||||
}
|
||||
}
|
||||
|
||||
// commenting out - we don't want Commands in MQTT or Console
|
||||
// else if (dv.type == DeviceValueType::CMD && output_target != EMSdevice::OUTPUT_TARGET::MQTT) {
|
||||
// json[name] = "";
|
||||
@@ -1642,22 +1672,6 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
return has_values;
|
||||
}
|
||||
|
||||
// remove the Home Assistant configs for each device value/entity if its not visible or active or marked as read-only
|
||||
// this is called when an MQTT publish is done via an EMS Device in emsesp.cpp::publish_device_values()
|
||||
void EMSdevice::mqtt_ha_entity_config_remove() {
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED)
|
||||
&& ((dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) || (!dv.has_state(DeviceValueState::DV_ACTIVE)))) {
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
if (dv.short_name == FL_(climate)[0]) {
|
||||
Mqtt::publish_ha_climate_config(dv.tag, false, true); // delete topic (remove = true)
|
||||
} else {
|
||||
Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the Home Assistant configs for each device value / entity
|
||||
// this is called when an MQTT publish is done via an EMS Device in emsesp.cpp::publish_device_values()
|
||||
void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
@@ -1667,25 +1681,32 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
// create climate if roomtemp is visible
|
||||
// create the discovery topic if if hasn't already been created, not a command (like reset) and is active and visible
|
||||
for (auto & dv : devicevalues_) {
|
||||
if ((dv.short_name == FL_(climate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) {
|
||||
if (!strcmp(dv.short_name, FL_(haclimate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) {
|
||||
if (*(int8_t *)(dv.value_p) == 1 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
|
||||
dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max); // roomTemp
|
||||
} else if (*(int8_t *)(dv.value_p) == 0
|
||||
&& (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || !dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
|
||||
dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, false, false, dv.min, dv.max);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, false, false, dv.min, dv.max); // no roomTemp
|
||||
}
|
||||
}
|
||||
|
||||
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && (dv.type != DeviceValueType::CMD) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
||||
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
||||
// create_device_config is only done once for the EMS device. It can added to any entity, so we take the first
|
||||
Mqtt::publish_ha_sensor_config(dv, name(), brand_to_string(), false, create_device_config);
|
||||
Mqtt::publish_ha_sensor_config(dv, name(), brand_to_char(), false, create_device_config);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
create_device_config = false; // only create the main config once
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (ESP.getFreeHeap() < (65 * 1024)) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ha_config_done(!create_device_config);
|
||||
@@ -1694,7 +1715,6 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
// remove all config topics in HA
|
||||
void EMSdevice::ha_config_clear() {
|
||||
for (auto & dv : devicevalues_) {
|
||||
Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true)
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
|
||||
@@ -1711,7 +1731,7 @@ bool EMSdevice::has_telegram_id(uint16_t id) const {
|
||||
}
|
||||
|
||||
// return the name of the telegram type
|
||||
std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) const {
|
||||
const char * EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) {
|
||||
// see if it's one of the common ones, like Version
|
||||
if (telegram->type_id == EMS_TYPE_VERSION) {
|
||||
return "Version";
|
||||
@@ -1725,7 +1745,7 @@ std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegr
|
||||
}
|
||||
}
|
||||
|
||||
return std::string{};
|
||||
return "";
|
||||
}
|
||||
|
||||
// take a telegram_type_id and call the matching handler
|
||||
@@ -1736,7 +1756,9 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
// if the data block is empty and we have not received data before, assume that this telegram
|
||||
// is not recognized by the bus master. So remove it from the automatic fetch list
|
||||
if (telegram->message_length == 0 && telegram->offset == 0 && !tf.received_) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_);
|
||||
#endif
|
||||
tf.fetch_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -47,15 +47,17 @@ class EMSdevice {
|
||||
// static functions, used outside the class like in console.cpp, command.cpp, emsesp.cpp, mqtt.cpp
|
||||
static const char * device_type_2_device_name(const uint8_t device_type);
|
||||
static uint8_t device_name_2_device_type(const char * topic);
|
||||
static std::string uom_to_string(uint8_t uom);
|
||||
static std::string tag_to_string(uint8_t tag, const bool translate = true);
|
||||
static std::string tag_to_mqtt(uint8_t tag);
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
|
||||
static const char * tag_to_string(uint8_t tag, const bool translate = true);
|
||||
static const char * uom_to_string(uint8_t uom);
|
||||
static const char * tag_to_mqtt(uint8_t tag);
|
||||
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
|
||||
const char * device_type_name(); // returns short non-translated device type name
|
||||
const char * device_type_2_device_name_translated(); // returns translated device type name
|
||||
|
||||
bool has_tag(const uint8_t tag) const;
|
||||
bool has_tags(const uint8_t tag) const;
|
||||
bool has_cmd(const char * cmd, const int8_t id) const;
|
||||
|
||||
inline uint8_t device_id() const {
|
||||
@@ -176,7 +178,7 @@ class EMSdevice {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string brand_to_string();
|
||||
const char * brand_to_char();
|
||||
const std::string to_string();
|
||||
const std::string to_string_short();
|
||||
|
||||
@@ -289,9 +291,8 @@ class EMSdevice {
|
||||
void publish_all_values();
|
||||
|
||||
void mqtt_ha_entity_config_create();
|
||||
void mqtt_ha_entity_config_remove();
|
||||
|
||||
std::string telegram_type_name(std::shared_ptr<const Telegram> telegram) const;
|
||||
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void fetch_values();
|
||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||
@@ -306,13 +307,6 @@ class EMSdevice {
|
||||
ha_config_done_ = v;
|
||||
}
|
||||
|
||||
bool ha_config_firstrun() const {
|
||||
return ha_config_firstrun_;
|
||||
}
|
||||
void ha_config_firstrun(const bool v) {
|
||||
ha_config_firstrun_ = v;
|
||||
}
|
||||
|
||||
enum Brand : uint8_t {
|
||||
NO_BRAND = 0, // 0
|
||||
BOSCH, // 1
|
||||
@@ -366,6 +360,7 @@ class EMSdevice {
|
||||
static constexpr uint8_t EMS_DEVICE_ID_RFSENSOR = 0x40; // RF sensor only sending, no reply
|
||||
static constexpr uint8_t EMS_DEVICE_ID_RFBASE = 0x50;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_ROOMTHERMOSTAT = 0x17; // TADO using this with no version reply
|
||||
static constexpr uint8_t EMS_DEVICE_ID_TADO_OLD = 0x19; // TADO using this with no broadcast and version
|
||||
|
||||
// generic type IDs
|
||||
static constexpr uint16_t EMS_TYPE_VERSION = 0x02; // type ID for Version information. Generic across all EMS devices.
|
||||
@@ -415,7 +410,17 @@ class EMSdevice {
|
||||
uint8_t count_entities();
|
||||
bool has_entities() const;
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
/*
|
||||
void reserve_device_values(uint8_t elements) {
|
||||
devicevalues_.reserve(elements);
|
||||
}
|
||||
|
||||
void reserve_telegram_functions(uint8_t elements) {
|
||||
telegram_functions_.reserve(elements);
|
||||
}
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
void dump_value_info();
|
||||
#endif
|
||||
|
||||
@@ -429,16 +434,15 @@ class EMSdevice {
|
||||
uint8_t flags_ = 0;
|
||||
uint8_t brand_ = Brand::NO_BRAND;
|
||||
|
||||
bool ha_config_done_ = false;
|
||||
bool has_update_ = false;
|
||||
bool ha_config_firstrun_ = true; // this means a first setup of HA is needed after a restart
|
||||
bool ha_config_done_ = false;
|
||||
bool has_update_ = false;
|
||||
|
||||
struct TelegramFunction {
|
||||
uint16_t telegram_type_id_; // it's type_id
|
||||
const char * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
bool received_;
|
||||
process_function_p process_function_;
|
||||
const uint16_t telegram_type_id_; // it's type_id
|
||||
const char * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
bool received_;
|
||||
const process_function_p process_function_;
|
||||
|
||||
TelegramFunction(uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, bool received, const process_function_p process_function)
|
||||
: telegram_type_id_(telegram_type_id)
|
||||
@@ -449,10 +453,6 @@ class EMSdevice {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
void debug_print_dv(const char * shortname);
|
||||
#endif
|
||||
|
||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
|
||||
std::vector<DeviceValue> devicevalues_; // all the device values
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -107,14 +107,14 @@ DeviceValue::DeviceValue(uint8_t device_type,
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
const char * DeviceValue::DeviceValueUOM_s[] = {
|
||||
|
||||
F_(uom_blank), F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0],
|
||||
F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv),
|
||||
F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_blank)
|
||||
F_(uom_blank), // 0
|
||||
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0],
|
||||
F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit),
|
||||
F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_blank)
|
||||
|
||||
};
|
||||
|
||||
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevice.h
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevicevalue.h
|
||||
const char * const * DeviceValue::DeviceValueTAG_s[] = {
|
||||
|
||||
FL_(tag_none), // ""
|
||||
@@ -160,7 +160,7 @@ const char * const * DeviceValue::DeviceValueTAG_s[] = {
|
||||
|
||||
};
|
||||
|
||||
// MQTT topics derived from tags
|
||||
// tags used in MQTT topic names. Macthes sequence from DeviceValueTAG_s
|
||||
const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
|
||||
|
||||
FL_(tag_none)[0], // ""
|
||||
@@ -207,7 +207,7 @@ const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
|
||||
};
|
||||
|
||||
// count #tags once at compile time
|
||||
size_t DeviceValue::tag_count = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(char * const *);
|
||||
uint8_t DeviceValue::NUM_TAGS = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(char * const *);
|
||||
|
||||
// checks whether the device value has an actual value
|
||||
// returns true if its valid
|
||||
@@ -252,6 +252,11 @@ bool DeviceValue::hasValue() const {
|
||||
return has_value;
|
||||
}
|
||||
|
||||
// See if the device value has a tag and it's not empty
|
||||
bool DeviceValue::has_tag() const {
|
||||
return ((tag < DeviceValue::NUM_TAGS) && (tag != TAG_NONE) && strlen(DeviceValueTAG_s[tag][0]));
|
||||
}
|
||||
|
||||
// set the min and max value for a device value
|
||||
// converts to signed int, which means rounding to an whole integer
|
||||
// returns false if there is no min/max needed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -179,6 +179,7 @@ class DeviceValue {
|
||||
uint8_t state);
|
||||
|
||||
bool hasValue() const;
|
||||
bool has_tag() const;
|
||||
bool get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max);
|
||||
|
||||
void set_custom_minmax();
|
||||
@@ -205,7 +206,7 @@ class DeviceValue {
|
||||
static const char * DeviceValueUOM_s[];
|
||||
static const char * const * DeviceValueTAG_s[];
|
||||
static const char * const DeviceValueTAG_mqtt[];
|
||||
static size_t tag_count; // # tags
|
||||
static uint8_t NUM_TAGS; // # tags
|
||||
};
|
||||
|
||||
}; // namespace emsesp
|
||||
|
||||
315
src/emsesp.cpp
315
src/emsesp.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -18,6 +18,10 @@
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
static_assert(uuid::thread_safe, "uuid-common must be thread-safe");
|
||||
static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe");
|
||||
static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe");
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
AsyncWebServer webServer(80);
|
||||
@@ -27,10 +31,12 @@ FS dummyFS;
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &dummyFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#else
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &LittleFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#endif
|
||||
|
||||
WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
@@ -49,12 +55,15 @@ uuid::log::Logger EMSESP::logger() {
|
||||
return logger_;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::syslog::SyslogService System::syslog_;
|
||||
#endif
|
||||
|
||||
// The services
|
||||
RxService EMSESP::rxservice_; // incoming Telegram Rx handler
|
||||
TxService EMSESP::txservice_; // outgoing Telegram Tx handler
|
||||
Mqtt EMSESP::mqtt_; // mqtt handler
|
||||
System EMSESP::system_; // core system services
|
||||
Console EMSESP::console_; // telnet and serial console
|
||||
DallasSensor EMSESP::dallassensor_; // Dallas sensors
|
||||
AnalogSensor EMSESP::analogsensor_; // Analog sensors
|
||||
Shower EMSESP::shower_; // Shower logic
|
||||
@@ -309,7 +318,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
|
||||
// Dump all entities to Serial out
|
||||
// this is intended to run within the OS with lots of available memory!
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
void EMSESP::dump_all_values(uuid::console::Shell & shell) {
|
||||
Serial.println("---- CSV START ----"); // marker use by py script
|
||||
// add header for CSV
|
||||
@@ -322,19 +331,27 @@ void EMSESP::dump_all_values(uuid::console::Shell & shell) {
|
||||
for (const auto & device : device_library_) {
|
||||
if (device_class.first == device.device_type) {
|
||||
uint8_t device_id = 0;
|
||||
// Mixer class looks at device_id to determine type, so fixing to 0x28 which will give all the settings except flowSetTemp
|
||||
if ((device.device_type == DeviceType::MIXER) && (device.flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS)) {
|
||||
// pick one as hc and the other as having wwc
|
||||
if (device.product_id == 160) { // MM100
|
||||
device_id = 0x28; // wwc
|
||||
// Mixer class looks at device_id to determine type and the tag
|
||||
// so fixing to 0x28 which will give all the settings except flowSetTemp
|
||||
if (device.device_type == DeviceType::MIXER) {
|
||||
if (device.flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
if (device.product_id == 160) { // MM100
|
||||
device_id = 0x28; // wwc
|
||||
} else {
|
||||
device_id = 0x20; // hc
|
||||
}
|
||||
} else {
|
||||
device_id = 0x20; // hc
|
||||
device_id = 0x20; // should cover all the other device types
|
||||
}
|
||||
}
|
||||
|
||||
// add the device and print out all the entities
|
||||
|
||||
// if (device.product_id == 69) { // only for testing mixer
|
||||
emsdevices.push_back(
|
||||
EMSFactory::add(device.device_type, device_id, device.product_id, "1.0", device.name, device.flags, EMSdevice::Brand::NO_BRAND));
|
||||
emsdevices.back()->dump_value_info(); // dump all the entity information
|
||||
emsdevices.back()->dump_value_info();
|
||||
// } // only for testing mixer
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,7 +375,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
// print header, with device type translated
|
||||
shell.printfln("%s: %s (%d)", emsdevice->device_type_2_device_name_translated(), emsdevice->to_string().c_str(), emsdevice->count_entities());
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN); // use max size
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE); // use max size
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::CONSOLE);
|
||||
@@ -427,7 +444,7 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
sensor.name().c_str(),
|
||||
COLOR_BRIGHT_GREEN,
|
||||
Helpers::render_value(s, sensor.value(), 2),
|
||||
EMSdevice::uom_to_string(sensor.uom()).c_str(),
|
||||
EMSdevice::uom_to_string(sensor.uom()),
|
||||
COLOR_RESET,
|
||||
Helpers::render_value(s2, sensor.factor(), 4),
|
||||
sensor.offset());
|
||||
@@ -467,7 +484,7 @@ void EMSESP::publish_all(bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
// on command "publish HA" loop and wait between devices for publishing all sensors
|
||||
// loop and wait between devices for publishing all values
|
||||
void EMSESP::publish_all_loop() {
|
||||
if (!Mqtt::connected() || !publish_all_idx_) {
|
||||
return;
|
||||
@@ -526,39 +543,21 @@ void EMSESP::reset_mqtt_ha() {
|
||||
}
|
||||
|
||||
// create json doc for the devices values and add to MQTT publish queue
|
||||
// this will also create the HA /config topic
|
||||
// this will also create the HA /config topic for each device value
|
||||
// generate_values_json is called to build the device value (dv) object array
|
||||
void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool need_publish = false;
|
||||
bool nested = (Mqtt::is_nested());
|
||||
|
||||
// group by device type
|
||||
if (Mqtt::ha_enabled()) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
// specially for MQTT Discovery
|
||||
// we may have some RETAINED /config topics that reference fields in the data payloads that no longer exist
|
||||
// remove them immediately to prevent HA from complaining
|
||||
// we need to do this first before the data payload is published, and only done once!
|
||||
if (emsdevice->ha_config_firstrun()) {
|
||||
emsdevice->ha_config_clear();
|
||||
emsdevice->ha_config_firstrun(false);
|
||||
return;
|
||||
} else {
|
||||
// see if we need to delete and /config topics before adding the payloads
|
||||
emsdevice->mqtt_ha_entity_config_remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint8_t tag = DeviceValueTAG::TAG_BOILER_DATA_WW; tag <= DeviceValueTAG::TAG_HS16; tag++) {
|
||||
JsonObject json_hc = json;
|
||||
bool nest_created = false;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
if (nested && !nest_created && emsdevice->has_tag(tag)) {
|
||||
if (nested && !nest_created && emsdevice->has_tags(tag)) {
|
||||
json_hc = doc.createNestedObject(EMSdevice::tag_to_mqtt(tag));
|
||||
nest_created = true;
|
||||
}
|
||||
@@ -566,16 +565,17 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
}
|
||||
}
|
||||
if (need_publish && ((!nested && tag >= DeviceValueTAG::TAG_DEVICE_DATA_WW) || (tag == DeviceValueTAG::TAG_BOILER_DATA_WW))) {
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, tag), json);
|
||||
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, tag), json);
|
||||
json = doc.to<JsonObject>();
|
||||
need_publish = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_publish) {
|
||||
if (doc.overflowed()) {
|
||||
LOG_WARNING("MQTT buffer overflow, please use individual topics");
|
||||
}
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
}
|
||||
|
||||
// we want to create the /config topic after the data payload to prevent HA from throwing up a warning
|
||||
@@ -636,7 +636,7 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
|
||||
doc["value"] = value;
|
||||
}
|
||||
|
||||
Mqtt::publish("response", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("response", doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
// builds json with the detail of each value, for a specific EMS device type or the dallas sensor
|
||||
@@ -1057,7 +1057,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
// see: https://github.com/emsesp/EMS-ESP32/issues/103#issuecomment-911717342 and https://github.com/emsesp/EMS-ESP32/issues/624
|
||||
name = "RF room temperature sensor";
|
||||
device_type = DeviceType::THERMOSTAT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_ROOMTHERMOSTAT) {
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_ROOMTHERMOSTAT || device_id == EMSdevice::EMS_DEVICE_ID_TADO_OLD) {
|
||||
name = "Generic thermostat";
|
||||
device_type = DeviceType::THERMOSTAT;
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_RC10 | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE;
|
||||
@@ -1068,7 +1068,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
name = "Terminal";
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_SERVICEKEY) {
|
||||
name = "Service key";
|
||||
name = "Service Key";
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_CASCADE) {
|
||||
name = "Cascade";
|
||||
@@ -1083,16 +1083,16 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
name = "Clock"; // generic
|
||||
device_type = DeviceType::CONTROLLER;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_CONTROLLER) {
|
||||
name = "Generic controller";
|
||||
name = "Generic Controller";
|
||||
device_type = DeviceType::CONTROLLER;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER) {
|
||||
name = "Generic boiler";
|
||||
name = "Generic Boiler";
|
||||
device_type = DeviceType::BOILER;
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP;
|
||||
LOG_WARNING("Unknown EMS boiler. Using generic profile. Please report on GitHub.");
|
||||
} else if (device_id >= 0x68 && device_id <= 0x6F) {
|
||||
// test for https://github.com/emsesp/EMS-ESP32/issues/882
|
||||
name = "Cascaded controller";
|
||||
name = "Cascaded Controller";
|
||||
device_type = DeviceType::CONTROLLER;
|
||||
} else {
|
||||
LOG_WARNING("Unrecognized EMS device (device ID 0x%02X, no product ID). Please report on GitHub.", device_id);
|
||||
@@ -1199,7 +1199,7 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t
|
||||
bool nest_created = false;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
if (!nest_created && emsdevice->has_tag(tag)) {
|
||||
if (!nest_created && emsdevice->has_tags(tag)) {
|
||||
output_hc = output.createNestedObject(EMSdevice::tag_to_mqtt(tag));
|
||||
nest_created = true;
|
||||
}
|
||||
@@ -1349,89 +1349,6 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
// start all the core services
|
||||
// the services must be loaded in the correct order
|
||||
void EMSESP::start() {
|
||||
console_.start_serial();
|
||||
|
||||
// start the file system
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!LittleFS.begin(true)) {
|
||||
Serial.println("LittleFS Mount Failed. EMS-ESP stopped.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// do a quick scan of the filesystem to see if we have a /config folder
|
||||
// so we know if this is a new install or not
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File root = LittleFS.open("/config");
|
||||
bool factory_settings = !root;
|
||||
if (!root) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
Serial.println("No config found, assuming factory settings");
|
||||
#endif
|
||||
}
|
||||
root.close();
|
||||
#else
|
||||
bool factory_settings = false;
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_NOTICE("System is running in Debug mode");
|
||||
#endif
|
||||
|
||||
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
|
||||
// see if we're restoring a settings file
|
||||
if (system_.check_restore()) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings...
|
||||
|
||||
// do any system upgrades
|
||||
if (system_.check_upgrade(factory_settings)) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
system_.reload_settings(); // ... and store some of the settings locally
|
||||
|
||||
webCustomizationService.begin(); // load the customizations
|
||||
|
||||
// start telnet service if it's enabled
|
||||
if (system_.telnet_enabled()) {
|
||||
console_.start_telnet();
|
||||
}
|
||||
|
||||
// start all the EMS-ESP services
|
||||
mqtt_.start(); // mqtt init
|
||||
system_.start(); // starts commands, led, adc, button, network, syslog & uart
|
||||
LOG_INFO(("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); // welcome message
|
||||
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
dallassensor_.start(); // Dallas external sensors
|
||||
analogsensor_.start(); // Analog external sensors
|
||||
webLogService.start(); // apply settings to weblog service
|
||||
|
||||
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
LOG_INFO("Loaded EMS device library (%d records)", device_library_.size());
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Mqtt::on_connect(); // simulate an MQTT connection
|
||||
#endif
|
||||
|
||||
webServer.begin(); // start the web server
|
||||
}
|
||||
|
||||
// fetch devices one by one
|
||||
void EMSESP::scheduled_fetch_values() {
|
||||
static uint8_t no = 0;
|
||||
@@ -1455,29 +1372,153 @@ void EMSESP::scheduled_fetch_values() {
|
||||
}
|
||||
}
|
||||
|
||||
// EMSESP main class
|
||||
|
||||
EMSESP::EMSESP()
|
||||
#ifndef EMSESP_STANDALONE
|
||||
: telnet_([this](Stream & stream, const IPAddress & addr, uint16_t port) -> std::shared_ptr<uuid::console::Shell> {
|
||||
return std::make_shared<emsesp::EMSESPConsole>(*this, stream, addr, port);
|
||||
})
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// start all the core services
|
||||
// the services must be loaded in the correct order
|
||||
void EMSESP::start() {
|
||||
serial_console_.begin(SERIAL_CONSOLE_BAUD_RATE);
|
||||
|
||||
shell_ = std::make_shared<EMSESPConsole>(*this, serial_console_, true);
|
||||
shell_->maximum_log_messages(100);
|
||||
shell_->start();
|
||||
#if defined(EMSESP_DEBUG)
|
||||
shell_->log_level(uuid::log::Level::DEBUG);
|
||||
#else
|
||||
shell_->log_level(uuid::log::Level::TRACE);
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
shell_->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when running tests
|
||||
#endif
|
||||
|
||||
// start the file system
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!LittleFS.begin(true)) {
|
||||
Serial.println("LittleFS Mount Failed. EMS-ESP stopped.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// do a quick scan of the filesystem to see if we have a /config folder
|
||||
// so we know if this is a new install or not
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File root = LittleFS.open("/config");
|
||||
bool factory_settings = !root;
|
||||
if (!root) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Serial.println("No config found, assuming factory settings");
|
||||
#endif
|
||||
}
|
||||
root.close();
|
||||
#else
|
||||
bool factory_settings = false;
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
|
||||
|
||||
LOG_DEBUG("System is running in Debug mode");
|
||||
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
|
||||
// see if we're restoring a settings file
|
||||
if (system_.check_restore()) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings...
|
||||
|
||||
// do any system upgrades
|
||||
if (system_.check_upgrade(factory_settings)) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
system_.reload_settings(); // ... and store some of the settings locally
|
||||
|
||||
webCustomizationService.begin(); // load the customizations
|
||||
webSchedulerService.begin(); // load the scheduler events
|
||||
|
||||
// start telnet service if it's enabled
|
||||
// default idle is 10 minutes, default write timeout is 0 (automatic)
|
||||
// note, this must be started after the network/wifi for ESP32 otherwise it'll crash
|
||||
if (system_.telnet_enabled()) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
telnet_.start();
|
||||
telnet_.initial_idle_timeout(3600); // in sec, one hour idle timeout
|
||||
telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second
|
||||
#endif
|
||||
}
|
||||
|
||||
// start all the EMS-ESP services
|
||||
mqtt_.start(); // mqtt init
|
||||
|
||||
system_.start(); // starts commands, led, adc, button, network, syslog & uart
|
||||
|
||||
LOG_INFO(("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); // welcome message
|
||||
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
dallassensor_.start(); // Dallas external sensors
|
||||
analogsensor_.start(); // Analog external sensors
|
||||
webLogService.start(); // apply settings to weblog service
|
||||
|
||||
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
LOG_INFO("Loaded EMS device library (%d records)", device_library_.size());
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Mqtt::on_connect(); // simulate an MQTT connection
|
||||
#endif
|
||||
|
||||
webServer.begin(); // start the web server
|
||||
}
|
||||
|
||||
// main loop calling all services
|
||||
void EMSESP::loop() {
|
||||
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 we're doing an OTA upload, skip everything except from console refresh
|
||||
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
|
||||
analogsensor_.loop(); // read analog sensor values
|
||||
publish_all_loop(); // with HA messages in parts to avoid flooding the mqtt queue
|
||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||
// service loops
|
||||
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
|
||||
analogsensor_.loop(); // read analog sensor values
|
||||
publish_all_loop(); // with HA messages in parts to avoid flooding the mqtt queue
|
||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||
webSchedulerService.loop(); // handle any scheduled jobs
|
||||
|
||||
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
scheduled_fetch_values();
|
||||
}
|
||||
|
||||
console_.loop(); // telnet/serial console
|
||||
uuid::loop();
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/78#issuecomment-877599145
|
||||
// delay(1); // helps telnet catch up. don't think its needed in ESP32 >3.1.0?
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (system_.telnet_enabled()) {
|
||||
telnet_.loop();
|
||||
}
|
||||
#else
|
||||
if (!shell_->running()) {
|
||||
::exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
Shell::loop_all();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
65
src/emsesp.h
65
src/emsesp.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "web/WebDataService.h"
|
||||
#include "web/WebSettingsService.h"
|
||||
#include "web/WebCustomizationService.h"
|
||||
#include "web/WebSchedulerService.h"
|
||||
#include "web/WebAPIService.h"
|
||||
#include "web/WebLogService.h"
|
||||
|
||||
@@ -55,6 +56,7 @@
|
||||
#include "dallassensor.h"
|
||||
#include "analogsensor.h"
|
||||
#include "console.h"
|
||||
#include "console_stream.h"
|
||||
#include "shower.h"
|
||||
#include "roomcontrol.h"
|
||||
#include "command.h"
|
||||
@@ -62,25 +64,16 @@
|
||||
|
||||
#define WATCH_ID_NONE 0 // no watch id set
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_JSON_SIZE_HA_CONFIG 1024 // for HA config payloads, using StaticJsonDocument
|
||||
#else
|
||||
#define EMSESP_JSON_SIZE_HA_CONFIG 2024 // for HA config payloads, using StaticJsonDocument
|
||||
#endif
|
||||
#define EMSESP_JSON_SIZE_SMALL 256 // for smaller json docs, using StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_MEDIUM 768 // for medium json docs from ems devices, using StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_LARGE 1024 // for large json docs from ems devices, like boiler or thermostat data, using StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_MEDIUM_DYN 1024 // for large json docs, using DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_LARGE_DYN 2048 // for very large json docs, using DynamicJsonDocument
|
||||
// uses StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_SMALL 256
|
||||
#define EMSESP_JSON_SIZE_MEDIUM 768
|
||||
#define EMSESP_JSON_SIZE_LARGE 1024 // used in forming HA config payloads, also in *AsyncJsonResponse
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_JSON_SIZE_XLARGE_DYN 4096 // for very very large json docs, using DynamicJsonDocument
|
||||
#else
|
||||
#define EMSESP_JSON_SIZE_XLARGE_DYN 16384 // for very very large json docs, using DynamicJsonDocument
|
||||
#endif
|
||||
|
||||
#define EMSESP_JSON_SIZE_XXLARGE_DYN 16384 // for extra very very large json docs, using DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_XXXLARGE_DYN 20480 // web output (maybe for 4 hc)
|
||||
// used in larger buffers like DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_XLARGE 2048
|
||||
#define EMSESP_JSON_SIZE_XXLARGE 4096
|
||||
#define EMSESP_JSON_SIZE_XXXLARGE 16384
|
||||
#define EMSESP_JSON_SIZE_XXXXLARGE 20480 // web output
|
||||
|
||||
// helpers for callback functions
|
||||
#define MAKE_PF_CB(__f) [&](std::shared_ptr<const Telegram> t) { __f(t); } // for Process Function callbacks to EMSDevice::process_function_p
|
||||
@@ -94,13 +87,18 @@ using DeviceValueState = emsesp::DeviceValue::DeviceValueState;
|
||||
using DeviceValueTAG = emsesp::DeviceValue::DeviceValueTAG;
|
||||
using DeviceValueNumOp = emsesp::DeviceValue::DeviceValueNumOp;
|
||||
|
||||
|
||||
class Shower; // forward declaration for compiler
|
||||
// forward declarations for compiler
|
||||
class EMSESPShell;
|
||||
class Shower;
|
||||
|
||||
class EMSESP {
|
||||
public:
|
||||
static void start();
|
||||
static void loop();
|
||||
EMSESP();
|
||||
~EMSESP() = default;
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
|
||||
static void publish_device_values(uint8_t device_type);
|
||||
static void publish_other_values();
|
||||
@@ -219,7 +217,6 @@ class EMSESP {
|
||||
static System system_;
|
||||
static DallasSensor dallassensor_;
|
||||
static AnalogSensor analogsensor_;
|
||||
static Console console_;
|
||||
static Shower shower_;
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
@@ -232,14 +229,9 @@ class EMSESP {
|
||||
static WebAPIService webAPIService;
|
||||
static WebLogService webLogService;
|
||||
static WebCustomizationService webCustomizationService;
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
static WebSchedulerService webSchedulerService;
|
||||
|
||||
private:
|
||||
EMSESP() = delete;
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static std::string device_tostring(const uint8_t device_id);
|
||||
static void process_UBADevices(std::shared_ptr<const Telegram> telegram);
|
||||
static void process_version(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -272,6 +264,19 @@ class EMSESP {
|
||||
static uint16_t wait_validate_;
|
||||
static bool wait_km_;
|
||||
static uint32_t last_fetch_;
|
||||
|
||||
// UUID stuff
|
||||
static constexpr auto & serial_console_ = Serial;
|
||||
static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200;
|
||||
|
||||
std::shared_ptr<EMSESPShell> shell_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::telnet::TelnetService telnet_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// EMSESP();
|
||||
static uuid::log::Logger logger_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
102
src/helpers.cpp
102
src/helpers.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -442,7 +442,6 @@ int Helpers::atoint(const char * value) {
|
||||
|
||||
// rounds a number to 2 decimal places
|
||||
// example: round2(3.14159) -> 3.14
|
||||
// From mvdp:
|
||||
// The conversion to Fahrenheit is different for absolute temperatures and relative temperatures like hysteresis.
|
||||
// fahrenheit=0 - off, no conversion
|
||||
// fahrenheit=1 - relative, 1.8t
|
||||
@@ -647,7 +646,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
std::string s_on = Helpers::translated_word(FL_(on));
|
||||
std::string s_off = Helpers::translated_word(FL_(off));
|
||||
|
||||
// stops when a nullptr is found, which is the end delimeter of a MAKE_PSTR_LIST()
|
||||
// stops when a nullptr is found, which is the end delimeter of a MAKE_TRANSLATION()
|
||||
// could use count_items() to avoid buffer over-run but this works
|
||||
for (value_ui = 0; strs[value_ui]; value_ui++) {
|
||||
std::string enum_str = toLower((strs[value_ui]));
|
||||
@@ -682,39 +681,49 @@ std::string Helpers::toUpper(std::string const & s) {
|
||||
// capitalizes one UTF-8 character in char array
|
||||
// works with Latin1 (1 byte), Polish amd some other (2 bytes) characters
|
||||
// TODO add special characters that occur in other supported languages
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#endif
|
||||
void Helpers::CharToUpperUTF8(char * c) {
|
||||
auto p = (c + 1); // pointer to 2nd char of 2-byte unicode char
|
||||
char p_v = *p; // value of 2nd char in 2-byte unicode char
|
||||
|
||||
switch (*c) {
|
||||
case 0xC3:
|
||||
case (char)0xC3:
|
||||
// grave, acute, circumflex, diaeresis, etc.
|
||||
if ((*(c + 1) >= 0xA0) && (*(c + 1) <= 0xBE)) {
|
||||
*(c + 1) -= 0x20;
|
||||
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
|
||||
*p -= 0x20;
|
||||
}
|
||||
break;
|
||||
case 0xC4:
|
||||
switch (*(c + 1)) {
|
||||
case 0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
|
||||
case 0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
|
||||
case 0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
|
||||
*(c + 1) -= 1;
|
||||
case (char)0xC4:
|
||||
switch (p_v) {
|
||||
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
|
||||
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
|
||||
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xC5:
|
||||
switch (*(c + 1)) {
|
||||
case 0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
|
||||
case 0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
|
||||
case 0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
|
||||
case 0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
|
||||
case 0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
|
||||
*(c + 1) -= 1;
|
||||
case (char)0xC5:
|
||||
switch (p_v) {
|
||||
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
|
||||
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
|
||||
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
|
||||
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
|
||||
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*c = toupper(*c); //works on Latin1 letters
|
||||
*c = toupper(*c); // works on Latin1 letters
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// replace char in char string
|
||||
void Helpers::replace_char(char * str, char find, char replace) {
|
||||
@@ -774,4 +783,55 @@ const char * Helpers::translated_word(const char * const * strings, const bool f
|
||||
return strings[index];
|
||||
}
|
||||
|
||||
uint16_t Helpers::string2minutes(const std::string & str) {
|
||||
uint8_t i = 0;
|
||||
uint16_t res = 0;
|
||||
uint16_t tmp = 0;
|
||||
uint8_t state = 0;
|
||||
|
||||
while (str[i] != '\0') {
|
||||
// If we got a digit
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
tmp = tmp * 10 + str[i] - '0';
|
||||
}
|
||||
// Or if we got a colon
|
||||
else if (str[i] == ':') {
|
||||
// If we were reading the hours
|
||||
if (state == 0) {
|
||||
res = 60 * tmp;
|
||||
}
|
||||
// Or if we were reading the minutes
|
||||
else if (state == 1) {
|
||||
if (tmp > 60) {
|
||||
return 0;
|
||||
}
|
||||
Serial.print("*");
|
||||
Serial.print(tmp);
|
||||
Serial.println("*");
|
||||
|
||||
res += tmp;
|
||||
}
|
||||
// Or we got an extra colon
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
state++;
|
||||
tmp = 0;
|
||||
}
|
||||
|
||||
// Or we got something wrong
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (state == 1 && tmp < 60) {
|
||||
return res + tmp;
|
||||
} else {
|
||||
return 0; // Or if we were not, something is wrong in the given string
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -49,6 +49,7 @@ class Helpers {
|
||||
static int atoint(const char * value);
|
||||
static bool check_abs(const int32_t i);
|
||||
static uint32_t abs(const int32_t i);
|
||||
static uint16_t string2minutes(const std::string & str);
|
||||
|
||||
static float transformNumFloat(float value, const int8_t numeric_operator, const uint8_t fahrenheit = 0);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -27,309 +27,319 @@
|
||||
*/
|
||||
|
||||
// common words
|
||||
MAKE_PSTR_WORD(debug)
|
||||
MAKE_PSTR_WORD(exit)
|
||||
MAKE_PSTR_WORD(help)
|
||||
MAKE_PSTR_WORD(log)
|
||||
MAKE_PSTR_WORD(logout)
|
||||
MAKE_PSTR_WORD(enabled)
|
||||
MAKE_PSTR_WORD(disabled)
|
||||
MAKE_PSTR_WORD(set)
|
||||
MAKE_PSTR_WORD(show)
|
||||
MAKE_PSTR_WORD(su)
|
||||
MAKE_PSTR_WORD(name)
|
||||
MAKE_PSTR_WORD(scan)
|
||||
MAKE_PSTR_WORD(password)
|
||||
MAKE_PSTR_WORD(read)
|
||||
MAKE_PSTR_WORD(version)
|
||||
MAKE_PSTR_WORD(values)
|
||||
MAKE_PSTR_WORD(system)
|
||||
MAKE_PSTR_WORD(fetch)
|
||||
MAKE_PSTR_WORD(restart)
|
||||
MAKE_PSTR_WORD(format)
|
||||
MAKE_PSTR_WORD(raw)
|
||||
MAKE_PSTR_WORD(watch)
|
||||
MAKE_PSTR_WORD(syslog)
|
||||
MAKE_PSTR_WORD(send)
|
||||
MAKE_PSTR_WORD(telegram)
|
||||
MAKE_PSTR_WORD(bus_id)
|
||||
MAKE_PSTR_WORD(tx_mode)
|
||||
MAKE_PSTR_WORD(ems)
|
||||
MAKE_PSTR_WORD(devices)
|
||||
MAKE_PSTR_WORD(shower)
|
||||
MAKE_PSTR_WORD(mqtt)
|
||||
MAKE_PSTR_WORD(emsesp)
|
||||
MAKE_PSTR_WORD(connected)
|
||||
MAKE_PSTR_WORD(disconnected)
|
||||
MAKE_PSTR_WORD(passwd)
|
||||
MAKE_PSTR_WORD(hostname)
|
||||
MAKE_PSTR_WORD(wifi)
|
||||
MAKE_PSTR_WORD(reconnect)
|
||||
MAKE_PSTR_WORD(ssid)
|
||||
MAKE_PSTR_WORD(heartbeat)
|
||||
MAKE_PSTR_WORD(users)
|
||||
MAKE_PSTR_WORD(publish)
|
||||
MAKE_PSTR_WORD(timeout)
|
||||
MAKE_PSTR_WORD(board_profile)
|
||||
MAKE_PSTR_WORD(setvalue)
|
||||
MAKE_WORD(exit)
|
||||
MAKE_WORD(help)
|
||||
MAKE_WORD(log)
|
||||
MAKE_WORD(set)
|
||||
MAKE_WORD(show)
|
||||
MAKE_WORD(su)
|
||||
MAKE_WORD(scan)
|
||||
MAKE_WORD(password)
|
||||
MAKE_WORD(read)
|
||||
MAKE_WORD(values)
|
||||
MAKE_WORD(system)
|
||||
MAKE_WORD(fetch)
|
||||
MAKE_WORD(restart)
|
||||
MAKE_WORD(format)
|
||||
MAKE_WORD(raw)
|
||||
MAKE_WORD(watch)
|
||||
MAKE_WORD(syslog)
|
||||
MAKE_WORD(send)
|
||||
MAKE_WORD(telegram)
|
||||
MAKE_WORD(bus_id)
|
||||
MAKE_WORD(tx_mode)
|
||||
MAKE_WORD(ems)
|
||||
MAKE_WORD(devices)
|
||||
MAKE_WORD(shower)
|
||||
MAKE_WORD(mqtt)
|
||||
MAKE_WORD(emsesp)
|
||||
MAKE_WORD(connected)
|
||||
MAKE_WORD(disconnected)
|
||||
MAKE_WORD(passwd)
|
||||
MAKE_WORD(hostname)
|
||||
MAKE_WORD(wifi)
|
||||
MAKE_WORD(reconnect)
|
||||
MAKE_WORD(ssid)
|
||||
MAKE_WORD(heartbeat)
|
||||
MAKE_WORD(users)
|
||||
MAKE_WORD(publish)
|
||||
MAKE_WORD(board_profile)
|
||||
MAKE_WORD(setvalue)
|
||||
|
||||
// for commands
|
||||
MAKE_PSTR_WORD(call)
|
||||
MAKE_PSTR_WORD(cmd)
|
||||
MAKE_PSTR_WORD(id)
|
||||
MAKE_PSTR_WORD(hc)
|
||||
MAKE_PSTR_WORD(wwc)
|
||||
MAKE_PSTR_WORD(device)
|
||||
MAKE_PSTR_WORD(data)
|
||||
MAKE_PSTR_WORD(command)
|
||||
MAKE_PSTR_WORD(commands)
|
||||
MAKE_PSTR_WORD(info)
|
||||
MAKE_PSTR_WORD(settings)
|
||||
MAKE_PSTR_WORD(customizations)
|
||||
MAKE_PSTR_WORD(value)
|
||||
MAKE_PSTR_WORD(entities)
|
||||
MAKE_WORD(call)
|
||||
MAKE_WORD(cmd)
|
||||
MAKE_WORD(id)
|
||||
MAKE_WORD(hc)
|
||||
MAKE_WORD(wwc)
|
||||
MAKE_WORD(device)
|
||||
MAKE_WORD(data)
|
||||
MAKE_WORD(command)
|
||||
MAKE_WORD(commands)
|
||||
MAKE_WORD(info)
|
||||
MAKE_WORD(settings)
|
||||
MAKE_WORD(value)
|
||||
MAKE_WORD(entities)
|
||||
|
||||
// device types - lowercase, used in MQTT
|
||||
MAKE_PSTR_WORD(boiler)
|
||||
MAKE_PSTR_WORD(thermostat)
|
||||
MAKE_PSTR_WORD(switch)
|
||||
MAKE_PSTR_WORD(solar)
|
||||
MAKE_PSTR_WORD(mixer)
|
||||
MAKE_PSTR_WORD(gateway)
|
||||
MAKE_PSTR_WORD(controller)
|
||||
MAKE_PSTR_WORD(connect)
|
||||
MAKE_PSTR_WORD(heatpump)
|
||||
MAKE_PSTR_WORD(generic)
|
||||
MAKE_PSTR_WORD(analogsensor)
|
||||
MAKE_PSTR_WORD(dallassensor)
|
||||
MAKE_PSTR_WORD(alert)
|
||||
MAKE_PSTR_WORD(pump)
|
||||
MAKE_PSTR_WORD(heatsource)
|
||||
MAKE_WORD(boiler)
|
||||
MAKE_WORD(thermostat)
|
||||
MAKE_WORD(switch)
|
||||
MAKE_WORD(solar)
|
||||
MAKE_WORD(mixer)
|
||||
MAKE_WORD(gateway)
|
||||
MAKE_WORD(controller)
|
||||
MAKE_WORD(connect)
|
||||
MAKE_WORD(heatpump)
|
||||
MAKE_WORD(generic)
|
||||
MAKE_WORD(analogsensor)
|
||||
MAKE_WORD(dallassensor)
|
||||
MAKE_WORD(alert)
|
||||
MAKE_WORD(pump)
|
||||
MAKE_WORD(heatsource)
|
||||
|
||||
MAKE_PSTR(number, "number")
|
||||
MAKE_PSTR(enum, "enum")
|
||||
MAKE_PSTR(text, "text")
|
||||
// brands
|
||||
MAKE_WORD_CUSTOM(bosch, "Bosch")
|
||||
MAKE_WORD_CUSTOM(junkers, "Junkers")
|
||||
MAKE_WORD_CUSTOM(buderus, "Buderus")
|
||||
MAKE_WORD_CUSTOM(nefit, "Nefit")
|
||||
MAKE_WORD_CUSTOM(sieger, "Sieger")
|
||||
MAKE_WORD_CUSTOM(worcester, "Worcester")
|
||||
MAKE_WORD_CUSTOM(ivt, "IVT")
|
||||
MAKE_WORD_CUSTOM(no_brand, "")
|
||||
|
||||
// types
|
||||
MAKE_WORD_CUSTOM(number, "number")
|
||||
MAKE_WORD_CUSTOM(enum, "enum")
|
||||
MAKE_WORD_CUSTOM(text, "text")
|
||||
|
||||
// Console
|
||||
MAKE_PSTR(EMSESP, "EMS-ESP")
|
||||
MAKE_PSTR(host_fmt, "Host: %s")
|
||||
MAKE_PSTR(port_fmt, "Port: %d")
|
||||
MAKE_PSTR(hostname_fmt, "Hostname: %s")
|
||||
MAKE_PSTR(board_profile_fmt, "Board Profile: %s")
|
||||
MAKE_PSTR(mark_interval_fmt, "Mark interval: %lus")
|
||||
MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID: %s")
|
||||
MAKE_PSTR(wifi_password_fmt, "WiFi Password: %S")
|
||||
MAKE_PSTR(tx_mode_fmt, "Tx mode: %d")
|
||||
MAKE_PSTR(bus_id_fmt, "Bus ID: %02X")
|
||||
MAKE_PSTR(log_level_fmt, "Log level: %s")
|
||||
MAKE_PSTR(cmd_optional, "[cmd]")
|
||||
MAKE_PSTR(ha_optional, "[ha]")
|
||||
MAKE_PSTR(deep_optional, "[deep]")
|
||||
MAKE_PSTR(watchid_optional, "[ID]")
|
||||
MAKE_PSTR(watch_format_optional, "[off | on | raw | unknown]")
|
||||
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
||||
MAKE_PSTR(data_mandatory, "\"XX XX ...\"")
|
||||
MAKE_PSTR(asterisks, "********")
|
||||
MAKE_PSTR(n_mandatory, "<n>")
|
||||
MAKE_PSTR(sensorid_optional, "[sensor ID]")
|
||||
MAKE_PSTR(id_optional, "[id|hc]")
|
||||
MAKE_PSTR(data_optional, "[data]")
|
||||
MAKE_PSTR(offset_optional, "[offset]")
|
||||
MAKE_PSTR(length_optional, "[length]")
|
||||
MAKE_PSTR(typeid_mandatory, "<type ID>")
|
||||
MAKE_PSTR(deviceid_mandatory, "<device ID>")
|
||||
MAKE_PSTR(device_type_optional, "[device]")
|
||||
MAKE_PSTR(invalid_log_level, "Invalid log level")
|
||||
MAKE_PSTR(log_level_optional, "[level]")
|
||||
MAKE_PSTR(name_mandatory, "<name>")
|
||||
MAKE_PSTR(name_optional, "[name]")
|
||||
MAKE_PSTR(new_password_prompt1, "Enter new password: ")
|
||||
MAKE_PSTR(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_PSTR(password_prompt, "Password: ")
|
||||
MAKE_PSTR(unset, "<unset>")
|
||||
MAKE_WORD_CUSTOM(EMSESP, "EMS-ESP")
|
||||
MAKE_WORD_CUSTOM(host_fmt, "Host: %s")
|
||||
MAKE_WORD_CUSTOM(port_fmt, "Port: %d")
|
||||
MAKE_WORD_CUSTOM(hostname_fmt, "Hostname: %s")
|
||||
MAKE_WORD_CUSTOM(board_profile_fmt, "Board Profile: %s")
|
||||
MAKE_WORD_CUSTOM(mark_interval_fmt, "Mark interval: %lus")
|
||||
MAKE_WORD_CUSTOM(wifi_ssid_fmt, "WiFi SSID: %s")
|
||||
MAKE_WORD_CUSTOM(wifi_password_fmt, "WiFi Password: %S")
|
||||
MAKE_WORD_CUSTOM(tx_mode_fmt, "Tx mode: %d")
|
||||
MAKE_WORD_CUSTOM(bus_id_fmt, "Bus ID: %02X")
|
||||
MAKE_WORD_CUSTOM(log_level_fmt, "Log level: %s")
|
||||
MAKE_WORD_CUSTOM(cmd_optional, "[cmd]")
|
||||
MAKE_WORD_CUSTOM(ha_optional, "[ha]")
|
||||
MAKE_WORD_CUSTOM(deep_optional, "[deep]")
|
||||
MAKE_WORD_CUSTOM(watchid_optional, "[ID]")
|
||||
MAKE_WORD_CUSTOM(watch_format_optional, "[off | on | raw | unknown]")
|
||||
MAKE_WORD_CUSTOM(invalid_watch, "Invalid watch type")
|
||||
MAKE_WORD_CUSTOM(data_mandatory, "\"XX XX ...\"")
|
||||
MAKE_WORD_CUSTOM(asterisks, "********")
|
||||
MAKE_WORD_CUSTOM(n_mandatory, "<n>")
|
||||
MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]")
|
||||
MAKE_WORD_CUSTOM(id_optional, "[id|hc]")
|
||||
MAKE_WORD_CUSTOM(data_optional, "[data]")
|
||||
MAKE_WORD_CUSTOM(offset_optional, "[offset]")
|
||||
MAKE_WORD_CUSTOM(length_optional, "[length]")
|
||||
MAKE_WORD_CUSTOM(typeid_mandatory, "<type ID>")
|
||||
MAKE_WORD_CUSTOM(deviceid_mandatory, "<device ID>")
|
||||
MAKE_WORD_CUSTOM(device_type_optional, "[device]")
|
||||
MAKE_WORD_CUSTOM(invalid_log_level, "Invalid log level")
|
||||
MAKE_WORD_CUSTOM(log_level_optional, "[level]")
|
||||
MAKE_WORD_CUSTOM(name_mandatory, "<name>")
|
||||
MAKE_WORD_CUSTOM(name_optional, "[name]")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt1, "Enter new password: ")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_WORD_CUSTOM(password_prompt, "Password: ")
|
||||
MAKE_WORD_CUSTOM(unset, "<unset>")
|
||||
|
||||
// more common names that don't need translations
|
||||
MAKE_PSTR_LIST(1x3min, "1x3min")
|
||||
MAKE_PSTR_LIST(2x3min, "2x3min")
|
||||
MAKE_PSTR_LIST(3x3min, "3x3min")
|
||||
MAKE_PSTR_LIST(4x3min, "4x3min")
|
||||
MAKE_PSTR_LIST(5x3min, "5x3min")
|
||||
MAKE_PSTR_LIST(6x3min, "6x3min")
|
||||
MAKE_PSTR_LIST(auto, "auto")
|
||||
MAKE_PSTR_LIST(rc3x, "RC3x")
|
||||
MAKE_PSTR_LIST(rc20, "RC20")
|
||||
MAKE_PSTR_LIST(fb10, "FB10")
|
||||
MAKE_PSTR_LIST(fb100, "FB100")
|
||||
MAKE_PSTR_LIST(dash, "-")
|
||||
MAKE_PSTR_LIST(BLANK, "")
|
||||
MAKE_PSTR_LIST(pwm, "pwm")
|
||||
MAKE_PSTR_LIST(pwm_invers, "pwm inverse")
|
||||
MAKE_PSTR_LIST(mpc, "mpc")
|
||||
MAKE_PSTR_LIST(tempauto, "temp auto")
|
||||
MAKE_PSTR_LIST(bypass, "bypass")
|
||||
MAKE_PSTR_LIST(mixer, "mixer")
|
||||
MAKE_PSTR_LIST(monovalent, "monovalent")
|
||||
MAKE_PSTR_LIST(bivalent, "bivalent")
|
||||
MAKE_PSTR_LIST(n_o, "n_o")
|
||||
MAKE_PSTR_LIST(n_c, "n_c")
|
||||
MAKE_PSTR_LIST(prog1, "prog 1")
|
||||
MAKE_PSTR_LIST(prog2, "prog 2")
|
||||
MAKE_PSTR_LIST(proga, "prog a")
|
||||
MAKE_PSTR_LIST(progb, "prog b")
|
||||
MAKE_PSTR_LIST(progc, "prog c")
|
||||
MAKE_PSTR_LIST(progd, "prog d")
|
||||
MAKE_PSTR_LIST(proge, "prog e")
|
||||
MAKE_PSTR_LIST(progf, "prog f")
|
||||
MAKE_PSTR_LIST(rc35, "RC35")
|
||||
MAKE_PSTR_LIST(0kW, "0 kW")
|
||||
MAKE_PSTR_LIST(2kW, "2 kW")
|
||||
MAKE_PSTR_LIST(3kW, "3 kW")
|
||||
MAKE_PSTR_LIST(4kW, "4 kW")
|
||||
MAKE_PSTR_LIST(6kW, "6 kW")
|
||||
MAKE_PSTR_LIST(9kW, "9 kW")
|
||||
MAKE_NOTRANSLATION(1x3min, "1x3min")
|
||||
MAKE_NOTRANSLATION(2x3min, "2x3min")
|
||||
MAKE_NOTRANSLATION(3x3min, "3x3min")
|
||||
MAKE_NOTRANSLATION(4x3min, "4x3min")
|
||||
MAKE_NOTRANSLATION(5x3min, "5x3min")
|
||||
MAKE_NOTRANSLATION(6x3min, "6x3min")
|
||||
MAKE_NOTRANSLATION(auto, "auto")
|
||||
MAKE_NOTRANSLATION(rc3x, "RC3x")
|
||||
MAKE_NOTRANSLATION(rc20, "RC20")
|
||||
MAKE_NOTRANSLATION(fb10, "FB10")
|
||||
MAKE_NOTRANSLATION(fb100, "FB100")
|
||||
MAKE_NOTRANSLATION(dash, "-")
|
||||
MAKE_NOTRANSLATION(BLANK, "")
|
||||
MAKE_NOTRANSLATION(pwm, "pwm")
|
||||
MAKE_NOTRANSLATION(pwm_invers, "pwm inverse")
|
||||
MAKE_NOTRANSLATION(mpc, "mpc")
|
||||
MAKE_NOTRANSLATION(tempauto, "temp auto")
|
||||
MAKE_NOTRANSLATION(bypass, "bypass")
|
||||
MAKE_NOTRANSLATION(mixer, "mixer")
|
||||
MAKE_NOTRANSLATION(monovalent, "monovalent")
|
||||
MAKE_NOTRANSLATION(bivalent, "bivalent")
|
||||
MAKE_NOTRANSLATION(n_o, "n_o")
|
||||
MAKE_NOTRANSLATION(n_c, "n_c")
|
||||
MAKE_NOTRANSLATION(prog1, "prog 1")
|
||||
MAKE_NOTRANSLATION(prog2, "prog 2")
|
||||
MAKE_NOTRANSLATION(proga, "prog a")
|
||||
MAKE_NOTRANSLATION(progb, "prog b")
|
||||
MAKE_NOTRANSLATION(progc, "prog c")
|
||||
MAKE_NOTRANSLATION(progd, "prog d")
|
||||
MAKE_NOTRANSLATION(proge, "prog e")
|
||||
MAKE_NOTRANSLATION(progf, "prog f")
|
||||
MAKE_NOTRANSLATION(rc35, "RC35")
|
||||
MAKE_NOTRANSLATION(0kW, "0 kW")
|
||||
MAKE_NOTRANSLATION(2kW, "2 kW")
|
||||
MAKE_NOTRANSLATION(3kW, "3 kW")
|
||||
MAKE_NOTRANSLATION(4kW, "4 kW")
|
||||
MAKE_NOTRANSLATION(6kW, "6 kW")
|
||||
MAKE_NOTRANSLATION(9kW, "9 kW")
|
||||
|
||||
// templates - this are not translated and will be saved under options_single
|
||||
MAKE_PSTR_LIST(tpl_datetime, "Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
|
||||
MAKE_PSTR_LIST(tpl_switchtime, "Format: <nn> [ not_set | day hh:mm on|off ]")
|
||||
MAKE_PSTR_LIST(tpl_switchtime1, "Format: <nn> [ not_set | day hh:mm Tn ]")
|
||||
MAKE_PSTR_LIST(tpl_holidays, "Format: < dd.mm.yyyy-dd.mm.yyyy >")
|
||||
MAKE_PSTR_LIST(tpl_date, "Format: < dd.mm.yyyy >")
|
||||
MAKE_PSTR_LIST(tpl_input, "Format: <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_PSTR_LIST(tpl_input4, "Format: <inv>[<comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_NOTRANSLATION(tpl_datetime, "Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
|
||||
MAKE_NOTRANSLATION(tpl_switchtime, "Format: <nn> [ not_set | day hh:mm on|off ]")
|
||||
MAKE_NOTRANSLATION(tpl_switchtime1, "Format: <nn> [ not_set | day hh:mm Tn ]")
|
||||
MAKE_NOTRANSLATION(tpl_holidays, "Format: < dd.mm.yyyy-dd.mm.yyyy >")
|
||||
MAKE_NOTRANSLATION(tpl_date, "Format: < dd.mm.yyyy >")
|
||||
MAKE_NOTRANSLATION(tpl_input, "Format: <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_NOTRANSLATION(tpl_input4, "Format: <inv>[<comp><aux><cool><heat><dhw><pv>]")
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
MAKE_NOTRANSLATION(test_cmd, "run a test")
|
||||
#endif
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
// use empty string if want to suppress showing tags
|
||||
// mqtt tags must not have spaces
|
||||
MAKE_NOTRANSLATION(tag_none, "")
|
||||
MAKE_NOTRANSLATION(tag_heartbeat, "")
|
||||
MAKE_NOTRANSLATION(tag_device_data, "")
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// Translating hours/minute/seconds are done in emsdevice.cpp (uom_to_string())
|
||||
MAKE_PSTR(uom_blank, " ")
|
||||
MAKE_PSTR(uom_percent, "%")
|
||||
MAKE_PSTR(uom_degrees, "°C")
|
||||
MAKE_PSTR(uom_kwh, "kWh")
|
||||
MAKE_PSTR(uom_wh, "Wh")
|
||||
MAKE_PSTR(uom_bar, "bar")
|
||||
MAKE_PSTR(uom_ua, "µA")
|
||||
MAKE_PSTR(uom_lmin, "l/min")
|
||||
MAKE_PSTR(uom_kw, "kW")
|
||||
MAKE_PSTR(uom_w, "W")
|
||||
MAKE_PSTR(uom_kb, "KB")
|
||||
MAKE_PSTR(uom_dbm, "dBm")
|
||||
MAKE_PSTR(uom_fahrenheit, "°F")
|
||||
MAKE_PSTR(uom_mv, "mV")
|
||||
MAKE_PSTR(uom_sqm, "m²")
|
||||
MAKE_PSTR(uom_m3, "m³")
|
||||
MAKE_PSTR(uom_l, "l")
|
||||
MAKE_PSTR(uom_kmin, "K*min")
|
||||
MAKE_PSTR(uom_k, "K")
|
||||
MAKE_WORD_CUSTOM(uom_blank, " ")
|
||||
MAKE_WORD_CUSTOM(uom_percent, "%")
|
||||
MAKE_WORD_CUSTOM(uom_degrees, "°C")
|
||||
MAKE_WORD_CUSTOM(uom_kwh, "kWh")
|
||||
MAKE_WORD_CUSTOM(uom_wh, "Wh")
|
||||
MAKE_WORD_CUSTOM(uom_bar, "bar")
|
||||
MAKE_WORD_CUSTOM(uom_ua, "µA")
|
||||
MAKE_WORD_CUSTOM(uom_lmin, "l/min")
|
||||
MAKE_WORD_CUSTOM(uom_kw, "kW")
|
||||
MAKE_WORD_CUSTOM(uom_w, "W")
|
||||
MAKE_WORD_CUSTOM(uom_kb, "KB")
|
||||
MAKE_WORD_CUSTOM(uom_dbm, "dBm")
|
||||
MAKE_WORD_CUSTOM(uom_fahrenheit, "°F")
|
||||
MAKE_WORD_CUSTOM(uom_mv, "mV")
|
||||
MAKE_WORD_CUSTOM(uom_sqm, "m²")
|
||||
MAKE_WORD_CUSTOM(uom_m3, "m³")
|
||||
MAKE_WORD_CUSTOM(uom_l, "l")
|
||||
MAKE_WORD_CUSTOM(uom_kmin, "K*min")
|
||||
MAKE_WORD_CUSTOM(uom_k, "K")
|
||||
|
||||
// MQTT topics and prefixes
|
||||
MAKE_PSTR(heating_active, "heating_active")
|
||||
MAKE_PSTR(tapwater_active, "tapwater_active")
|
||||
MAKE_PSTR(response, "response")
|
||||
MAKE_PSTR(tag_boiler_data_ww_mqtt, "ww")
|
||||
MAKE_PSTR(tag_device_data_ww_mqtt, "")
|
||||
|
||||
// Home Assistant - this is special and has no translations
|
||||
MAKE_PSTR_LIST(climate, "HA climate config creation")
|
||||
MAKE_WORD_CUSTOM(heating_active, "heating_active")
|
||||
MAKE_WORD_CUSTOM(tapwater_active, "tapwater_active")
|
||||
MAKE_WORD_CUSTOM(response, "response")
|
||||
MAKE_WORD_CUSTOM(tag_boiler_data_ww_mqtt, "ww")
|
||||
MAKE_WORD_CUSTOM(tag_device_data_ww_mqtt, "")
|
||||
|
||||
// syslog
|
||||
MAKE_PSTR_LIST(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
|
||||
MAKE_ENUM_FIXED(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
|
||||
|
||||
// sensors
|
||||
MAKE_PSTR_LIST(counter, "counter")
|
||||
MAKE_PSTR_LIST(digital_out, "digital_out")
|
||||
MAKE_PSTR_LIST(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
|
||||
MAKE_ENUM_FIXED(counter, "counter")
|
||||
MAKE_ENUM_FIXED(digital_out, "digital_out")
|
||||
MAKE_ENUM_FIXED(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
|
||||
|
||||
// watch
|
||||
MAKE_PSTR_LIST(list_watch, "off", "on", "raw", "unknown")
|
||||
MAKE_ENUM_FIXED(list_watch, "off", "on", "raw", "unknown")
|
||||
|
||||
/*
|
||||
* The rest below are Enums and generated from translations lists
|
||||
*/
|
||||
|
||||
MAKE_PSTR_ENUM(enum_cylprio, FL_(cyl1), FL_(cyl2))
|
||||
MAKE_PSTR_ENUM(enum_progMode, FL_(prog1), FL_(prog2))
|
||||
MAKE_PSTR_ENUM(enum_progMode4, FL_(proga), FL_(progb), FL_(progc), FL_(progd), FL_(proge), FL_(progf))
|
||||
MAKE_PSTR_ENUM(enum_climate, FL_(seltemp), FL_(roomtemp))
|
||||
MAKE_PSTR_ENUM(enum_charge, FL_(chargepump), FL_(3wayvalve))
|
||||
MAKE_PSTR_ENUM(enum_freq, FL_(off), FL_(1x3min), FL_(2x3min), FL_(3x3min), FL_(4x3min), FL_(5x3min), FL_(6x3min), FL_(continuous))
|
||||
MAKE_PSTR_ENUM(enum_off_time_date_manual, FL_(off), FL_(time), FL_(date), FL_(manual))
|
||||
MAKE_PSTR_ENUM(enum_comfort, FL_(hot), FL_(eco), FL_(intelligent))
|
||||
MAKE_PSTR_ENUM(enum_comfort1, FL_(high_comfort), FL_(eco))
|
||||
MAKE_PSTR_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort))
|
||||
MAKE_PSTR_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer))
|
||||
MAKE_PSTR_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error))
|
||||
MAKE_PSTR_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_PSTR_ENUM(enum_pumpMode, FL_(proportional), FL_(deltaP1), FL_(deltaP2), FL_(deltaP3), FL_(deltaP4))
|
||||
MAKE_ENUM(enum_cylprio, FL_(cyl1), FL_(cyl2))
|
||||
MAKE_ENUM(enum_progMode, FL_(prog1), FL_(prog2))
|
||||
MAKE_ENUM(enum_progMode4, FL_(proga), FL_(progb), FL_(progc), FL_(progd), FL_(proge), FL_(progf))
|
||||
MAKE_ENUM(enum_climate, FL_(seltemp), FL_(roomtemp))
|
||||
MAKE_ENUM(enum_charge, FL_(chargepump), FL_(3wayvalve))
|
||||
MAKE_ENUM(enum_freq, FL_(off), FL_(1x3min), FL_(2x3min), FL_(3x3min), FL_(4x3min), FL_(5x3min), FL_(6x3min), FL_(continuous))
|
||||
MAKE_ENUM(enum_off_time_date_manual, FL_(off), FL_(time), FL_(date), FL_(manual))
|
||||
MAKE_ENUM(enum_comfort, FL_(hot), FL_(eco), FL_(intelligent))
|
||||
MAKE_ENUM(enum_comfort1, FL_(high_comfort), FL_(eco))
|
||||
MAKE_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort))
|
||||
MAKE_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer))
|
||||
MAKE_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error))
|
||||
MAKE_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_ENUM(enum_pumpMode, FL_(proportional), FL_(deltaP1), FL_(deltaP2), FL_(deltaP3), FL_(deltaP4))
|
||||
|
||||
// thermostat lists
|
||||
MAKE_PSTR_ENUM(enum_ibaMainDisplay, FL_(internal_temperature), FL_(internal_setpoint), FL_(external_temperature), FL_(burner_temperature), FL_(ww_temperature), FL_(functioning_mode), FL_(time), FL_(date), FL_(smoke_temperature))
|
||||
MAKE_PSTR_ENUM(enum_ibaLanguage, FL_(german), FL_(dutch), FL_(french), FL_(italian))
|
||||
MAKE_PSTR_ENUM(enum_ibaLanguage_RC30, FL_(german), FL_(dutch))
|
||||
MAKE_PSTR_ENUM(enum_floordrystatus, FL_(off), FL_(start), FL_(heat), FL_(hold), FL_(cool), FL_(end))
|
||||
MAKE_PSTR_ENUM(enum_ibaBuildingType, FL_(light), FL_(medium), FL_(heavy))
|
||||
MAKE_PSTR_ENUM(enum_PID, FL_(fast), FL_(medium), FL_(slow))
|
||||
MAKE_PSTR_ENUM(enum_wwMode, FL_(off), FL_(normal), FL_(comfort), FL_(auto), FL_(own_prog), FL_(eco))
|
||||
MAKE_PSTR_ENUM(enum_wwCircMode, FL_(off), FL_(on), FL_(auto), FL_(own_prog))
|
||||
MAKE_PSTR_ENUM(enum_wwMode2, FL_(off), FL_(on), FL_(auto))
|
||||
MAKE_PSTR_ENUM(enum_wwMode3, FL_(on), FL_(off), FL_(auto))
|
||||
MAKE_PSTR_ENUM(enum_heatingtype, FL_(off), FL_(radiator), FL_(convector), FL_(floor))
|
||||
MAKE_PSTR_ENUM(enum_summermode, FL_(summer), FL_(auto), FL_(winter))
|
||||
MAKE_PSTR_ENUM(enum_hpoperatingmode, FL_(off), FL_(auto), FL_(heating), FL_(cooling))
|
||||
MAKE_PSTR_ENUM(enum_summer, FL_(winter), FL_(summer))
|
||||
MAKE_PSTR_ENUM(enum_operatingstate, FL_(heating), FL_(off), FL_(cooling))
|
||||
MAKE_ENUM(enum_ibaMainDisplay, FL_(internal_temperature), FL_(internal_setpoint), FL_(external_temperature), FL_(burner_temperature), FL_(ww_temperature), FL_(functioning_mode), FL_(time), FL_(date), FL_(smoke_temperature))
|
||||
MAKE_ENUM(enum_ibaLanguage, FL_(german), FL_(dutch), FL_(french), FL_(italian))
|
||||
MAKE_ENUM(enum_ibaLanguage_RC30, FL_(german), FL_(dutch))
|
||||
MAKE_ENUM(enum_floordrystatus, FL_(off), FL_(start), FL_(heat), FL_(hold), FL_(cool), FL_(end))
|
||||
MAKE_ENUM(enum_ibaBuildingType, FL_(light), FL_(medium), FL_(heavy))
|
||||
MAKE_ENUM(enum_PID, FL_(fast), FL_(medium), FL_(slow))
|
||||
MAKE_ENUM(enum_wwMode, FL_(off), FL_(normal), FL_(comfort), FL_(auto), FL_(own_prog), FL_(eco))
|
||||
MAKE_ENUM(enum_wwCircMode, FL_(off), FL_(on), FL_(auto), FL_(own_prog))
|
||||
MAKE_ENUM(enum_wwMode2, FL_(off), FL_(on), FL_(auto))
|
||||
MAKE_ENUM(enum_wwMode3, FL_(on), FL_(off), FL_(auto))
|
||||
MAKE_ENUM(enum_heatingtype, FL_(off), FL_(radiator), FL_(convector), FL_(floor))
|
||||
MAKE_ENUM(enum_summermode, FL_(summer), FL_(auto), FL_(winter))
|
||||
MAKE_ENUM(enum_hpoperatingmode, FL_(off), FL_(auto), FL_(heating), FL_(cooling))
|
||||
MAKE_ENUM(enum_summer, FL_(winter), FL_(summer))
|
||||
MAKE_ENUM(enum_operatingstate, FL_(heating), FL_(off), FL_(cooling))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_mode, FL_(manual), FL_(auto)) // RC100, RC300, RC310
|
||||
MAKE_PSTR_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20, RC30
|
||||
MAKE_PSTR_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30_N, RC25, RC20_N
|
||||
MAKE_PSTR_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS
|
||||
MAKE_PSTR_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF
|
||||
MAKE_PSTR_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10
|
||||
MAKE_PSTR_ENUM(enum_mode_ha, FL_(off), FL_(heat), FL_(auto)) // HA climate
|
||||
MAKE_ENUM(enum_mode, FL_(manual), FL_(auto)) // RC100, RC300, RC310
|
||||
MAKE_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20, RC30
|
||||
MAKE_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30_N, RC25, RC20_N
|
||||
MAKE_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS
|
||||
MAKE_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF
|
||||
MAKE_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10
|
||||
MAKE_ENUM(enum_mode_ha, FL_(off), FL_(heat), FL_(auto)) // HA climate
|
||||
|
||||
MAKE_PSTR_ENUM(enum_modetype, FL_(eco), FL_(comfort))
|
||||
MAKE_PSTR_ENUM(enum_modetype3, FL_(night), FL_(day))
|
||||
MAKE_PSTR_ENUM(enum_modetype4, FL_(nofrost), FL_(eco), FL_(heat))
|
||||
MAKE_PSTR_ENUM(enum_modetype5, FL_(off), FL_(on))
|
||||
MAKE_ENUM(enum_modetype, FL_(eco), FL_(comfort))
|
||||
MAKE_ENUM(enum_modetype3, FL_(night), FL_(day))
|
||||
MAKE_ENUM(enum_modetype4, FL_(nofrost), FL_(eco), FL_(heat))
|
||||
MAKE_ENUM(enum_modetype5, FL_(off), FL_(on))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor))
|
||||
MAKE_PSTR_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3
|
||||
MAKE_PSTR_ENUM(enum_nofrostmode, FL_(off), FL_(room), FL_(outdoor))
|
||||
MAKE_PSTR_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
|
||||
MAKE_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor))
|
||||
MAKE_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3
|
||||
MAKE_ENUM(enum_nofrostmode, FL_(off), FL_(room), FL_(outdoor))
|
||||
MAKE_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant))
|
||||
MAKE_PSTR_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4
|
||||
MAKE_PSTR_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
|
||||
MAKE_PSTR_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
|
||||
MAKE_PSTR_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
|
||||
MAKE_PSTR_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
|
||||
MAKE_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant))
|
||||
MAKE_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4
|
||||
MAKE_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
|
||||
MAKE_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
|
||||
MAKE_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
|
||||
MAKE_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))
|
||||
MAKE_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_dayOfWeek, FL_(day_mo), FL_(day_tu), FL_(day_we), FL_(day_th), FL_(day_fr), FL_(day_sa), FL_(day_su), FL_(all))
|
||||
MAKE_PSTR_ENUM(enum_progMode2, FL_(own_1), FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors), FL_(new), FL_(own_2))
|
||||
MAKE_PSTR_ENUM(enum_progMode3, FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors))
|
||||
MAKE_PSTR_ENUM(enum_hybridStrategy, FL_(co2_optimized), FL_(cost_optimized), FL_(outside_temp_switched), FL_(co2_cost_mix))
|
||||
MAKE_PSTR_ENUM(enum_hybridStrategy1, FL_(cost_optimized), FL_(co2_optimized), FL_(outside_temp_alt), FL_(outside_temp_par), FL_(hp_prefered), FL_(boiler_only))
|
||||
MAKE_PSTR_ENUM(enum_lowNoiseMode, FL_(off), FL_(reduced_output), FL_(switchoff), FL_(perm))
|
||||
MAKE_ENUM(enum_dayOfWeek, FL_(day_mo), FL_(day_tu), FL_(day_we), FL_(day_th), FL_(day_fr), FL_(day_sa), FL_(day_su), FL_(all))
|
||||
MAKE_ENUM(enum_progMode2, FL_(own_1), FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors), FL_(new), FL_(own_2))
|
||||
MAKE_ENUM(enum_progMode3, FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors))
|
||||
MAKE_ENUM(enum_hybridStrategy, FL_(co2_optimized), FL_(cost_optimized), FL_(outside_temp_switched), FL_(co2_cost_mix))
|
||||
MAKE_ENUM(enum_hybridStrategy1, FL_(cost_optimized), FL_(co2_optimized), FL_(outside_temp_alt), FL_(outside_temp_par), FL_(hp_prefered), FL_(boiler_only))
|
||||
MAKE_ENUM(enum_lowNoiseMode, FL_(off), FL_(reduced_output), FL_(switchoff), FL_(perm))
|
||||
|
||||
// heat pump
|
||||
MAKE_PSTR_ENUM(enum_hpactivity, FL_(none), FL_(heating), FL_(cooling), FL_(hot_water), FL_(pool), FL_(unknown), FL_(defrost))
|
||||
MAKE_PSTR_ENUM(enum_silentMode, FL_(off), FL_(auto), FL_(on))
|
||||
MAKE_ENUM(enum_hpactivity, FL_(none), FL_(heating), FL_(cooling), FL_(hot_water), FL_(pool), FL_(unknown), FL_(defrost))
|
||||
MAKE_ENUM(enum_silentMode, FL_(off), FL_(auto), FL_(on))
|
||||
|
||||
// solar
|
||||
MAKE_PSTR_ENUM(enum_solarmode, FL_(constant), FL_(pwm), FL_(analog))
|
||||
MAKE_PSTR_ENUM(enum_collectortype, FL_(flat), FL_(vacuum))
|
||||
MAKE_PSTR_ENUM(enum_wwStatus2, FL_(BLANK), FL_(BLANK), FL_(BLANK), FL_(no_heat), FL_(BLANK), FL_(BLANK), FL_(heatrequest), FL_(BLANK), FL_(disinfecting), FL_(hold))
|
||||
MAKE_ENUM(enum_solarmode, FL_(constant), FL_(pwm), FL_(analog))
|
||||
MAKE_ENUM(enum_collectortype, FL_(flat), FL_(vacuum))
|
||||
MAKE_ENUM(enum_wwStatus2, FL_(BLANK), FL_(BLANK), FL_(BLANK), FL_(no_heat), FL_(BLANK), FL_(BLANK), FL_(heatrequest), FL_(BLANK), FL_(disinfecting), FL_(hold))
|
||||
|
||||
// mixer
|
||||
MAKE_PSTR_ENUM(enum_shunt, FL_(stopped), FL_(opening), FL_(closing), FL_(open), FL_(close))
|
||||
MAKE_PSTR_ENUM(enum_wwProgMode, FL_(std_prog), FL_(own_prog))
|
||||
MAKE_ENUM(enum_shunt, FL_(stopped), FL_(opening), FL_(closing), FL_(open), FL_(close))
|
||||
MAKE_ENUM(enum_wwProgMode, FL_(std_prog), FL_(own_prog))
|
||||
|
||||
// AM200 lists
|
||||
MAKE_PSTR_ENUM(enum_vr2Config, FL_(off), FL_(bypass))
|
||||
MAKE_PSTR_ENUM(enum_aPumpSignal, FL_(off), FL_(pwm), FL_(pwm_invers))
|
||||
MAKE_PSTR_ENUM(enum_bufBypass, FL_(no), FL_(mixer), FL_(valve))
|
||||
MAKE_PSTR_ENUM(enum_blockMode, FL_(off), FL_(auto), FL_(blocking))
|
||||
MAKE_PSTR_ENUM(enum_bufConfig, FL_(off), FL_(monovalent), FL_(bivalent))
|
||||
MAKE_PSTR_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
|
||||
MAKE_ENUM(enum_vr2Config, FL_(off), FL_(bypass))
|
||||
MAKE_ENUM(enum_aPumpSignal, FL_(off), FL_(pwm), FL_(pwm_invers))
|
||||
MAKE_ENUM(enum_bufBypass, FL_(no), FL_(mixer), FL_(valve))
|
||||
MAKE_ENUM(enum_blockMode, FL_(off), FL_(auto), FL_(blocking))
|
||||
MAKE_ENUM(enum_bufConfig, FL_(off), FL_(monovalent), FL_(bivalent))
|
||||
MAKE_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -18,10 +18,12 @@
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
static emsesp::EMSESP application;
|
||||
|
||||
void setup() {
|
||||
emsesp::EMSESP::start();
|
||||
application.start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
emsesp::EMSESP::loop();
|
||||
application.loop();
|
||||
}
|
||||
|
||||
367
src/mqtt.cpp
367
src/mqtt.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -42,6 +42,7 @@ uint8_t Mqtt::entity_format_;
|
||||
bool Mqtt::ha_enabled_;
|
||||
uint8_t Mqtt::nested_format_;
|
||||
std::string Mqtt::discovery_prefix_;
|
||||
uint8_t Mqtt::discovery_type_;
|
||||
bool Mqtt::send_response_;
|
||||
bool Mqtt::publish_single_;
|
||||
bool Mqtt::publish_single2cmd_;
|
||||
@@ -62,14 +63,14 @@ std::string Mqtt::lastpayload_ = "";
|
||||
|
||||
// Home Assistant specific
|
||||
// icons from https://materialdesignicons.com used with the UOMs (unit of measurements)
|
||||
MAKE_PSTR_WORD(measurement)
|
||||
MAKE_PSTR_WORD(total_increasing)
|
||||
MAKE_PSTR(icondegrees, "mdi:coolant-temperature") // DeviceValueUOM::DEGREES
|
||||
MAKE_PSTR(iconpercent, "mdi:percent-outline") // DeviceValueUOM::PERCENT
|
||||
MAKE_PSTR(iconkb, "mdi:memory") // DeviceValueUOM::KB
|
||||
MAKE_PSTR(iconlmin, "mdi:water-boiler") // DeviceValueUOM::LMIN
|
||||
MAKE_PSTR(iconua, "mdi:lightning-bolt-circle") // DeviceValueUOM::UA
|
||||
MAKE_PSTR(iconnum, "mdi:counter") // DeviceValueUOM::NONE
|
||||
MAKE_WORD(measurement)
|
||||
MAKE_WORD(total_increasing)
|
||||
MAKE_WORD_CUSTOM(icondegrees, "mdi:coolant-temperature") // DeviceValueUOM::DEGREES
|
||||
MAKE_WORD_CUSTOM(iconpercent, "mdi:percent-outline") // DeviceValueUOM::PERCENT
|
||||
MAKE_WORD_CUSTOM(iconkb, "mdi:memory") // DeviceValueUOM::KB
|
||||
MAKE_WORD_CUSTOM(iconlmin, "mdi:water-boiler") // DeviceValueUOM::LMIN
|
||||
MAKE_WORD_CUSTOM(iconua, "mdi:lightning-bolt-circle") // DeviceValueUOM::UA
|
||||
MAKE_WORD_CUSTOM(iconnum, "mdi:counter") // DeviceValueUOM::NONE
|
||||
|
||||
uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON};
|
||||
|
||||
@@ -136,7 +137,7 @@ void Mqtt::loop() {
|
||||
|
||||
uint32_t currentMillis = uuid::get_uptime();
|
||||
|
||||
// publish top item from MQTT queue to stop flooding
|
||||
// publish MQTT queue, but timed to avoid overloading the TCP pipe
|
||||
if ((uint32_t)(currentMillis - last_mqtt_poll_) > MQTT_PUBLISH_WAIT) {
|
||||
last_mqtt_poll_ = currentMillis;
|
||||
process_queue();
|
||||
@@ -211,7 +212,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell.printfln("MQTT queue (%d/%d messages):", mqtt_messages_.size(), MAX_MQTT_MESSAGES);
|
||||
shell.printfln("MQTT queue (%d):", mqtt_messages_.size());
|
||||
|
||||
for (const auto & message : mqtt_messages_) {
|
||||
auto content = message.content_;
|
||||
@@ -248,7 +249,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
shell.println();
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
// simulate receiving a MQTT message, used only for testing
|
||||
void Mqtt::incoming(const char * topic, const char * payload) {
|
||||
if (payload != nullptr) {
|
||||
@@ -300,16 +301,16 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons
|
||||
if (!(mf.mqtt_subfunction_)(message)) {
|
||||
LOG_ERROR("error: invalid payload %s for this topic %s", message, topic);
|
||||
if (send_response_) {
|
||||
Mqtt::publish("response", "error: invalid data");
|
||||
Mqtt::queue_publish("response", "error: invalid data");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE_DYN> output_doc;
|
||||
JsonObject input, output;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
|
||||
DynamicJsonDocument output_doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject input, output;
|
||||
|
||||
// convert payload into a json doc
|
||||
// if the payload doesn't not contain the key 'value' or 'data', treat the whole payload as the 'value'
|
||||
@@ -335,12 +336,12 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) cons
|
||||
}
|
||||
LOG_ERROR(error);
|
||||
if (send_response_) {
|
||||
Mqtt::publish("response", error);
|
||||
Mqtt::queue_publish("response", error);
|
||||
}
|
||||
} else {
|
||||
// all good, send back json output from call
|
||||
if (send_response_) {
|
||||
Mqtt::publish("response", output);
|
||||
Mqtt::queue_publish("response", output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,9 +371,7 @@ void Mqtt::show_topic_handlers(uuid::console::Shell & shell, const uint8_t devic
|
||||
void Mqtt::on_publish(uint16_t packetId) const {
|
||||
// find the MQTT message in the queue and remove it
|
||||
if (mqtt_messages_.empty()) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] No message stored for ACK pid %d", packetId);
|
||||
#endif
|
||||
LOG_DEBUG("No message stored for ACK pid %d", packetId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -381,7 +380,7 @@ void Mqtt::on_publish(uint16_t packetId) const {
|
||||
// if the last published failed, don't bother checking it. wait for the next retry
|
||||
if (mqtt_message.packet_id_ == 0) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] ACK for failed message pid 0");
|
||||
LOG_DEBUG("ACK for failed message pid 0");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -391,9 +390,7 @@ void Mqtt::on_publish(uint16_t packetId) const {
|
||||
mqtt_publish_fails_++; // increment error count
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] ACK pid %d", packetId);
|
||||
#endif
|
||||
LOG_DEBUG("ACK pid %d", packetId);
|
||||
|
||||
mqtt_messages_.pop_front(); // always remove from queue, regardless if there was a successful ACK
|
||||
}
|
||||
@@ -453,18 +450,24 @@ void Mqtt::start() {
|
||||
}
|
||||
|
||||
// if already initialized, don't do it again
|
||||
// also to prevent duplicated loading from MqttSettingsService::onConfigUpdated()
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
|
||||
// add the 'publish' command ('call system publish' in console or via API)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, FL_(publish_cmd));
|
||||
|
||||
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
|
||||
mqttClient_->onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
|
||||
if (!connecting_) {
|
||||
// only show the error once, not every 2 seconds
|
||||
if (!connecting_ && first_connect_attempted_) {
|
||||
return;
|
||||
}
|
||||
connecting_ = false;
|
||||
first_connect_attempted_ = true;
|
||||
connecting_ = false;
|
||||
if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {
|
||||
LOG_WARNING("MQTT disconnected: TCP");
|
||||
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) {
|
||||
@@ -482,7 +485,10 @@ void Mqtt::start() {
|
||||
|
||||
// create last will topic with the base prefixed. It has to be static because asyncmqttclient destroys the reference
|
||||
static char will_topic[MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "%s/status", mqtt_base_.c_str());
|
||||
if (!mqtt_base_.empty()) {
|
||||
snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "%s/status", mqtt_base_.c_str());
|
||||
}
|
||||
|
||||
mqttClient_->setWill(will_topic, 1, true, "offline"); // with qos 1, retain true
|
||||
|
||||
mqttClient_->onMessage([this](const char * topic, const char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
@@ -583,7 +589,7 @@ void Mqtt::on_connect() {
|
||||
resubscribe();
|
||||
|
||||
// publish to the last will topic (see Mqtt::start() function) to say we're alive
|
||||
publish_retain("status", "online", true); // with retain on
|
||||
queue_publish_retain("status", "online", true); // with retain on
|
||||
|
||||
mqtt_publish_fails_ = 0; // reset fail count to 0
|
||||
|
||||
@@ -601,7 +607,7 @@ void Mqtt::on_connect() {
|
||||
// e.g. homeassistant/sensor/ems-esp/status/config
|
||||
// all the values from the heartbeat payload will be added as attributes to the entity state
|
||||
void Mqtt::ha_status() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
|
||||
char uniq[70];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
@@ -632,13 +638,12 @@ void Mqtt::ha_status() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
dev["cu"] = "http://" + (EMSESP::system_.ethernet_connected() ? ETH.localIP().toString() : WiFi.localIP().toString());
|
||||
#endif
|
||||
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/system_status/config", mqtt_basename_.c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
// create the sensors - must match the MQTT payload keys
|
||||
// these are all from the heartbeat MQTT topic
|
||||
@@ -652,6 +657,7 @@ void Mqtt::ha_status() {
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, "Uptime (sec)", "uptime_sec", DeviceValueUOM::SECONDS);
|
||||
publish_system_ha_sensor_config(DeviceValueType::BOOL, "NTP status", "ntp_status", DeviceValueUOM::CONNECTIVITY);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, "Free memory", "freemem", DeviceValueUOM::KB);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, "Max Alloc", "max_alloc", DeviceValueUOM::KB);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, "MQTT fails", "mqttfails", DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, "Rx received", "rxreceived", DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, "Rx fails", "rxfails", DeviceValueUOM::NONE);
|
||||
@@ -661,93 +667,93 @@ void Mqtt::ha_status() {
|
||||
}
|
||||
|
||||
// add sub or pub task to the queue.
|
||||
// returns a pointer to the message created
|
||||
// the base is not included in the topic
|
||||
std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain) {
|
||||
void Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain) {
|
||||
if (topic.empty()) {
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// take the topic and prefix the base, unless its for HA
|
||||
std::shared_ptr<MqttMessage> message;
|
||||
message = std::make_shared<MqttMessage>(operation, topic, payload, retain);
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
if (operation == Operation::PUBLISH) {
|
||||
if (message->payload.empty()) {
|
||||
LOG_INFO("[DEBUG] Adding to queue: (publish) topic='%s' empty payload", message->topic.c_str());
|
||||
} else {
|
||||
LOG_INFO("[DEBUG] Adding to queue: (publish) topic='%s' payload=%s", message->topic.c_str(), message->payload.c_str());
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("[DEBUG] Adding to queue: (subscribe) topic='%s'", message->topic.c_str());
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// anything below 60MB available free heap is dangerously low, so we stop adding to prevent a crash
|
||||
// instead of doing a mqtt_messages_.pop_front()
|
||||
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES || ESP.getFreeHeap() < (60 * 1024)) {
|
||||
LOG_WARNING("Queue overflow (queue count=%d, topic=%s)", mqtt_messages_.size(), topic.c_str());
|
||||
mqtt_publish_fails_++;
|
||||
return; // don't add to top of queue
|
||||
}
|
||||
#endif
|
||||
|
||||
// if the queue is full, make room but removing the last one
|
||||
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) {
|
||||
mqtt_messages_.pop_front();
|
||||
LOG_WARNING("Queue overflow, removing one message");
|
||||
mqtt_publish_fails_++;
|
||||
// take the topic and prefix the base, unless its for HA
|
||||
std::shared_ptr<MqttMessage> message = std::make_shared<MqttMessage>(operation, topic, payload, retain);
|
||||
|
||||
if (operation == Operation::PUBLISH) {
|
||||
if (message->payload.empty()) {
|
||||
LOG_DEBUG("Adding to queue: (remove) topic='%s'", message->topic.c_str());
|
||||
} else {
|
||||
LOG_DEBUG("Adding to queue: (publish) topic='%s' payload=%s", message->topic.c_str(), message->payload.c_str());
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("Adding to queue: (subscribe) topic='%s'", message->topic.c_str());
|
||||
}
|
||||
|
||||
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
|
||||
|
||||
return mqtt_messages_.back().content_; // this is because the message has been moved
|
||||
return;
|
||||
}
|
||||
|
||||
// add MQTT message to queue, payload is a string
|
||||
std::shared_ptr<const MqttMessage> Mqtt::queue_publish_message(const std::string & topic, const std::string & payload, bool retain) {
|
||||
void Mqtt::queue_publish_message(const std::string & topic, const std::string & payload, bool retain) {
|
||||
if (!enabled()) {
|
||||
return nullptr;
|
||||
return;
|
||||
};
|
||||
return queue_message(Operation::PUBLISH, topic, payload, retain);
|
||||
queue_message(Operation::PUBLISH, topic, payload, retain);
|
||||
}
|
||||
|
||||
// add MQTT subscribe message to queue
|
||||
std::shared_ptr<const MqttMessage> Mqtt::queue_subscribe_message(const std::string & topic) {
|
||||
return queue_message(Operation::SUBSCRIBE, topic, "", false); // no payload
|
||||
void Mqtt::queue_subscribe_message(const std::string & topic) {
|
||||
queue_message(Operation::SUBSCRIBE, topic, "", false); // no payload
|
||||
}
|
||||
|
||||
// add MQTT unsubscribe message to queue
|
||||
std::shared_ptr<const MqttMessage> Mqtt::queue_unsubscribe_message(const std::string & topic) {
|
||||
return queue_message(Operation::UNSUBSCRIBE, topic, "", false); // no payload
|
||||
void Mqtt::queue_unsubscribe_message(const std::string & topic) {
|
||||
queue_message(Operation::UNSUBSCRIBE, topic, "", false); // no payload
|
||||
}
|
||||
|
||||
// MQTT Publish, using a user's retain flag
|
||||
void Mqtt::publish(const std::string & topic, const std::string & payload) {
|
||||
void Mqtt::queue_publish(const std::string & topic, const std::string & payload) {
|
||||
queue_publish_message(topic, payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
// MQTT Publish, using a user's retain flag - except for char * strings
|
||||
void Mqtt::publish(const char * topic, const char * payload) {
|
||||
void Mqtt::queue_publish(const char * topic, const char * payload) {
|
||||
queue_publish_message((topic), payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
// MQTT Publish, using a specific retain flag, topic is a flash string
|
||||
void Mqtt::publish(const char * topic, const std::string & payload) {
|
||||
void Mqtt::queue_publish(const char * topic, const std::string & payload) {
|
||||
queue_publish_message((topic), payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
void Mqtt::publish(const char * topic, const JsonObject & payload) {
|
||||
publish_retain(topic, payload, mqtt_retain_);
|
||||
void Mqtt::queue_publish(const char * topic, const JsonObject & payload) {
|
||||
queue_publish_retain(topic, payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
// publish json doc, only if its not empty
|
||||
void Mqtt::publish(const std::string & topic, const JsonObject & payload) {
|
||||
publish_retain(topic, payload, mqtt_retain_);
|
||||
void Mqtt::queue_publish(const std::string & topic, const JsonObject & payload) {
|
||||
queue_publish_retain(topic, payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
// MQTT Publish, using a specific retain flag, topic is a flash string, forcing retain flag
|
||||
void Mqtt::publish_retain(const char * topic, const std::string & payload, bool retain) {
|
||||
void Mqtt::queue_publish_retain(const char * topic, const std::string & payload, bool retain) {
|
||||
queue_publish_message((topic), payload, retain);
|
||||
}
|
||||
|
||||
// publish json doc, only if its not empty, using the retain flag
|
||||
void Mqtt::publish_retain(const std::string & topic, const JsonObject & payload, bool retain) {
|
||||
publish_retain(topic.c_str(), payload, retain);
|
||||
void Mqtt::queue_publish_retain(const std::string & topic, const JsonObject & payload, bool retain) {
|
||||
queue_publish_retain(topic.c_str(), payload, retain);
|
||||
}
|
||||
|
||||
void Mqtt::publish_retain(const char * topic, const JsonObject & payload, bool retain) {
|
||||
void Mqtt::queue_publish_retain(const char * topic, const JsonObject & payload, bool retain) {
|
||||
if (enabled() && payload.size()) {
|
||||
std::string payload_text;
|
||||
serializeJson(payload, payload_text); // convert json to string
|
||||
@@ -756,21 +762,20 @@ void Mqtt::publish_retain(const char * topic, const JsonObject & payload, bool r
|
||||
}
|
||||
|
||||
// publish empty payload to remove the topic
|
||||
void Mqtt::publish_ha(const char * topic) {
|
||||
void Mqtt::queue_remove_topic(const char * topic) {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string fulltopic = Mqtt::discovery_prefix() + topic;
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Publishing empty HA topic=%s", fulltopic.c_str());
|
||||
#endif
|
||||
|
||||
queue_publish_message(fulltopic, "", true); // publish with retain to remove from broker
|
||||
if (ha_enabled_) {
|
||||
queue_publish_message(Mqtt::discovery_prefix() + topic, "", true); // publish with retain to remove from broker
|
||||
} else {
|
||||
queue_publish_message(topic, "", true); // publish with retain to remove from broker
|
||||
}
|
||||
}
|
||||
|
||||
// publish a Home Assistant config topic and payload, with retain flag off.
|
||||
void Mqtt::publish_ha(const char * topic, const JsonObject & payload) {
|
||||
// queue a Home Assistant config topic and payload, with retain flag off.
|
||||
void Mqtt::queue_ha(const char * topic, const JsonObject & payload) {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -779,15 +784,7 @@ void Mqtt::publish_ha(const char * topic, const JsonObject & payload) {
|
||||
payload_text.reserve(measureJson(payload) + 1);
|
||||
serializeJson(payload, payload_text); // convert json to string
|
||||
|
||||
std::string fulltopic = Mqtt::discovery_prefix() + topic;
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
LOG_DEBUG("Publishing HA topic=%s, payload=%s", fulltopic.c_str(), payload_text.c_str());
|
||||
#elif defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[debug] Publishing HA topic=%s, payload=%s", fulltopic.c_str(), payload_text.c_str());
|
||||
#endif
|
||||
|
||||
// queue messages if the MQTT connection is not yet established. to ensure we don't miss messages
|
||||
queue_publish_message(fulltopic, payload_text, true); // with retain true
|
||||
queue_publish_message(Mqtt::discovery_prefix() + topic, payload_text, true); // with retain true
|
||||
}
|
||||
|
||||
// take top from queue and perform the publish or subscribe action
|
||||
@@ -812,9 +809,7 @@ void Mqtt::process_queue() {
|
||||
// if this has already been published and we're waiting for an ACK, don't publish again
|
||||
// it will have a real packet ID
|
||||
if (mqtt_message.packet_id_ > 0) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Waiting for QOS-ACK");
|
||||
#endif
|
||||
LOG_DEBUG("Waiting for QOS-ACK");
|
||||
// if we don't get the ack within 10 minutes, republish with new packet_id
|
||||
if (uuid::get_uptime_sec() - last_publish_queue_ < 600) {
|
||||
return;
|
||||
@@ -857,19 +852,25 @@ void Mqtt::process_queue() {
|
||||
}
|
||||
|
||||
// else try and publish it
|
||||
// this is where the *real* publish happens
|
||||
uint16_t packet_id = mqttClient_->publish(topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_);
|
||||
lasttopic_ = topic;
|
||||
lastpayload_ = message->payload;
|
||||
LOG_DEBUG("Publishing topic %s (#%02d, retain=%d, retry=%d, size=%d, pid=%d)",
|
||||
|
||||
LOG_DEBUG("Published topic %s (#%02d, retain=%d, retry=%d, size=%d, pid=%d)",
|
||||
topic,
|
||||
mqtt_message.id_,
|
||||
message->retain,
|
||||
mqtt_message.retry_count_ + 1,
|
||||
message->payload.size(),
|
||||
packet_id);
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("Payload:%s", message->payload.c_str()); // extra message for #784
|
||||
#endif
|
||||
|
||||
/*
|
||||
if (!message->payload.empty()) {
|
||||
LOG_DEBUG("Payload: %s", message->payload.c_str()); // extra message for #784
|
||||
}
|
||||
*/
|
||||
|
||||
if (packet_id == 0) {
|
||||
// it failed. if we retried n times, give up. remove from queue
|
||||
if (mqtt_message.retry_count_ == (MQTT_PUBLISH_MAX_RETRY - 1)) {
|
||||
@@ -889,9 +890,7 @@ void Mqtt::process_queue() {
|
||||
// but add the packet_id so we can check it later
|
||||
if (mqtt_qos_ != 0) {
|
||||
mqtt_messages_.front().packet_id_ = packet_id;
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Setting packetID for ACK to %d", packet_id);
|
||||
#endif
|
||||
LOG_DEBUG("Setting packetID for ACK to %d", packet_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -900,8 +899,8 @@ void Mqtt::process_queue() {
|
||||
|
||||
// create's a ha sensor config topic from a device value object
|
||||
// and also takes a flag (create_device_config) used to also create the main HA device config. This is only needed for one entity
|
||||
void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model, const std::string & brand, const bool remove, const bool create_device_config) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> dev_json;
|
||||
void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> dev_json;
|
||||
|
||||
// always create the ids
|
||||
JsonArray ids = dev_json.createNestedArray("ids");
|
||||
@@ -948,8 +947,8 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model,
|
||||
|
||||
// publish HA sensor for System using the heartbeat tag
|
||||
void Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
JsonObject dev_json = doc.createNestedObject("dev");
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
JsonObject dev_json = doc.createNestedObject("dev");
|
||||
|
||||
JsonArray ids = dev_json.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
@@ -963,7 +962,7 @@ void Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, cons
|
||||
void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType
|
||||
uint8_t tag, // EMSdevice::DeviceValueTAG
|
||||
const char * const fullname, // fullname, already translated
|
||||
const char * const en_name, // original name
|
||||
const char * const en_name, // original name in english
|
||||
const uint8_t device_type, // EMSdevice::DeviceType
|
||||
const char * const entity, // same as shortname
|
||||
const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE)
|
||||
@@ -983,15 +982,17 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
// create the device name
|
||||
auto device_name = EMSdevice::device_type_2_device_name(device_type);
|
||||
|
||||
// create entity by add the hc/wwc tag if present, separating with a _
|
||||
bool has_tag = ((tag < DeviceValue::NUM_TAGS) && (tag != DeviceValue::DeviceValueTAG::TAG_NONE) && strlen(DeviceValue::DeviceValueTAG_s[tag][0]));
|
||||
|
||||
// create entity by add the hc/wwc tag if present, separating with an _
|
||||
char entity_with_tag[50];
|
||||
if (tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%s_%s", EMSdevice::tag_to_mqtt(tag).c_str(), entity);
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%s_%s", EMSdevice::tag_to_mqtt(tag), entity);
|
||||
} else {
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%s", entity);
|
||||
}
|
||||
|
||||
// build unique identifier also used as object_id and becomes the Entity ID in HA
|
||||
// build unique identifier also used as object_id which also becomes the Entity ID in HA
|
||||
char uniq_id[80];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
// prefix base name to each uniq_id and use the shortname
|
||||
@@ -1005,15 +1006,14 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
char uniq_s[60];
|
||||
strlcpy(uniq_s, en_name, sizeof(uniq_s));
|
||||
Helpers::replace_char(uniq_s, ' ', '_');
|
||||
if (EMSdevice::tag_to_string(tag).empty()) {
|
||||
if (has_tag) {
|
||||
snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, Helpers::toLower(uniq_s).c_str());
|
||||
} else {
|
||||
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", device_name, EMSdevice::tag_to_string(tag, false).c_str(), Helpers::toLower(uniq_s).c_str());
|
||||
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", device_name, DeviceValue::DeviceValueTAG_s[tag][0], Helpers::toLower(uniq_s).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// build a config topic that will be prefix onto a HA type (e.g. number, switch)
|
||||
// e.g. homeassistant/number/ems-esp/thermostat_hc1_manualtemp
|
||||
char config_topic[70];
|
||||
snprintf(config_topic, sizeof(config_topic), "%s/%s_%s/config", mqtt_basename_.c_str(), device_name, entity_with_tag);
|
||||
|
||||
@@ -1030,9 +1030,15 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
case DeviceValueType::SHORT:
|
||||
case DeviceValueType::USHORT:
|
||||
case DeviceValueType::ULONG:
|
||||
// number - https://www.home-assistant.io/integrations/number.mqtt
|
||||
// https://developers.home-assistant.io/docs/core/entity/number
|
||||
snprintf(topic, sizeof(topic), "number/%s", config_topic);
|
||||
if (discovery_type() == 0) {
|
||||
// Home Assistant
|
||||
// number - https://www.home-assistant.io/integrations/number.mqtt
|
||||
snprintf(topic, sizeof(topic), "number/%s", config_topic);
|
||||
} else {
|
||||
// Domoticz
|
||||
// Does not support number, use sensor
|
||||
snprintf(topic, sizeof(topic), "sensor/%s", config_topic);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::BOOL:
|
||||
// switch - https://www.home-assistant.io/integrations/switch.mqtt
|
||||
@@ -1060,13 +1066,13 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
// if we're asking to remove this topic, send an empty payload and exit
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/196
|
||||
if (remove) {
|
||||
LOG_DEBUG("Removing HA config for %s", uniq_id);
|
||||
publish_ha(topic);
|
||||
LOG_DEBUG("Queuing removing topic %s", topic);
|
||||
queue_remove_topic(topic);
|
||||
return;
|
||||
}
|
||||
|
||||
// build the payload
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
doc["uniq_id"] = uniq_id;
|
||||
doc["obj_id"] = uniq_id; // same as unique_id
|
||||
|
||||
@@ -1074,6 +1080,8 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
const char * sc_ha = "stat_cla"; // state class
|
||||
const char * uom_ha = "unit_of_meas"; // unit of measure
|
||||
|
||||
char sample_val[30] = "0"; // sample, correct(!) entity value, used only to prevent warning/error in HA if real value is not published yet
|
||||
|
||||
// handle commands, which are device entities that are writable
|
||||
// we add the command topic parameter
|
||||
// note: there is no way to handle strings in HA so datetimes (e.g. set_datetime, set_holiday, set_wwswitchtime etc) are excluded
|
||||
@@ -1081,7 +1089,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
// command topic back to EMS-ESP
|
||||
char command_topic[MQTT_TOPIC_MAX_SIZE];
|
||||
if (tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s/%s", mqtt_basename_.c_str(), device_name, EMSdevice::tag_to_mqtt(tag).c_str(), entity);
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s/%s", mqtt_basename_.c_str(), device_name, EMSdevice::tag_to_mqtt(tag), entity);
|
||||
} else {
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", mqtt_basename_.c_str(), device_name, entity);
|
||||
}
|
||||
@@ -1089,10 +1097,11 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
|
||||
// for enums, add options
|
||||
if (type == DeviceValueType::ENUM) {
|
||||
JsonArray option_list = doc.createNestedArray("ops"); //options
|
||||
JsonArray option_list = doc.createNestedArray("ops"); // options
|
||||
for (uint8_t i = 0; i < options_size; i++) {
|
||||
option_list.add(Helpers::translated_word(options[i]));
|
||||
}
|
||||
snprintf(sample_val, sizeof(sample_val), "'%s'", Helpers::translated_word(options[0]));
|
||||
} else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) {
|
||||
// Must be Numeric....
|
||||
doc["mode"] = "box"; // auto, slider or box
|
||||
@@ -1109,6 +1118,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
if (dv_set_min != 0 || dv_set_max != 0) {
|
||||
doc["min"] = dv_set_min;
|
||||
doc["max"] = dv_set_max;
|
||||
snprintf(sample_val, sizeof(sample_val), "%i", dv_set_min);
|
||||
}
|
||||
|
||||
// set icons
|
||||
@@ -1136,27 +1146,26 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
char ha_name[70];
|
||||
char * F_name = strdup(fullname);
|
||||
Helpers::CharToUpperUTF8(F_name); // capitalize first letter
|
||||
if (EMSdevice::tag_to_string(tag).empty()) {
|
||||
snprintf(ha_name, sizeof(ha_name), "%s", F_name); // no tag
|
||||
if (has_tag) {
|
||||
// exclude heartbeat tag
|
||||
snprintf(ha_name, sizeof(ha_name), "%s %s", DeviceValue::DeviceValueTAG_s[tag][0], F_name);
|
||||
} else {
|
||||
snprintf(ha_name, sizeof(ha_name), "%s %s", EMSdevice::tag_to_string(tag).c_str(), F_name);
|
||||
snprintf(ha_name, sizeof(ha_name), "%s", F_name); // no tag
|
||||
}
|
||||
free(F_name); // very important!
|
||||
doc["name"] = ha_name;
|
||||
|
||||
// value template
|
||||
// if its nested mqtt format then use the appended entity name, otherwise take the original name
|
||||
char val_tpl[75];
|
||||
if (is_nested()) {
|
||||
if (tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s.%s}}", EMSdevice::tag_to_mqtt(tag).c_str(), entity);
|
||||
} else {
|
||||
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity);
|
||||
}
|
||||
char val_obj[100];
|
||||
char val_cond[200];
|
||||
if (is_nested() && tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json.%s.%s", EMSdevice::tag_to_mqtt(tag), entity);
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json.%s is defined and %s is defined", EMSdevice::tag_to_mqtt(tag), val_obj);
|
||||
} else {
|
||||
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity);
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json.%s", entity);
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
doc["val_tpl"] = val_tpl;
|
||||
|
||||
// special case to handle booleans
|
||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||
@@ -1165,6 +1174,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc["pl_on"] = true;
|
||||
doc["pl_off"] = false;
|
||||
snprintf(sample_val, sizeof(sample_val), "false");
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc["pl_on"] = 1;
|
||||
doc["pl_off"] = 0;
|
||||
@@ -1172,8 +1182,8 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
char result[12];
|
||||
doc["pl_on"] = Helpers::render_boolean(result, true);
|
||||
doc["pl_off"] = Helpers::render_boolean(result, false);
|
||||
snprintf(sample_val, sizeof(sample_val), "'%s'", Helpers::render_boolean(result, false));
|
||||
}
|
||||
doc[sc_ha] = F_(measurement); //do we want this???
|
||||
} else {
|
||||
// always set the uom, using the standards except for hours/minutes/seconds
|
||||
// using HA specific codes from https://github.com/home-assistant/core/blob/dev/homeassistant/const.py
|
||||
@@ -1184,10 +1194,10 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
} else if (uom == DeviceValueUOM::SECONDS) {
|
||||
doc[uom_ha] = "s";
|
||||
} else if (uom != DeviceValueUOM::NONE) {
|
||||
// default
|
||||
doc[uom_ha] = EMSdevice::uom_to_string(uom);
|
||||
doc[uom_ha] = EMSdevice::uom_to_string(uom); // default
|
||||
}
|
||||
}
|
||||
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
|
||||
|
||||
// this next section is adding the state class, device class and sometimes the icon
|
||||
// used for Sensor and Binary Sensor Entities in HA
|
||||
@@ -1284,7 +1294,11 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
// add the dev json object to the end
|
||||
doc["dev"] = dev_json;
|
||||
|
||||
publish_ha(topic, doc.as<JsonObject>());
|
||||
// add "availability" section
|
||||
add_avty_to_doc(stat_t, doc.as<JsonObject>(), val_cond);
|
||||
|
||||
// TODO queue it or send it directly via publish?
|
||||
queue_ha(topic, doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint16_t max) {
|
||||
@@ -1295,6 +1309,9 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
char hc_mode_s[30];
|
||||
char seltemp_s[30];
|
||||
char currtemp_s[30];
|
||||
char hc_mode_cond[80];
|
||||
char seltemp_cond[80];
|
||||
char currtemp_cond[170];
|
||||
char mode_str_tpl[400];
|
||||
char name_s[10];
|
||||
char uniq_id_s[60];
|
||||
@@ -1305,31 +1322,40 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
|
||||
snprintf(topic, sizeof(topic), "climate/%s/thermostat_hc%d/config", mqtt_basename_.c_str(), hc_num);
|
||||
if (remove) {
|
||||
publish_ha(topic); // publish empty payload with retain flag
|
||||
queue_remove_topic(topic); // publish empty payload with retain flag
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mqtt::is_nested()) {
|
||||
// nested format
|
||||
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.hc%d.mode", hc_num);
|
||||
snprintf(seltemp_s, sizeof(seltemp_s), "{{value_json.hc%d.seltemp}}", hc_num);
|
||||
snprintf(hc_mode_cond, sizeof(hc_mode_cond), "value_json.hc%d is undefined or %s is undefined", hc_num, hc_mode_s);
|
||||
snprintf(seltemp_s, sizeof(seltemp_s), "value_json.hc%d.seltemp", hc_num);
|
||||
snprintf(seltemp_cond, sizeof(seltemp_cond), "value_json.hc%d is defined and %s is defined", hc_num, seltemp_s);
|
||||
|
||||
if (has_roomtemp) {
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "{{value_json.hc%d.currtemp}}", hc_num);
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.hc%d.currtemp", hc_num);
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "value_json.hc%d is defined and %s is defined", hc_num, currtemp_s);
|
||||
}
|
||||
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str());
|
||||
} else {
|
||||
// single format
|
||||
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.mode");
|
||||
snprintf(seltemp_s, sizeof(seltemp_s), "{{value_json.seltemp}}");
|
||||
snprintf(hc_mode_cond, sizeof(hc_mode_cond), "%s is undefined", hc_mode_s);
|
||||
snprintf(seltemp_s, sizeof(seltemp_s), "value_json.seltemp");
|
||||
snprintf(seltemp_cond, sizeof(seltemp_cond), "%s is defined", seltemp_s);
|
||||
|
||||
if (has_roomtemp) {
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "{{value_json.currtemp}}");
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.currtemp");
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "%s is defined", currtemp_s);
|
||||
}
|
||||
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_HC1 + hc_num - 1).c_str());
|
||||
}
|
||||
|
||||
snprintf(mode_str_tpl,
|
||||
sizeof(mode_str_tpl),
|
||||
"{%%if %s=='manual'%%}heat{%%elif %s=='day'%%}heat{%%elif %s=='night'%%}off{%%elif %s=='off'%%}off{%%else%%}auto{%%endif%%}",
|
||||
"{%%if %s%%}off{%%elif %s=='manual'%%}heat{%%elif %s=='day'%%}heat{%%elif %s=='night'%%}off{%%elif %s=='off'%%}off{%%else%%}auto{%%endif%%}",
|
||||
hc_mode_cond,
|
||||
hc_mode_s,
|
||||
hc_mode_s,
|
||||
hc_mode_s,
|
||||
@@ -1346,7 +1372,7 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/seltemp", hc_num);
|
||||
snprintf(mode_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/mode", hc_num);
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc; // doc is 787 typically so 1024 should be enough
|
||||
|
||||
doc["~"] = mqtt_base_;
|
||||
doc["uniq_id"] = uniq_id_s;
|
||||
@@ -1356,17 +1382,17 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
doc["mode_stat_tpl"] = mode_str_tpl;
|
||||
doc["temp_cmd_t"] = temp_cmd_s;
|
||||
doc["temp_stat_t"] = topic_t;
|
||||
doc["temp_stat_tpl"] = seltemp_s;
|
||||
doc["mode_cmd_t"] = mode_cmd_s;
|
||||
doc["temp_stat_tpl"] = (std::string) "{{" + seltemp_s + " if " + seltemp_cond + " else 0}}";
|
||||
|
||||
if (has_roomtemp) {
|
||||
doc["curr_temp_t"] = topic_t;
|
||||
doc["curr_temp_tpl"] = currtemp_s;
|
||||
doc["curr_temp_tpl"] = (std::string) "{{" + currtemp_s + " if " + currtemp_cond + " else 0}}";
|
||||
}
|
||||
|
||||
doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
|
||||
doc["max_temp"] = Helpers::render_value(max_s, max, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
|
||||
doc["temp_step"] = "0.5";
|
||||
doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
|
||||
doc["max_temp"] = Helpers::render_value(max_s, max, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
|
||||
doc["temp_step"] = "0.5";
|
||||
doc["mode_cmd_t"] = mode_cmd_s;
|
||||
|
||||
// the HA climate component only responds to auto, heat and off
|
||||
JsonArray modes = doc.createNestedArray("modes");
|
||||
@@ -1379,7 +1405,10 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp-thermostat");
|
||||
|
||||
publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
// add "availability" section
|
||||
add_avty_to_doc(topic_t, doc.as<JsonObject>(), seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond);
|
||||
|
||||
queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
}
|
||||
|
||||
// based on the device and tag, create the MQTT topic name (without the basename)
|
||||
@@ -1394,11 +1423,49 @@ std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
|
||||
std::string topic = EMSdevice::device_type_2_device_name(device_type);
|
||||
|
||||
// if there is a tag add it
|
||||
if (!EMSdevice::tag_to_mqtt(tag).empty() && ((tag == DeviceValueTAG::TAG_BOILER_DATA_WW) || (!is_nested() && tag >= DeviceValueTAG::TAG_HC1))) {
|
||||
if ((tag == DeviceValueTAG::TAG_BOILER_DATA_WW) || (!is_nested() && tag >= DeviceValueTAG::TAG_HC1)) {
|
||||
return topic + "_data_" + EMSdevice::tag_to_mqtt(tag);
|
||||
} else {
|
||||
return topic + "_data";
|
||||
}
|
||||
}
|
||||
|
||||
// adds "availability" section to HA Discovery config
|
||||
void Mqtt::add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1, const char * cond2, const char * negcond) {
|
||||
const char * tpl_draft = "{{'online' if %s else 'offline'}}";
|
||||
char tpl[150];
|
||||
JsonArray avty = doc.createNestedArray("avty");
|
||||
|
||||
StaticJsonDocument<512> avty_json;
|
||||
|
||||
snprintf(tpl, sizeof(tpl), "%s/status", mqtt_base_.c_str());
|
||||
avty_json["t"] = tpl;
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, "value == 'online'");
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
avty.clear();
|
||||
|
||||
avty_json["t"] = state_t;
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, cond1 == nullptr ? "value is defined" : cond1);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
|
||||
if (cond2 != nullptr) {
|
||||
avty.clear();
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, cond2);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
}
|
||||
|
||||
if (negcond != nullptr) {
|
||||
avty.clear();
|
||||
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
}
|
||||
|
||||
doc["avty_mode"] = "all";
|
||||
}
|
||||
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
52
src/mqtt.h
52
src/mqtt.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -29,13 +29,12 @@
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// size of queue
|
||||
static constexpr uint16_t MAX_MQTT_MESSAGES = 300;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using mqtt_sub_function_p = std::function<bool(const char * message)>;
|
||||
using cmdfunction_p = std::function<bool(const char * data, const int8_t id)>;
|
||||
|
||||
struct MqttMessage {
|
||||
const uint8_t operation;
|
||||
@@ -79,19 +78,18 @@ class Mqtt {
|
||||
static void subscribe(const std::string & topic);
|
||||
static void resubscribe();
|
||||
|
||||
static void publish(const std::string & topic, const std::string & payload);
|
||||
static void publish(const char * topic, const char * payload);
|
||||
static void publish(const std::string & topic, const JsonObject & payload);
|
||||
static void publish(const char * topic, const JsonObject & payload);
|
||||
static void publish(const char * topic, const std::string & payload);
|
||||
static void publish_retain(const std::string & topic, const JsonObject & payload, bool retain);
|
||||
static void publish_retain(const char * topic, const std::string & payload, bool retain);
|
||||
static void publish_retain(const char * topic, const JsonObject & payload, bool retain);
|
||||
static void publish_ha(const char * topic, const JsonObject & payload);
|
||||
static void publish_ha(const char * topic);
|
||||
static void queue_publish(const std::string & topic, const std::string & payload);
|
||||
static void queue_publish(const char * topic, const char * payload);
|
||||
static void queue_publish(const std::string & topic, const JsonObject & payload);
|
||||
static void queue_publish(const char * topic, const JsonObject & payload);
|
||||
static void queue_publish(const char * topic, const std::string & payload);
|
||||
static void queue_publish_retain(const std::string & topic, const JsonObject & payload, bool retain);
|
||||
static void queue_publish_retain(const char * topic, const std::string & payload, bool retain);
|
||||
static void queue_publish_retain(const char * topic, const JsonObject & payload, bool retain);
|
||||
static void queue_ha(const char * topic, const JsonObject & payload);
|
||||
static void queue_remove_topic(const char * topic);
|
||||
|
||||
static void
|
||||
publish_ha_sensor_config(DeviceValue & dv, const std::string & model, const std::string & brand, const bool remove, const bool create_device_config = false);
|
||||
static void publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
|
||||
static void publish_ha_sensor_config(uint8_t type,
|
||||
uint8_t tag,
|
||||
const char * const fullname,
|
||||
@@ -116,7 +114,7 @@ class Mqtt {
|
||||
|
||||
static void ha_status();
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
void incoming(const char * topic, const char * payload = ""); // for testing only
|
||||
#endif
|
||||
|
||||
@@ -186,6 +184,10 @@ class Mqtt {
|
||||
return entity_format_;
|
||||
}
|
||||
|
||||
static uint8_t discovery_type() {
|
||||
return discovery_type_;
|
||||
}
|
||||
|
||||
static void nested_format(uint8_t nested_format) {
|
||||
nested_format_ = nested_format;
|
||||
}
|
||||
@@ -240,6 +242,9 @@ class Mqtt {
|
||||
|
||||
static std::string tag_to_topic(uint8_t device_type, uint8_t tag);
|
||||
|
||||
static void
|
||||
add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1 = nullptr, const char * cond2 = nullptr, const char * negcond = nullptr);
|
||||
|
||||
struct QueuedMqttMessage {
|
||||
const uint32_t id_;
|
||||
const std::shared_ptr<const MqttMessage> content_;
|
||||
@@ -261,13 +266,13 @@ class Mqtt {
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
static uint32_t mqtt_message_id_;
|
||||
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 100; // delay between sending publishes, to account for large payloads
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 100; // delay in ms between sending publishes, to account for large payloads
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
|
||||
static std::shared_ptr<const MqttMessage> queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain);
|
||||
static std::shared_ptr<const MqttMessage> queue_publish_message(const std::string & topic, const std::string & payload, bool retain);
|
||||
static std::shared_ptr<const MqttMessage> queue_subscribe_message(const std::string & topic);
|
||||
static std::shared_ptr<const MqttMessage> queue_unsubscribe_message(const std::string & topic);
|
||||
static void queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain);
|
||||
static void queue_publish_message(const std::string & topic, const std::string & payload, bool retain);
|
||||
static void queue_subscribe_message(const std::string & topic);
|
||||
static void queue_unsubscribe_message(const std::string & topic);
|
||||
|
||||
void on_publish(uint16_t packetId) const;
|
||||
void on_message(const char * topic, const char * payload, size_t len) const;
|
||||
@@ -298,6 +303,8 @@ class Mqtt {
|
||||
uint32_t last_publish_heartbeat_ = 0;
|
||||
uint32_t last_publish_queue_ = 0;
|
||||
|
||||
bool first_connect_attempted_ = false;
|
||||
|
||||
static bool connecting_;
|
||||
static bool initialized_;
|
||||
static uint32_t mqtt_publish_fails_;
|
||||
@@ -325,6 +332,7 @@ class Mqtt {
|
||||
static uint8_t nested_format_;
|
||||
static uint8_t entity_format_;
|
||||
static std::string discovery_prefix_;
|
||||
static uint8_t discovery_type_;
|
||||
static bool publish_single_;
|
||||
static bool publish_single2cmd_;
|
||||
static bool send_response_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -82,7 +82,7 @@ void Shower::loop() {
|
||||
char s[50];
|
||||
snprintf(s, 50, "%d minutes and %d seconds", (uint8_t)(duration_ / 60000), (uint8_t)((duration_ / 1000) % 60));
|
||||
doc["duration"] = s;
|
||||
Mqtt::publish("shower_data", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("shower_data", doc.as<JsonObject>());
|
||||
LOG_DEBUG("[Shower] finished with duration %d", duration_);
|
||||
}
|
||||
}
|
||||
@@ -144,13 +144,13 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
|
||||
// always publish as a string
|
||||
char s[12];
|
||||
Mqtt::publish("shower_active", Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
|
||||
Mqtt::queue_publish("shower_active", Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
|
||||
|
||||
// send out HA MQTT Discovery config topic
|
||||
if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) {
|
||||
ha_configdone_ = true;
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
|
||||
doc["name"] = "Shower Active";
|
||||
|
||||
@@ -168,8 +168,8 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc["pl_on"] = true;
|
||||
doc["pl_off"] = false;
|
||||
doc["pl_on"] = "true";
|
||||
doc["pl_off"] = "false";
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc["pl_on"] = 1;
|
||||
doc["pl_off"] = 0;
|
||||
@@ -183,9 +183,13 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>());
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
214
src/system.cpp
214
src/system.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -19,9 +19,13 @@
|
||||
#include "system.h"
|
||||
#include "emsesp.h" // for send_raw_telegram() command
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "esp_ota_ops.h"
|
||||
#endif
|
||||
|
||||
#include <semver200.h>
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
#include "test/test.h"
|
||||
#endif
|
||||
|
||||
@@ -50,24 +54,28 @@ Adafruit_NeoPixel pixels(1, 7, NEO_GRB + NEO_KHZ800);
|
||||
namespace emsesp {
|
||||
|
||||
// Languages supported. Note: the order is important and must match locale_translations.h
|
||||
#if defined(EMSESP_TEST)
|
||||
// in Debug mode use one language (en) to save flash memory needed for the tests
|
||||
const char * const languages[] = {EMSESP_LOCALE_EN};
|
||||
#else
|
||||
const char * const languages[] = {EMSESP_LOCALE_EN, EMSESP_LOCALE_DE, EMSESP_LOCALE_NL, EMSESP_LOCALE_SV, EMSESP_LOCALE_PL, EMSESP_LOCALE_NO, EMSESP_LOCALE_FR};
|
||||
#endif
|
||||
|
||||
size_t num_languages = sizeof(languages) / sizeof(const char *);
|
||||
static constexpr uint8_t NUM_LANGUAGES = sizeof(languages) / sizeof(const char *);
|
||||
|
||||
uuid::log::Logger System::logger_{F_(system), uuid::log::Facility::KERN};
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::syslog::SyslogService System::syslog_;
|
||||
#endif
|
||||
|
||||
// init statics
|
||||
PButton System::myPButton_;
|
||||
bool System::restart_requested_ = false;
|
||||
PButton System::myPButton_;
|
||||
bool System::restart_requested_ = false;
|
||||
bool System::test_set_all_active_ = false;
|
||||
uint32_t System::max_alloc_mem_;
|
||||
uint32_t System::heap_mem_;
|
||||
|
||||
// find the index of the language
|
||||
// 0 = EN, 1 = DE, etc...
|
||||
uint8_t System::language_index() {
|
||||
for (uint8_t i = 0; i < num_languages; i++) {
|
||||
for (uint8_t i = 0; i < NUM_LANGUAGES; i++) {
|
||||
if (languages[i] == locale()) {
|
||||
return i;
|
||||
}
|
||||
@@ -136,13 +144,15 @@ bool System::command_publish(const char * value, const int8_t id) {
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::publish_all();
|
||||
LOG_INFO("Publishing all data to MQTT");
|
||||
EMSESP::publish_all();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// syslog level
|
||||
// commenting this out - don't see the point on having an API service to change the syslog level
|
||||
/*
|
||||
bool System::command_syslog_level(const char * value, const int8_t id) {
|
||||
uint8_t s = 0xff;
|
||||
if (Helpers::value2enum(value, s, FL_(list_syslog_level))) {
|
||||
@@ -163,6 +173,7 @@ bool System::command_syslog_level(const char * value, const int8_t id) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// watch
|
||||
bool System::command_watch(const char * value, const int8_t id) {
|
||||
@@ -174,9 +185,9 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
}
|
||||
if (Mqtt::publish_single() && w != EMSESP::watch()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::publish("system/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
Mqtt::queue_publish("system/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
} else {
|
||||
Mqtt::publish("system_data/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
Mqtt::queue_publish("system_data/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
}
|
||||
}
|
||||
EMSESP::watch(w);
|
||||
@@ -184,9 +195,9 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
} else if (i) {
|
||||
if (Mqtt::publish_single() && i != EMSESP::watch_id()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::publish("system/watch", Helpers::hextoa(i));
|
||||
Mqtt::queue_publish("system/watch", Helpers::hextoa(i));
|
||||
} else {
|
||||
Mqtt::publish("system_data/watch", Helpers::hextoa(i));
|
||||
Mqtt::queue_publish("system_data/watch", Helpers::hextoa(i));
|
||||
}
|
||||
}
|
||||
EMSESP::watch_id(i);
|
||||
@@ -212,7 +223,6 @@ void System::system_restart() {
|
||||
void System::wifi_reconnect() {
|
||||
LOG_INFO("WiFi reconnecting...");
|
||||
Shell::loop_all();
|
||||
EMSESP::console_.loop();
|
||||
delay(1000); // wait a second
|
||||
EMSESP::webSettingsService.save(); // local settings
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers("local"); // in case we've changed ssid or password
|
||||
@@ -222,8 +232,6 @@ void System::wifi_reconnect() {
|
||||
void System::format(uuid::console::Shell & shell) {
|
||||
auto msg = ("Formatting file system. This will reset all settings to their defaults");
|
||||
shell.logger().warning(msg);
|
||||
// shell.flush();
|
||||
|
||||
EMSuart::stop();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -234,9 +242,6 @@ void System::format(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
void System::syslog_init() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bool was_enabled = syslog_enabled_;
|
||||
#endif
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
syslog_enabled_ = settings.syslog_enabled;
|
||||
syslog_level_ = settings.syslog_level;
|
||||
@@ -247,44 +252,42 @@ void System::syslog_init() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (syslog_enabled_) {
|
||||
// start & configure syslog
|
||||
if (!was_enabled) {
|
||||
syslog_.start();
|
||||
EMSESP::logger().info("Starting Syslog");
|
||||
}
|
||||
EMSESP::logger().info("Starting Syslog service");
|
||||
syslog_.start();
|
||||
|
||||
syslog_.log_level((uuid::log::Level)syslog_level_);
|
||||
syslog_.mark_interval(syslog_mark_interval_);
|
||||
syslog_.destination(syslog_host_.c_str(), syslog_port_);
|
||||
syslog_.hostname(hostname().c_str());
|
||||
|
||||
// register the command
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, FL_(changeloglevel_cmd), CommandFlag::ADMIN_ONLY);
|
||||
// removed in 3.6.0
|
||||
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, FL_(changeloglevel_cmd), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
} else if (was_enabled) {
|
||||
} else if (syslog_.started()) {
|
||||
// in case service is still running, this flushes the queue
|
||||
// https://github.com/emsesp/EMS-ESP/issues/496
|
||||
EMSESP::logger().info("Stopping Syslog");
|
||||
syslog_.log_level((uuid::log::Level)-1);
|
||||
syslog_.log_level((uuid::log::Level)-1); // stop server
|
||||
syslog_.mark_interval(0);
|
||||
syslog_.destination("");
|
||||
}
|
||||
|
||||
if (Mqtt::publish_single()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::publish("system/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
Mqtt::queue_publish("system/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
|
||||
Mqtt::publish("system/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
Mqtt::queue_publish("system/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
} else {
|
||||
Mqtt::publish("system/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
Mqtt::queue_publish("system/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
}
|
||||
|
||||
} else {
|
||||
Mqtt::publish("system_data/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
Mqtt::queue_publish("system_data/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
|
||||
Mqtt::publish("system_data/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
Mqtt::queue_publish("system_data/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
} else {
|
||||
Mqtt::publish("system_data/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
Mqtt::queue_publish("system_data/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,9 +355,7 @@ void System::wifi_tweak() {
|
||||
bool s1 = WiFi.getSleep();
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
bool s2 = WiFi.getSleep();
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Adjusting WiFi - Tx power %d->%d, Sleep %d->%d", p1, p2, s1, s2);
|
||||
#endif
|
||||
LOG_DEBUG("Adjusting WiFi - Tx power %d->%d, Sleep %d->%d", p1, p2, s1, s2);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -391,10 +392,13 @@ void System::start() {
|
||||
setCpuFrequencyMhz(160);
|
||||
#endif
|
||||
}
|
||||
|
||||
// get current memory values
|
||||
fstotal_ = LittleFS.totalBytes() / 1024; // read only once, it takes 500 ms to read
|
||||
psram_ = ESP.getPsramSize() / 1024;
|
||||
appused_ = ESP.getSketchSize() / 1024;
|
||||
appfree_ = ESP.getFreeSketchSpace() / 1024 - appused_;
|
||||
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
#endif
|
||||
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
@@ -405,16 +409,15 @@ void System::start() {
|
||||
led_init(false); // init LED
|
||||
button_init(false); // the special button
|
||||
network_init(false); // network
|
||||
syslog_init(); // start Syslog
|
||||
|
||||
EMSESP::uart_init(); // start UART
|
||||
syslog_init(); // start syslog
|
||||
}
|
||||
|
||||
// button single click
|
||||
void System::button_OnClick(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - single click");
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
#ifndef EMSESP_STANDALONE
|
||||
Test::listDir(LittleFS, FS_CONFIG_DIRECTORY, 3);
|
||||
#endif
|
||||
@@ -437,7 +440,6 @@ void System::button_OnVLongPress(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - very long press");
|
||||
#ifndef EMSESP_STANDALONE
|
||||
LOG_WARNING("Performing factory reset...");
|
||||
EMSESP::console_.loop();
|
||||
EMSESP::esp8266React.factoryReset();
|
||||
#endif
|
||||
}
|
||||
@@ -519,21 +521,6 @@ void System::loop() {
|
||||
|
||||
led_monitor(); // check status and report back using the LED
|
||||
system_check(); // check system health
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
/*
|
||||
static uint32_t last_memcheck_ = 0;
|
||||
if (currentMillis - last_memcheck_ > 10000) { // 10 seconds
|
||||
last_memcheck_ = currentMillis;
|
||||
show_mem("core");
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -554,15 +541,18 @@ void System::send_info_mqtt(const char * event_str, bool send_ntp) {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
doc["network"] = "ethernet";
|
||||
doc["hostname"] = ETH.getHostname();
|
||||
doc["network"] = "ethernet";
|
||||
doc["hostname"] = ETH.getHostname();
|
||||
/*
|
||||
doc["MAC"] = ETH.macAddress();
|
||||
doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
||||
doc["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
||||
doc["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
||||
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") {
|
||||
doc["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
} else if (WiFi.status() == WL_CONNECTED) {
|
||||
doc["network"] = "wifi";
|
||||
doc["hostname"] = WiFi.getHostname();
|
||||
@@ -578,7 +568,7 @@ void System::send_info_mqtt(const char * event_str, bool send_ntp) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Mqtt::publish_retain(F_(info), doc.as<JsonObject>(), true); // topic called "info" and it's Retained
|
||||
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>(), true); // topic called "info" and it's Retained
|
||||
}
|
||||
|
||||
// create the json for heartbeat
|
||||
@@ -623,7 +613,8 @@ bool System::heartbeat_json(JsonObject & output) {
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
output["freemem"] = ESP.getFreeHeap() / 1024; // kilobytes
|
||||
output["freemem"] = getHeapMem();
|
||||
output["max_alloc"] = getMaxAllocMem();
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -644,11 +635,13 @@ void System::send_heartbeat() {
|
||||
return;
|
||||
}
|
||||
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
if (heartbeat_json(json)) {
|
||||
Mqtt::publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,6 +654,7 @@ void System::network_init(bool refresh) {
|
||||
last_system_check_ = 0; // force the LED to go from fast flash to pulse
|
||||
send_heartbeat();
|
||||
|
||||
// no ethernet present
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE) {
|
||||
return;
|
||||
}
|
||||
@@ -671,14 +665,14 @@ void System::network_init(bool refresh) {
|
||||
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||
int8_t power = eth_power_; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
||||
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720 : ETH_PHY_TLK110; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||
// clock mode
|
||||
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
||||
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
||||
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
||||
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
||||
// clock mode:
|
||||
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
||||
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
||||
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
||||
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
||||
auto clock_mode = (eth_clock_mode_t)eth_clock_mode_;
|
||||
|
||||
ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
||||
eth_present_ = ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
||||
}
|
||||
|
||||
// check health of system, done every 5 seconds
|
||||
@@ -732,24 +726,22 @@ void System::system_check() {
|
||||
}
|
||||
|
||||
// commands - takes static function pointers
|
||||
// can be called via Console using 'call system <cmd>'
|
||||
void System::commands_init() {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
// restart and watch (and test) are also exposed as Console commands
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, FL_(watch_cmd));
|
||||
|
||||
if (Mqtt::enabled()) {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, FL_(publish_cmd));
|
||||
}
|
||||
#if defined(EMSESP_TEST)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, ("test"), System::command_test, FL_(test_cmd));
|
||||
#endif
|
||||
|
||||
// these commands will return data in JSON format
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, FL_(system_info_cmd));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, FL_(commands_cmd));
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, ("test"), System::command_test, FL_(test_cmd));
|
||||
#endif
|
||||
|
||||
// MQTT subscribe "ems-esp/system/#"
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::SYSTEM, "system/#", nullptr); // use empty function callback
|
||||
}
|
||||
@@ -880,19 +872,27 @@ void System::show_users(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
void System::show_system(uuid::console::Shell & shell) {
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
shell.println("System:");
|
||||
shell.printfln(" Board profile: %s", board_profile().c_str());
|
||||
shell.printfln(" Uptime: %s", uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str());
|
||||
#ifndef EMSESP_STANDALONE
|
||||
shell.printfln(" SDK version: %s", ESP.getSdkVersion());
|
||||
shell.printfln(" CPU frequency: %lu MHz", ESP.getCpuFreqMHz());
|
||||
shell.printfln(" Free heap: %lu KB", (uint32_t)ESP.getFreeHeap() / 1024);
|
||||
shell.printfln(" Free heap/Max alloc: %lu KB / %lu KB", getHeapMem(), getMaxAllocMem());
|
||||
shell.printfln(" App used/free: %lu KB / %lu KB", appUsed(), appFree());
|
||||
uint32_t FSused = LittleFS.usedBytes() / 1024;
|
||||
shell.printfln(" FS used/free: %lu KB / %lu KB", FSused, FStotal() - FSused);
|
||||
shell.println();
|
||||
|
||||
shell.println("Network:");
|
||||
|
||||
// show ethernet mac address if we have an eth controller present
|
||||
if (eth_present_) {
|
||||
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
||||
}
|
||||
|
||||
switch (WiFi.status()) {
|
||||
case WL_IDLE_STATUS:
|
||||
shell.printfln(" Status: Idle");
|
||||
@@ -907,7 +907,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
break;
|
||||
|
||||
case WL_CONNECTED:
|
||||
shell.printfln(" Status: connected");
|
||||
shell.printfln(" Status: WiFi connected");
|
||||
shell.printfln(" SSID: %s", WiFi.SSID().c_str());
|
||||
shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str());
|
||||
shell.printfln(" RSSI: %d dBm (%d %%)", WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
||||
@@ -935,7 +935,8 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
default:
|
||||
shell.printfln(" WiFi Network: Unknown");
|
||||
shell.printfln(" WiFi MAC address: %s", WiFi.macAddress().c_str());
|
||||
shell.printfln(" WiFi Network: not connected");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -943,7 +944,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
if (ethernet_connected_) {
|
||||
shell.println();
|
||||
shell.printfln(" Status: Ethernet connected");
|
||||
shell.printfln(" MAC address: %s", ETH.macAddress().c_str());
|
||||
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
||||
shell.printfln(" Hostname: %s", ETH.getHostname());
|
||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
||||
@@ -990,15 +991,18 @@ bool System::check_restore() {
|
||||
std::string settings_type = input["type"];
|
||||
if (settings_type == "settings") {
|
||||
// It's a settings file. Parse each section separately. If it's system related it will require a reboot
|
||||
reboot_required = saveSettings(NETWORK_SETTINGS_FILE, "Network", input);
|
||||
reboot_required |= saveSettings(AP_SETTINGS_FILE, "AP", input);
|
||||
reboot_required |= saveSettings(MQTT_SETTINGS_FILE, "MQTT", input);
|
||||
reboot_required |= saveSettings(NTP_SETTINGS_FILE, "NTP", input);
|
||||
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, "Security", input);
|
||||
reboot_required = saveSettings(NETWORK_SETTINGS_FILE, "Network Settings", input);
|
||||
reboot_required |= saveSettings(AP_SETTINGS_FILE, "AP Settings", input);
|
||||
reboot_required |= saveSettings(MQTT_SETTINGS_FILE, "MQTT Settings", input);
|
||||
reboot_required |= saveSettings(NTP_SETTINGS_FILE, "NTP Settings", input);
|
||||
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, "Security Settings", input);
|
||||
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, "Settings", input);
|
||||
} else if (settings_type == "customizations") {
|
||||
// it's a customization file, just replace it and there's no need to reboot
|
||||
saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input);
|
||||
} else if (settings_type == "schedule") {
|
||||
// it's a schedule file, just replace it and there's no need to reboot
|
||||
saveSettings(EMSESP_SCHEDULER_FILE, "Schedule", input);
|
||||
} else {
|
||||
LOG_ERROR("Unrecognized file uploaded");
|
||||
}
|
||||
@@ -1029,15 +1033,14 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
|
||||
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
|
||||
if (missing_version) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("No version information found (%s)", settingsVersion.c_str());
|
||||
#endif
|
||||
settingsVersion = "3.4.4"; // this was the last stable version
|
||||
}
|
||||
}
|
||||
|
||||
version::Semver200_version settings_version(settingsVersion);
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
if (!missing_version) {
|
||||
LOG_INFO("Current version from settings is %d.%d.%d-%s",
|
||||
settings_version.major(),
|
||||
@@ -1045,6 +1048,7 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
settings_version.patch(),
|
||||
settings_version.prerelease().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// always save the new version to the settings
|
||||
EMSESP::webSettingsService.update(
|
||||
@@ -1067,9 +1071,7 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
|
||||
// if we're coming from 3.4.4 or 3.5.0b14 then we need to apply new settings
|
||||
if (missing_version) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("Setting MQTT ID Entity to v3.4 format");
|
||||
#endif
|
||||
EMSESP::esp8266React.getMqttSettingsService()->update(
|
||||
[&](MqttSettings & mqttSettings) {
|
||||
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4
|
||||
@@ -1119,7 +1121,7 @@ bool System::saveSettings(const char * filename, const char * section, JsonObjec
|
||||
if (section_json) {
|
||||
File section_file = LittleFS.open(filename, "w");
|
||||
if (section_file) {
|
||||
LOG_INFO("Applying new %s settings", section);
|
||||
LOG_INFO("Applying new %s", section);
|
||||
serializeJson(section_json, section_file);
|
||||
section_file.close();
|
||||
return true; // reboot required
|
||||
@@ -1141,9 +1143,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
node["uptime (seconds)"] = uuid::get_uptime_sec();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["free mem"] = ESP.getFreeHeap() / 1024; // kilobytes
|
||||
node["max alloc"] = ESP.getMaxAllocHeap() / 1024; // kilobytes
|
||||
node["free app"] = EMSESP::system_.appFree(); // kilobytes
|
||||
node["free mem"] = getHeapMem();
|
||||
node["max alloc"] = getMaxAllocMem();
|
||||
node["free app"] = EMSESP::system_.appFree(); // kilobytes
|
||||
#endif
|
||||
node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
|
||||
|
||||
@@ -1151,9 +1153,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
// Network Status
|
||||
node = output.createNestedObject("Network Info");
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
node["network"] = "Ethernet";
|
||||
node["hostname"] = ETH.getHostname();
|
||||
node["MAC"] = ETH.macAddress();
|
||||
node["network"] = "Ethernet";
|
||||
node["hostname"] = ETH.getHostname();
|
||||
// node["MAC"] = ETH.macAddress();
|
||||
node["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
||||
node["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
||||
node["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
||||
@@ -1163,9 +1165,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
} else if (WiFi.status() == WL_CONNECTED) {
|
||||
node["network"] = "WiFi";
|
||||
node["hostname"] = WiFi.getHostname();
|
||||
// node["SSID"] = WiFi.SSID();
|
||||
// node["BSSID"] = WiFi.BSSIDstr();
|
||||
node["RSSI"] = WiFi.RSSI();
|
||||
node["RSSI"] = WiFi.RSSI();
|
||||
// node["MAC"] = WiFi.macAddress();
|
||||
node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
||||
node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||
@@ -1231,6 +1231,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["entity format"] = settings.entity_format;
|
||||
node["base"] = settings.base;
|
||||
node["discovery prefix"] = settings.discovery_prefix;
|
||||
node["discovery type"] = settings.discovery_type;
|
||||
node["nested format"] = settings.nested_format;
|
||||
node["ha enabled"] = settings.ha_enabled;
|
||||
node["mqtt qos"] = settings.mqtt_qos;
|
||||
@@ -1383,11 +1384,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
// run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
|
||||
bool System::command_test(const char * value, const int8_t id) {
|
||||
Test::run_test(value, id);
|
||||
return true;
|
||||
return Test::run_test(value, id);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
40
src/system.h
40
src/system.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -55,14 +55,13 @@ class System {
|
||||
static bool command_publish(const char * value, const int8_t id);
|
||||
static bool command_fetch(const char * value, const int8_t id);
|
||||
static bool command_restart(const char * value, const int8_t id);
|
||||
#if defined(EMSESP_DEBUG)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
static bool command_syslog_level(const char * value, const int8_t id);
|
||||
static bool command_watch(const char * value, const int8_t id);
|
||||
|
||||
static bool command_info(const char * value, const int8_t id, JsonObject & output);
|
||||
static bool command_commands(const char * value, const int8_t id, JsonObject & output);
|
||||
#if defined(EMSESP_TEST)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
|
||||
std::string reset_reason(uint8_t cpu) const;
|
||||
|
||||
@@ -230,9 +229,38 @@ class System {
|
||||
return appused_;
|
||||
}
|
||||
|
||||
// memory in kb
|
||||
static uint32_t getMaxAllocMem() {
|
||||
return max_alloc_mem_;
|
||||
}
|
||||
static uint32_t getHeapMem() {
|
||||
return heap_mem_;
|
||||
}
|
||||
static void refreshHeapMem() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
max_alloc_mem_ = ESP.getMaxAllocHeap() / 1024;
|
||||
heap_mem_ = ESP.getFreeHeap() / 1024;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool test_set_all_active() {
|
||||
return test_set_all_active_;
|
||||
}
|
||||
static void test_set_all_active(bool n) {
|
||||
#if defined(EMSESP_TEST)
|
||||
if (n) {
|
||||
logger_.debug("Using dummy entity values");
|
||||
}
|
||||
#endif
|
||||
test_set_all_active_ = n;
|
||||
}
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
static bool restart_requested_;
|
||||
static bool test_set_all_active_; // force all entities in a device to have a value
|
||||
static uint32_t max_alloc_mem_;
|
||||
static uint32_t heap_mem_;
|
||||
|
||||
// button
|
||||
static PButton myPButton_; // PButton instance
|
||||
@@ -271,6 +299,8 @@ class System {
|
||||
bool ntp_connected_ = false;
|
||||
uint32_t ntp_last_check_ = 0;
|
||||
|
||||
bool eth_present_ = false;
|
||||
|
||||
// EMS-ESP settings
|
||||
// copies from WebSettings class in WebSettingsService.h and loaded with reload_settings()
|
||||
std::string hostname_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -211,9 +211,7 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
LOG_TRACE("Rx: %s", Helpers::data_to_hex(data, length).c_str());
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] New Rx telegram, message length %d", message_length);
|
||||
#endif
|
||||
LOG_DEBUG("New Rx telegram, message length %d", message_length);
|
||||
|
||||
// if we don't have a type_id exit,
|
||||
// do not exit on empty message, it is checked for toggle fetch
|
||||
@@ -443,9 +441,7 @@ void TxService::add(const uint8_t operation,
|
||||
const bool front) {
|
||||
auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
#endif
|
||||
LOG_DEBUG("New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
|
||||
// if the queue is full, make room by removing the last one
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
@@ -507,9 +503,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
|
||||
// if we don't have a type_id or empty data block, exit
|
||||
if ((type_id == 0) || (message_length == 0)) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] Tx telegram type %d failed, length %d", type_id, message_length);
|
||||
#endif
|
||||
LOG_DEBUG("Tx telegram type %d failed, length %d", type_id, message_length);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -537,9 +531,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
tx_telegrams_.pop_front();
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
#endif
|
||||
LOG_DEBUG("New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
|
||||
if (front) {
|
||||
// tx_telegrams_.push_front(qtxt); // add to front of queue
|
||||
@@ -639,13 +631,11 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] Last Tx %s operation failed. Retry #%d. sent message: %s, received: %s",
|
||||
LOG_DEBUG("Last Tx %s operation failed. Retry #%d. sent message: %s, received: %s",
|
||||
(operation == Telegram::Operation::TX_WRITE) ? ("Write") : ("Read"),
|
||||
retry_count_,
|
||||
telegram_last_->to_string().c_str(),
|
||||
Helpers::data_to_hex(data, length - 1).c_str());
|
||||
#endif
|
||||
|
||||
// add to the top of the queue
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -37,30 +37,20 @@
|
||||
#define MAX_TX_TELEGRAMS 50 // size of Tx queue
|
||||
|
||||
// default values for null values
|
||||
static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_OFF = 0x00; // boolean false
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_ON = 0x01; // boolean true. True can be 0x01 or 0xFF sometimes
|
||||
|
||||
static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_OFF = 0x00; // boolean false
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_ON = 0x01; // boolean true. True can be 0x01 or 0xFF sometimes
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_NOTSET = 0xFE; // random number for booleans, that's not 0, 1 or FF
|
||||
static constexpr uint8_t EMS_VALUE_UINT_NOTSET = 0xFF; // for 8-bit unsigned ints/bytes
|
||||
static constexpr int8_t EMS_VALUE_INT_NOTSET = 0x7F; // for signed 8-bit ints/bytes
|
||||
static constexpr uint16_t EMS_VALUE_USHORT_NOTSET = 0x7D00; // 32000: for 2-byte unsigned shorts
|
||||
static constexpr int16_t EMS_VALUE_SHORT_NOTSET = 0x7D00; // 32000: for 2-byte signed shorts
|
||||
static constexpr uint16_t EMS_VALUE_USHORT_NOTSET = 0x7D00; // 32000: for 2-byte unsigned shorts
|
||||
static constexpr int16_t EMS_VALUE_SHORT_NOTSET = 0x7D00; // 32000: for 2-byte signed shorts
|
||||
static constexpr uint32_t EMS_VALUE_ULONG_NOTSET = 0x00FFFFFF; // for 3-byte longs
|
||||
static constexpr uint32_t EMS_VALUE_ULLONG_NOTSET = 0xFFFFFFFF; // for 4-byte longs
|
||||
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_LENGTH = 32; // max length of a complete EMS telegram
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of message block, assuming EMS1.0
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#define EMS_VALUE_DEFAULT_INT 11
|
||||
#define EMS_VALUE_DEFAULT_UINT -12
|
||||
#define EMS_VALUE_DEFAULT_SHORT -1234
|
||||
#define EMS_VALUE_DEFAULT_USHORT 1234
|
||||
#define EMS_VALUE_DEFAULT_ULONG 12356
|
||||
#define EMS_VALUE_DEFAULT_BOOL 1
|
||||
#define EMS_VALUE_DEFAULT_ENUM 1
|
||||
#else
|
||||
#define EMS_VALUE_DEFAULT_INT EMS_VALUE_INT_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_UINT EMS_VALUE_UINT_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_SHORT EMS_VALUE_SHORT_NOTSET
|
||||
@@ -68,7 +58,15 @@ static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of
|
||||
#define EMS_VALUE_DEFAULT_ULONG EMS_VALUE_ULONG_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_BOOL EMS_VALUE_BOOL_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_ENUM EMS_VALUE_UINT_NOTSET
|
||||
#endif
|
||||
|
||||
// used when System::test_set_all_active() is set
|
||||
#define EMS_VALUE_DEFAULT_INT_DUMMY 11
|
||||
#define EMS_VALUE_DEFAULT_UINT_DUMMY -12
|
||||
#define EMS_VALUE_DEFAULT_SHORT_DUMMY -1234
|
||||
#define EMS_VALUE_DEFAULT_USHORT_DUMMY 1235
|
||||
#define EMS_VALUE_DEFAULT_ULONG_DUMMY 12456
|
||||
#define EMS_VALUE_DEFAULT_BOOL_DUMMY 1
|
||||
#define EMS_VALUE_DEFAULT_ENUM_DUMMY 1
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -17,18 +17,33 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
|
||||
#include "test.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// no shell
|
||||
// no shell, called via the API or 'call system test' command
|
||||
// or http://ems-esp/api?device=system&cmd=test&data=boiler
|
||||
bool Test::run_test(const char * command, int8_t id) {
|
||||
if ((command == nullptr) || (strlen(command) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(command, "memory") == 0) {
|
||||
EMSESP::logger().notice("Testing memory by adding lots of devices and entities...");
|
||||
|
||||
System::test_set_all_active(true); // include all entities and give them fake values
|
||||
|
||||
// simulate HansRemmerswaal's setup - see https://github.com/emsesp/EMS-ESP32/issues/859
|
||||
add_device(0x08, 172); // 176 entities - boiler: Enviline/Compress 6000AW/Hybrid 3000-7000iAW/SupraEco/Geo 5xx/WLW196i
|
||||
|
||||
// add_device(0x10, 158); // 62 entities - thermostat: RC300/RC310/Moduline 3000/1010H/CW400/Sense II/HPC410
|
||||
// add_device(0x38, 200); // 4 entities - thermostat: RC100H
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(command, "general") == 0) {
|
||||
EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat");
|
||||
|
||||
@@ -53,7 +68,11 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_DEBUG_LIMITED
|
||||
//
|
||||
// the tests take a lot of memory when built for the ESP32
|
||||
// so only including the full set in standalone, otherwise a limited selection of basic tests
|
||||
//
|
||||
#ifdef EMSESP_STANDALONE
|
||||
|
||||
if (strcmp(command, "2thermostats") == 0) {
|
||||
EMSESP::logger().info("Testing with multiple thermostats...");
|
||||
@@ -223,41 +242,79 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These next tests are run from the Console
|
||||
// using the test command
|
||||
// These next tests are run from the Consol via the test command, so inherit the Shell
|
||||
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & data) {
|
||||
// switch to su
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
shell.add_flags(CommandFlags::ADMIN); // switch to su
|
||||
|
||||
// init stuff
|
||||
Mqtt::ha_enabled(true);
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw
|
||||
|
||||
// EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw mode
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_ON); // verbose mode
|
||||
|
||||
std::string command(20, '\0');
|
||||
|
||||
#ifndef EMSESP_DEBUG_LIMITED
|
||||
|
||||
if ((cmd.empty()) || (cmd == "default")) {
|
||||
command = EMSESP_DEBUG_DEFAULT;
|
||||
} else {
|
||||
command = cmd;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
if (command == "dump") {
|
||||
shell.printfln("Adding all devices and entities...");
|
||||
EMSESP::dump_all_values(shell);
|
||||
}
|
||||
#endif
|
||||
bool ok = false;
|
||||
|
||||
if (command == "general") {
|
||||
shell.printfln("Testing adding a general boiler & thermostat...");
|
||||
run_test("general");
|
||||
shell.invoke_command("show devices");
|
||||
shell.invoke_command("show");
|
||||
// shell.invoke_command("show devices");
|
||||
// shell.invoke_command("show values");
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
// shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/869
|
||||
if (command == "memory") {
|
||||
shell.printfln("Testing memory by adding lots of devices and entities...");
|
||||
run_test("memory");
|
||||
shell.invoke_command("show values");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "string2minutes") {
|
||||
shell.printfln("Testing string2minutes()...");
|
||||
std::string time_s = "12:00";
|
||||
shell.printfln("Testing %s is %d", time_s.c_str(), Helpers::string2minutes(time_s));
|
||||
std::string time_s2 = "12:12";
|
||||
shell.printfln("Testing %s is %d", time_s2.c_str(), Helpers::string2minutes(time_s2));
|
||||
std::string time_s3 = "00:50";
|
||||
shell.printfln("Testing %s is %d", time_s3.c_str(), Helpers::string2minutes(time_s3));
|
||||
std::string time_s4 = "03:49";
|
||||
shell.printfln("Testing %s is %d", time_s4.c_str(), Helpers::string2minutes(time_s4));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// THESE ONLY WORK WITH AN ESP32, not in standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (command == "ls") {
|
||||
listDir(LittleFS, "/", 3);
|
||||
Serial.println();
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// the tests take a lot of memory when built for the ESP32
|
||||
// so only including the full set in standalone, otherwise a limited selection of basic tests
|
||||
//
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// all tests with EMSESP_STANDALONE
|
||||
|
||||
if (command == "entity_dump") {
|
||||
shell.printfln("Adding all devices and entities...");
|
||||
System::test_set_all_active(true);
|
||||
EMSESP::dump_all_values(shell);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "modes") {
|
||||
@@ -266,6 +323,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call thermostat mode auto");
|
||||
shell.invoke_command("call thermostat mode Manuell"); // DE
|
||||
shell.invoke_command("call thermostat mode 1");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "render") {
|
||||
@@ -334,6 +392,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
temp = 0x63;
|
||||
doub = Helpers::transformNumFloat(temp, 2); // divide by 2
|
||||
shell.printfln("Round test div2 from x%02X to %d to %f", temp, temp, doub);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "devices") {
|
||||
@@ -341,6 +400,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// A fake response - UBADevices(0x07)
|
||||
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// check for boiler and controller on same product_id
|
||||
@@ -352,6 +412,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// UBAuptime
|
||||
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "620") {
|
||||
@@ -362,6 +423,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// Version Boiler
|
||||
uart_telegram({0x08, 0x0B, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// unknown device
|
||||
@@ -380,6 +442,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("show devices");
|
||||
shell.invoke_command("call system report");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "unknown2") {
|
||||
@@ -387,11 +450,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // productID is 90 which doesn't exist
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "gateway") {
|
||||
shell.printfln("Testing Gateway...");
|
||||
run_test("gateway");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "310") {
|
||||
@@ -401,6 +466,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "2thermostats") {
|
||||
@@ -408,6 +474,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("2thermostats");
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("show devices");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "web") {
|
||||
@@ -419,8 +486,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
|
||||
DynamicJsonDocument doc(8000); // some absurd high number
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
@@ -455,8 +520,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
Serial.println(" **");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "board_profile") {
|
||||
@@ -466,6 +530,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("set board_profile wemos");
|
||||
shell.invoke_command("exit");
|
||||
shell.invoke_command("call system settings");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "boiler") {
|
||||
@@ -494,6 +559,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler", "{\"cmd\":\"heatingactivated\",\"data\":1}");
|
||||
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "shower_alert") {
|
||||
@@ -503,6 +569,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// device type, command, data
|
||||
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "fr120") {
|
||||
@@ -515,6 +582,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("show devices");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "ha") {
|
||||
@@ -537,6 +605,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// shell.invoke_command("call thermostat seltemp"); // sensor.thermostat_hc1_selected_room_temperature
|
||||
// shell.invoke_command("call thermostat entities");
|
||||
// shell.invoke_command("call boiler entities");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "lastcode") {
|
||||
@@ -555,6 +624,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
{0x08, 0x0B, 0xC2, 0, 0x08, 0xAC, 00, 0x10, 0x31, 0x48, 0x30, 0x31, 0x15, 0x80, 0x95, 0x0B, 0x0E, 0x10, 0x38, 00, 0x7F, 0xFF, 0xFF, 0xFF});
|
||||
|
||||
// shell.invoke_command("show");
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dv") {
|
||||
@@ -569,11 +640,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dallas") {
|
||||
shell.printfln("Testing adding Dallas sensor");
|
||||
emsesp::EMSESP::dallassensor_.test();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dallas_full") {
|
||||
@@ -590,6 +663,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::dallassensor_.update("01-0203-0405-0607", "testdallas", 2);
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "analog") {
|
||||
@@ -609,6 +683,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::analogsensor_.update(36, "analogtest", 2, 0.7, 17, 1);
|
||||
shell.invoke_command("show");
|
||||
// shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "healthcheck") {
|
||||
@@ -621,10 +696,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK
|
||||
shell.printfln("Testing healthcheck with %d", n);
|
||||
EMSESP::system_.healthcheck(n);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "custom") {
|
||||
shell.printfln(F("Testing custom entities"));
|
||||
shell.printfln("Testing custom entities");
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::send_response(false);
|
||||
@@ -650,8 +726,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call thermostat seltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "masked") {
|
||||
shell.printfln("Testing masked entities");
|
||||
@@ -675,6 +752,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dv2") {
|
||||
@@ -695,10 +773,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "api_values") {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
shell.printfln("Testing API getting values");
|
||||
Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
@@ -728,7 +806,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
request.url("/api/boiler/flamecurr/bad");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
#endif
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt_post") {
|
||||
@@ -743,9 +821,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("thermostat");
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/541
|
||||
if (command == "api_wwmode") {
|
||||
shell.printfln("Testing API wwmode");
|
||||
@@ -763,8 +841,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/wwmode");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (command == "api") {
|
||||
shell.printfln("Testing API with MQTT and REST, standalone");
|
||||
@@ -778,8 +856,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
|
||||
AsyncWebServerRequest requestX;
|
||||
DynamicJsonDocument docX(2000);
|
||||
JsonVariant jsonX;
|
||||
@@ -799,7 +875,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/thermostat/mode/auto");
|
||||
requestX.url("/api/thermostat/mode/auto");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
return;
|
||||
*/
|
||||
@@ -871,7 +947,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
return;
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// char dataX[] = "{\"value\":\"0B 88 19 19 02\"}";
|
||||
char dataX[] = "{\"name\":\"temp\",\"value\":11}";
|
||||
@@ -917,8 +992,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
ncmd = Command::parse_command_string(command_s, id_n);
|
||||
shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
|
||||
|
||||
#endif
|
||||
|
||||
// Console tests
|
||||
shell.invoke_command("call thermostat entities");
|
||||
shell.invoke_command("call thermostat mode auto");
|
||||
@@ -951,7 +1024,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
Mqtt::base("home/cellar/heating");
|
||||
EMSESP::mqtt_.incoming("home/cellar/heating/thermostat/mode"); // empty payload, sends reponse
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// Web API TESTS
|
||||
AsyncWebServerRequest request;
|
||||
|
||||
@@ -1037,8 +1109,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/mode/auto");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
|
||||
#endif
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt_nested") {
|
||||
@@ -1059,6 +1130,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
Mqtt::nested_format(2);
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "thermostat") {
|
||||
@@ -1075,6 +1147,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc3", "{\"cmd\":\"temp\",\"data\":-3}");
|
||||
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "tc100") {
|
||||
@@ -1086,6 +1159,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// 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});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "solar") {
|
||||
@@ -1099,6 +1173,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call system publish");
|
||||
|
||||
// EMSESP::txservice_.send_raw("B0 00 FF 18 02 62 80 00 B8");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "heatpump") {
|
||||
@@ -1106,6 +1181,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("heatpump");
|
||||
shell.invoke_command("call");
|
||||
shell.invoke_command("call heatpump info");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "solar200") {
|
||||
@@ -1131,6 +1207,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
|
||||
|
||||
shell.invoke_command("show");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "km") {
|
||||
@@ -1189,6 +1266,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
EMSESP::show_ems(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "cr100") {
|
||||
@@ -1214,12 +1292,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.loop_all();
|
||||
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "rx2") {
|
||||
shell.printfln("Testing Rx2...");
|
||||
for (uint8_t i = 0; i < 30; i++) {
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1278,6 +1358,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// 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});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "tx") {
|
||||
@@ -1315,6 +1396,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
for (uint8_t i = 0; i < 10; i++) {
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "poll") {
|
||||
@@ -1340,6 +1423,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
uint8_t t2[] = {0x21, 0x22};
|
||||
EMSESP::send_write_request(0x91, 0x17, 0x00, t2, sizeof(t2), 0);
|
||||
EMSESP::show_ems(shell);
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "cmd") {
|
||||
@@ -1371,18 +1456,20 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call thermostat wwmode"); // should do nothing
|
||||
shell.invoke_command("call thermostat mode auto 2"); // should error, no hc2
|
||||
shell.invoke_command("call thermostat temp 22.56");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "pin") {
|
||||
shell.printfln("Testing pin...");
|
||||
shell.invoke_command("call system pin");
|
||||
shell.invoke_command("call system pin 1 true");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt2") {
|
||||
shell.printfln("Testing MQTT large payloads...");
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE);
|
||||
|
||||
char key[8];
|
||||
char value[8];
|
||||
@@ -1398,8 +1485,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.printfln("Size of JSON payload = %d", jo.memoryUsage());
|
||||
shell.printfln("Length of JSON payload = %d", measureJson(jo));
|
||||
|
||||
Mqtt::publish("test", jo);
|
||||
Mqtt::queue_publish("test", jo);
|
||||
Mqtt::show_mqtt(shell); // show queue
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt") {
|
||||
@@ -1448,7 +1536,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
strlcpy(system_topic, "ems-esp/system", sizeof(system_topic));
|
||||
|
||||
// test publishing
|
||||
EMSESP::mqtt_.publish(boiler_topic, "test me");
|
||||
EMSESP::mqtt_.queue_publish(boiler_topic, "test me");
|
||||
|
||||
// test receiving
|
||||
EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads, should return values
|
||||
@@ -1494,6 +1582,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
Mqtt::resubscribe();
|
||||
Mqtt::show_mqtt(shell); // show queue
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "poll2") {
|
||||
@@ -1505,6 +1594,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::incoming_telegram(poll, 1);
|
||||
|
||||
EMSESP::show_ems(shell);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "rx2") {
|
||||
@@ -1512,6 +1602,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
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});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP/issues/380#issuecomment-633663007
|
||||
@@ -1519,6 +1610,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.printfln("Testing rx3...");
|
||||
|
||||
uart_telegram({0x21, 0x0B, 0xFF, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// testing the UART tx command, without a queue
|
||||
@@ -1527,6 +1619,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
uint8_t t[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
|
||||
EMSuart::transmit(t, sizeof(t));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// send read request with offset
|
||||
@@ -1535,6 +1628,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// send_read_request(0x18, 0x08);
|
||||
EMSESP::txservice_.read_request(0x18, 0x08, 27); // no offset
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mixer") {
|
||||
@@ -1550,11 +1644,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
|
||||
// shell.invoke_command("call mixer wwc1 info");
|
||||
// shell.invoke_command("call mixer wwc2 info");
|
||||
// shell.invoke_command("call mixer wwc1 info");
|
||||
// shell.invoke_command("call mixer wwc2 info");
|
||||
|
||||
// test API
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// test API
|
||||
AsyncWebServerRequest request;
|
||||
request.url("/api/mixer");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
@@ -1562,30 +1655,35 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
request.url("/api/mixer/wwc2/pumpstatus");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
#endif
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "crash") {
|
||||
shell.printfln("Forcing a crash...");
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
uint8_t a = 2 / 0;
|
||||
shell.printfln("Testing %s", a);
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (command == "limited") {
|
||||
shell.printfln("Run a limited memory test...");
|
||||
|
||||
run_test("general");
|
||||
if (!ok) {
|
||||
shell.printfln("Unknown test command: %s", command.c_str());
|
||||
EMSESP::logger().notice("Unknown test command: %s", command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// loop console. simulates what EMSESP::loop() does
|
||||
void Test::refresh() {
|
||||
uuid::loop();
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::mqtt_.loop();
|
||||
Shell::loop_all();
|
||||
}
|
||||
|
||||
// simulates a telegram in the Rx queue, but without the CRC which is added automatically
|
||||
void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
uint8_t len = rx_data.size();
|
||||
@@ -1598,9 +1696,7 @@ void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::rxservice_.add(data, len + 1);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// simulates a telegram straight from UART, but without the CRC which is added automatically
|
||||
@@ -1615,9 +1711,7 @@ void Test::uart_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::incoming_telegram(data, i + 1);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// takes raw string, assuming it contains the CRC. This is what is output from 'watch raw'
|
||||
@@ -1656,9 +1750,7 @@ void Test::uart_telegram_withCRC(const char * rx_data) {
|
||||
|
||||
EMSESP::incoming_telegram(data, count + 1);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// takes raw string, adds CRC to end
|
||||
@@ -1699,17 +1791,14 @@ void Test::uart_telegram(const char * rx_data) {
|
||||
|
||||
EMSESP::incoming_telegram(data, count + 2);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// Sends version telegram. Version is hardcoded to 1.0
|
||||
void Test::add_device(uint8_t device_id, uint8_t product_id) {
|
||||
// Send version: 09 0B 02 00 PP V1 V2
|
||||
uart_telegram({device_id, EMSESP_DEFAULT_EMS_BUS_ID, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0});
|
||||
}
|
||||
|
||||
#ifdef EMSESP_TEST
|
||||
#ifndef EMSESP_STANDALONE
|
||||
void Test::listDir(fs::FS & fs, const char * dirname, uint8_t levels) {
|
||||
Serial.println();
|
||||
@@ -1744,24 +1833,7 @@ void Test::listDir(fs::FS & fs, const char * dirname, uint8_t levels) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Test::debug(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
// shell.add_flags(CommandFlags::ADMIN); // switch to su
|
||||
|
||||
std::string command(20, '\0');
|
||||
if ((cmd.empty()) || (cmd == "default")) {
|
||||
command = "ls";
|
||||
} else {
|
||||
command = cmd;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (command == "ls") {
|
||||
listDir(LittleFS, "/", 3);
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
|
||||
#ifndef EMSESP_TEST_H
|
||||
#define EMSESP_TEST_H
|
||||
@@ -28,10 +28,9 @@ namespace emsesp {
|
||||
|
||||
// #define EMSESP_DEBUG_DEFAULT "thermostat"
|
||||
// #define EMSESP_DEBUG_DEFAULT "solar"
|
||||
#define EMSESP_DEBUG_DEFAULT "mixer"
|
||||
// #define EMSESP_DEBUG_DEFAULT "web"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt"
|
||||
// #define EMSESP_DEBUG_DEFAULT "general"
|
||||
#define EMSESP_DEBUG_DEFAULT "general"
|
||||
// #define EMSESP_DEBUG_DEFAULT "boiler"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt2"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
|
||||
@@ -52,7 +51,8 @@ namespace emsesp {
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_post"
|
||||
// #define EMSESP_DEBUG_DEFAULT "api_wwmode"
|
||||
// #define EMSESP_DEBUG_DEFAULT "custom"
|
||||
// #define EMSESP_DEBUG_DEFAULT "dump"
|
||||
// #define EMSESP_DEBUG_DEFAULT "entity_dump"
|
||||
// #define EMSESP_DEBUG_DEFAULT "memory"
|
||||
|
||||
class Test {
|
||||
public:
|
||||
@@ -64,10 +64,8 @@ class Test {
|
||||
static void uart_telegram(const char * rx_data);
|
||||
static void uart_telegram_withCRC(const char * rx_data);
|
||||
static void add_device(uint8_t device_id, uint8_t product_id);
|
||||
static void debug(uuid::console::Shell & shell, const std::string & command);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static void refresh();
|
||||
static void listDir(fs::FS & fs, const char * dirname, uint8_t levels);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.5.1-dev.0"
|
||||
#define EMSESP_APP_VERSION "3.6.0-dev.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -100,8 +100,11 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
}
|
||||
}
|
||||
|
||||
// capture current heap memory before allocating the large return buffer
|
||||
emsesp::EMSESP::system_.refreshHeapMem();
|
||||
|
||||
// output json buffer
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXLARGE_DYN;
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE;
|
||||
auto * response = new PrettyAsyncJsonResponse(false, buffer);
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
@@ -123,7 +126,6 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
api_fails_++;
|
||||
} else {
|
||||
// emsesp::EMSESP::logger().debug("API command called successfully");
|
||||
// if there was no json output from the call, default to the output message 'OK'.
|
||||
if (!output.size()) {
|
||||
output["message"] = "OK";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -178,7 +178,7 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques
|
||||
|
||||
// send back a list of devices used in the customization web page
|
||||
void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
@@ -201,7 +201,7 @@ void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
// send back list of device entities
|
||||
void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE_DYN;
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
|
||||
auto * response = new MsgpackAsyncJsonResponse(true, buffer);
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
@@ -328,4 +328,4 @@ void WebCustomizationService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -70,7 +70,7 @@ void WebDataService::scan_devices(AsyncWebServerRequest * request) {
|
||||
// this is used in the dashboard and contains all ems device information
|
||||
// /coreData endpoint
|
||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
@@ -83,7 +83,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
obj["id"] = Helpers::smallitoa(buffer, emsdevice->unique_id()); // a unique id as a string
|
||||
obj["tn"] = emsdevice->device_type_2_device_name_translated(); // translated device type name
|
||||
obj["t"] = emsdevice->device_type(); // device type number
|
||||
obj["b"] = emsdevice->brand_to_string(); // brand
|
||||
obj["b"] = emsdevice->brand_to_char(); // brand
|
||||
obj["n"] = emsdevice->name(); // name
|
||||
obj["d"] = emsdevice->device_id(); // deviceid
|
||||
obj["p"] = emsdevice->product_id(); // productid
|
||||
@@ -106,7 +106,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
// /sensorData endpoint
|
||||
// the "sensors" and "analogs" are arrays and must exist
|
||||
void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// dallas sensors
|
||||
@@ -167,7 +167,7 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
||||
void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE_DYN;
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
|
||||
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
@@ -186,12 +186,6 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
emsdevice->generate_values_web(output);
|
||||
#endif
|
||||
|
||||
// #ifdef EMSESP_USE_SERIAL
|
||||
// #ifdef EMSESP_DEBUG
|
||||
// serializeJson(output, Serial);
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
size_t length = response->setLength();
|
||||
EMSESP::logger().debug("Dashboard buffer used: %d", length);
|
||||
@@ -251,7 +245,9 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
|
||||
if (return_code != CommandRet::OK) {
|
||||
EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code).c_str());
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Write command successful");
|
||||
#endif
|
||||
}
|
||||
|
||||
response->setCode((return_code == CommandRet::OK) ? 200 : 204);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -136,7 +136,7 @@ void WebLogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
||||
log_messages_.pop_front();
|
||||
}
|
||||
|
||||
log_messages_.emplace_back(log_message_id_++, std::move(message));
|
||||
log_messages_.emplace_back(++log_message_id_, std::move(message));
|
||||
|
||||
EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) {
|
||||
if (!settings.enabled || (time(nullptr) < 1500000000L)) {
|
||||
@@ -191,9 +191,9 @@ char * WebLogService::messagetime(char * out, const uint64_t t, const size_t buf
|
||||
|
||||
// send to web eventsource
|
||||
void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
auto jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_MEDIUM);
|
||||
JsonObject logEvent = jsonDocument.to<JsonObject>();
|
||||
char time_string[25];
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> jsonDocument;
|
||||
JsonObject logEvent = jsonDocument.to<JsonObject>();
|
||||
char time_string[25];
|
||||
|
||||
logEvent["t"] = messagetime(time_string, message.content_->uptime_ms, sizeof(time_string));
|
||||
logEvent["l"] = message.content_->level;
|
||||
@@ -212,32 +212,8 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
|
||||
// send the complete log buffer to the API, not filtering on log level
|
||||
void WebLogService::fetchLog(AsyncWebServerRequest * request) {
|
||||
// auto * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN + 192 * log_messages_.size());
|
||||
size_t buffer = EMSESP_JSON_SIZE_XLARGE_DYN + 192 * log_messages_.size();
|
||||
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
buffer -= 1024;
|
||||
response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
}
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray log = root.createNestedArray("events");
|
||||
|
||||
log_message_id_tail_ = log_messages_.back().id_;
|
||||
last_transmit_ = uuid::get_uptime_ms();
|
||||
for (const auto & message : log_messages_) {
|
||||
JsonObject logEvent = log.createNestedObject();
|
||||
char time_string[25];
|
||||
|
||||
logEvent["t"] = messagetime(time_string, message.content_->uptime_ms, sizeof(time_string));
|
||||
logEvent["l"] = message.content_->level;
|
||||
logEvent["i"] = message.id_;
|
||||
logEvent["n"] = message.content_->name;
|
||||
logEvent["m"] = message.content_->text;
|
||||
}
|
||||
log_message_id_tail_ = log_messages_.back().id_;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
log_message_id_tail_ = 0;
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
// sets the values like level after a POST
|
||||
@@ -271,4 +247,4 @@ void WebLogService::getValues(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
191
src/web/WebSchedulerService.cpp
Normal file
191
src/web/WebSchedulerService.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 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"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebSchedulerService::WebSchedulerService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebScheduler::read, WebScheduler::update, this, server, EMSESP_SCHEDULER_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED)
|
||||
, _fsPersistence(WebScheduler::read, WebScheduler::update, this, fs, EMSESP_SCHEDULER_FILE) {
|
||||
}
|
||||
|
||||
// load the settings when the service starts
|
||||
void WebSchedulerService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
EMSESP::logger().info("Starting Scheduler service");
|
||||
}
|
||||
|
||||
// this creates the scheduler file, saving it to the FS
|
||||
void WebScheduler::read(WebScheduler & webScheduler, JsonObject & root) {
|
||||
JsonArray schedule = root.createNestedArray("schedule");
|
||||
uint8_t count = 0;
|
||||
char s[3];
|
||||
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||
JsonObject si = schedule.createNestedObject();
|
||||
si["id"] = Helpers::smallitoa(s, count++); // create unique ID as a string
|
||||
si["active"] = scheduleItem.active;
|
||||
si["flags"] = scheduleItem.flags;
|
||||
si["time"] = scheduleItem.time;
|
||||
si["cmd"] = scheduleItem.cmd;
|
||||
si["value"] = scheduleItem.value;
|
||||
si["description"] = scheduleItem.description;
|
||||
}
|
||||
}
|
||||
|
||||
// call on initialization and also when the page is saved via web UI
|
||||
// this loads the data into the internal class
|
||||
StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webScheduler) {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// invoke some fake data for testing
|
||||
const char * json =
|
||||
"{[{\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1/mode\",\"value\": \"day\",\"description\": \"turn on central heating\"}]}";
|
||||
StaticJsonDocument<500> doc;
|
||||
deserializeJson(doc, json);
|
||||
root = doc.as<JsonObject>();
|
||||
Serial.println(COLOR_BRIGHT_MAGENTA);
|
||||
Serial.print("Using custom file: ");
|
||||
serializeJson(root, Serial);
|
||||
Serial.println(COLOR_RESET);
|
||||
#endif
|
||||
|
||||
webScheduler.scheduleItems.clear();
|
||||
|
||||
if (root["schedule"].is<JsonArray>()) {
|
||||
for (const JsonObject schedule : root["schedule"].as<JsonArray>()) {
|
||||
// create each schedule item, overwriting any previous settings
|
||||
// ignore the id (as this is only used in the web for table rendering)
|
||||
auto si = ScheduleItem();
|
||||
si.id = schedule["id"].as<std::string>();
|
||||
si.active = schedule["active"];
|
||||
si.flags = schedule["flags"];
|
||||
si.time = schedule["time"].as<std::string>();
|
||||
si.cmd = schedule["cmd"].as<std::string>();
|
||||
si.value = schedule["value"].as<std::string>();
|
||||
si.description = schedule["description"].as<std::string>();
|
||||
|
||||
// calculated elapsed minutes
|
||||
si.elapsed_min = Helpers::string2minutes(si.time);
|
||||
si.retry_cnt = 0xFF; // no starup retries
|
||||
|
||||
webScheduler.scheduleItems.push_back(si); // add to list
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
// execute scheduled command
|
||||
bool WebSchedulerService::command(const char * cmd, const char * data) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_input;
|
||||
JsonObject input = doc_input.to<JsonObject>();
|
||||
if (strlen(data)) { // empty data queries a value
|
||||
input["data"] = data;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_output; // only for commands without output
|
||||
JsonObject output = doc_output.to<JsonObject>();
|
||||
|
||||
// prefix "api/" to command string
|
||||
char command_str[100];
|
||||
snprintf(command_str, sizeof(command_str), "/api/%s", cmd);
|
||||
uint8_t return_code = Command::process(command_str, true, input, output); // admin set
|
||||
|
||||
if (return_code == CommandRet::OK) {
|
||||
EMSESP::logger().debug("Scheduled command %s with data %s successfully", cmd, data);
|
||||
if (strlen(data) == 0 && Mqtt::enabled() && Mqtt::send_response() && output.size()) {
|
||||
Mqtt::queue_publish("response", output);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char error[100];
|
||||
if (output.size()) {
|
||||
snprintf(error,
|
||||
sizeof(error),
|
||||
"Scheduled command %s failed with error: %s (%s)",
|
||||
cmd,
|
||||
(const char *)output["message"],
|
||||
Command::return_code_string(return_code).c_str());
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Scheduled command %s failed with error code (%s)", cmd, Command::return_code_string(return_code).c_str());
|
||||
}
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// process any scheduled jobs
|
||||
// checks on the minute
|
||||
void WebSchedulerService::loop() {
|
||||
// initialize static value on startup
|
||||
static int8_t last_tm_min = -1; // invalid value also used for startup commands
|
||||
static uint32_t last_uptime_min = 0;
|
||||
|
||||
// get list of scheduler events and exit if it's empty
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check startup commands
|
||||
if (last_tm_min == -1) {
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0) {
|
||||
scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
last_tm_min = 0; // startup done, now use for RTC
|
||||
}
|
||||
|
||||
// check timer every minute, sync to EMS-ESP clock
|
||||
uint32_t uptime_min = uuid::get_uptime_sec() / 60;
|
||||
if (last_uptime_min != uptime_min) {
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
// retry startup commands not yet executed
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0
|
||||
&& scheduleItem.retry_cnt < MAX_STARTUP_RETRIES) {
|
||||
scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : scheduleItem.retry_cnt + 1;
|
||||
}
|
||||
// scheduled timer commands
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0
|
||||
&& (uptime_min % scheduleItem.elapsed_min == 0)) {
|
||||
command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str());
|
||||
}
|
||||
}
|
||||
last_uptime_min = uptime_min;
|
||||
}
|
||||
|
||||
// check calender, sync to RTC, only execute if year is valid
|
||||
time_t now = time(nullptr);
|
||||
tm * tm = localtime(&now);
|
||||
if (tm->tm_min != last_tm_min && tm->tm_year > 120) {
|
||||
// find the real dow and minute from RTC
|
||||
uint8_t real_dow = 1 << tm->tm_wday; // 1 is Sunday
|
||||
uint16_t real_min = tm->tm_hour * 60 + tm->tm_min;
|
||||
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.active && (real_dow & scheduleItem.flags) && real_min == scheduleItem.elapsed_min) {
|
||||
command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str());
|
||||
}
|
||||
}
|
||||
last_tm_min = tm->tm_min;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
74
src/web/WebSchedulerService.h
Normal file
74
src/web/WebSchedulerService.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 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 WebSchedulerService_h
|
||||
#define WebSchedulerService_h
|
||||
|
||||
#define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json"
|
||||
#define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST
|
||||
|
||||
#define SCHEDULEFLAG_SCHEDULE_TIMER 0x80 // 7th bit for Timer
|
||||
#define MAX_STARTUP_RETRIES 3 // retry the statup commands x times
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class ScheduleItem {
|
||||
public:
|
||||
std::string id; // unqiue id
|
||||
boolean active;
|
||||
uint8_t flags;
|
||||
uint16_t elapsed_min; // total mins from 00:00
|
||||
std::string time; // HH:MM
|
||||
std::string cmd;
|
||||
std::string value;
|
||||
std::string description;
|
||||
uint8_t retry_cnt;
|
||||
};
|
||||
|
||||
class WebScheduler {
|
||||
public:
|
||||
std::list<ScheduleItem> scheduleItems;
|
||||
|
||||
static void read(WebScheduler & webScheduler, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebScheduler & webScheduler);
|
||||
};
|
||||
|
||||
class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
public:
|
||||
WebSchedulerService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
|
||||
private:
|
||||
bool command(const char * cmd, const char * data);
|
||||
|
||||
// make all functions public so we can test in the debug and standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
private:
|
||||
#endif
|
||||
|
||||
HttpEndpoint<WebScheduler> _httpEndpoint;
|
||||
FSPersistence<WebScheduler> _fsPersistence;
|
||||
|
||||
std::list<ScheduleItem> * scheduleItems; // pointer to the list of schedule events
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -240,14 +240,22 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.low_clock = root["low_clock"] | false;
|
||||
check_flag(prev, settings.low_clock, ChangeFlags::RESTART);
|
||||
|
||||
String old_local = settings.locale;
|
||||
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
|
||||
EMSESP::system_.locale(settings.locale);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!old_local.equals(settings.locale)) {
|
||||
add_flags(ChangeFlags::RESTART); // force restart
|
||||
//
|
||||
// these may need mqtt restart to rebuild HA discovery topics
|
||||
//
|
||||
prev = settings.bool_format;
|
||||
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
||||
EMSESP::system_.bool_format(settings.bool_format);
|
||||
if (Mqtt::ha_enabled()) {
|
||||
check_flag(prev, settings.bool_format, ChangeFlags::MQTT);
|
||||
}
|
||||
|
||||
prev = settings.enum_format;
|
||||
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
|
||||
EMSESP::system_.enum_format(settings.enum_format);
|
||||
if (Mqtt::ha_enabled()) {
|
||||
check_flag(prev, settings.enum_format, ChangeFlags::MQTT);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// these may need mqtt restart to rebuild HA discovery topics
|
||||
@@ -267,6 +275,9 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
//
|
||||
// without checks or necessary restarts...
|
||||
//
|
||||
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
|
||||
EMSESP::system_.locale(settings.locale);
|
||||
|
||||
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
||||
EMSESP::trace_raw(settings.trace_raw);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
@@ -32,6 +32,8 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
||||
|
||||
// handles both WiFI and Ethernet
|
||||
void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
EMSESP::logger().warning("WiFi disconnected. Reason code=%s", disconnectReason(info.wifi_sta_disconnected.reason)); // IDF 4.0
|
||||
@@ -39,10 +41,8 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info("WiFi connected with IP=%s, hostname=%s", WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
#endif
|
||||
EMSESP::system_.syslog_init();
|
||||
// EMSESP::system_.syslog_init();
|
||||
mDNS_start();
|
||||
EMSESP::system_.send_info_mqtt("connected");
|
||||
break;
|
||||
@@ -63,11 +63,9 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
// prevent double calls
|
||||
if (!EMSESP::system_.ethernet_connected()) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info("Ethernet connected with IP=%s, speed %d Mbps", ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
#endif
|
||||
// EMSESP::system_.send_heartbeat();
|
||||
EMSESP::system_.syslog_init();
|
||||
// EMSESP::system_.syslog_init();
|
||||
EMSESP::system_.ethernet_connected(true);
|
||||
mDNS_start();
|
||||
EMSESP::system_.send_info_mqtt("connected");
|
||||
@@ -84,7 +82,6 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
EMSESP::system_.ethernet_connected(false);
|
||||
break;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (networkSettings.enableIPv6) {
|
||||
@@ -108,19 +105,19 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
} else {
|
||||
EMSESP::logger().info("WiFi connected with IPv6=%s, hostname=%s", WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
|
||||
}
|
||||
EMSESP::system_.syslog_init();
|
||||
// EMSESP::system_.syslog_init();
|
||||
mDNS_start();
|
||||
EMSESP::system_.send_info_mqtt("connected");
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 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
|
||||
|
||||
Reference in New Issue
Block a user