mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-26 18:13:36 +05:00
Compare commits
50 Commits
1.4.5
...
c6df74f06e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6df74f06e | ||
|
|
24a46f4c16 | ||
|
|
ddc9cf7c90 | ||
|
|
2d74d0c0ad | ||
|
|
e04462811b | ||
|
|
99d82657c0 | ||
|
|
ed50208546 | ||
|
|
fda18cdb13 | ||
|
|
6a9bd9673a | ||
|
|
5aea80e630 | ||
|
|
fcf7d61ca5 | ||
|
|
e71f3868fd | ||
|
|
c3d0d94806 | ||
|
|
e4211c872c | ||
|
|
467cfea449 | ||
|
|
0e3473e065 | ||
|
|
8780e5245a | ||
|
|
94e8288d76 | ||
|
|
261a53207c | ||
|
|
1dbc895cdb | ||
|
|
747d8841bc | ||
|
|
7ed47a4eca | ||
|
|
0ccea290cb | ||
|
|
c03df67900 | ||
|
|
f86857c279 | ||
|
|
acd8348a5b | ||
|
|
cdde3c30af | ||
|
|
392242ef3e | ||
|
|
a6e8953807 | ||
|
|
11b1277d79 | ||
|
|
f62e687d3f | ||
|
|
42fa95969f | ||
|
|
56a0d1322f | ||
|
|
45762967ee | ||
|
|
10f9cde17a | ||
|
|
351a884685 | ||
|
|
98db62cc9e | ||
|
|
355d983437 | ||
|
|
3d11d13631 | ||
|
|
6c4f8a78a0 | ||
|
|
3fb5eb32c3 | ||
|
|
c1447098da | ||
|
|
87b222e7bc | ||
|
|
0eea1b8121 | ||
|
|
7f701a74e7 | ||
|
|
57cf98ca19 | ||
|
|
c32c643442 | ||
|
|
5553a13cc0 | ||
|
|
a9e97c15ad | ||
|
|
dc62f99b7d |
Binary file not shown.
@@ -10,7 +10,8 @@ public:
|
|||||||
free(this->buffer);
|
free(this->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send(int code, const char* contentType, JsonDocument& content) {
|
template <class T>
|
||||||
|
void send(int code, T contentType, const JsonVariantConst content, bool pretty = false) {
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
if (!this->webServer->chunkedResponseModeStart(code, contentType)) {
|
if (!this->webServer->chunkedResponseModeStart(code, contentType)) {
|
||||||
this->webServer->send(505, F("text/html"), F("HTTP1.1 required"));
|
this->webServer->send(505, F("text/html"), F("HTTP1.1 required"));
|
||||||
@@ -24,7 +25,13 @@ public:
|
|||||||
this->webServer->send(code, contentType, emptyString);
|
this->webServer->send(code, contentType, emptyString);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
serializeJson(content, *this);
|
if (pretty) {
|
||||||
|
serializeJsonPretty(content, *this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
serializeJson(content, *this);
|
||||||
|
}
|
||||||
|
|
||||||
this->flush();
|
this->flush();
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
@@ -70,10 +77,19 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->webServer->sendContent((const char*)this->buffer, this->bufferPos);
|
|
||||||
this->bufferPos = 0;
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto& client = this->webServer->client();
|
||||||
|
if (client.connected()) {
|
||||||
|
this->webServer->sendContent((const char*)this->buffer, this->bufferPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->bufferPos = 0;
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,66 +98,6 @@ public:
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setHeatingCh1Temp(float temperature) {
|
|
||||||
unsigned long response = this->sendRequest(buildRequest(
|
|
||||||
OpenThermMessageType::WRITE_DATA,
|
|
||||||
OpenThermMessageID::TSet,
|
|
||||||
temperatureToData(temperature)
|
|
||||||
));
|
|
||||||
|
|
||||||
return isValidResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setHeatingCh2Temp(float temperature) {
|
|
||||||
unsigned long response = this->sendRequest(buildRequest(
|
|
||||||
OpenThermMessageType::WRITE_DATA,
|
|
||||||
OpenThermMessageID::TsetCH2,
|
|
||||||
temperatureToData(temperature)
|
|
||||||
));
|
|
||||||
|
|
||||||
return isValidResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setDhwTemp(float temperature) {
|
|
||||||
unsigned long response = this->sendRequest(buildRequest(
|
|
||||||
OpenThermMessageType::WRITE_DATA,
|
|
||||||
OpenThermMessageID::TdhwSet,
|
|
||||||
temperatureToData(temperature)
|
|
||||||
));
|
|
||||||
|
|
||||||
return isValidResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setRoomSetpoint(float temperature) {
|
|
||||||
unsigned long response = this->sendRequest(buildRequest(
|
|
||||||
OpenThermMessageType::WRITE_DATA,
|
|
||||||
OpenThermMessageID::TrSet,
|
|
||||||
temperatureToData(temperature)
|
|
||||||
));
|
|
||||||
|
|
||||||
return isValidResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setRoomSetpointCh2(float temperature) {
|
|
||||||
unsigned long response = this->sendRequest(buildRequest(
|
|
||||||
OpenThermMessageType::WRITE_DATA,
|
|
||||||
OpenThermMessageID::TrSetCH2,
|
|
||||||
temperatureToData(temperature)
|
|
||||||
));
|
|
||||||
|
|
||||||
return isValidResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setRoomTemp(float temperature) {
|
|
||||||
unsigned long response = this->sendRequest(buildRequest(
|
|
||||||
OpenThermMessageType::WRITE_DATA,
|
|
||||||
OpenThermMessageID::Tr,
|
|
||||||
temperatureToData(temperature)
|
|
||||||
));
|
|
||||||
|
|
||||||
return isValidResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sendBoilerReset() {
|
bool sendBoilerReset() {
|
||||||
unsigned int data = 1;
|
unsigned int data = 1;
|
||||||
data <<= 8;
|
data <<= 8;
|
||||||
@@ -167,7 +107,7 @@ public:
|
|||||||
data
|
data
|
||||||
));
|
));
|
||||||
|
|
||||||
return isValidResponse(response);
|
return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sendServiceReset() {
|
bool sendServiceReset() {
|
||||||
@@ -179,7 +119,7 @@ public:
|
|||||||
data
|
data
|
||||||
));
|
));
|
||||||
|
|
||||||
return isValidResponse(response);
|
return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sendWaterFilling() {
|
bool sendWaterFilling() {
|
||||||
@@ -191,7 +131,13 @@ public:
|
|||||||
data
|
data
|
||||||
));
|
));
|
||||||
|
|
||||||
return isValidResponse(response);
|
return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isValidResponseId(unsigned long response, OpenThermMessageID id) {
|
||||||
|
byte responseId = (response >> 16) & 0xFF;
|
||||||
|
|
||||||
|
return (byte)id == responseId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// converters
|
// converters
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// лимит выходной величины
|
// лимит выходной величины
|
||||||
void setLimits(int min_output, int max_output) {
|
void setLimits(unsigned short min_output, unsigned short max_output) {
|
||||||
_minOut = min_output;
|
_minOut = min_output;
|
||||||
_maxOut = max_output;
|
_maxOut = max_output;
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _minOut = 20, _maxOut = 90;
|
unsigned short _minOut = 20, _maxOut = 90;
|
||||||
|
|
||||||
// температура контура отопления в зависимости от наружной температуры
|
// температура контура отопления в зависимости от наружной температуры
|
||||||
datatype getResultN() {
|
datatype getResultN() {
|
||||||
@@ -58,6 +58,6 @@ private:
|
|||||||
|
|
||||||
// Расчет поправки (ошибки) термостата
|
// Расчет поправки (ошибки) термостата
|
||||||
datatype getResultT() {
|
datatype getResultT() {
|
||||||
return constrain((targetTemp - indoorTemp), -2, 2) * Kt;
|
return constrain((targetTemp - indoorTemp), -3, 3) * Kt;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -100,8 +100,8 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class CT, class NT>
|
||||||
String getTopic(T category, T name, char nameSeparator = '/') {
|
String makeConfigTopic(CT category, NT name, char nameSeparator = '/') {
|
||||||
String topic = "";
|
String topic = "";
|
||||||
topic.concat(this->prefix);
|
topic.concat(this->prefix);
|
||||||
topic.concat('/');
|
topic.concat('/');
|
||||||
@@ -110,21 +110,45 @@ public:
|
|||||||
topic.concat(this->devicePrefix);
|
topic.concat(this->devicePrefix);
|
||||||
topic.concat(nameSeparator);
|
topic.concat(nameSeparator);
|
||||||
topic.concat(name);
|
topic.concat(name);
|
||||||
topic.concat("/config");
|
topic.concat(F("/config"));
|
||||||
return topic;
|
return topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
String getDeviceTopic(T value, char separator = '/') {
|
String getDeviceTopic(T value, char dpvSeparator = '/') {
|
||||||
String topic = "";
|
String topic = "";
|
||||||
topic.concat(this->devicePrefix);
|
topic.concat(this->devicePrefix);
|
||||||
topic.concat(separator);
|
topic.concat(dpvSeparator);
|
||||||
topic.concat(value);
|
topic.concat(value);
|
||||||
return topic;
|
return topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class CT, class NT>
|
||||||
|
String getDeviceTopic(CT category, NT name, char dpcSeparator = '/', char cnSeparator = '/') {
|
||||||
|
String topic = "";
|
||||||
|
topic.concat(this->devicePrefix);
|
||||||
|
topic.concat(dpcSeparator);
|
||||||
|
topic.concat(category);
|
||||||
|
topic.concat(cnSeparator);
|
||||||
|
topic.concat(name);
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class CT, class NT, class ST>
|
||||||
|
String getDeviceTopic(CT category, NT name, ST suffix, char dpcSeparator = '/', char cnSeparator = '/', char nsSeparator = '/') {
|
||||||
|
String topic = "";
|
||||||
|
topic.concat(this->devicePrefix);
|
||||||
|
topic.concat(dpcSeparator);
|
||||||
|
topic.concat(category);
|
||||||
|
topic.concat(cnSeparator);
|
||||||
|
topic.concat(name);
|
||||||
|
topic.concat(nsSeparator);
|
||||||
|
topic.concat(suffix);
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
String getObjectId(T value, char separator = '_') {
|
String getObjectIdWithPrefix(T value, char separator = '_') {
|
||||||
String topic = "";
|
String topic = "";
|
||||||
topic.concat(this->devicePrefix);
|
topic.concat(this->devicePrefix);
|
||||||
topic.concat(separator);
|
topic.concat(separator);
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ const char HA_ENABLED_BY_DEFAULT[] PROGMEM = "enabled_by_default";
|
|||||||
const char HA_UNIQUE_ID[] PROGMEM = "unique_id";
|
const char HA_UNIQUE_ID[] PROGMEM = "unique_id";
|
||||||
const char HA_OBJECT_ID[] PROGMEM = "object_id";
|
const char HA_OBJECT_ID[] PROGMEM = "object_id";
|
||||||
const char HA_ENTITY_CATEGORY[] PROGMEM = "entity_category";
|
const char HA_ENTITY_CATEGORY[] PROGMEM = "entity_category";
|
||||||
|
const char HA_ENTITY_CATEGORY_DIAGNOSTIC[] PROGMEM = "diagnostic";
|
||||||
|
const char HA_ENTITY_CATEGORY_CONFIG[] PROGMEM = "config";
|
||||||
const char HA_STATE_TOPIC[] PROGMEM = "state_topic";
|
const char HA_STATE_TOPIC[] PROGMEM = "state_topic";
|
||||||
const char HA_VALUE_TEMPLATE[] PROGMEM = "value_template";
|
const char HA_VALUE_TEMPLATE[] PROGMEM = "value_template";
|
||||||
const char HA_OPTIONS[] PROGMEM = "options";
|
const char HA_OPTIONS[] PROGMEM = "options";
|
||||||
@@ -35,16 +37,21 @@ const char HA_DEVICE_CLASS[] PROGMEM = "device_class";
|
|||||||
const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_measurement";
|
const char HA_UNIT_OF_MEASUREMENT[] PROGMEM = "unit_of_measurement";
|
||||||
const char HA_UNIT_OF_MEASUREMENT_C[] PROGMEM = "°C";
|
const char HA_UNIT_OF_MEASUREMENT_C[] PROGMEM = "°C";
|
||||||
const char HA_UNIT_OF_MEASUREMENT_F[] PROGMEM = "°F";
|
const char HA_UNIT_OF_MEASUREMENT_F[] PROGMEM = "°F";
|
||||||
|
const char HA_UNIT_OF_MEASUREMENT_PERCENT[] PROGMEM = "%";
|
||||||
|
const char HA_UNIT_OF_MEASUREMENT_L_MIN[] PROGMEM = "L/min";
|
||||||
|
const char HA_UNIT_OF_MEASUREMENT_GAL_MIN[] PROGMEM = "gal/min";
|
||||||
const char HA_ICON[] PROGMEM = "icon";
|
const char HA_ICON[] PROGMEM = "icon";
|
||||||
const char HA_MIN[] PROGMEM = "min";
|
const char HA_MIN[] PROGMEM = "min";
|
||||||
const char HA_MAX[] PROGMEM = "max";
|
const char HA_MAX[] PROGMEM = "max";
|
||||||
const char HA_STEP[] PROGMEM = "step";
|
const char HA_STEP[] PROGMEM = "step";
|
||||||
const char HA_MODE[] PROGMEM = "mode";
|
const char HA_MODE[] PROGMEM = "mode";
|
||||||
|
const char HA_MODE_BOX[] PROGMEM = "box";
|
||||||
const char HA_STATE_ON[] PROGMEM = "state_on";
|
const char HA_STATE_ON[] PROGMEM = "state_on";
|
||||||
const char HA_STATE_OFF[] PROGMEM = "state_off";
|
const char HA_STATE_OFF[] PROGMEM = "state_off";
|
||||||
const char HA_PAYLOAD_ON[] PROGMEM = "payload_on";
|
const char HA_PAYLOAD_ON[] PROGMEM = "payload_on";
|
||||||
const char HA_PAYLOAD_OFF[] PROGMEM = "payload_off";
|
const char HA_PAYLOAD_OFF[] PROGMEM = "payload_off";
|
||||||
const char HA_STATE_CLASS[] PROGMEM = "state_class";
|
const char HA_STATE_CLASS[] PROGMEM = "state_class";
|
||||||
|
const char HA_STATE_CLASS_MEASUREMENT[] PROGMEM = "measurement";
|
||||||
const char HA_EXPIRE_AFTER[] PROGMEM = "expire_after";
|
const char HA_EXPIRE_AFTER[] PROGMEM = "expire_after";
|
||||||
const char HA_CURRENT_TEMPERATURE_TOPIC[] PROGMEM = "current_temperature_topic";
|
const char HA_CURRENT_TEMPERATURE_TOPIC[] PROGMEM = "current_temperature_topic";
|
||||||
const char HA_CURRENT_TEMPERATURE_TEMPLATE[] PROGMEM = "current_temperature_template";
|
const char HA_CURRENT_TEMPERATURE_TEMPLATE[] PROGMEM = "current_temperature_template";
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publish(const char* topic, JsonDocument& doc, bool retained = false) {
|
bool publish(const char* topic, const JsonVariantConst doc, bool retained = false) {
|
||||||
if (!this->client->connected()) {
|
if (!this->client->connected()) {
|
||||||
this->bufferPos = 0;
|
this->bufferPos = 0;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -247,11 +247,19 @@ namespace NetworkUtils {
|
|||||||
this->delayCallback(250);
|
this->delayCallback(250);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!this->useDhcp) {
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
WiFi.config(this->staticIp, this->staticGateway, this->staticSubnet, this->staticDns);
|
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
|
||||||
}
|
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
|
||||||
|
#endif
|
||||||
|
|
||||||
WiFi.begin(this->staSsid, this->staPassword, this->staChannel);
|
if (!this->useDhcp) {
|
||||||
|
WiFi.begin(this->staSsid, this->staPassword, this->staChannel, nullptr, false);
|
||||||
|
WiFi.config(this->staticIp, this->staticGateway, this->staticSubnet, this->staticDns);
|
||||||
|
WiFi.reconnect();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
WiFi.begin(this->staSsid, this->staPassword, this->staChannel, nullptr, true);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long beginConnectionTime = millis();
|
unsigned long beginConnectionTime = millis();
|
||||||
while (millis() - beginConnectionTime < timeout) {
|
while (millis() - beginConnectionTime < timeout) {
|
||||||
@@ -267,7 +275,16 @@ namespace NetworkUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void disconnect() {
|
void disconnect() {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
WiFi.disconnectAsync(false, true);
|
||||||
|
|
||||||
|
const unsigned long start = millis();
|
||||||
|
while (WiFi.isConnected() && (millis() - start) < 5000) {
|
||||||
|
this->delayCallback(100);
|
||||||
|
}
|
||||||
|
#else
|
||||||
WiFi.disconnect(false, true);
|
WiFi.disconnect(false, true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
@@ -365,10 +382,10 @@ namespace NetworkUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const unsigned int reconnectInterval = 5000;
|
const unsigned int reconnectInterval = 15000;
|
||||||
const unsigned int failedConnectTimeout = 120000;
|
const unsigned int failedConnectTimeout = 185000;
|
||||||
const unsigned int connectionTimeout = 15000;
|
const unsigned int connectionTimeout = 5000;
|
||||||
const unsigned int resetConnectionTimeout = 30000;
|
const unsigned int resetConnectionTimeout = 90000;
|
||||||
|
|
||||||
YieldCallback yieldCallback = []() {
|
YieldCallback yieldCallback = []() {
|
||||||
::yield();
|
::yield();
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->cacheHeader != nullptr) {
|
if (this->cacheHeader != nullptr) {
|
||||||
server.sendHeader("Cache-Control", this->cacheHeader);
|
server.sendHeader(F("Cache-Control"), this->cacheHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ public:
|
|||||||
typedef std::function<bool(HTTPMethod, const String&)> CanHandleCallback;
|
typedef std::function<bool(HTTPMethod, const String&)> CanHandleCallback;
|
||||||
typedef std::function<bool()> BeforeSendCallback;
|
typedef std::function<bool()> BeforeSendCallback;
|
||||||
|
|
||||||
StaticPage(const char* uri, FS* fs, const char* path, const char* cacheHeader = nullptr) {
|
template <class T>
|
||||||
|
StaticPage(const char* uri, FS* fs, T path, const char* cacheHeader = nullptr) {
|
||||||
this->uri = uri;
|
this->uri = uri;
|
||||||
this->fs = fs;
|
this->fs = fs;
|
||||||
this->path = path;
|
this->path = path;
|
||||||
@@ -55,7 +56,7 @@ public:
|
|||||||
this->eTag = esp8266webserver::calcETag(*this->fs, this->path);
|
this->eTag = esp8266webserver::calcETag(*this->fs, this->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.header("If-None-Match").equals(this->eTag.c_str())) {
|
if (server.header(F("If-None-Match")).equals(this->eTag.c_str())) {
|
||||||
server.send(304);
|
server.send(304);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -80,12 +81,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->cacheHeader != nullptr) {
|
if (this->cacheHeader != nullptr) {
|
||||||
server.sendHeader("Cache-Control", this->cacheHeader);
|
server.sendHeader(F("Cache-Control"), this->cacheHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
if (server._eTagEnabled && this->eTag.length() > 0) {
|
if (server._eTagEnabled && this->eTag.length() > 0) {
|
||||||
server.sendHeader("ETag", this->eTag);
|
server.sendHeader(F("ETag"), this->eTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.streamFile(file, F("text/html"), method);
|
server.streamFile(file, F("text/html"), method);
|
||||||
|
|||||||
@@ -93,10 +93,12 @@ public:
|
|||||||
|
|
||||||
void upload(WebServer& server, const String& uri, HTTPUpload& upload) override {
|
void upload(WebServer& server, const String& uri, HTTPUpload& upload) override {
|
||||||
UpgradeResult* result;
|
UpgradeResult* result;
|
||||||
if (upload.name.equals("firmware")) {
|
if (upload.name.equals(F("firmware"))) {
|
||||||
result = &this->firmwareResult;
|
result = &this->firmwareResult;
|
||||||
} else if (upload.name.equals("filesystem")) {
|
|
||||||
|
} else if (upload.name.equals(F("filesystem"))) {
|
||||||
result = &this->filesystemResult;
|
result = &this->filesystemResult;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,21 +11,24 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
;extra_configs = secrets.ini
|
;extra_configs = secrets.ini
|
||||||
extra_configs = secrets.default.ini
|
extra_configs = secrets.default.ini
|
||||||
|
core_dir = .pio
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
version = 1.4.5
|
version = 1.5.0-alpha
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@^7.1.0
|
bblanchon/ArduinoJson@^7.1.0
|
||||||
;ihormelnyk/OpenTherm Library@^1.1.5
|
;ihormelnyk/OpenTherm Library@^1.1.5
|
||||||
https://github.com/ihormelnyk/opentherm_library#master
|
https://github.com/ihormelnyk/opentherm_library#master
|
||||||
arduino-libraries/ArduinoMqttClient@^0.1.8
|
;arduino-libraries/ArduinoMqttClient@^0.1.8
|
||||||
|
https://github.com/Laxilef/ArduinoMqttClient.git#esp32_core_310
|
||||||
lennarthennigs/ESP Telnet@^2.2
|
lennarthennigs/ESP Telnet@^2.2
|
||||||
gyverlibs/FileData@^1.0.2
|
gyverlibs/FileData@^1.0.2
|
||||||
gyverlibs/GyverPID@^3.3.2
|
gyverlibs/GyverPID@^3.3.2
|
||||||
gyverlibs/GyverBlinker@^1.1.1
|
gyverlibs/GyverBlinker@^1.1.1
|
||||||
https://github.com/pstolarz/Arduino-Temperature-Control-Library.git#OneWireNg
|
https://github.com/pstolarz/Arduino-Temperature-Control-Library.git#OneWireNg
|
||||||
laxilef/TinyLogger@^1.1.1
|
laxilef/TinyLogger@^1.1.1
|
||||||
|
build_type = ${secrets.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
-D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
-D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||||
;-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
;-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||||
@@ -35,9 +38,11 @@ build_flags =
|
|||||||
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial
|
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial
|
||||||
-D BUILD_VERSION='"${this.version}"'
|
-D BUILD_VERSION='"${this.version}"'
|
||||||
-D BUILD_ENV='"$PIOENV"'
|
-D BUILD_ENV='"$PIOENV"'
|
||||||
-D USE_SERIAL=${secrets.use_serial}
|
-D DEFAULT_SERIAL_ENABLED=${secrets.serial_enabled}
|
||||||
-D USE_TELNET=${secrets.use_telnet}
|
-D DEFAULT_SERIAL_BAUD=${secrets.serial_baud}
|
||||||
-D DEBUG_BY_DEFAULT=${secrets.debug}
|
-D DEFAULT_TELNET_ENABLED=${secrets.telnet_enabled}
|
||||||
|
-D DEFAULT_TELNET_PORT=${secrets.telnet_port}
|
||||||
|
-D DEFAULT_LOG_LEVEL=${secrets.log_level}
|
||||||
-D DEFAULT_HOSTNAME='"${secrets.hostname}"'
|
-D DEFAULT_HOSTNAME='"${secrets.hostname}"'
|
||||||
-D DEFAULT_AP_SSID='"${secrets.ap_ssid}"'
|
-D DEFAULT_AP_SSID='"${secrets.ap_ssid}"'
|
||||||
-D DEFAULT_AP_PASSWORD='"${secrets.ap_password}"'
|
-D DEFAULT_AP_PASSWORD='"${secrets.ap_password}"'
|
||||||
@@ -45,6 +50,7 @@ build_flags =
|
|||||||
-D DEFAULT_STA_PASSWORD='"${secrets.sta_password}"'
|
-D DEFAULT_STA_PASSWORD='"${secrets.sta_password}"'
|
||||||
-D DEFAULT_PORTAL_LOGIN='"${secrets.portal_login}"'
|
-D DEFAULT_PORTAL_LOGIN='"${secrets.portal_login}"'
|
||||||
-D DEFAULT_PORTAL_PASSWORD='"${secrets.portal_password}"'
|
-D DEFAULT_PORTAL_PASSWORD='"${secrets.portal_password}"'
|
||||||
|
-D DEFAULT_MQTT_ENABLED=${secrets.mqtt_enabled}
|
||||||
-D DEFAULT_MQTT_SERVER='"${secrets.mqtt_server}"'
|
-D DEFAULT_MQTT_SERVER='"${secrets.mqtt_server}"'
|
||||||
-D DEFAULT_MQTT_PORT=${secrets.mqtt_port}
|
-D DEFAULT_MQTT_PORT=${secrets.mqtt_port}
|
||||||
-D DEFAULT_MQTT_USER='"${secrets.mqtt_user}"'
|
-D DEFAULT_MQTT_USER='"${secrets.mqtt_user}"'
|
||||||
@@ -52,7 +58,10 @@ build_flags =
|
|||||||
-D DEFAULT_MQTT_PREFIX='"${secrets.mqtt_prefix}"'
|
-D DEFAULT_MQTT_PREFIX='"${secrets.mqtt_prefix}"'
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = direct
|
;monitor_filters = direct
|
||||||
|
monitor_filters =
|
||||||
|
esp32_exception_decoder
|
||||||
|
esp8266_exception_decoder
|
||||||
board_build.flash_mode = dio
|
board_build.flash_mode = dio
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
|
|
||||||
@@ -65,6 +74,7 @@ lib_deps =
|
|||||||
lib_ignore =
|
lib_ignore =
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
post:tools/build.py
|
post:tools/build.py
|
||||||
|
build_type = ${env.build_type}
|
||||||
build_flags = ${env.build_flags}
|
build_flags = ${env.build_flags}
|
||||||
board_build.ldscript = eagle.flash.4m1m.ld
|
board_build.ldscript = eagle.flash.4m1m.ld
|
||||||
|
|
||||||
@@ -74,7 +84,7 @@ board_build.ldscript = eagle.flash.4m1m.ld
|
|||||||
;platform_packages =
|
;platform_packages =
|
||||||
; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5
|
; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5
|
||||||
; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip
|
; framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.1/esp32-arduino-libs-idf-release_v5.1-33fbade6.zip
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc2/platform-espressif32.zip
|
||||||
platform_packages =
|
platform_packages =
|
||||||
board_build.partitions = esp32_partitions.csv
|
board_build.partitions = esp32_partitions.csv
|
||||||
lib_deps =
|
lib_deps =
|
||||||
@@ -85,9 +95,11 @@ lib_ignore =
|
|||||||
extra_scripts =
|
extra_scripts =
|
||||||
post:tools/esp32.py
|
post:tools/esp32.py
|
||||||
post:tools/build.py
|
post:tools/build.py
|
||||||
|
build_type = ${env.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D CORE_DEBUG_LEVEL=0
|
-D CORE_DEBUG_LEVEL=0
|
||||||
|
-Wl,--wrap=esp_panic_handler
|
||||||
|
|
||||||
|
|
||||||
; Boards
|
; Boards
|
||||||
@@ -98,6 +110,7 @@ lib_deps = ${esp8266_defaults.lib_deps}
|
|||||||
lib_ignore = ${esp8266_defaults.lib_ignore}
|
lib_ignore = ${esp8266_defaults.lib_ignore}
|
||||||
extra_scripts = ${esp8266_defaults.extra_scripts}
|
extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||||
|
build_type = ${esp8266_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp8266_defaults.build_flags}
|
${esp8266_defaults.build_flags}
|
||||||
-D DEFAULT_OT_IN_GPIO=4
|
-D DEFAULT_OT_IN_GPIO=4
|
||||||
@@ -114,6 +127,7 @@ lib_deps = ${esp8266_defaults.lib_deps}
|
|||||||
lib_ignore = ${esp8266_defaults.lib_ignore}
|
lib_ignore = ${esp8266_defaults.lib_ignore}
|
||||||
extra_scripts = ${esp8266_defaults.extra_scripts}
|
extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||||
|
build_type = ${esp8266_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp8266_defaults.build_flags}
|
${esp8266_defaults.build_flags}
|
||||||
-D DEFAULT_OT_IN_GPIO=4
|
-D DEFAULT_OT_IN_GPIO=4
|
||||||
@@ -130,6 +144,7 @@ lib_deps = ${esp8266_defaults.lib_deps}
|
|||||||
lib_ignore = ${esp8266_defaults.lib_ignore}
|
lib_ignore = ${esp8266_defaults.lib_ignore}
|
||||||
extra_scripts = ${esp8266_defaults.extra_scripts}
|
extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||||
|
build_type = ${esp8266_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp8266_defaults.build_flags}
|
${esp8266_defaults.build_flags}
|
||||||
-D DEFAULT_OT_IN_GPIO=4
|
-D DEFAULT_OT_IN_GPIO=4
|
||||||
@@ -146,6 +161,7 @@ lib_deps = ${esp8266_defaults.lib_deps}
|
|||||||
lib_ignore = ${esp8266_defaults.lib_ignore}
|
lib_ignore = ${esp8266_defaults.lib_ignore}
|
||||||
extra_scripts = ${esp8266_defaults.extra_scripts}
|
extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||||
|
build_type = ${esp8266_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp8266_defaults.build_flags}
|
${esp8266_defaults.build_flags}
|
||||||
-D DEFAULT_OT_IN_GPIO=13
|
-D DEFAULT_OT_IN_GPIO=13
|
||||||
@@ -165,6 +181,7 @@ lib_ignore = ${esp32_defaults.lib_ignore}
|
|||||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-DARDUINO_USB_MODE=1
|
-DARDUINO_USB_MODE=1
|
||||||
|
build_type = ${esp32_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_defaults.build_flags}
|
${esp32_defaults.build_flags}
|
||||||
-D ARDUINO_USB_MODE=0
|
-D ARDUINO_USB_MODE=0
|
||||||
@@ -188,6 +205,7 @@ lib_ignore = ${esp32_defaults.lib_ignore}
|
|||||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-DARDUINO_USB_MODE=1
|
-DARDUINO_USB_MODE=1
|
||||||
|
build_type = ${esp32_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_defaults.build_flags}
|
${esp32_defaults.build_flags}
|
||||||
-D ARDUINO_USB_MODE=0
|
-D ARDUINO_USB_MODE=0
|
||||||
@@ -212,6 +230,7 @@ lib_ignore = ${esp32_defaults.lib_ignore}
|
|||||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-mtext-section-literals
|
-mtext-section-literals
|
||||||
|
build_type = ${esp32_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_defaults.build_flags}
|
${esp32_defaults.build_flags}
|
||||||
-D USE_BLE=1
|
-D USE_BLE=1
|
||||||
@@ -232,6 +251,7 @@ lib_deps =
|
|||||||
${esp32_defaults.nimble_lib}
|
${esp32_defaults.nimble_lib}
|
||||||
lib_ignore = ${esp32_defaults.lib_ignore}
|
lib_ignore = ${esp32_defaults.lib_ignore}
|
||||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||||
|
build_type = ${esp32_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_defaults.build_flags}
|
${esp32_defaults.build_flags}
|
||||||
-D USE_BLE=1
|
-D USE_BLE=1
|
||||||
@@ -252,6 +272,7 @@ lib_deps =
|
|||||||
${esp32_defaults.nimble_lib}
|
${esp32_defaults.nimble_lib}
|
||||||
lib_ignore = ${esp32_defaults.lib_ignore}
|
lib_ignore = ${esp32_defaults.lib_ignore}
|
||||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||||
|
build_type = ${esp32_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_defaults.build_flags}
|
${esp32_defaults.build_flags}
|
||||||
-D USE_BLE=1
|
-D USE_BLE=1
|
||||||
@@ -274,6 +295,7 @@ lib_ignore = ${esp32_defaults.lib_ignore}
|
|||||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-mtext-section-literals
|
-mtext-section-literals
|
||||||
|
build_type = ${esp32_defaults.build_type}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_defaults.build_flags}
|
${esp32_defaults.build_flags}
|
||||||
; Currently the NimBLE library is incompatible with ESP32 C6
|
; Currently the NimBLE library is incompatible with ESP32 C6
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
[secrets]
|
[secrets]
|
||||||
use_serial = true
|
build_type = release
|
||||||
use_telnet = true
|
|
||||||
debug = true
|
serial_enabled = true
|
||||||
|
serial_baud = 115200
|
||||||
|
telnet_enabled = true
|
||||||
|
telnet_port = 23
|
||||||
|
log_level = 5
|
||||||
hostname = opentherm
|
hostname = opentherm
|
||||||
|
|
||||||
ap_ssid = OpenTherm Gateway
|
ap_ssid = OpenTherm Gateway
|
||||||
@@ -13,6 +17,7 @@ sta_password =
|
|||||||
portal_login = admin
|
portal_login = admin
|
||||||
portal_password = admin
|
portal_password = admin
|
||||||
|
|
||||||
|
mqtt_enabled = false
|
||||||
mqtt_server =
|
mqtt_server =
|
||||||
mqtt_port = 1883
|
mqtt_port = 1883
|
||||||
mqtt_user =
|
mqtt_user =
|
||||||
|
|||||||
132
src/CrashRecorder.h
Normal file
132
src/CrashRecorder.h
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#include "esp_err.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
extern "C" {
|
||||||
|
#include <user_interface.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/components/esp8266/include/esp_attr.h
|
||||||
|
#define _COUNTER_STRINGIFY(COUNTER) #COUNTER
|
||||||
|
#define _SECTION_ATTR_IMPL(SECTION, COUNTER) __attribute__((section(SECTION "." _COUNTER_STRINGIFY(COUNTER))))
|
||||||
|
#define __NOINIT_ATTR _SECTION_ATTR_IMPL(".noinit", __COUNTER__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace CrashRecorder {
|
||||||
|
typedef struct {
|
||||||
|
unsigned int data[32];
|
||||||
|
uint8_t length;
|
||||||
|
bool continues;
|
||||||
|
} backtrace_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int data[4];
|
||||||
|
uint8_t length;
|
||||||
|
} epc_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t core;
|
||||||
|
size_t heap;
|
||||||
|
unsigned long uptime;
|
||||||
|
} ext_t;
|
||||||
|
|
||||||
|
|
||||||
|
__NOINIT_ATTR volatile static backtrace_t backtrace;
|
||||||
|
__NOINIT_ATTR volatile static epc_t epc;
|
||||||
|
__NOINIT_ATTR volatile static ext_t ext;
|
||||||
|
|
||||||
|
uint8_t backtraceMaxLength = sizeof(backtrace.data) / sizeof(*backtrace.data);
|
||||||
|
uint8_t epcMaxLength = sizeof(epc.data) / sizeof(*epc.data);
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
void IRAM_ATTR panicHandler(arduino_panic_info_t *info, void *arg) {;
|
||||||
|
ext.core = info->core;
|
||||||
|
ext.heap = ESP.getFreeHeap();
|
||||||
|
ext.uptime = millis() / 1000u;
|
||||||
|
|
||||||
|
// Backtrace
|
||||||
|
backtrace.length = info->backtrace_len < backtraceMaxLength ? info->backtrace_len : backtraceMaxLength;
|
||||||
|
backtrace.continues = false;
|
||||||
|
for (unsigned int i = 0; i < info->backtrace_len; i++) {
|
||||||
|
if (i >= backtraceMaxLength) {
|
||||||
|
backtrace.continues = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace.data[i] = info->backtrace[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// EPC
|
||||||
|
if (info->pc) {
|
||||||
|
epc.data[0] = (unsigned int) info->pc;
|
||||||
|
epc.length = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
epc.length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
if (backtrace.length > backtraceMaxLength) {
|
||||||
|
backtrace.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (epc.length > epcMaxLength) {
|
||||||
|
epc.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
set_arduino_panic_handler(panicHandler, nullptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
extern "C" void custom_crash_callback(struct rst_info *info, uint32_t stack, uint32_t stack_end) {
|
||||||
|
uint8_t _length = 0;
|
||||||
|
|
||||||
|
CrashRecorder::ext.core = 0;
|
||||||
|
CrashRecorder::ext.heap = ESP.getFreeHeap();
|
||||||
|
CrashRecorder::ext.uptime = millis() / 1000u;
|
||||||
|
|
||||||
|
// Backtrace
|
||||||
|
CrashRecorder::backtrace.continues = false;
|
||||||
|
uint32_t value;
|
||||||
|
for (uint32_t i = stack; i < stack_end; i += 4) {
|
||||||
|
value = *((uint32_t*) i);
|
||||||
|
|
||||||
|
// keep only addresses in code area
|
||||||
|
if ((value >= 0x40000000) && (value < 0x40300000)) {
|
||||||
|
if (_length >= CrashRecorder::backtraceMaxLength) {
|
||||||
|
CrashRecorder::backtrace.continues = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrashRecorder::backtrace.data[_length++] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CrashRecorder::backtrace.length = _length;
|
||||||
|
|
||||||
|
// EPC
|
||||||
|
_length = 0;
|
||||||
|
if (info->epc1 > 0) {
|
||||||
|
CrashRecorder::epc.data[_length++] = info->epc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->epc2 > 0) {
|
||||||
|
CrashRecorder::epc.data[_length++] = info->epc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->epc3 > 0) {
|
||||||
|
CrashRecorder::epc.data[_length++] = info->epc3;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrashRecorder::epc.length = _length;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1833
src/HaHelper.h
1833
src/HaHelper.h
File diff suppressed because it is too large
Load Diff
327
src/MainTask.h
327
src/MainTask.h
@@ -5,7 +5,7 @@ using namespace NetworkUtils;
|
|||||||
extern NetworkMgr* network;
|
extern NetworkMgr* network;
|
||||||
extern MqttTask* tMqtt;
|
extern MqttTask* tMqtt;
|
||||||
extern OpenThermTask* tOt;
|
extern OpenThermTask* tOt;
|
||||||
extern FileData fsSettings, fsNetworkSettings;
|
extern FileData fsNetworkSettings, fsSettings, fsSensorsSettings;
|
||||||
extern ESPTelnetStream* telnetStream;
|
extern ESPTelnetStream* telnetStream;
|
||||||
|
|
||||||
|
|
||||||
@@ -60,31 +60,34 @@ protected:
|
|||||||
void loop() {
|
void loop() {
|
||||||
network->loop();
|
network->loop();
|
||||||
|
|
||||||
|
if (fsNetworkSettings.tick() == FD_WRITE) {
|
||||||
|
Log.sinfoln(FPSTR(L_NETWORK_SETTINGS), F("Updated"));
|
||||||
|
}
|
||||||
|
|
||||||
if (fsSettings.tick() == FD_WRITE) {
|
if (fsSettings.tick() == FD_WRITE) {
|
||||||
Log.sinfoln(FPSTR(L_SETTINGS), F("Updated"));
|
Log.sinfoln(FPSTR(L_SETTINGS), F("Updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fsNetworkSettings.tick() == FD_WRITE) {
|
if (fsSensorsSettings.tick() == FD_WRITE) {
|
||||||
Log.sinfoln(FPSTR(L_NETWORK_SETTINGS), F("Updated"));
|
Log.sinfoln(FPSTR(L_SENSORS_SETTINGS), F("Updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vars.actions.restart) {
|
if (vars.actions.restart) {
|
||||||
vars.actions.restart = false;
|
vars.actions.restart = false;
|
||||||
this->restartSignalTime = millis();
|
this->restartSignalTime = millis();
|
||||||
|
|
||||||
// save settings
|
|
||||||
fsSettings.updateNow();
|
|
||||||
|
|
||||||
// force save network settings
|
|
||||||
if (fsNetworkSettings.updateNow() == FD_FILE_ERR && LittleFS.begin()) {
|
|
||||||
fsNetworkSettings.write();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
|
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.states.mqtt = tMqtt->isConnected();
|
vars.mqtt.connected = tMqtt->isConnected();
|
||||||
vars.sensors.rssi = network->isConnected() ? WiFi.RSSI() : 0;
|
vars.network.connected = network->isConnected();
|
||||||
|
vars.network.rssi = network->isConnected() ? WiFi.RSSI() : 0;
|
||||||
|
|
||||||
|
if (settings.system.logLevel >= TinyLogger::Level::SILENT && settings.system.logLevel <= TinyLogger::Level::VERBOSE) {
|
||||||
|
if (Log.getLevel() != settings.system.logLevel) {
|
||||||
|
Log.setLevel(static_cast<TinyLogger::Level>(settings.system.logLevel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (network->isConnected()) {
|
if (network->isConnected()) {
|
||||||
if (!this->telnetStarted && telnetStream != nullptr) {
|
if (!this->telnetStarted && telnetStream != nullptr) {
|
||||||
@@ -92,19 +95,14 @@ protected:
|
|||||||
this->telnetStarted = true;
|
this->telnetStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.mqtt.enable && !tMqtt->isEnabled()) {
|
if (settings.mqtt.enabled && !tMqtt->isEnabled()) {
|
||||||
tMqtt->enable();
|
tMqtt->enable();
|
||||||
|
|
||||||
} else if (!settings.mqtt.enable && tMqtt->isEnabled()) {
|
} else if (!settings.mqtt.enabled && tMqtt->isEnabled()) {
|
||||||
tMqtt->disable();
|
tMqtt->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Log.getLevel() != TinyLogger::Level::INFO && !settings.system.debug ) {
|
Sensors::setConnectionStatusByType(Sensors::Type::MANUAL, !settings.mqtt.enabled || vars.mqtt.connected, false);
|
||||||
Log.setLevel(TinyLogger::Level::INFO);
|
|
||||||
|
|
||||||
} else if ( Log.getLevel() != TinyLogger::Level::VERBOSE && settings.system.debug ) {
|
|
||||||
Log.setLevel(TinyLogger::Level::VERBOSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this->telnetStarted) {
|
if (this->telnetStarted) {
|
||||||
@@ -115,11 +113,14 @@ protected:
|
|||||||
if (tMqtt->isEnabled()) {
|
if (tMqtt->isEnabled()) {
|
||||||
tMqtt->disable();
|
tMqtt->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sensors::setConnectionStatusByType(Sensors::Type::MANUAL, false, false);
|
||||||
}
|
}
|
||||||
this->yield();
|
this->yield();
|
||||||
|
|
||||||
this->emergency();
|
this->emergency();
|
||||||
this->ledStatus();
|
this->ledStatus();
|
||||||
|
this->cascadeControl();
|
||||||
this->externalPump();
|
this->externalPump();
|
||||||
this->yield();
|
this->yield();
|
||||||
|
|
||||||
@@ -135,8 +136,9 @@ protected:
|
|||||||
for (Stream* stream : Log.getStreams()) {
|
for (Stream* stream : Log.getStreams()) {
|
||||||
while (stream->available() > 0) {
|
while (stream->available() > 0) {
|
||||||
stream->read();
|
stream->read();
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +149,19 @@ protected:
|
|||||||
|
|
||||||
// restart
|
// restart
|
||||||
if (this->restartSignalTime > 0 && millis() - this->restartSignalTime > 10000) {
|
if (this->restartSignalTime > 0 && millis() - this->restartSignalTime > 10000) {
|
||||||
|
// save settings
|
||||||
|
fsSettings.updateNow();
|
||||||
|
|
||||||
|
// save sensors settings
|
||||||
|
fsSensorsSettings.updateNow();
|
||||||
|
|
||||||
|
// force save network settings
|
||||||
|
if (fsNetworkSettings.updateNow() == FD_FILE_ERR && LittleFS.begin()) {
|
||||||
|
fsNetworkSettings.write();
|
||||||
|
}
|
||||||
|
|
||||||
this->restartSignalTime = 0;
|
this->restartSignalTime = 0;
|
||||||
|
this->delay(500);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +174,7 @@ protected:
|
|||||||
this->restartSignalTime = millis();
|
this->restartSignalTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.system.debug) {
|
if (settings.system.logLevel < TinyLogger::Level::VERBOSE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,41 +203,24 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void emergency() {
|
void emergency() {
|
||||||
if (!settings.emergency.enable && vars.states.emergency) {
|
|
||||||
this->emergencyDetected = false;
|
|
||||||
vars.states.emergency = false;
|
|
||||||
|
|
||||||
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!settings.emergency.enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
uint8_t emergencyFlags = 0b00000000;
|
uint8_t emergencyFlags = 0b00000000;
|
||||||
|
|
||||||
// set network flag
|
|
||||||
if (settings.emergency.onNetworkFault && !network->isConnected()) {
|
|
||||||
emergencyFlags |= 0b00000001;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set mqtt flag
|
|
||||||
if (settings.emergency.onMqttFault && (!tMqtt->isEnabled() || !tMqtt->isConnected())) {
|
|
||||||
emergencyFlags |= 0b00000010;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set outdoor sensor flag
|
// set outdoor sensor flag
|
||||||
if (settings.sensors.outdoor.type == SensorType::DS18B20 || settings.sensors.outdoor.type == SensorType::BLUETOOTH) {
|
if (settings.equitherm.enabled) {
|
||||||
if (settings.emergency.onOutdoorSensorDisconnect && !vars.sensors.outdoor.connected) {
|
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
|
||||||
emergencyFlags |= 0b00000100;
|
emergencyFlags |= 0b00000001;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set indoor sensor flag
|
// set indoor sensor flags
|
||||||
if (settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) {
|
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP)) {
|
||||||
if (settings.emergency.onIndoorSensorDisconnect && !vars.sensors.indoor.connected) {
|
if (!settings.equitherm.enabled && settings.pid.enabled) {
|
||||||
emergencyFlags |= 0b00001000;
|
emergencyFlags |= 0b00000010;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.opentherm.nativeHeatingControl) {
|
||||||
|
emergencyFlags |= 0b00000100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,10 +231,10 @@ protected:
|
|||||||
this->emergencyDetected = true;
|
this->emergencyDetected = true;
|
||||||
this->emergencyFlipTime = millis();
|
this->emergencyFlipTime = millis();
|
||||||
|
|
||||||
} else if (this->emergencyDetected && !vars.states.emergency) {
|
} else if (this->emergencyDetected && !vars.emergency.state) {
|
||||||
// enable emergency
|
// enable emergency
|
||||||
if (millis() - this->emergencyFlipTime > (settings.emergency.tresholdTime * 1000)) {
|
if (millis() - this->emergencyFlipTime > (settings.emergency.tresholdTime * 1000)) {
|
||||||
vars.states.emergency = true;
|
vars.emergency.state = true;
|
||||||
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode enabled (%hhu)"), emergencyFlags);
|
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode enabled (%hhu)"), emergencyFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,10 +245,10 @@ protected:
|
|||||||
this->emergencyDetected = false;
|
this->emergencyDetected = false;
|
||||||
this->emergencyFlipTime = millis();
|
this->emergencyFlipTime = millis();
|
||||||
|
|
||||||
} else if (!this->emergencyDetected && vars.states.emergency) {
|
} else if (!this->emergencyDetected && vars.emergency.state) {
|
||||||
// disable emergency
|
// disable emergency
|
||||||
if (millis() - this->emergencyFlipTime > 30000) {
|
if (millis() - this->emergencyFlipTime > (settings.emergency.tresholdTime * 1000)) {
|
||||||
vars.states.emergency = false;
|
vars.emergency.state = false;
|
||||||
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
|
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,15 +287,15 @@ protected:
|
|||||||
errors[errCount++] = 2;
|
errors[errCount++] = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vars.states.otStatus) {
|
if (!vars.slave.connected) {
|
||||||
errors[errCount++] = 3;
|
errors[errCount++] = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vars.states.fault) {
|
if (vars.slave.fault.active) {
|
||||||
errors[errCount++] = 4;
|
errors[errCount++] = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vars.states.emergency) {
|
if (vars.emergency.state) {
|
||||||
errors[errCount++] = 5;
|
errors[errCount++] = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +334,170 @@ protected:
|
|||||||
this->blinker->tick();
|
this->blinker->tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cascadeControl() {
|
||||||
|
static uint8_t configuredInputGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
static uint8_t configuredOutputGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
static bool inputTempValue = false;
|
||||||
|
static unsigned long inputChangedTs = 0;
|
||||||
|
static bool outputTempValue = false;
|
||||||
|
static unsigned long outputChangedTs = 0;
|
||||||
|
|
||||||
|
// input
|
||||||
|
if (settings.cascadeControl.input.enabled) {
|
||||||
|
if (settings.cascadeControl.input.gpio != configuredInputGpio) {
|
||||||
|
if (configuredInputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
pinMode(configuredInputGpio, OUTPUT);
|
||||||
|
digitalWrite(configuredInputGpio, LOW);
|
||||||
|
|
||||||
|
Log.sinfoln(FPSTR(L_CASCADE_INPUT), F("Deinitialized on GPIO %hhu"), configuredInputGpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GPIO_IS_VALID(settings.cascadeControl.input.gpio)) {
|
||||||
|
configuredInputGpio = settings.cascadeControl.input.gpio;
|
||||||
|
pinMode(configuredInputGpio, INPUT);
|
||||||
|
|
||||||
|
Log.sinfoln(FPSTR(L_CASCADE_INPUT), F("Initialized on GPIO %hhu"), configuredInputGpio);
|
||||||
|
|
||||||
|
} else if (configuredInputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
configuredInputGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
|
||||||
|
Log.swarningln(FPSTR(L_CASCADE_INPUT), F("Failed initialize: GPIO %hhu is not valid!"), configuredInputGpio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configuredInputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
bool value;
|
||||||
|
if (digitalRead(configuredInputGpio) == HIGH) {
|
||||||
|
value = true ^ settings.cascadeControl.input.invertState;
|
||||||
|
} else {
|
||||||
|
value = false ^ settings.cascadeControl.input.invertState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != vars.cascadeControl.input) {
|
||||||
|
if (value != inputTempValue) {
|
||||||
|
inputTempValue = value;
|
||||||
|
inputChangedTs = millis();
|
||||||
|
|
||||||
|
} else if (millis() - inputChangedTs >= settings.cascadeControl.input.thresholdTime * 1000u) {
|
||||||
|
vars.cascadeControl.input = value;
|
||||||
|
|
||||||
|
Log.sinfoln(
|
||||||
|
FPSTR(L_CASCADE_INPUT),
|
||||||
|
F("State changed to %s"),
|
||||||
|
value ? F("TRUE") : F("FALSE")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value != inputTempValue) {
|
||||||
|
inputTempValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.cascadeControl.input.enabled || configuredInputGpio == GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
if (!vars.cascadeControl.input) {
|
||||||
|
vars.cascadeControl.input = true;
|
||||||
|
|
||||||
|
Log.sinfoln(
|
||||||
|
FPSTR(L_CASCADE_INPUT),
|
||||||
|
F("Disabled, state changed to %s"),
|
||||||
|
vars.cascadeControl.input ? F("TRUE") : F("FALSE")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// output
|
||||||
|
if (settings.cascadeControl.output.enabled) {
|
||||||
|
if (settings.cascadeControl.output.gpio != configuredOutputGpio) {
|
||||||
|
if (configuredOutputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
pinMode(configuredOutputGpio, OUTPUT);
|
||||||
|
digitalWrite(configuredOutputGpio, LOW);
|
||||||
|
|
||||||
|
Log.sinfoln(FPSTR(L_CASCADE_OUTPUT), F("Deinitialized on GPIO %hhu"), configuredOutputGpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GPIO_IS_VALID(settings.cascadeControl.output.gpio)) {
|
||||||
|
configuredOutputGpio = settings.cascadeControl.output.gpio;
|
||||||
|
pinMode(configuredOutputGpio, OUTPUT);
|
||||||
|
digitalWrite(
|
||||||
|
configuredOutputGpio,
|
||||||
|
settings.cascadeControl.output.invertState
|
||||||
|
? HIGH
|
||||||
|
: LOW
|
||||||
|
);
|
||||||
|
|
||||||
|
Log.sinfoln(FPSTR(L_CASCADE_OUTPUT), F("Initialized on GPIO %hhu"), configuredOutputGpio);
|
||||||
|
|
||||||
|
} else if (configuredOutputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
configuredOutputGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
|
||||||
|
Log.swarningln(FPSTR(L_CASCADE_OUTPUT), F("Failed initialize: GPIO %hhu is not valid!"), configuredOutputGpio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configuredOutputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
bool value = false;
|
||||||
|
if (settings.cascadeControl.output.onFault && vars.slave.fault.active) {
|
||||||
|
value = true;
|
||||||
|
|
||||||
|
} else if (settings.cascadeControl.output.onLossConnection && !vars.slave.connected) {
|
||||||
|
value = true;
|
||||||
|
|
||||||
|
} else if (settings.cascadeControl.output.onEnabledHeating && settings.heating.enabled && vars.cascadeControl.input) {
|
||||||
|
value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != vars.cascadeControl.output) {
|
||||||
|
if (value != outputTempValue) {
|
||||||
|
outputTempValue = value;
|
||||||
|
outputChangedTs = millis();
|
||||||
|
|
||||||
|
} else if (millis() - outputChangedTs >= settings.cascadeControl.output.thresholdTime * 1000u) {
|
||||||
|
vars.cascadeControl.output = value;
|
||||||
|
|
||||||
|
digitalWrite(
|
||||||
|
configuredOutputGpio,
|
||||||
|
vars.cascadeControl.output ^ settings.cascadeControl.output.invertState
|
||||||
|
? HIGH
|
||||||
|
: LOW
|
||||||
|
);
|
||||||
|
|
||||||
|
Log.sinfoln(
|
||||||
|
FPSTR(L_CASCADE_OUTPUT),
|
||||||
|
F("State changed to %s"),
|
||||||
|
value ? F("TRUE") : F("FALSE")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value != outputTempValue) {
|
||||||
|
outputTempValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.cascadeControl.output.enabled || configuredOutputGpio == GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
if (vars.cascadeControl.output) {
|
||||||
|
vars.cascadeControl.output = false;
|
||||||
|
|
||||||
|
if (configuredOutputGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
|
digitalWrite(
|
||||||
|
configuredOutputGpio,
|
||||||
|
vars.cascadeControl.output ^ settings.cascadeControl.output.invertState
|
||||||
|
? HIGH
|
||||||
|
: LOW
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.sinfoln(
|
||||||
|
FPSTR(L_CASCADE_OUTPUT),
|
||||||
|
F("Disabled, state changed to %s"),
|
||||||
|
vars.cascadeControl.output ? F("TRUE") : F("FALSE")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void externalPump() {
|
void externalPump() {
|
||||||
static uint8_t configuredGpio = GPIO_IS_NOT_CONFIGURED;
|
static uint8_t configuredGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
|
||||||
@@ -356,75 +517,75 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (configuredGpio == GPIO_IS_NOT_CONFIGURED) {
|
if (configuredGpio == GPIO_IS_NOT_CONFIGURED) {
|
||||||
if (vars.states.externalPump) {
|
if (vars.externalPump.state) {
|
||||||
vars.states.externalPump = false;
|
vars.externalPump.state = false;
|
||||||
vars.parameters.extPumpLastEnableTime = millis();
|
vars.externalPump.lastEnabledTime = millis();
|
||||||
|
|
||||||
Log.sinfoln("EXTPUMP", F("Disabled: use = off"));
|
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: use = off"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vars.states.heating && this->heatingEnabled) {
|
if (!vars.master.heating.enabled && this->heatingEnabled) {
|
||||||
this->heatingEnabled = false;
|
this->heatingEnabled = false;
|
||||||
this->heatingDisabledTime = millis();
|
this->heatingDisabledTime = millis();
|
||||||
|
|
||||||
} else if (vars.states.heating && !this->heatingEnabled) {
|
} else if (vars.master.heating.enabled && !this->heatingEnabled) {
|
||||||
this->heatingEnabled = true;
|
this->heatingEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.externalPump.use) {
|
if (!settings.externalPump.use) {
|
||||||
if (vars.states.externalPump) {
|
if (vars.externalPump.state) {
|
||||||
digitalWrite(configuredGpio, LOW);
|
digitalWrite(configuredGpio, LOW);
|
||||||
|
|
||||||
vars.states.externalPump = false;
|
vars.externalPump.state = false;
|
||||||
vars.parameters.extPumpLastEnableTime = millis();
|
vars.externalPump.lastEnabledTime = millis();
|
||||||
|
|
||||||
Log.sinfoln("EXTPUMP", F("Disabled: use = off"));
|
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: use = off"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vars.states.externalPump && !this->heatingEnabled) {
|
if (vars.externalPump.state && !this->heatingEnabled) {
|
||||||
if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
|
if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
|
||||||
digitalWrite(configuredGpio, LOW);
|
digitalWrite(configuredGpio, LOW);
|
||||||
|
|
||||||
vars.states.externalPump = false;
|
vars.externalPump.state = false;
|
||||||
vars.parameters.extPumpLastEnableTime = millis();
|
vars.externalPump.lastEnabledTime = millis();
|
||||||
|
|
||||||
Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time"));
|
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: expired post circulation time"));
|
||||||
|
|
||||||
} else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
|
} else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
|
||||||
digitalWrite(configuredGpio, LOW);
|
digitalWrite(configuredGpio, LOW);
|
||||||
|
|
||||||
vars.states.externalPump = false;
|
vars.externalPump.state = false;
|
||||||
vars.parameters.extPumpLastEnableTime = millis();
|
vars.externalPump.lastEnabledTime = millis();
|
||||||
|
|
||||||
Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time"));
|
Log.sinfoln(FPSTR(L_EXTPUMP), F("Disabled: expired anti stuck time"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (vars.states.externalPump && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
|
} else if (vars.externalPump.state && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
|
||||||
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
||||||
|
|
||||||
} else if (!vars.states.externalPump && this->heatingEnabled) {
|
} else if (!vars.externalPump.state && this->heatingEnabled) {
|
||||||
vars.states.externalPump = true;
|
vars.externalPump.state = true;
|
||||||
this->externalPumpStartTime = millis();
|
this->externalPumpStartTime = millis();
|
||||||
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
||||||
|
|
||||||
digitalWrite(configuredGpio, HIGH);
|
digitalWrite(configuredGpio, HIGH);
|
||||||
|
|
||||||
Log.sinfoln("EXTPUMP", F("Enabled: heating on"));
|
Log.sinfoln(FPSTR(L_EXTPUMP), F("Enabled: heating on"));
|
||||||
|
|
||||||
} else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) {
|
} else if (!vars.externalPump.state && (vars.externalPump.lastEnabledTime == 0 || millis() - vars.externalPump.lastEnabledTime >= (settings.externalPump.antiStuckInterval * 1000lu))) {
|
||||||
vars.states.externalPump = true;
|
vars.externalPump.state = true;
|
||||||
this->externalPumpStartTime = millis();
|
this->externalPumpStartTime = millis();
|
||||||
this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK;
|
this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK;
|
||||||
|
|
||||||
digitalWrite(configuredGpio, HIGH);
|
digitalWrite(configuredGpio, HIGH);
|
||||||
|
|
||||||
Log.sinfoln("EXTPUMP", F("Enabled: anti stuck"));
|
Log.sinfoln(FPSTR(L_EXTPUMP), F("Enabled: anti stuck"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
395
src/MqttTask.h
395
src/MqttTask.h
@@ -1,3 +1,4 @@
|
|||||||
|
#include <unordered_map>
|
||||||
#include <MqttClient.h>
|
#include <MqttClient.h>
|
||||||
#include <MqttWiFiClient.h>
|
#include <MqttWiFiClient.h>
|
||||||
#include <MqttWriter.h>
|
#include <MqttWriter.h>
|
||||||
@@ -61,10 +62,18 @@ public:
|
|||||||
this->prevPubSettingsTime = 0;
|
this->prevPubSettingsTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void resetPublishedSensorTime(uint8_t sensorId) {
|
||||||
|
this->prevPubSensorTime[sensorId] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline void resetPublishedVarsTime() {
|
inline void resetPublishedVarsTime() {
|
||||||
this->prevPubVarsTime = 0;
|
this->prevPubVarsTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void rebuildHaEntity(uint8_t sensorId, Sensors::Settings& prevSettings) {
|
||||||
|
this->queueRebuildingHaEntities[sensorId] = prevSettings;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MqttWiFiClient* wifiClient = nullptr;
|
MqttWiFiClient* wifiClient = nullptr;
|
||||||
MqttClient* client = nullptr;
|
MqttClient* client = nullptr;
|
||||||
@@ -72,12 +81,14 @@ protected:
|
|||||||
MqttWriter* writer = nullptr;
|
MqttWriter* writer = nullptr;
|
||||||
UnitSystem currentUnitSystem = UnitSystem::METRIC;
|
UnitSystem currentUnitSystem = UnitSystem::METRIC;
|
||||||
bool currentHomeAssistantDiscovery = false;
|
bool currentHomeAssistantDiscovery = false;
|
||||||
unsigned short readyForSendTime = 15000;
|
std::unordered_map<uint8_t, Sensors::Settings> queueRebuildingHaEntities;
|
||||||
|
unsigned short readyForSendTime = 30000;
|
||||||
unsigned long lastReconnectTime = 0;
|
unsigned long lastReconnectTime = 0;
|
||||||
unsigned long connectedTime = 0;
|
unsigned long connectedTime = 0;
|
||||||
unsigned long disconnectedTime = 0;
|
unsigned long disconnectedTime = 0;
|
||||||
unsigned long prevPubVarsTime = 0;
|
unsigned long prevPubVarsTime = 0;
|
||||||
unsigned long prevPubSettingsTime = 0;
|
unsigned long prevPubSettingsTime = 0;
|
||||||
|
std::unordered_map<uint8_t, unsigned long> prevPubSensorTime;
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
bool newConnection = false;
|
bool newConnection = false;
|
||||||
|
|
||||||
@@ -118,7 +129,7 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->client->onMessage([this] (void*, size_t length) {
|
this->client->onMessage([this] (void*, size_t length) {
|
||||||
String topic = this->client->messageTopic();
|
const String& topic = this->client->messageTopic();
|
||||||
if (!length || length > 2048 || !topic.length()) {
|
if (!length || length > 2048 || !topic.length()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -128,7 +139,7 @@ protected:
|
|||||||
payload[i] = this->client->read();
|
payload[i] = this->client->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->onMessage(topic.c_str(), payload, length);
|
this->onMessage(topic, payload, length);
|
||||||
});
|
});
|
||||||
|
|
||||||
// writer settings
|
// writer settings
|
||||||
@@ -142,7 +153,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_MQTT), F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
Log.straceln(FPSTR(L_MQTT), F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//this->client->poll();
|
//this->client->poll();
|
||||||
@@ -151,13 +162,13 @@ protected:
|
|||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
this->writer->setFlushEventCallback([this] (size_t, size_t) {
|
this->writer->setFlushEventCallback([this] (size_t, size_t) {
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
|
|
||||||
if (this->wifiClient->connected()) {
|
if (this->wifiClient->connected()) {
|
||||||
this->wifiClient->flush();
|
this->wifiClient->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -173,11 +184,6 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (settings.mqtt.interval > 120) {
|
|
||||||
settings.mqtt.interval = 5;
|
|
||||||
fsSettings.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->connected && !this->client->connected()) {
|
if (this->connected && !this->client->connected()) {
|
||||||
this->connected = false;
|
this->connected = false;
|
||||||
this->onDisconnect();
|
this->onDisconnect();
|
||||||
@@ -186,9 +192,15 @@ protected:
|
|||||||
Log.sinfoln(FPSTR(L_MQTT), F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port);
|
Log.sinfoln(FPSTR(L_MQTT), F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port);
|
||||||
|
|
||||||
this->haHelper->setDevicePrefix(settings.mqtt.prefix);
|
this->haHelper->setDevicePrefix(settings.mqtt.prefix);
|
||||||
|
this->haHelper->updateCachedTopics();
|
||||||
this->client->stop();
|
this->client->stop();
|
||||||
this->client->setId(networkSettings.hostname);
|
this->client->setId(networkSettings.hostname);
|
||||||
this->client->setUsernamePassword(settings.mqtt.user, settings.mqtt.password);
|
this->client->setUsernamePassword(settings.mqtt.user, settings.mqtt.password);
|
||||||
|
|
||||||
|
this->client->beginWill(this->haHelper->getDeviceTopic(F("status")).c_str(), 7, true, 1);
|
||||||
|
this->client->print(F("offline"));
|
||||||
|
this->client->endWill();
|
||||||
|
|
||||||
this->client->connect(settings.mqtt.server, settings.mqtt.port);
|
this->client->connect(settings.mqtt.server, settings.mqtt.port);
|
||||||
this->lastReconnectTime = millis();
|
this->lastReconnectTime = millis();
|
||||||
this->yield();
|
this->yield();
|
||||||
@@ -210,22 +222,44 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// publish variables and status
|
// publish variables and status
|
||||||
if (this->newConnection || millis() - this->prevPubVarsTime > (settings.mqtt.interval * 1000u)) {
|
if (this->newConnection || millis() - this->prevPubVarsTime > (settings.mqtt.interval * 1000u)) {
|
||||||
this->writer->publish(this->haHelper->getDeviceTopic("status").c_str(), "online", false);
|
this->writer->publish(this->haHelper->getDeviceTopic(F("status")).c_str(), "online", false);
|
||||||
this->publishVariables(this->haHelper->getDeviceTopic("state").c_str());
|
this->publishVariables(this->haHelper->getDeviceTopic(F("state")).c_str());
|
||||||
this->prevPubVarsTime = millis();
|
this->prevPubVarsTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish settings
|
// publish settings
|
||||||
if (this->newConnection || millis() - this->prevPubSettingsTime > (settings.mqtt.interval * 10000u)) {
|
if (this->newConnection || millis() - this->prevPubSettingsTime > (settings.mqtt.interval * 10000u)) {
|
||||||
this->publishSettings(this->haHelper->getDeviceTopic("settings").c_str());
|
this->publishSettings(this->haHelper->getDeviceTopic(F("settings")).c_str());
|
||||||
this->prevPubSettingsTime = millis();
|
this->prevPubSettingsTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// publish sensors
|
||||||
|
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
||||||
|
if (!Sensors::hasEnabledAndValid(sensorId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& rSensor = Sensors::results[sensorId];
|
||||||
|
bool needUpdate = false;
|
||||||
|
if (millis() - this->prevPubSensorTime[sensorId] > ((this->haHelper->getExpireAfter() - 10) * 1000u)) {
|
||||||
|
needUpdate = true;
|
||||||
|
|
||||||
|
} else if (rSensor.activityTime >= this->prevPubSensorTime[sensorId]) {
|
||||||
|
auto estimated = rSensor.activityTime - this->prevPubSensorTime[sensorId];
|
||||||
|
needUpdate = estimated > 1000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->newConnection || needUpdate) {
|
||||||
|
this->publishSensor(sensorId);
|
||||||
|
this->prevPubSensorTime[sensorId] = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// publish ha entities if not published
|
// publish ha entities if not published
|
||||||
if (settings.mqtt.homeAssistantDiscovery) {
|
if (settings.mqtt.homeAssistantDiscovery) {
|
||||||
if (this->newConnection || !this->currentHomeAssistantDiscovery || this->currentUnitSystem != settings.system.unitSystem) {
|
if (this->newConnection || !this->currentHomeAssistantDiscovery || this->currentUnitSystem != settings.system.unitSystem) {
|
||||||
@@ -239,6 +273,79 @@ protected:
|
|||||||
this->publishNonStaticHaEntities();
|
this->publishNonStaticHaEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (auto& [sensorId, prevSettings] : this->queueRebuildingHaEntities) {
|
||||||
|
Log.sinfoln(FPSTR(L_MQTT_HA), F("Rebuilding config for sensor #%hhu '%s'"), sensorId, prevSettings.name);
|
||||||
|
|
||||||
|
// delete old config
|
||||||
|
if (strlen(prevSettings.name) && prevSettings.enabled) {
|
||||||
|
switch (prevSettings.type) {
|
||||||
|
case Sensors::Type::BLUETOOTH:
|
||||||
|
this->haHelper->deleteConnectionDynamicSensor(prevSettings);
|
||||||
|
this->haHelper->deleteSignalQualityDynamicSensor(prevSettings);
|
||||||
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE);
|
||||||
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::HUMIDITY);
|
||||||
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::BATTERY);
|
||||||
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::RSSI);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Type::DALLAS_TEMP:
|
||||||
|
this->haHelper->deleteConnectionDynamicSensor(prevSettings);
|
||||||
|
this->haHelper->deleteSignalQualityDynamicSensor(prevSettings);
|
||||||
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Type::MANUAL:
|
||||||
|
this->client->unsubscribe(
|
||||||
|
this->haHelper->getDeviceTopic(
|
||||||
|
F("sensors"),
|
||||||
|
Sensors::makeObjectId(prevSettings.name).c_str(),
|
||||||
|
F("set")
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::PRIMARY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Sensors::hasEnabledAndValid(sensorId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make new config
|
||||||
|
auto& sSettings = Sensors::settings[sensorId];
|
||||||
|
switch (sSettings.type) {
|
||||||
|
case Sensors::Type::BLUETOOTH:
|
||||||
|
this->haHelper->publishConnectionDynamicSensor(sSettings);
|
||||||
|
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::HUMIDITY, settings.system.unitSystem);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::BATTERY, settings.system.unitSystem);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::RSSI, settings.system.unitSystem, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Type::DALLAS_TEMP:
|
||||||
|
this->haHelper->publishConnectionDynamicSensor(sSettings);
|
||||||
|
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Type::MANUAL:
|
||||||
|
this->client->subscribe(
|
||||||
|
this->haHelper->getDeviceTopic(
|
||||||
|
F("sensors"),
|
||||||
|
Sensors::makeObjectId(prevSettings.name).c_str(),
|
||||||
|
F("set")
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->queueRebuildingHaEntities.clear();
|
||||||
|
|
||||||
} else if (this->currentHomeAssistantDiscovery) {
|
} else if (this->currentHomeAssistantDiscovery) {
|
||||||
this->currentHomeAssistantDiscovery = false;
|
this->currentHomeAssistantDiscovery = false;
|
||||||
}
|
}
|
||||||
@@ -254,24 +361,24 @@ protected:
|
|||||||
unsigned long downtime = (millis() - this->disconnectedTime) / 1000;
|
unsigned long downtime = (millis() - this->disconnectedTime) / 1000;
|
||||||
Log.sinfoln(FPSTR(L_MQTT), F("Connected (downtime: %u s.)"), downtime);
|
Log.sinfoln(FPSTR(L_MQTT), F("Connected (downtime: %u s.)"), downtime);
|
||||||
|
|
||||||
this->client->subscribe(this->haHelper->getDeviceTopic("settings/set").c_str());
|
this->client->subscribe(this->haHelper->getDeviceTopic(F("settings/set")).c_str());
|
||||||
this->client->subscribe(this->haHelper->getDeviceTopic("state/set").c_str());
|
this->client->subscribe(this->haHelper->getDeviceTopic(F("state/set")).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnect() {
|
void onDisconnect() {
|
||||||
this->disconnectedTime = millis();
|
this->disconnectedTime = millis();
|
||||||
|
|
||||||
unsigned long uptime = (millis() - this->connectedTime) / 1000;
|
unsigned long uptime = (millis() - this->connectedTime) / 1000;
|
||||||
Log.swarningln(FPSTR(L_MQTT), F("Disconnected (reason: %d uptime: %u s.)"), this->client->connectError(), uptime);
|
Log.swarningln(FPSTR(L_MQTT), F("Disconnected (reason: %d uptime: %lu s.)"), this->client->connectError(), uptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMessage(const char* topic, uint8_t* payload, size_t length) {
|
void onMessage(const String& topic, uint8_t* payload, size_t length) {
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.system.debug) {
|
if (settings.system.logLevel >= TinyLogger::Level::TRACE) {
|
||||||
Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic);
|
Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic.c_str());
|
||||||
if (Log.lock()) {
|
if (Log.lock()) {
|
||||||
for (size_t i = 0; i < length; i++) {
|
for (size_t i = 0; i < length; i++) {
|
||||||
if (payload[i] == 0) {
|
if (payload[i] == 0) {
|
||||||
@@ -279,12 +386,12 @@ protected:
|
|||||||
} else if (payload[i] == 13) {
|
} else if (payload[i] == 13) {
|
||||||
continue;
|
continue;
|
||||||
} else if (payload[i] == 10) {
|
} else if (payload[i] == 10) {
|
||||||
Log.print("\r\n> ");
|
Log.print(F("\r\n> "));
|
||||||
} else {
|
} else {
|
||||||
Log.print((char) payload[i]);
|
Log.print((char) payload[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.print("\r\n\n");
|
Log.print(F("\r\n\n"));
|
||||||
Log.flush();
|
Log.flush();
|
||||||
Log.unlock();
|
Log.unlock();
|
||||||
}
|
}
|
||||||
@@ -302,138 +409,157 @@ protected:
|
|||||||
}
|
}
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
if (this->haHelper->getDeviceTopic("state/set").equals(topic)) {
|
// delete topic
|
||||||
this->writer->publish(this->haHelper->getDeviceTopic("state/set").c_str(), nullptr, 0, true);
|
this->writer->publish(topic.c_str(), nullptr, 0, true);
|
||||||
|
|
||||||
|
if (this->haHelper->getDeviceTopic(F("state/set")).equals(topic)) {
|
||||||
if (jsonToVars(doc, vars)) {
|
if (jsonToVars(doc, vars)) {
|
||||||
this->resetPublishedVarsTime();
|
this->resetPublishedVarsTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (this->haHelper->getDeviceTopic("settings/set").equals(topic)) {
|
} else if (this->haHelper->getDeviceTopic(F("settings/set")).equals(topic)) {
|
||||||
this->writer->publish(this->haHelper->getDeviceTopic("settings/set").c_str(), nullptr, 0, true);
|
|
||||||
|
|
||||||
if (safeJsonToSettings(doc, settings)) {
|
if (safeJsonToSettings(doc, settings)) {
|
||||||
this->resetPublishedSettingsTime();
|
this->resetPublishedSettingsTime();
|
||||||
fsSettings.update();
|
fsSettings.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const String& sensorsTopic = this->haHelper->getDeviceTopic(F("sensors/"));
|
||||||
|
auto stLength = sensorsTopic.length();
|
||||||
|
|
||||||
|
if (topic.startsWith(sensorsTopic) && topic.endsWith(F("/set"))) {
|
||||||
|
if (topic.length() > stLength + 4) {
|
||||||
|
const String& name = topic.substring(stLength, topic.indexOf('/', stLength));
|
||||||
|
int16_t id = Sensors::getIdByObjectId(name.c_str());
|
||||||
|
|
||||||
|
if (id == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonToSensorResult(id, doc)) {
|
||||||
|
this->resetPublishedSensorTime(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void publishHaEntities() {
|
void publishHaEntities() {
|
||||||
// heating
|
// heating
|
||||||
this->haHelper->publishSwitchHeating(false);
|
this->haHelper->publishSwitchHeatingTurbo(false);
|
||||||
this->haHelper->publishSwitchHeatingTurbo();
|
this->haHelper->publishInputHeatingHysteresis(settings.system.unitSystem);
|
||||||
this->haHelper->publishNumberHeatingHysteresis(settings.system.unitSystem);
|
this->haHelper->publishInputHeatingTurboFactor(false);
|
||||||
this->haHelper->publishSensorHeatingSetpoint(settings.system.unitSystem, false);
|
this->haHelper->publishInputHeatingMinTemp(settings.system.unitSystem);
|
||||||
this->haHelper->publishSensorBoilerHeatingMinTemp(settings.system.unitSystem, false);
|
this->haHelper->publishInputHeatingMaxTemp(settings.system.unitSystem);
|
||||||
this->haHelper->publishSensorBoilerHeatingMaxTemp(settings.system.unitSystem, false);
|
|
||||||
this->haHelper->publishNumberHeatingMinTemp(settings.system.unitSystem, false);
|
|
||||||
this->haHelper->publishNumberHeatingMaxTemp(settings.system.unitSystem, false);
|
|
||||||
this->haHelper->publishNumberHeatingMaxModulation(false);
|
|
||||||
|
|
||||||
// pid
|
// pid
|
||||||
this->haHelper->publishSwitchPid();
|
this->haHelper->publishSwitchPid();
|
||||||
this->haHelper->publishNumberPidFactorP();
|
this->haHelper->publishInputPidFactorP(false);
|
||||||
this->haHelper->publishNumberPidFactorI();
|
this->haHelper->publishInputPidFactorI(false);
|
||||||
this->haHelper->publishNumberPidFactorD();
|
this->haHelper->publishInputPidFactorD(false);
|
||||||
this->haHelper->publishNumberPidDt(false);
|
this->haHelper->publishInputPidDt(false);
|
||||||
this->haHelper->publishNumberPidMinTemp(settings.system.unitSystem, false);
|
this->haHelper->publishInputPidMinTemp(settings.system.unitSystem, false);
|
||||||
this->haHelper->publishNumberPidMaxTemp(settings.system.unitSystem, false);
|
this->haHelper->publishInputPidMaxTemp(settings.system.unitSystem, false);
|
||||||
|
|
||||||
// equitherm
|
// equitherm
|
||||||
this->haHelper->publishSwitchEquitherm();
|
this->haHelper->publishSwitchEquitherm();
|
||||||
this->haHelper->publishNumberEquithermFactorN();
|
this->haHelper->publishInputEquithermFactorN(false);
|
||||||
this->haHelper->publishNumberEquithermFactorK();
|
this->haHelper->publishInputEquithermFactorK(false);
|
||||||
this->haHelper->publishNumberEquithermFactorT();
|
this->haHelper->publishInputEquithermFactorT(false);
|
||||||
|
|
||||||
// states
|
// states
|
||||||
this->haHelper->publishBinSensorStatus();
|
this->haHelper->publishStatusState();
|
||||||
this->haHelper->publishBinSensorOtStatus();
|
this->haHelper->publishEmergencyState();
|
||||||
this->haHelper->publishBinSensorHeating();
|
this->haHelper->publishOpenthermConnectedState();
|
||||||
this->haHelper->publishBinSensorFlame();
|
this->haHelper->publishHeatingState();
|
||||||
this->haHelper->publishBinSensorFault();
|
this->haHelper->publishFlameState();
|
||||||
this->haHelper->publishBinSensorDiagnostic();
|
this->haHelper->publishFaultState();
|
||||||
|
this->haHelper->publishDiagState();
|
||||||
|
this->haHelper->publishExternalPumpState(false);
|
||||||
|
|
||||||
// sensors
|
// sensors
|
||||||
this->haHelper->publishSensorModulation(false);
|
this->haHelper->publishFaultCode();
|
||||||
this->haHelper->publishSensorPressure(settings.system.unitSystem, false);
|
this->haHelper->publishDiagCode();
|
||||||
this->haHelper->publishSensorFaultCode();
|
this->haHelper->publishNetworkRssi(false);
|
||||||
this->haHelper->publishSensorDiagnosticCode();
|
this->haHelper->publishUptime(false);
|
||||||
this->haHelper->publishSensorRssi(false);
|
|
||||||
this->haHelper->publishSensorUptime(false);
|
|
||||||
|
|
||||||
// temperatures
|
|
||||||
this->haHelper->publishNumberIndoorTemp(settings.system.unitSystem);
|
|
||||||
this->haHelper->publishSensorHeatingTemp(settings.system.unitSystem);
|
|
||||||
this->haHelper->publishSensorHeatingReturnTemp(settings.system.unitSystem, false);
|
|
||||||
this->haHelper->publishSensorExhaustTemp(settings.system.unitSystem, false);
|
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
this->haHelper->publishButtonRestart(false);
|
this->haHelper->publishRestartButton(false);
|
||||||
this->haHelper->publishButtonResetFault();
|
this->haHelper->publishResetFaultButton();
|
||||||
this->haHelper->publishButtonResetDiagnostic();
|
this->haHelper->publishResetDiagButton();
|
||||||
|
|
||||||
|
// dynamic sensors
|
||||||
|
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
||||||
|
if (!Sensors::hasEnabledAndValid(sensorId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& sSettings = Sensors::settings[sensorId];
|
||||||
|
switch (sSettings.type) {
|
||||||
|
case Sensors::Type::BLUETOOTH:
|
||||||
|
this->haHelper->publishConnectionDynamicSensor(sSettings);
|
||||||
|
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::HUMIDITY, settings.system.unitSystem);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::BATTERY, settings.system.unitSystem);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::RSSI, settings.system.unitSystem, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Type::DALLAS_TEMP:
|
||||||
|
this->haHelper->publishConnectionDynamicSensor(sSettings);
|
||||||
|
this->haHelper->publishSignalQualityDynamicSensor(sSettings, false);
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Sensors::Type::MANUAL:
|
||||||
|
this->client->subscribe(
|
||||||
|
this->haHelper->getDeviceTopic(
|
||||||
|
F("sensors"),
|
||||||
|
Sensors::makeObjectId(sSettings.name).c_str(),
|
||||||
|
F("set")
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishNonStaticHaEntities(bool force = false) {
|
bool publishNonStaticHaEntities(bool force = false) {
|
||||||
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0;
|
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0;
|
||||||
static bool _noRegulators, _editableOutdoorTemp, _editableIndoorTemp, _dhwPresent = false;
|
static bool _indoorTempControl, _dhwPresent = false;
|
||||||
|
|
||||||
bool published = false;
|
bool published = false;
|
||||||
bool noRegulators = !settings.opentherm.nativeHeatingControl && !settings.pid.enable && !settings.equitherm.enable;
|
|
||||||
byte heatingMinTemp = 0;
|
|
||||||
byte heatingMaxTemp = 0;
|
|
||||||
bool editableOutdoorTemp = settings.sensors.outdoor.type == SensorType::MANUAL;
|
|
||||||
bool editableIndoorTemp = settings.sensors.indoor.type == SensorType::MANUAL;
|
|
||||||
|
|
||||||
if (noRegulators) {
|
|
||||||
heatingMinTemp = settings.heating.minTemp;
|
|
||||||
heatingMaxTemp = settings.heating.maxTemp;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
heatingMinTemp = convertTemp(THERMOSTAT_INDOOR_MIN_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
|
|
||||||
heatingMaxTemp = convertTemp(THERMOSTAT_INDOOR_MAX_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force || _dhwPresent != settings.opentherm.dhwPresent) {
|
if (force || _dhwPresent != settings.opentherm.dhwPresent) {
|
||||||
_dhwPresent = settings.opentherm.dhwPresent;
|
_dhwPresent = settings.opentherm.dhwPresent;
|
||||||
|
|
||||||
if (_dhwPresent) {
|
if (_dhwPresent) {
|
||||||
this->haHelper->publishSwitchDhw(false);
|
this->haHelper->publishInputDhwMinTemp(settings.system.unitSystem);
|
||||||
this->haHelper->publishSensorBoilerDhwMinTemp(settings.system.unitSystem, false);
|
this->haHelper->publishInputDhwMaxTemp(settings.system.unitSystem);
|
||||||
this->haHelper->publishSensorBoilerDhwMaxTemp(settings.system.unitSystem, false);
|
this->haHelper->publishDhwState();
|
||||||
this->haHelper->publishNumberDhwMinTemp(settings.system.unitSystem, false);
|
|
||||||
this->haHelper->publishNumberDhwMaxTemp(settings.system.unitSystem, false);
|
|
||||||
this->haHelper->publishBinSensorDhw();
|
|
||||||
this->haHelper->publishSensorDhwTemp(settings.system.unitSystem);
|
|
||||||
this->haHelper->publishSensorDhwFlowRate(settings.system.unitSystem, false);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->haHelper->deleteSwitchDhw();
|
this->haHelper->deleteSwitchDhw();
|
||||||
this->haHelper->deleteSensorBoilerDhwMinTemp();
|
this->haHelper->deleteInputDhwMinTemp();
|
||||||
this->haHelper->deleteSensorBoilerDhwMaxTemp();
|
this->haHelper->deleteInputDhwMaxTemp();
|
||||||
this->haHelper->deleteNumberDhwMinTemp();
|
this->haHelper->deleteDhwState();
|
||||||
this->haHelper->deleteNumberDhwMaxTemp();
|
this->haHelper->deleteInputDhwTarget();
|
||||||
this->haHelper->deleteBinSensorDhw();
|
|
||||||
this->haHelper->deleteSensorDhwTemp();
|
|
||||||
this->haHelper->deleteNumberDhwTarget();
|
|
||||||
this->haHelper->deleteClimateDhw();
|
this->haHelper->deleteClimateDhw();
|
||||||
this->haHelper->deleteSensorDhwFlowRate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
published = true;
|
published = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force || _noRegulators != noRegulators || _heatingMinTemp != heatingMinTemp || _heatingMaxTemp != heatingMaxTemp) {
|
if (force || _indoorTempControl != vars.master.heating.indoorTempControl || _heatingMinTemp != vars.master.heating.minTemp || _heatingMaxTemp != vars.master.heating.maxTemp) {
|
||||||
_heatingMinTemp = heatingMinTemp;
|
_heatingMinTemp = vars.master.heating.minTemp;
|
||||||
_heatingMaxTemp = heatingMaxTemp;
|
_heatingMaxTemp = vars.master.heating.maxTemp;
|
||||||
_noRegulators = noRegulators;
|
_indoorTempControl = vars.master.heating.indoorTempControl;
|
||||||
|
|
||||||
this->haHelper->publishNumberHeatingTarget(settings.system.unitSystem, heatingMinTemp, heatingMaxTemp, false);
|
|
||||||
this->haHelper->publishClimateHeating(
|
this->haHelper->publishClimateHeating(
|
||||||
settings.system.unitSystem,
|
settings.system.unitSystem,
|
||||||
heatingMinTemp,
|
vars.master.heating.minTemp,
|
||||||
heatingMaxTemp,
|
vars.master.heating.maxTemp
|
||||||
noRegulators ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR
|
|
||||||
);
|
);
|
||||||
|
|
||||||
published = true;
|
published = true;
|
||||||
@@ -443,40 +569,11 @@ protected:
|
|||||||
_dhwMinTemp = settings.dhw.minTemp;
|
_dhwMinTemp = settings.dhw.minTemp;
|
||||||
_dhwMaxTemp = settings.dhw.maxTemp;
|
_dhwMaxTemp = settings.dhw.maxTemp;
|
||||||
|
|
||||||
this->haHelper->publishNumberDhwTarget(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp, false);
|
|
||||||
this->haHelper->publishClimateDhw(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp);
|
this->haHelper->publishClimateDhw(settings.system.unitSystem, settings.dhw.minTemp, settings.dhw.maxTemp);
|
||||||
|
|
||||||
published = true;
|
published = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force || _editableOutdoorTemp != editableOutdoorTemp) {
|
|
||||||
_editableOutdoorTemp = editableOutdoorTemp;
|
|
||||||
|
|
||||||
if (editableOutdoorTemp) {
|
|
||||||
this->haHelper->deleteSensorOutdoorTemp();
|
|
||||||
this->haHelper->publishNumberOutdoorTemp(settings.system.unitSystem);
|
|
||||||
} else {
|
|
||||||
this->haHelper->deleteNumberOutdoorTemp();
|
|
||||||
this->haHelper->publishSensorOutdoorTemp(settings.system.unitSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
published = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force || _editableIndoorTemp != editableIndoorTemp) {
|
|
||||||
_editableIndoorTemp = editableIndoorTemp;
|
|
||||||
|
|
||||||
if (editableIndoorTemp) {
|
|
||||||
this->haHelper->deleteSensorIndoorTemp();
|
|
||||||
this->haHelper->publishNumberIndoorTemp(settings.system.unitSystem);
|
|
||||||
} else {
|
|
||||||
this->haHelper->deleteNumberIndoorTemp();
|
|
||||||
this->haHelper->publishSensorIndoorTemp(settings.system.unitSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
published = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return published;
|
return published;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,6 +585,30 @@ protected:
|
|||||||
return this->writer->publish(topic, doc, true);
|
return this->writer->publish(topic, doc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool publishSensor(uint8_t sensorId) {
|
||||||
|
auto& sSettings = Sensors::settings[sensorId];
|
||||||
|
|
||||||
|
if (!Sensors::isValidSensorId(sensorId)) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if (!strlen(sSettings.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
sensorResultToJson(sensorId, doc);
|
||||||
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
return this->writer->publish(
|
||||||
|
this->haHelper->getDeviceTopic(
|
||||||
|
F("sensors"),
|
||||||
|
Sensors::makeObjectId(sSettings.name).c_str()
|
||||||
|
).c_str(),
|
||||||
|
doc,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bool publishVariables(const char* topic) {
|
bool publishVariables(const char* topic) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
varsToJson(vars, doc);
|
varsToJson(vars, doc);
|
||||||
|
|||||||
1476
src/OpenThermTask.h
1476
src/OpenThermTask.h
File diff suppressed because it is too large
Load Diff
550
src/PortalTask.h
550
src/PortalTask.h
@@ -1,5 +1,5 @@
|
|||||||
#define PORTAL_CACHE_TIME "max-age=86400"
|
#define PORTAL_CACHE_TIME "max-age=86400"
|
||||||
#define PORTAL_CACHE settings.system.debug ? nullptr : PORTAL_CACHE_TIME
|
#define PORTAL_CACHE (settings.system.logLevel >= TinyLogger::Level::TRACE ? nullptr : PORTAL_CACHE_TIME)
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <Updater.h>
|
#include <Updater.h>
|
||||||
@@ -17,7 +17,7 @@ using WebServer = ESP8266WebServer;
|
|||||||
using namespace NetworkUtils;
|
using namespace NetworkUtils;
|
||||||
|
|
||||||
extern NetworkMgr* network;
|
extern NetworkMgr* network;
|
||||||
extern FileData fsSettings, fsNetworkSettings;
|
extern FileData fsNetworkSettings, fsSettings, fsSensorsSettings;
|
||||||
extern MqttTask* tMqtt;
|
extern MqttTask* tMqtt;
|
||||||
|
|
||||||
|
|
||||||
@@ -88,10 +88,10 @@ protected:
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
this->webServer->addHandler(indexPage);*/
|
this->webServer->addHandler(indexPage);*/
|
||||||
this->webServer->addHandler(new StaticPage("/", &LittleFS, "/pages/index.html", PORTAL_CACHE));
|
this->webServer->addHandler(new StaticPage("/", &LittleFS, F("/pages/index.html"), PORTAL_CACHE));
|
||||||
|
|
||||||
// dashboard page
|
// dashboard page
|
||||||
auto dashboardPage = (new StaticPage("/dashboard.html", &LittleFS, "/pages/dashboard.html", PORTAL_CACHE))
|
auto dashboardPage = (new StaticPage("/dashboard.html", &LittleFS, F("/pages/dashboard.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||||
@@ -103,7 +103,7 @@ protected:
|
|||||||
this->webServer->addHandler(dashboardPage);
|
this->webServer->addHandler(dashboardPage);
|
||||||
|
|
||||||
// restart
|
// restart
|
||||||
this->webServer->on("/restart.html", HTTP_GET, [this]() {
|
this->webServer->on(F("/restart.html"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->send(401);
|
this->webServer->send(401);
|
||||||
@@ -112,12 +112,12 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
vars.actions.restart = true;
|
vars.actions.restart = true;
|
||||||
this->webServer->sendHeader("Location", "/");
|
this->webServer->sendHeader(F("Location"), "/");
|
||||||
this->webServer->send(302);
|
this->webServer->send(302);
|
||||||
});
|
});
|
||||||
|
|
||||||
// network settings page
|
// network settings page
|
||||||
auto networkPage = (new StaticPage("/network.html", &LittleFS, "/pages/network.html", PORTAL_CACHE))
|
auto networkPage = (new StaticPage("/network.html", &LittleFS, F("/pages/network.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||||
@@ -129,7 +129,7 @@ protected:
|
|||||||
this->webServer->addHandler(networkPage);
|
this->webServer->addHandler(networkPage);
|
||||||
|
|
||||||
// settings page
|
// settings page
|
||||||
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, "/pages/settings.html", PORTAL_CACHE))
|
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, F("/pages/settings.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||||
@@ -140,8 +140,20 @@ protected:
|
|||||||
});
|
});
|
||||||
this->webServer->addHandler(settingsPage);
|
this->webServer->addHandler(settingsPage);
|
||||||
|
|
||||||
|
// sensors page
|
||||||
|
auto sensorsPage = (new StaticPage("/sensors.html", &LittleFS, F("/pages/sensors.html"), PORTAL_CACHE))
|
||||||
|
->setBeforeSendCallback([this]() {
|
||||||
|
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
|
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
this->webServer->addHandler(sensorsPage);
|
||||||
|
|
||||||
// upgrade page
|
// upgrade page
|
||||||
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, "/pages/upgrade.html", PORTAL_CACHE))
|
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, F("/pages/upgrade.html"), PORTAL_CACHE))
|
||||||
->setBeforeSendCallback([this]() {
|
->setBeforeSendCallback([this]() {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->requestAuthentication(DIGEST_AUTH);
|
this->webServer->requestAuthentication(DIGEST_AUTH);
|
||||||
@@ -155,7 +167,7 @@ protected:
|
|||||||
// OTA
|
// OTA
|
||||||
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadCallback([this](const String& uri) {
|
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadCallback([this](const String& uri) {
|
||||||
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->sendHeader("Connection", "close");
|
this->webServer->sendHeader(F("Connection"), F("close"));
|
||||||
this->webServer->send(401);
|
this->webServer->send(401);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -172,67 +184,68 @@ protected:
|
|||||||
status = 400;
|
status = 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
String response = "{\"firmware\": {\"status\": ";
|
String response = F("{\"firmware\": {\"status\": ");
|
||||||
response.concat((short int) fwResult.status);
|
response.concat((short int) fwResult.status);
|
||||||
response.concat(", \"error\": \"");
|
response.concat(F(", \"error\": \""));
|
||||||
response.concat(fwResult.error);
|
response.concat(fwResult.error);
|
||||||
response.concat("\"}, \"filesystem\": {\"status\": ");
|
response.concat(F("\"}, \"filesystem\": {\"status\": "));
|
||||||
response.concat((short int) fsResult.status);
|
response.concat((short int) fsResult.status);
|
||||||
response.concat(", \"error\": \"");
|
response.concat(F(", \"error\": \""));
|
||||||
response.concat(fsResult.error);
|
response.concat(fsResult.error);
|
||||||
response.concat("\"}}");
|
response.concat(F("\"}}"));
|
||||||
this->webServer->send(status, "application/json", response);
|
this->webServer->send(status, F("application/json"), response);
|
||||||
});
|
});
|
||||||
this->webServer->addHandler(upgradeHandler);
|
this->webServer->addHandler(upgradeHandler);
|
||||||
|
|
||||||
|
|
||||||
// backup
|
// backup
|
||||||
this->webServer->on("/api/backup/save", HTTP_GET, [this]() {
|
this->webServer->on(F("/api/backup/save"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument networkSettingsDoc;
|
|
||||||
networkSettingsToJson(networkSettings, networkSettingsDoc);
|
|
||||||
networkSettingsDoc.shrinkToFit();
|
|
||||||
|
|
||||||
JsonDocument settingsDoc;
|
|
||||||
settingsToJson(settings, settingsDoc);
|
|
||||||
settingsDoc.shrinkToFit();
|
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc["network"] = networkSettingsDoc;
|
|
||||||
doc["settings"] = settingsDoc;
|
auto networkDoc = doc[FPSTR(S_NETWORK)].to<JsonObject>();
|
||||||
|
networkSettingsToJson(networkSettings, networkDoc);
|
||||||
|
|
||||||
|
auto settingsDoc = doc[FPSTR(S_SETTINGS)].to<JsonObject>();
|
||||||
|
settingsToJson(settings, settingsDoc);
|
||||||
|
|
||||||
|
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
||||||
|
auto sensorsettingsDoc = doc[FPSTR(S_SENSORS)][sensorId].to<JsonObject>();
|
||||||
|
sensorSettingsToJson(sensorId, Sensors::settings[sensorId], sensorsettingsDoc);
|
||||||
|
}
|
||||||
|
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->webServer->sendHeader(F("Content-Disposition"), F("attachment; filename=\"backup.json\""));
|
this->webServer->sendHeader(F("Content-Disposition"), F("attachment; filename=\"backup.json\""));
|
||||||
this->bufferedWebServer->send(200, "application/json", doc);
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on("/api/backup/restore", HTTP_POST, [this]() {
|
this->webServer->on(F("/api/backup/restore"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/backup/restore %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/backup/restore %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
this->webServer->send(406);
|
this->webServer->send(406);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if (plain.length() > 2048) {
|
} else if (plain.length() > 2536) {
|
||||||
this->webServer->send(413);
|
this->webServer->send(413);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -240,13 +253,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (doc["settings"] && jsonToSettings(doc["settings"], settings)) {
|
if (!doc[FPSTR(S_NETWORK)].isNull() && jsonToNetworkSettings(doc[FPSTR(S_NETWORK)], networkSettings)) {
|
||||||
vars.actions.restart = true;
|
|
||||||
fsSettings.update();
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doc["network"] && jsonToNetworkSettings(doc["network"], networkSettings)) {
|
|
||||||
fsNetworkSettings.update();
|
fsNetworkSettings.update();
|
||||||
network->setHostname(networkSettings.hostname)
|
network->setHostname(networkSettings.hostname)
|
||||||
->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel)
|
->setStaCredentials(networkSettings.sta.ssid, networkSettings.sta.password, networkSettings.sta.channel)
|
||||||
@@ -257,19 +264,45 @@ protected:
|
|||||||
networkSettings.staticConfig.gateway,
|
networkSettings.staticConfig.gateway,
|
||||||
networkSettings.staticConfig.subnet,
|
networkSettings.staticConfig.subnet,
|
||||||
networkSettings.staticConfig.dns
|
networkSettings.staticConfig.dns
|
||||||
)
|
);
|
||||||
->reconnect();
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!doc[FPSTR(S_SETTINGS)].isNull() && jsonToSettings(doc[FPSTR(S_SETTINGS)], settings)) {
|
||||||
|
fsSettings.update();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc[FPSTR(S_SENSORS)].isNull()) {
|
||||||
|
for (auto sensor : doc[FPSTR(S_SENSORS)].as<JsonObject>()) {
|
||||||
|
if (!isDigit(sensor.key().c_str())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sensorId = atoi(sensor.key().c_str());
|
||||||
|
if (sensorId < 0 || sensorId > 255 || !Sensors::isValidSensorId(sensorId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonToSensorSettings(sensorId, sensor.value(), Sensors::settings[sensorId])) {
|
||||||
|
fsSensorsSettings.update();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
doc.clear();
|
doc.clear();
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
vars.actions.restart = true;
|
||||||
|
}
|
||||||
|
|
||||||
this->webServer->send(changed ? 201 : 200);
|
this->webServer->send(changed ? 201 : 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
// network
|
// network
|
||||||
this->webServer->on("/api/network/settings", HTTP_GET, [this]() {
|
this->webServer->on(F("/api/network/settings"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
@@ -280,17 +313,17 @@ protected:
|
|||||||
networkSettingsToJson(networkSettings, doc);
|
networkSettingsToJson(networkSettings, doc);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(200, "application/json", doc);
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on("/api/network/settings", HTTP_POST, [this]() {
|
this->webServer->on(F("/api/network/settings"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/network/settings %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/network/settings %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
@@ -304,7 +337,6 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -318,7 +350,7 @@ protected:
|
|||||||
networkSettingsToJson(networkSettings, doc);
|
networkSettingsToJson(networkSettings, doc);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
|
this->bufferedWebServer->send(changed ? 201 : 200, F("application/json"), doc);
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
doc.clear();
|
doc.clear();
|
||||||
@@ -339,7 +371,7 @@ protected:
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on("/api/network/scan", HTTP_GET, [this]() {
|
this->webServer->on(F("/api/network/scan"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
this->webServer->send(401);
|
this->webServer->send(401);
|
||||||
@@ -363,29 +395,29 @@ protected:
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
for (short int i = 0; i < apCount; i++) {
|
for (short int i = 0; i < apCount; i++) {
|
||||||
String ssid = WiFi.SSID(i);
|
const String& ssid = WiFi.SSID(i);
|
||||||
doc[i]["ssid"] = ssid;
|
doc[i][FPSTR(S_SSID)] = ssid;
|
||||||
doc[i]["bssid"] = WiFi.BSSIDstr(i);
|
doc[i][FPSTR(S_BSSID)] = WiFi.BSSIDstr(i);
|
||||||
doc[i]["signalQuality"] = NetworkMgr::rssiToSignalQuality(WiFi.RSSI(i));
|
doc[i][FPSTR(S_SIGNAL_QUALITY)] = NetworkMgr::rssiToSignalQuality(WiFi.RSSI(i));
|
||||||
doc[i]["channel"] = WiFi.channel(i);
|
doc[i][FPSTR(S_CHANNEL)] = WiFi.channel(i);
|
||||||
doc[i]["hidden"] = !ssid.length();
|
doc[i][FPSTR(S_HIDDEN)] = !ssid.length();
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
const bss_info* info = WiFi.getScanInfoByIndex(i);
|
const bss_info* info = WiFi.getScanInfoByIndex(i);
|
||||||
doc[i]["auth"] = info->authmode;
|
doc[i][FPSTR(S_AUTH)] = info->authmode;
|
||||||
#else
|
#else
|
||||||
doc[i]["auth"] = WiFi.encryptionType(i);
|
doc[i][FPSTR(S_AUTH)] = WiFi.encryptionType(i);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(200, "application/json", doc);
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
|
|
||||||
WiFi.scanDelete();
|
WiFi.scanDelete();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
this->webServer->on("/api/settings", HTTP_GET, [this]() {
|
this->webServer->on(F("/api/settings"), HTTP_GET, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
@@ -396,31 +428,30 @@ protected:
|
|||||||
settingsToJson(settings, doc);
|
settingsToJson(settings, doc);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(200, "application/json", doc);
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on("/api/settings", HTTP_POST, [this]() {
|
this->webServer->on(F("/api/settings"), HTTP_POST, [this]() {
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/settings %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/settings %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
this->webServer->send(406);
|
this->webServer->send(406);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if (plain.length() > 2048) {
|
} else if (plain.length() > 2536) {
|
||||||
this->webServer->send(413);
|
this->webServer->send(413);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -434,7 +465,7 @@ protected:
|
|||||||
settingsToJson(settings, doc);
|
settingsToJson(settings, doc);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
|
this->bufferedWebServer->send(changed ? 201 : 200, F("application/json"), doc);
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
doc.clear();
|
doc.clear();
|
||||||
@@ -446,37 +477,165 @@ protected:
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// vars
|
// sensors list
|
||||||
this->webServer->on("/api/vars", HTTP_GET, [this]() {
|
this->webServer->on(F("/api/sensors"), HTTP_GET, [this]() {
|
||||||
JsonDocument doc;
|
|
||||||
varsToJson(vars, doc);
|
|
||||||
doc.shrinkToFit();
|
|
||||||
|
|
||||||
this->bufferedWebServer->send(200, "application/json", doc);
|
|
||||||
});
|
|
||||||
|
|
||||||
this->webServer->on("/api/vars", HTTP_POST, [this]() {
|
|
||||||
if (this->isAuthRequired()) {
|
if (this->isAuthRequired()) {
|
||||||
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
return this->webServer->send(401);
|
return this->webServer->send(401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String plain = this->webServer->arg(0);
|
bool detailed = false;
|
||||||
|
if (this->webServer->hasArg(F("detailed"))) {
|
||||||
|
detailed = this->webServer->arg(F("detailed")).toInt() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
|
||||||
|
if (detailed) {
|
||||||
|
auto& sSensor = Sensors::settings[sensorId];
|
||||||
|
doc[sensorId][FPSTR(S_NAME)] = sSensor.name;
|
||||||
|
doc[sensorId][FPSTR(S_PURPOSE)] = static_cast<uint8_t>(sSensor.purpose);
|
||||||
|
sensorResultToJson(sensorId, doc[sensorId]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
doc[sensorId] = Sensors::settings[sensorId].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.shrinkToFit();
|
||||||
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
// sensor settings
|
||||||
|
this->webServer->on(F("/api/sensor"), HTTP_GET, [this]() {
|
||||||
|
if (this->isAuthRequired()) {
|
||||||
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
|
return this->webServer->send(401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->webServer->hasArg(F("id"))) {
|
||||||
|
return this->webServer->send(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto id = this->webServer->arg(F("id"));
|
||||||
|
if (!isDigit(id.c_str())) {
|
||||||
|
return this->webServer->send(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sensorId = id.toInt();
|
||||||
|
id.clear();
|
||||||
|
if (!Sensors::isValidSensorId(sensorId)) {
|
||||||
|
return this->webServer->send(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
sensorSettingsToJson(sensorId, Sensors::settings[sensorId], doc);
|
||||||
|
doc.shrinkToFit();
|
||||||
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->webServer->on(F("/api/sensor"), HTTP_POST, [this]() {
|
||||||
|
if (this->isAuthRequired()) {
|
||||||
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
|
return this->webServer->send(401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
if (!this->webServer->hasArg(F("id")) || this->webServer->args() != 1) {
|
||||||
|
return this->webServer->send(400);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!this->webServer->hasArg(F("id")) || this->webServer->args() != 2) {
|
||||||
|
return this->webServer->send(400);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto id = this->webServer->arg(F("id"));
|
||||||
|
if (!isDigit(id.c_str())) {
|
||||||
|
return this->webServer->send(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sensorId = id.toInt();
|
||||||
|
id.clear();
|
||||||
|
if (!Sensors::isValidSensorId(sensorId)) {
|
||||||
|
return this->webServer->send(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto plain = this->webServer->arg(1);
|
||||||
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/sensor/?id=%hhu %d bytes: %s"), sensorId, plain.length(), plain.c_str());
|
||||||
|
|
||||||
|
if (plain.length() < 5) {
|
||||||
|
return this->webServer->send(406);
|
||||||
|
|
||||||
|
} else if (plain.length() > 1024) {
|
||||||
|
return this->webServer->send(413);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
auto prevSettings = Sensors::settings[sensorId];
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
|
plain.clear();
|
||||||
|
|
||||||
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
|
return this->webServer->send(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonToSensorSettings(sensorId, doc, Sensors::settings[sensorId])) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
auto& sSettings = Sensors::settings[sensorId];
|
||||||
|
sensorSettingsToJson(sensorId, sSettings, doc);
|
||||||
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
this->bufferedWebServer->send(changed ? 201 : 200, F("application/json"), doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
tMqtt->rebuildHaEntity(sensorId, prevSettings);
|
||||||
|
fsSensorsSettings.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// vars
|
||||||
|
this->webServer->on(F("/api/vars"), HTTP_GET, [this]() {
|
||||||
|
JsonDocument doc;
|
||||||
|
varsToJson(vars, doc);
|
||||||
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->webServer->on(F("/api/vars"), HTTP_POST, [this]() {
|
||||||
|
if (this->isAuthRequired()) {
|
||||||
|
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
|
||||||
|
return this->webServer->send(401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& plain = this->webServer->arg(0);
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/vars %d bytes: %s"), plain.length(), plain.c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/vars %d bytes: %s"), plain.length(), plain.c_str());
|
||||||
|
|
||||||
if (plain.length() < 5) {
|
if (plain.length() < 5) {
|
||||||
this->webServer->send(406);
|
this->webServer->send(406);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if (plain.length() > 1024) {
|
} else if (plain.length() > 1536) {
|
||||||
this->webServer->send(413);
|
this->webServer->send(413);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, plain);
|
DeserializationError dErr = deserializeJson(doc, plain);
|
||||||
plain.clear();
|
|
||||||
|
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) {
|
||||||
this->webServer->send(400);
|
this->webServer->send(400);
|
||||||
@@ -490,7 +649,7 @@ protected:
|
|||||||
varsToJson(vars, doc);
|
varsToJson(vars, doc);
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(changed ? 201 : 200, "application/json", doc);
|
this->bufferedWebServer->send(changed ? 201 : 200, F("application/json"), doc);
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
doc.clear();
|
doc.clear();
|
||||||
@@ -500,62 +659,172 @@ protected:
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this->webServer->on("/api/info", HTTP_GET, [this]() {
|
this->webServer->on(F("/api/info"), HTTP_GET, [this]() {
|
||||||
bool isConnected = network->isConnected();
|
bool isConnected = network->isConnected();
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc["network"]["hostname"] = networkSettings.hostname;
|
|
||||||
doc["network"]["mac"] = network->getStaMac();
|
|
||||||
doc["network"]["connected"] = isConnected;
|
|
||||||
doc["network"]["ssid"] = network->getStaSsid();
|
|
||||||
doc["network"]["signalQuality"] = isConnected ? NetworkMgr::rssiToSignalQuality(network->getRssi()) : 0;
|
|
||||||
doc["network"]["channel"] = isConnected ? network->getStaChannel() : 0;
|
|
||||||
doc["network"]["ip"] = isConnected ? network->getStaIp().toString() : "";
|
|
||||||
doc["network"]["subnet"] = isConnected ? network->getStaSubnet().toString() : "";
|
|
||||||
doc["network"]["gateway"] = isConnected ? network->getStaGateway().toString() : "";
|
|
||||||
doc["network"]["dns"] = isConnected ? network->getStaDns().toString() : "";
|
|
||||||
|
|
||||||
doc["system"]["buildVersion"] = BUILD_VERSION;
|
auto docSystem = doc[FPSTR(S_SYSTEM)].to<JsonObject>();
|
||||||
doc["system"]["buildDate"] = __DATE__ " " __TIME__;
|
docSystem[FPSTR(S_RESET_REASON)] = getResetReason();
|
||||||
doc["system"]["buildEnv"] = BUILD_ENV;
|
docSystem[FPSTR(S_UPTIME)] = millis() / 1000;
|
||||||
doc["system"]["uptime"] = millis() / 1000ul;
|
|
||||||
doc["system"]["totalHeap"] = getTotalHeap();
|
auto docNetwork = doc[FPSTR(S_NETWORK)].to<JsonObject>();
|
||||||
doc["system"]["freeHeap"] = getFreeHeap();
|
docNetwork[FPSTR(S_HOSTNAME)] = networkSettings.hostname;
|
||||||
doc["system"]["minFreeHeap"] = getFreeHeap(true);
|
docNetwork[FPSTR(S_MAC)] = network->getStaMac();
|
||||||
doc["system"]["maxFreeBlockHeap"] = getMaxFreeBlockHeap();
|
docNetwork[FPSTR(S_CONNECTED)] = isConnected;
|
||||||
doc["system"]["minMaxFreeBlockHeap"] = getMaxFreeBlockHeap(true);
|
docNetwork[FPSTR(S_SSID)] = network->getStaSsid();
|
||||||
doc["system"]["resetReason"] = getResetReason();
|
docNetwork[FPSTR(S_SIGNAL_QUALITY)] = isConnected ? NetworkMgr::rssiToSignalQuality(network->getRssi()) : 0;
|
||||||
|
docNetwork[FPSTR(S_CHANNEL)] = isConnected ? network->getStaChannel() : 0;
|
||||||
|
docNetwork[FPSTR(S_IP)] = isConnected ? network->getStaIp().toString() : "";
|
||||||
|
docNetwork[FPSTR(S_SUBNET)] = isConnected ? network->getStaSubnet().toString() : "";
|
||||||
|
docNetwork[FPSTR(S_GATEWAY)] = isConnected ? network->getStaGateway().toString() : "";
|
||||||
|
docNetwork[FPSTR(S_DNS)] = isConnected ? network->getStaDns().toString() : "";
|
||||||
|
|
||||||
|
auto docBuild = doc[FPSTR(S_BUILD)].to<JsonObject>();
|
||||||
|
docBuild[FPSTR(S_VERSION)] = BUILD_VERSION;
|
||||||
|
docBuild[FPSTR(S_DATE)] = __DATE__ " " __TIME__;
|
||||||
|
docBuild[FPSTR(S_ENV)] = BUILD_ENV;
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
doc["system"]["chipModel"] = esp_is_8285() ? "ESP8285" : "ESP8266";
|
docBuild[FPSTR(S_CORE)] = ESP.getCoreVersion();
|
||||||
doc["system"]["chipRevision"] = 0;
|
docBuild[FPSTR(S_SDK)] = ESP.getSdkVersion();
|
||||||
doc["system"]["chipCores"] = 1;
|
|
||||||
doc["system"]["cpuFreq"] = ESP.getCpuFreqMHz();
|
|
||||||
doc["system"]["coreVersion"] = ESP.getCoreVersion();
|
|
||||||
doc["system"]["flashSize"] = ESP.getFlashChipSize();
|
|
||||||
doc["system"]["flashRealSize"] = ESP.getFlashChipRealSize();
|
|
||||||
#elif ARDUINO_ARCH_ESP32
|
#elif ARDUINO_ARCH_ESP32
|
||||||
doc["system"]["chipModel"] = ESP.getChipModel();
|
docBuild[FPSTR(S_CORE)] = ESP.getCoreVersion();
|
||||||
doc["system"]["chipRevision"] = ESP.getChipRevision();
|
docBuild[FPSTR(S_SDK)] = ESP.getSdkVersion();
|
||||||
doc["system"]["chipCores"] = ESP.getChipCores();
|
|
||||||
doc["system"]["cpuFreq"] = ESP.getCpuFreqMHz();
|
|
||||||
doc["system"]["coreVersion"] = ESP.getSdkVersion();
|
|
||||||
doc["system"]["flashSize"] = ESP.getFlashChipSize();
|
|
||||||
doc["system"]["flashRealSize"] = doc["system"]["flashSize"];
|
|
||||||
#else
|
#else
|
||||||
doc["system"]["chipModel"] = 0;
|
docBuild[FPSTR(S_CORE)] = 0;
|
||||||
doc["system"]["chipRevision"] = 0;
|
docBuild[FPSTR(S_SDK)] = 0;
|
||||||
doc["system"]["chipCores"] = 0;
|
|
||||||
doc["system"]["cpuFreq"] = 0;
|
|
||||||
doc["system"]["coreVersion"] = 0;
|
|
||||||
doc["system"]["flashSize"] = 0;
|
|
||||||
doc["system"]["flashRealSize"] = 0;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
auto docHeap = doc[FPSTR(S_HEAP)].to<JsonObject>();
|
||||||
|
docHeap[FPSTR(S_TOTAL)] = getTotalHeap();
|
||||||
|
docHeap[FPSTR(S_FREE)] = getFreeHeap();
|
||||||
|
docHeap[FPSTR(S_MIN_FREE)] = getFreeHeap(true);
|
||||||
|
docHeap[FPSTR(S_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap();
|
||||||
|
docHeap[FPSTR(S_MIN_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap(true);
|
||||||
|
|
||||||
|
auto docChip = doc[FPSTR(S_CHIP)].to<JsonObject>();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
docChip[FPSTR(S_MODEL)] = esp_is_8285() ? F("ESP8285") : F("ESP8266");
|
||||||
|
docChip[FPSTR(S_REV)] = 0;
|
||||||
|
docChip[FPSTR(S_CORES)] = 1;
|
||||||
|
docChip[FPSTR(S_FREQ)] = ESP.getCpuFreqMHz();
|
||||||
|
#elif ARDUINO_ARCH_ESP32
|
||||||
|
docChip[FPSTR(S_MODEL)] = ESP.getChipModel();
|
||||||
|
docChip[FPSTR(S_REV)] = ESP.getChipRevision();
|
||||||
|
docChip[FPSTR(S_CORES)] = ESP.getChipCores();
|
||||||
|
docChip[FPSTR(S_FREQ)] = ESP.getCpuFreqMHz();
|
||||||
|
#else
|
||||||
|
docChip[FPSTR(S_MODEL)] = 0;
|
||||||
|
docChip[FPSTR(S_REV)] = 0;
|
||||||
|
docChip[FPSTR(S_CORES)] = 0;
|
||||||
|
docChip[FPSTR(S_FREQ)] = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto docFlash = doc[FPSTR(S_FLASH)].to<JsonObject>();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
docFlash[FPSTR(S_SIZE)] = ESP.getFlashChipSize();
|
||||||
|
docFlash[FPSTR(S_REAL_SIZE)] = ESP.getFlashChipRealSize();
|
||||||
|
#elif ARDUINO_ARCH_ESP32
|
||||||
|
docFlash[FPSTR(S_SIZE)] = ESP.getFlashChipSize();
|
||||||
|
docFlash[FPSTR(S_REAL_SIZE)] = docFlash[FPSTR(S_SIZE)];
|
||||||
|
#else
|
||||||
|
docFlash[FPSTR(S_SIZE)] = 0;
|
||||||
|
docFlash[FPSTR(S_REAL_SIZE)] = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
this->bufferedWebServer->send(200, "application/json", doc);
|
this->bufferedWebServer->send(200, F("application/json"), doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->webServer->on(F("/api/debug"), HTTP_GET, [this]() {
|
||||||
|
JsonDocument doc;
|
||||||
|
|
||||||
|
auto docBuild = doc[FPSTR(S_BUILD)].to<JsonObject>();
|
||||||
|
docBuild[FPSTR(S_VERSION)] = BUILD_VERSION;
|
||||||
|
docBuild[FPSTR(S_DATE)] = __DATE__ " " __TIME__;
|
||||||
|
docBuild[FPSTR(S_ENV)] = BUILD_ENV;
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
docBuild[FPSTR(S_CORE)] = ESP.getCoreVersion();
|
||||||
|
docBuild[FPSTR(S_SDK)] = ESP.getSdkVersion();
|
||||||
|
#elif ARDUINO_ARCH_ESP32
|
||||||
|
docBuild[FPSTR(S_CORE)] = ESP.getCoreVersion();
|
||||||
|
docBuild[FPSTR(S_SDK)] = ESP.getSdkVersion();
|
||||||
|
#else
|
||||||
|
docBuild[FPSTR(S_CORE)] = 0;
|
||||||
|
docBuild[FPSTR(S_SDK)] = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto docHeap = doc[FPSTR(S_HEAP)].to<JsonObject>();
|
||||||
|
docHeap[FPSTR(S_TOTAL)] = getTotalHeap();
|
||||||
|
docHeap[FPSTR(S_FREE)] = getFreeHeap();
|
||||||
|
docHeap[FPSTR(S_MIN_FREE)] = getFreeHeap(true);
|
||||||
|
docHeap[FPSTR(S_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap();
|
||||||
|
docHeap[FPSTR(S_MIN_MAX_FREE_BLOCK)] = getMaxFreeBlockHeap(true);
|
||||||
|
|
||||||
|
auto docChip = doc[FPSTR(S_CHIP)].to<JsonObject>();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
docChip[FPSTR(S_MODEL)] = esp_is_8285() ? F("ESP8285") : F("ESP8266");
|
||||||
|
docChip[FPSTR(S_REV)] = 0;
|
||||||
|
docChip[FPSTR(S_CORES)] = 1;
|
||||||
|
docChip[FPSTR(S_FREQ)] = ESP.getCpuFreqMHz();
|
||||||
|
#elif ARDUINO_ARCH_ESP32
|
||||||
|
docChip[FPSTR(S_MODEL)] = ESP.getChipModel();
|
||||||
|
docChip[FPSTR(S_REV)] = ESP.getChipRevision();
|
||||||
|
docChip[FPSTR(S_CORES)] = ESP.getChipCores();
|
||||||
|
docChip[FPSTR(S_FREQ)] = ESP.getCpuFreqMHz();
|
||||||
|
#else
|
||||||
|
docChip[FPSTR(S_MODEL)] = 0;
|
||||||
|
docChip[FPSTR(S_REV)] = 0;
|
||||||
|
docChip[FPSTR(S_CORES)] = 0;
|
||||||
|
docChip[FPSTR(S_FREQ)] = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto docFlash = doc[FPSTR(S_FLASH)].to<JsonObject>();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
docFlash[FPSTR(S_SIZE)] = ESP.getFlashChipSize();
|
||||||
|
docFlash[FPSTR(S_REAL_SIZE)] = ESP.getFlashChipRealSize();
|
||||||
|
#elif ARDUINO_ARCH_ESP32
|
||||||
|
docFlash[FPSTR(S_SIZE)] = ESP.getFlashChipSize();
|
||||||
|
docFlash[FPSTR(S_REAL_SIZE)] = docFlash[FPSTR(S_SIZE)];
|
||||||
|
#else
|
||||||
|
docFlash[FPSTR(S_SIZE)] = 0;
|
||||||
|
docFlash[FPSTR(S_REAL_SIZE)] = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
auto reason = esp_reset_reason();
|
||||||
|
if (reason != ESP_RST_UNKNOWN && reason != ESP_RST_POWERON && reason != ESP_RST_SW) {
|
||||||
|
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||||
|
auto reason = ESP.getResetInfoPtr()->reason;
|
||||||
|
if (reason != REASON_DEFAULT_RST && reason != REASON_SOFT_RESTART && reason != REASON_EXT_SYS_RST) {
|
||||||
|
#else
|
||||||
|
if (false) {
|
||||||
|
#endif
|
||||||
|
auto docCrash = doc[FPSTR(S_CRASH)].to<JsonObject>();
|
||||||
|
docCrash[FPSTR(S_REASON)] = getResetReason();
|
||||||
|
docCrash[FPSTR(S_CORE)] = CrashRecorder::ext.core;
|
||||||
|
docCrash[FPSTR(S_HEAP)] = CrashRecorder::ext.heap;
|
||||||
|
docCrash[FPSTR(S_UPTIME)] = CrashRecorder::ext.uptime;
|
||||||
|
|
||||||
|
if (CrashRecorder::backtrace.length > 0 && CrashRecorder::backtrace.length <= CrashRecorder::backtraceMaxLength) {
|
||||||
|
String backtraceStr;
|
||||||
|
arr2str(backtraceStr, CrashRecorder::backtrace.data, CrashRecorder::backtrace.length);
|
||||||
|
docCrash[FPSTR(S_BACKTRACE)][FPSTR(S_DATA)] = backtraceStr;
|
||||||
|
docCrash[FPSTR(S_BACKTRACE)][FPSTR(S_CONTINUES)] = CrashRecorder::backtrace.continues;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CrashRecorder::epc.length > 0 && CrashRecorder::epc.length <= CrashRecorder::epcMaxLength) {
|
||||||
|
String epcStr;
|
||||||
|
arr2str(epcStr, CrashRecorder::epc.data, CrashRecorder::epc.length);
|
||||||
|
docCrash[FPSTR(S_EPC)] = epcStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.shrinkToFit();
|
||||||
|
|
||||||
|
this->webServer->sendHeader(F("Content-Disposition"), F("attachment; filename=\"debug.json\""));
|
||||||
|
this->bufferedWebServer->send(200, F("application/json"), doc, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -563,15 +832,15 @@ protected:
|
|||||||
this->webServer->onNotFound([this]() {
|
this->webServer->onNotFound([this]() {
|
||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), this->webServer->uri().c_str());
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), this->webServer->uri().c_str());
|
||||||
|
|
||||||
const String uri = this->webServer->uri();
|
const String& uri = this->webServer->uri();
|
||||||
if (uri.equals("/")) {
|
if (uri.equals(F("/"))) {
|
||||||
this->webServer->send(200, "text/plain", F("The file system is not flashed!"));
|
this->webServer->send(200, F("text/plain"), F("The file system is not flashed!"));
|
||||||
|
|
||||||
} else if (network->isApEnabled()) {
|
} else if (network->isApEnabled()) {
|
||||||
this->onCaptivePortal();
|
this->onCaptivePortal();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->webServer->send(404, "text/plain", F("Page not found"));
|
this->webServer->send(404, F("text/plain"), F("Page not found"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -590,7 +859,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started: AP up or STA connected"));
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started: AP up or STA connected"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else if (this->stateWebServer() && !network->isApEnabled() && !network->isStaEnabled()) {
|
} else if (this->stateWebServer() && !network->isApEnabled() && !network->isStaEnabled()) {
|
||||||
@@ -598,7 +867,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Stopped: AP and STA down"));
|
Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Stopped: AP and STA down"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +877,7 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up"));
|
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else if (this->stateDnsServer() && (!network->isApEnabled() || !this->stateWebServer())) {
|
} else if (this->stateDnsServer() && (!network->isApEnabled() || !this->stateWebServer())) {
|
||||||
@@ -616,14 +885,15 @@ protected:
|
|||||||
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down"));
|
Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down"));
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->stateDnsServer()) {
|
if (this->stateDnsServer()) {
|
||||||
this->dnsServer->processNextRequest();
|
this->dnsServer->processNextRequest();
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
::delay(0);
|
::optimistic_yield(1000);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,28 +911,30 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onCaptivePortal() {
|
void onCaptivePortal() {
|
||||||
const String uri = this->webServer->uri();
|
const String& uri = this->webServer->uri();
|
||||||
|
|
||||||
if (uri.equals("/connecttest.txt")) {
|
if (uri.equals(F("/connecttest.txt"))) {
|
||||||
this->webServer->sendHeader(F("Location"), F("http://logout.net"));
|
this->webServer->sendHeader(F("Location"), F("http://logout.net"));
|
||||||
this->webServer->send(302);
|
this->webServer->send(302);
|
||||||
|
|
||||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to http://logout.net with 302 code"));
|
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to http://logout.net with 302 code"));
|
||||||
|
|
||||||
} else if (uri.equals("/wpad.dat")) {
|
} else if (uri.equals(F("/wpad.dat"))) {
|
||||||
this->webServer->send(404);
|
this->webServer->send(404);
|
||||||
|
|
||||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 404 code"));
|
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 404 code"));
|
||||||
|
|
||||||
} else if (uri.equals("/success.txt")) {
|
} else if (uri.equals(F("/success.txt"))) {
|
||||||
this->webServer->send(200);
|
this->webServer->send(200);
|
||||||
|
|
||||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 200 code"));
|
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Send empty page with 200 code"));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
String portalUrl = "http://" + network->getApIp().toString() + '/';
|
String portalUrl = F("http://");
|
||||||
|
portalUrl += network->getApIp().toString().c_str();
|
||||||
|
portalUrl += '/';
|
||||||
|
|
||||||
this->webServer->sendHeader("Location", portalUrl.c_str());
|
this->webServer->sendHeader(F("Location"), portalUrl);
|
||||||
this->webServer->send(302);
|
this->webServer->send(302);
|
||||||
|
|
||||||
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code"));
|
Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code"));
|
||||||
@@ -691,7 +963,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->webServer->handleClient();
|
//this->webServer->handleClient();
|
||||||
this->webServer->stop();
|
this->webServer->stop();
|
||||||
this->webServerEnabled = false;
|
this->webServerEnabled = false;
|
||||||
this->webServerChangeState = millis();
|
this->webServerChangeState = millis();
|
||||||
@@ -716,7 +988,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->dnsServer->processNextRequest();
|
//this->dnsServer->processNextRequest();
|
||||||
this->dnsServer->stop();
|
this->dnsServer->stop();
|
||||||
this->dnsServerEnabled = false;
|
this->dnsServerEnabled = false;
|
||||||
this->dnsServerChangeState = millis();
|
this->dnsServerChangeState = millis();
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ public:
|
|||||||
RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
|
RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float prevHeatingTarget = 0;
|
float prevHeatingTarget = 0.0f;
|
||||||
float prevEtResult = 0;
|
float prevEtResult = 0.0f;
|
||||||
float prevPidResult = 0;
|
float prevPidResult = 0.0f;
|
||||||
|
|
||||||
|
bool indoorSensorsConnected = false;
|
||||||
|
//bool outdoorSensorsConnected = false;
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
const char* getTaskName() override {
|
const char* getTaskName() override {
|
||||||
@@ -29,104 +32,139 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
float newTemp = vars.parameters.heatingSetpoint;
|
this->indoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP);
|
||||||
|
//this->outdoorSensorsConnected = Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP);
|
||||||
|
|
||||||
if (vars.states.emergency) {
|
if (settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.nativeHeatingControl) {
|
||||||
if (settings.heating.turbo) {
|
vars.master.heating.indoorTempControl = true;
|
||||||
settings.heating.turbo = false;
|
vars.master.heating.minTemp = THERMOSTAT_INDOOR_MIN_TEMP;
|
||||||
|
vars.master.heating.maxTemp = THERMOSTAT_INDOOR_MAX_TEMP;
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
|
|
||||||
}
|
|
||||||
|
|
||||||
newTemp = this->getEmergencyModeTemp();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (settings.heating.turbo && (fabs(settings.heating.target - vars.temperatures.indoor) < 1 || !settings.heating.enable || (settings.equitherm.enable && settings.pid.enable))) {
|
vars.master.heating.indoorTempControl = false;
|
||||||
settings.heating.turbo = false;
|
vars.master.heating.minTemp = settings.heating.minTemp;
|
||||||
|
vars.master.heating.maxTemp = settings.heating.maxTemp;
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
|
|
||||||
}
|
|
||||||
|
|
||||||
newTemp = this->getNormalModeTemp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limits
|
if (!settings.pid.enabled && fabsf(pidRegulator.integral) > 0.01f) {
|
||||||
newTemp = constrain(
|
pidRegulator.integral = 0.0f;
|
||||||
newTemp,
|
|
||||||
!settings.opentherm.nativeHeatingControl ? settings.heating.minTemp : THERMOSTAT_INDOOR_MIN_TEMP,
|
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
||||||
!settings.opentherm.nativeHeatingControl ? settings.heating.maxTemp : THERMOSTAT_INDOOR_MAX_TEMP
|
}
|
||||||
|
|
||||||
|
this->turbo();
|
||||||
|
this->hysteresis();
|
||||||
|
|
||||||
|
vars.master.heating.targetTemp = constrain(
|
||||||
|
this->getHeatingSetpoint(),
|
||||||
|
vars.master.heating.minTemp,
|
||||||
|
vars.master.heating.maxTemp
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fabs(vars.parameters.heatingSetpoint - newTemp) > 0.4999f) {
|
Sensors::setValueByType(
|
||||||
vars.parameters.heatingSetpoint = newTemp;
|
Sensors::Type::HEATING_SETPOINT_TEMP, vars.master.heating.targetTemp,
|
||||||
|
Sensors::ValueType::PRIMARY, true, true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void turbo() {
|
||||||
|
if (settings.heating.turbo) {
|
||||||
|
if (!settings.heating.enabled || vars.emergency.state || !this->indoorSensorsConnected) {
|
||||||
|
settings.heating.turbo = false;
|
||||||
|
|
||||||
|
} else if (!settings.pid.enabled && !settings.equitherm.enabled) {
|
||||||
|
settings.heating.turbo = false;
|
||||||
|
|
||||||
|
} else if (fabsf(settings.heating.target - vars.master.heating.indoorTemp) <= 1.0f) {
|
||||||
|
settings.heating.turbo = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.heating.turbo) {
|
||||||
|
Log.sinfoln(FPSTR(L_REGULATOR), F("Turbo mode auto disabled"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hysteresis() {
|
||||||
|
bool useHyst = false;
|
||||||
|
if (settings.heating.hysteresis > 0.01f && this->indoorSensorsConnected) {
|
||||||
|
useHyst = settings.equitherm.enabled || settings.pid.enabled || settings.opentherm.nativeHeatingControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useHyst) {
|
||||||
|
if (!vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target + 0.0001f >= settings.heating.hysteresis) {
|
||||||
|
vars.master.heating.blocking = true;
|
||||||
|
|
||||||
|
} else if (vars.master.heating.blocking && vars.master.heating.indoorTemp - settings.heating.target - 0.0001f <= -(settings.heating.hysteresis)) {
|
||||||
|
vars.master.heating.blocking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (vars.master.heating.blocking) {
|
||||||
|
vars.master.heating.blocking = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float getEmergencyModeTemp() {
|
float getHeatingSetpoint() {
|
||||||
float newTemp = 0;
|
float newTemp = 0;
|
||||||
|
|
||||||
// if use equitherm
|
if (fabsf(prevHeatingTarget - settings.heating.target) > 0.0001f) {
|
||||||
if (settings.emergency.useEquitherm) {
|
|
||||||
float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp);
|
|
||||||
|
|
||||||
if (fabs(prevEtResult - etResult) > 0.4999f) {
|
|
||||||
prevEtResult = etResult;
|
|
||||||
newTemp += etResult;
|
|
||||||
|
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_EQUITHERM), F("New emergency result: %.2f"), etResult);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
newTemp += prevEtResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(settings.emergency.usePid) {
|
|
||||||
if (vars.parameters.heatingEnabled) {
|
|
||||||
float pidResult = getPidTemp(
|
|
||||||
settings.heating.minTemp,
|
|
||||||
settings.heating.maxTemp
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fabs(prevPidResult - pidResult) > 0.4999f) {
|
|
||||||
prevPidResult = pidResult;
|
|
||||||
newTemp += pidResult;
|
|
||||||
|
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("New emergency result: %.2f"), pidResult);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
newTemp += prevPidResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!vars.parameters.heatingEnabled && prevPidResult != 0) {
|
|
||||||
newTemp += prevPidResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// default temp, manual mode
|
|
||||||
newTemp = settings.emergency.target;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getNormalModeTemp() {
|
|
||||||
float newTemp = 0;
|
|
||||||
|
|
||||||
if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001f) {
|
|
||||||
prevHeatingTarget = settings.heating.target;
|
prevHeatingTarget = settings.heating.target;
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR), F("New target: %.2f"), settings.heating.target);
|
Log.sinfoln(FPSTR(L_REGULATOR), F("New target: %.2f"), settings.heating.target);
|
||||||
|
|
||||||
if (/*settings.equitherm.enable && */settings.pid.enable) {
|
/*if (settings.pid.enabled) {
|
||||||
pidRegulator.integral = 0;
|
pidRegulator.integral = 0.0f;
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
||||||
}
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vars.emergency.state) {
|
||||||
|
return settings.emergency.target;
|
||||||
|
|
||||||
|
} else if (settings.opentherm.nativeHeatingControl) {
|
||||||
|
return settings.heating.target;
|
||||||
|
|
||||||
|
} else if (!settings.equitherm.enabled && !settings.pid.enabled) {
|
||||||
|
return settings.heating.target;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if use equitherm
|
// if use equitherm
|
||||||
if (settings.equitherm.enable) {
|
if (settings.equitherm.enabled) {
|
||||||
float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp);
|
unsigned short minTemp = settings.heating.minTemp;
|
||||||
|
unsigned short maxTemp = settings.heating.maxTemp;
|
||||||
|
float targetTemp = settings.heating.target;
|
||||||
|
float indoorTemp = vars.master.heating.indoorTemp;
|
||||||
|
float outdoorTemp = vars.master.heating.outdoorTemp;
|
||||||
|
|
||||||
if (fabs(prevEtResult - etResult) > 0.4999f) {
|
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||||
|
minTemp = f2c(minTemp);
|
||||||
|
maxTemp = f2c(maxTemp);
|
||||||
|
targetTemp = f2c(targetTemp);
|
||||||
|
indoorTemp = f2c(indoorTemp);
|
||||||
|
outdoorTemp = f2c(outdoorTemp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->indoorSensorsConnected || settings.pid.enabled) {
|
||||||
|
etRegulator.Kt = 0.0f;
|
||||||
|
etRegulator.indoorTemp = 0.0f;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
etRegulator.Kt = settings.heating.turbo ? 0.0f : settings.equitherm.t_factor;
|
||||||
|
etRegulator.indoorTemp = indoorTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
etRegulator.setLimits(minTemp, maxTemp);
|
||||||
|
etRegulator.Kn = settings.equitherm.n_factor;
|
||||||
|
etRegulator.Kk = settings.equitherm.k_factor;
|
||||||
|
etRegulator.targetTemp = targetTemp;
|
||||||
|
etRegulator.outdoorTemp = outdoorTemp;
|
||||||
|
float etResult = etRegulator.getResult();
|
||||||
|
|
||||||
|
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||||
|
etResult = c2f(etResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(prevEtResult - etResult) > 0.09f) {
|
||||||
prevEtResult = etResult;
|
prevEtResult = etResult;
|
||||||
newTemp += etResult;
|
newTemp += etResult;
|
||||||
|
|
||||||
@@ -138,15 +176,27 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if use pid
|
// if use pid
|
||||||
if (settings.pid.enable) {
|
if (settings.pid.enabled) {
|
||||||
//if (vars.parameters.heatingEnabled) {
|
//if (vars.parameters.heatingEnabled) {
|
||||||
if (settings.heating.enable) {
|
if (settings.heating.enabled && this->indoorSensorsConnected) {
|
||||||
float pidResult = getPidTemp(
|
pidRegulator.Kp = settings.heating.turbo ? 0.0f : settings.pid.p_factor;
|
||||||
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
|
pidRegulator.Kd = settings.pid.d_factor;
|
||||||
settings.pid.maxTemp
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fabs(prevPidResult - pidResult) > 0.4999f) {
|
pidRegulator.setLimits(settings.pid.minTemp, settings.pid.maxTemp);
|
||||||
|
pidRegulator.setDt(settings.pid.dt * 1000u);
|
||||||
|
pidRegulator.input = vars.master.heating.indoorTemp;
|
||||||
|
pidRegulator.setpoint = settings.heating.target;
|
||||||
|
|
||||||
|
if (fabsf(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
|
||||||
|
pidRegulator.Ki = settings.pid.i_factor;
|
||||||
|
pidRegulator.integral = 0.0f;
|
||||||
|
pidRegulator.getResultNow();
|
||||||
|
|
||||||
|
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
||||||
|
}
|
||||||
|
|
||||||
|
float pidResult = pidRegulator.getResultTimer();
|
||||||
|
if (fabsf(prevPidResult - pidResult) > 0.09f) {
|
||||||
prevPidResult = pidResult;
|
prevPidResult = pidResult;
|
||||||
newTemp += pidResult;
|
newTemp += pidResult;
|
||||||
|
|
||||||
@@ -156,112 +206,21 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
newTemp += prevPidResult;
|
newTemp += prevPidResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
newTemp += prevPidResult;
|
newTemp += prevPidResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (fabs(pidRegulator.integral) > 0.0001f) {
|
|
||||||
pidRegulator.integral = 0;
|
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// default temp, manual mode
|
// Turbo mode
|
||||||
if (!settings.equitherm.enable && !settings.pid.enable) {
|
if (settings.heating.turbo && (settings.equitherm.enabled || settings.pid.enabled)) {
|
||||||
newTemp = settings.heating.target;
|
newTemp += constrain(
|
||||||
|
settings.heating.target - vars.master.heating.indoorTemp,
|
||||||
|
-3.0f,
|
||||||
|
3.0f
|
||||||
|
) * settings.heating.turboFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTemp;
|
return newTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Equitherm Temp
|
|
||||||
* Calculations in degrees C, conversion occurs when using F
|
|
||||||
*
|
|
||||||
* @param minTemp
|
|
||||||
* @param maxTemp
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
float getEquithermTemp(int minTemp, int maxTemp) {
|
|
||||||
float targetTemp = vars.states.emergency ? settings.emergency.target : settings.heating.target;
|
|
||||||
float indoorTemp = vars.temperatures.indoor;
|
|
||||||
float outdoorTemp = vars.temperatures.outdoor;
|
|
||||||
|
|
||||||
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
|
||||||
minTemp = f2c(minTemp);
|
|
||||||
maxTemp = f2c(maxTemp);
|
|
||||||
targetTemp = f2c(targetTemp);
|
|
||||||
indoorTemp = f2c(indoorTemp);
|
|
||||||
outdoorTemp = f2c(outdoorTemp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vars.states.emergency) {
|
|
||||||
if (settings.sensors.indoor.type == SensorType::MANUAL) {
|
|
||||||
etRegulator.Kt = 0;
|
|
||||||
etRegulator.indoorTemp = 0;
|
|
||||||
|
|
||||||
} else if ((settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) && !vars.sensors.indoor.connected) {
|
|
||||||
etRegulator.Kt = 0;
|
|
||||||
etRegulator.indoorTemp = 0;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
etRegulator.Kt = settings.equitherm.t_factor;
|
|
||||||
etRegulator.indoorTemp = indoorTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
etRegulator.outdoorTemp = outdoorTemp;
|
|
||||||
|
|
||||||
} else if (settings.pid.enable) {
|
|
||||||
etRegulator.Kt = 0;
|
|
||||||
etRegulator.indoorTemp = round(indoorTemp);
|
|
||||||
etRegulator.outdoorTemp = round(outdoorTemp);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (settings.heating.turbo) {
|
|
||||||
etRegulator.Kt = 10;
|
|
||||||
} else {
|
|
||||||
etRegulator.Kt = settings.equitherm.t_factor;
|
|
||||||
}
|
|
||||||
etRegulator.indoorTemp = indoorTemp;
|
|
||||||
etRegulator.outdoorTemp = outdoorTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
etRegulator.setLimits(minTemp, maxTemp);
|
|
||||||
etRegulator.Kn = settings.equitherm.n_factor;
|
|
||||||
etRegulator.Kk = settings.equitherm.k_factor;
|
|
||||||
etRegulator.targetTemp = targetTemp;
|
|
||||||
float result = etRegulator.getResult();
|
|
||||||
|
|
||||||
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
|
||||||
result = c2f(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getPidTemp(int minTemp, int maxTemp) {
|
|
||||||
if (fabs(pidRegulator.Kp - settings.pid.p_factor) >= 0.0001f) {
|
|
||||||
pidRegulator.Kp = settings.pid.p_factor;
|
|
||||||
pidRegulator.integral = 0;
|
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabs(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
|
|
||||||
pidRegulator.Ki = settings.pid.i_factor;
|
|
||||||
pidRegulator.integral = 0;
|
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabs(pidRegulator.Kd - settings.pid.d_factor) >= 0.0001f) {
|
|
||||||
pidRegulator.Kd = settings.pid.d_factor;
|
|
||||||
pidRegulator.integral = 0;
|
|
||||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
|
||||||
}
|
|
||||||
|
|
||||||
pidRegulator.setLimits(minTemp, maxTemp);
|
|
||||||
pidRegulator.setDt(settings.pid.dt * 1000u);
|
|
||||||
pidRegulator.input = vars.temperatures.indoor;
|
|
||||||
pidRegulator.setpoint = vars.states.emergency ? settings.emergency.target : settings.heating.target;
|
|
||||||
|
|
||||||
return pidRegulator.getResultTimer();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
451
src/Sensors.h
Normal file
451
src/Sensors.h
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Sensors {
|
||||||
|
protected:
|
||||||
|
static uint8_t maxSensors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Type : uint8_t {
|
||||||
|
OT_OUTDOOR_TEMP = 0,
|
||||||
|
OT_HEATING_TEMP = 1,
|
||||||
|
OT_HEATING_RETURN_TEMP = 2,
|
||||||
|
OT_DHW_TEMP = 3,
|
||||||
|
OT_DHW_TEMP2 = 4,
|
||||||
|
OT_DHW_FLOW_RATE = 5,
|
||||||
|
OT_CH2_TEMP = 6,
|
||||||
|
OT_EXHAUST_TEMP = 7,
|
||||||
|
OT_HEAT_EXCHANGER_TEMP = 8,
|
||||||
|
OT_PRESSURE = 9,
|
||||||
|
OT_MODULATION_LEVEL = 10,
|
||||||
|
OT_CURRENT_POWER = 11,
|
||||||
|
|
||||||
|
NTC_10K_TEMP = 50,
|
||||||
|
DALLAS_TEMP = 51,
|
||||||
|
BLUETOOTH = 52,
|
||||||
|
|
||||||
|
HEATING_SETPOINT_TEMP = 253,
|
||||||
|
MANUAL = 254,
|
||||||
|
NOT_CONFIGURED = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Purpose : uint8_t {
|
||||||
|
OUTDOOR_TEMP = 0,
|
||||||
|
INDOOR_TEMP = 1,
|
||||||
|
HEATING_TEMP = 2,
|
||||||
|
HEATING_RETURN_TEMP = 3,
|
||||||
|
DHW_TEMP = 4,
|
||||||
|
DHW_RETURN_TEMP = 5,
|
||||||
|
DHW_FLOW_RATE = 6,
|
||||||
|
EXHAUST_TEMP = 7,
|
||||||
|
MODULATION_LEVEL = 8,
|
||||||
|
CURRENT_POWER = 9,
|
||||||
|
|
||||||
|
PRESSURE = 252,
|
||||||
|
HUMIDITY = 253,
|
||||||
|
TEMPERATURE = 254,
|
||||||
|
NOT_CONFIGURED = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ValueType : uint8_t {
|
||||||
|
PRIMARY = 0,
|
||||||
|
TEMPERATURE = 0,
|
||||||
|
HUMIDITY = 1,
|
||||||
|
BATTERY = 2,
|
||||||
|
RSSI = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool enabled = false;
|
||||||
|
char name[33];
|
||||||
|
Purpose purpose = Purpose::NOT_CONFIGURED;
|
||||||
|
Type type = Type::NOT_CONFIGURED;
|
||||||
|
uint8_t gpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
uint8_t address[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
float offset = 0.0f;
|
||||||
|
float factor = 1.0f;
|
||||||
|
bool filtering = false;
|
||||||
|
float filteringFactor = 0.15f;
|
||||||
|
} Settings;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool connected = false;
|
||||||
|
unsigned long activityTime = 0;
|
||||||
|
uint8_t signalQuality = 0;
|
||||||
|
//float raw[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
float values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
} Result;
|
||||||
|
|
||||||
|
|
||||||
|
static Settings* settings;
|
||||||
|
static Result* results;
|
||||||
|
|
||||||
|
static inline void setMaxSensors(uint8_t value) {
|
||||||
|
maxSensors = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t getMaxSensors() {
|
||||||
|
return maxSensors;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t getMaxSensorId() {
|
||||||
|
uint8_t maxSensors = getMaxSensors();
|
||||||
|
return maxSensors > 1 ? (maxSensors - 1) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool isValidSensorId(const uint8_t id) {
|
||||||
|
return id >= 0 && id <= getMaxSensorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool isValidValueId(const uint8_t id) {
|
||||||
|
return id >= (uint8_t) ValueType::TEMPERATURE && id <= (uint8_t) ValueType::RSSI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasEnabledAndValid(const uint8_t id) {
|
||||||
|
if (!isValidSensorId(id) || !settings[id].enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings[id].type == Type::NOT_CONFIGURED || settings[id].purpose == Purpose::NOT_CONFIGURED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t getAmountByType(Type type) {
|
||||||
|
if (settings == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t amount = 0;
|
||||||
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
|
if (settings[id].type == type) {
|
||||||
|
amount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int16_t getIdByName(const char* name) {
|
||||||
|
if (settings == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
|
if (strcmp(settings[id].name, name) == 0) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int16_t getIdByObjectId(const char* objectId) {
|
||||||
|
if (settings == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String refObjectId;
|
||||||
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
|
Sensors::makeObjectId(refObjectId, settings[id].name);
|
||||||
|
if (refObjectId.equals(objectId)) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setValueById(const uint8_t sensorId, float value, const ValueType valueType, const bool updateActivityTime = false, const bool markConnected = false) {
|
||||||
|
if (settings == nullptr || results == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t valueId = (uint8_t) valueType;
|
||||||
|
if (!isValidSensorId(sensorId) || !isValidValueId(valueId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& sSensor = settings[sensorId];
|
||||||
|
auto& rSensor = results[sensorId];
|
||||||
|
|
||||||
|
float compensatedValue = value;
|
||||||
|
if (valueType == ValueType::PRIMARY) {
|
||||||
|
if (fabsf(sSensor.factor) > 0.001f) {
|
||||||
|
compensatedValue *= sSensor.factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(sSensor.offset) > 0.001f) {
|
||||||
|
compensatedValue += sSensor.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (valueType == ValueType::RSSI) {
|
||||||
|
if (sSensor.type == Type::BLUETOOTH) {
|
||||||
|
rSensor.signalQuality = Sensors::bluetoothRssiToQuality(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sSensor.filtering && fabs(rSensor.values[valueId]) >= 0.1f) {
|
||||||
|
rSensor.values[valueId] += (compensatedValue - rSensor.values[valueId]) * sSensor.filteringFactor;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rSensor.values[valueId] = compensatedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateActivityTime) {
|
||||||
|
rSensor.activityTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markConnected) {
|
||||||
|
if (!rSensor.connected) {
|
||||||
|
rSensor.connected = true;
|
||||||
|
|
||||||
|
Log.snoticeln(
|
||||||
|
FPSTR(L_SENSORS), F("#%hhu '%s' new status: CONNECTED"),
|
||||||
|
sensorId, sSensor.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.snoticeln(
|
||||||
|
FPSTR(L_SENSORS), F("#%hhu '%s' new value %hhu: %.2f, compensated: %.2f, raw: %.2f"),
|
||||||
|
sensorId, sSensor.name, valueId, rSensor.values[valueId], compensatedValue, value
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t setValueByType(Type type, float value, const ValueType valueType, const bool updateActivityTime = false, const bool markConnected = false) {
|
||||||
|
if (settings == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t updated = 0;
|
||||||
|
|
||||||
|
// read sensors data for current instance
|
||||||
|
for (uint8_t sensorId = 0; sensorId < getMaxSensorId(); sensorId++) {
|
||||||
|
auto& sSensor = settings[sensorId];
|
||||||
|
|
||||||
|
// only target & valid sensors
|
||||||
|
if (!sSensor.enabled || sSensor.type != type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setValueById(sensorId, value, valueType, updateActivityTime, markConnected)) {
|
||||||
|
updated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getConnectionStatusById(const uint8_t sensorId) {
|
||||||
|
if (settings == nullptr || results == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidSensorId(sensorId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results[sensorId].connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setConnectionStatusById(const uint8_t sensorId, const bool status, const bool updateActivityTime = true) {
|
||||||
|
if (settings == nullptr || results == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidSensorId(sensorId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& sSensor = settings[sensorId];
|
||||||
|
auto& rSensor = results[sensorId];
|
||||||
|
|
||||||
|
if (rSensor.connected != status) {
|
||||||
|
Log.snoticeln(
|
||||||
|
FPSTR(L_SENSORS), F("#%hhu '%s' new status: %s"),
|
||||||
|
sensorId, sSensor.name, status ? F("CONNECTED") : F("DISCONNECTED")
|
||||||
|
);
|
||||||
|
|
||||||
|
rSensor.connected = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateActivityTime) {
|
||||||
|
rSensor.activityTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t setConnectionStatusByType(Type type, const bool status, const bool updateActivityTime = true) {
|
||||||
|
if (settings == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t updated = 0;
|
||||||
|
|
||||||
|
// read sensors data for current instance
|
||||||
|
for (uint8_t sensorId = 0; sensorId < getMaxSensorId(); sensorId++) {
|
||||||
|
auto& sSensor = settings[sensorId];
|
||||||
|
|
||||||
|
// only target & valid sensors
|
||||||
|
if (!sSensor.enabled || sSensor.type != type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setConnectionStatusById(sensorId, status, updateActivityTime)) {
|
||||||
|
updated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getMeanValueByPurpose(Purpose purpose, const ValueType valueType, bool onlyConnected = true) {
|
||||||
|
if (settings == nullptr || results == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t valueId = (uint8_t) valueType;
|
||||||
|
if (!isValidValueId(valueId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float value = 0.0f;
|
||||||
|
uint8_t amount = 0;
|
||||||
|
|
||||||
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
|
auto& sSensor = settings[id];
|
||||||
|
auto& rSensor = results[id];
|
||||||
|
|
||||||
|
if (sSensor.purpose == purpose && (!onlyConnected || rSensor.connected)) {
|
||||||
|
value += rSensor.values[valueId];
|
||||||
|
amount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amount) {
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
} else if (amount == 1) {
|
||||||
|
return value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return value / amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool existsConnectedSensorsByPurpose(Purpose purpose) {
|
||||||
|
if (settings == nullptr || results == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t id = 0; id < getMaxSensorId(); id++) {
|
||||||
|
if (settings[id].purpose == purpose && results[id].connected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String& cleanName(String& value, char space = ' ') {
|
||||||
|
// only valid symbols
|
||||||
|
for (uint8_t pos = 0; pos < value.length(); pos++) {
|
||||||
|
char symbol = value.charAt(pos);
|
||||||
|
|
||||||
|
// 0..9
|
||||||
|
if (symbol >= 48 && symbol <= 57) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A..Z
|
||||||
|
if (symbol >= 65 && symbol <= 90) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a..z
|
||||||
|
if (symbol >= 97 && symbol <= 122) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _-
|
||||||
|
if (symbol == 95 || symbol == 45 || symbol == space) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.setCharAt(pos, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
value.trim();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static String cleanName(T value, char space = ' ') {
|
||||||
|
String res = value;
|
||||||
|
return cleanName(res, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static String& makeObjectId(String& res, T value, char separator = '_') {
|
||||||
|
res = value;
|
||||||
|
cleanName(res);
|
||||||
|
res.toLowerCase();
|
||||||
|
res.replace(' ', separator);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static String makeObjectId(T value, char separator = '_') {
|
||||||
|
String res;
|
||||||
|
makeObjectId(res, value, separator);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TV, class TS>
|
||||||
|
static String& makeObjectIdWithSuffix(String& res, TV value, TS suffix, char separator = '_') {
|
||||||
|
res.clear();
|
||||||
|
makeObjectId(res, value, separator);
|
||||||
|
res += separator;
|
||||||
|
res += suffix;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TV, class TS>
|
||||||
|
static String makeObjectIdWithSuffix(TV value, TS suffix, char separator = '_') {
|
||||||
|
String res;
|
||||||
|
makeObjectIdWithSuffix(res, value, suffix, separator);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TV, class TP>
|
||||||
|
static String& makeObjectIdWithPrefix(String& res, TV value, TP prefix, char separator = '_') {
|
||||||
|
res = prefix;
|
||||||
|
res += separator;
|
||||||
|
res += makeObjectId(value, separator).c_str();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TV, class TP>
|
||||||
|
static String makeObjectIdWithPrefix(TV value, TP prefix, char separator = '_') {
|
||||||
|
String res;
|
||||||
|
return makeObjectIdWithPrefix(res, value, prefix, separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t bluetoothRssiToQuality(int rssi) {
|
||||||
|
return constrain(map(rssi, -110, -50, 0, 100), 0, 100);;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t Sensors::maxSensors = 0;
|
||||||
|
Sensors::Settings* Sensors::settings = nullptr;
|
||||||
|
Sensors::Result* Sensors::results = nullptr;
|
||||||
1013
src/SensorsTask.h
1013
src/SensorsTask.h
File diff suppressed because it is too large
Load Diff
333
src/Settings.h
333
src/Settings.h
@@ -24,16 +24,16 @@ struct NetworkSettings {
|
|||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
struct {
|
struct {
|
||||||
bool debug = DEBUG_BY_DEFAULT;
|
uint8_t logLevel = DEFAULT_LOG_LEVEL;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = USE_SERIAL;
|
bool enabled = DEFAULT_SERIAL_ENABLED;
|
||||||
unsigned int baudrate = 115200;
|
unsigned int baudrate = DEFAULT_SERIAL_BAUD;
|
||||||
} serial;
|
} serial;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = USE_TELNET;
|
bool enabled = DEFAULT_TELNET_ENABLED;
|
||||||
unsigned short port = 23;
|
unsigned short port = DEFAULT_TELNET_PORT;
|
||||||
} telnet;
|
} telnet;
|
||||||
|
|
||||||
UnitSystem unitSystem = UnitSystem::METRIC;
|
UnitSystem unitSystem = UnitSystem::METRIC;
|
||||||
@@ -51,11 +51,12 @@ struct Settings {
|
|||||||
byte inGpio = DEFAULT_OT_IN_GPIO;
|
byte inGpio = DEFAULT_OT_IN_GPIO;
|
||||||
byte outGpio = DEFAULT_OT_OUT_GPIO;
|
byte outGpio = DEFAULT_OT_OUT_GPIO;
|
||||||
byte rxLedGpio = DEFAULT_OT_RX_LED_GPIO;
|
byte rxLedGpio = DEFAULT_OT_RX_LED_GPIO;
|
||||||
byte faultStateGpio = DEFAULT_OT_FAULT_STATE_GPIO;
|
uint8_t memberId = 0;
|
||||||
byte invertFaultState = false;
|
uint8_t flags = 0;
|
||||||
unsigned int memberIdCode = 0;
|
uint8_t maxModulation = 100;
|
||||||
float pressureFactor = 1.0f;
|
float minPower = 0.0f;
|
||||||
float dhwFlowRateFactor = 1.0f;
|
float maxPower = 0.0f;
|
||||||
|
|
||||||
bool dhwPresent = true;
|
bool dhwPresent = true;
|
||||||
bool summerWinterMode = false;
|
bool summerWinterMode = false;
|
||||||
bool heatingCh2Enabled = true;
|
bool heatingCh2Enabled = true;
|
||||||
@@ -66,15 +67,10 @@ struct Settings {
|
|||||||
bool getMinMaxTemp = true;
|
bool getMinMaxTemp = true;
|
||||||
bool nativeHeatingControl = false;
|
bool nativeHeatingControl = false;
|
||||||
bool immergasFix = false;
|
bool immergasFix = false;
|
||||||
|
|
||||||
struct {
|
|
||||||
bool enable = false;
|
|
||||||
float factor = 0.1f;
|
|
||||||
} filterNumValues;
|
|
||||||
} opentherm;
|
} opentherm;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = false;
|
bool enabled = DEFAULT_MQTT_ENABLED;
|
||||||
char server[81] = DEFAULT_MQTT_SERVER;
|
char server[81] = DEFAULT_MQTT_SERVER;
|
||||||
unsigned short port = DEFAULT_MQTT_PORT;
|
unsigned short port = DEFAULT_MQTT_PORT;
|
||||||
char user[33] = DEFAULT_MQTT_USER;
|
char user[33] = DEFAULT_MQTT_USER;
|
||||||
@@ -85,67 +81,44 @@ struct Settings {
|
|||||||
} mqtt;
|
} mqtt;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = false;
|
|
||||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||||
unsigned short tresholdTime = 120;
|
unsigned short tresholdTime = 120;
|
||||||
bool useEquitherm = false;
|
|
||||||
bool usePid = false;
|
|
||||||
bool onNetworkFault = true;
|
|
||||||
bool onMqttFault = true;
|
|
||||||
bool onIndoorSensorDisconnect = false;
|
|
||||||
bool onOutdoorSensorDisconnect = false;
|
|
||||||
} emergency;
|
} emergency;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = true;
|
bool enabled = true;
|
||||||
bool turbo = false;
|
bool turbo = false;
|
||||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||||
float hysteresis = 0.5f;
|
float hysteresis = 0.5f;
|
||||||
|
float turboFactor = 7.5f;
|
||||||
byte minTemp = DEFAULT_HEATING_MIN_TEMP;
|
byte minTemp = DEFAULT_HEATING_MIN_TEMP;
|
||||||
byte maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
byte maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||||
byte maxModulation = 100;
|
|
||||||
} heating;
|
} heating;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = true;
|
bool enabled = true;
|
||||||
float target = DEFAULT_DHW_TARGET_TEMP;
|
float target = DEFAULT_DHW_TARGET_TEMP;
|
||||||
byte minTemp = DEFAULT_DHW_MIN_TEMP;
|
byte minTemp = DEFAULT_DHW_MIN_TEMP;
|
||||||
byte maxTemp = DEFAULT_DHW_MAX_TEMP;
|
byte maxTemp = DEFAULT_DHW_MAX_TEMP;
|
||||||
} dhw;
|
} dhw;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = false;
|
bool enabled = false;
|
||||||
float p_factor = 2;
|
float p_factor = 2.0f;
|
||||||
float i_factor = 0.0055f;
|
float i_factor = 0.0055f;
|
||||||
float d_factor = 0;
|
float d_factor = 0.0f;
|
||||||
unsigned short dt = 180;
|
unsigned short dt = 180;
|
||||||
byte minTemp = 0;
|
short minTemp = 0;
|
||||||
byte maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
short maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||||
} pid;
|
} pid;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = false;
|
bool enabled = false;
|
||||||
float n_factor = 0.7f;
|
float n_factor = 0.7f;
|
||||||
float k_factor = 3.0f;
|
float k_factor = 3.0f;
|
||||||
float t_factor = 2.0f;
|
float t_factor = 2.0f;
|
||||||
} equitherm;
|
} equitherm;
|
||||||
|
|
||||||
struct {
|
|
||||||
struct {
|
|
||||||
SensorType type = SensorType::BOILER;
|
|
||||||
byte gpio = DEFAULT_SENSOR_OUTDOOR_GPIO;
|
|
||||||
uint8_t bleAddress[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
float offset = 0.0f;
|
|
||||||
} outdoor;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
SensorType type = SensorType::MANUAL;
|
|
||||||
byte gpio = DEFAULT_SENSOR_INDOOR_GPIO;
|
|
||||||
uint8_t bleAddress[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
float offset = 0.0f;
|
|
||||||
} indoor;
|
|
||||||
} sensors;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool use = false;
|
bool use = false;
|
||||||
byte gpio = DEFAULT_EXT_PUMP_GPIO;
|
byte gpio = DEFAULT_EXT_PUMP_GPIO;
|
||||||
@@ -154,74 +127,222 @@ struct Settings {
|
|||||||
unsigned short antiStuckTime = 300;
|
unsigned short antiStuckTime = 300;
|
||||||
} externalPump;
|
} externalPump;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
byte gpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
byte invertState = false;
|
||||||
|
unsigned short thresholdTime = 60;
|
||||||
|
} input;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
byte gpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
byte invertState = false;
|
||||||
|
unsigned short thresholdTime = 60;
|
||||||
|
bool onFault = true;
|
||||||
|
bool onLossConnection = true;
|
||||||
|
bool onEnabledHeating = false;
|
||||||
|
} output;
|
||||||
|
} cascadeControl;
|
||||||
|
|
||||||
char validationValue[8] = SETTINGS_VALID_VALUE;
|
char validationValue[8] = SETTINGS_VALID_VALUE;
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
|
Sensors::Settings sensorsSettings[SENSORS_AMOUNT] = {
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"Outdoor temp",
|
||||||
|
Sensors::Purpose::OUTDOOR_TEMP,
|
||||||
|
Sensors::Type::OT_OUTDOOR_TEMP,
|
||||||
|
DEFAULT_SENSOR_OUTDOOR_GPIO
|
||||||
|
},
|
||||||
|
{
|
||||||
|
false,
|
||||||
|
"Indoor temp",
|
||||||
|
Sensors::Purpose::INDOOR_TEMP,
|
||||||
|
Sensors::Type::DALLAS_TEMP,
|
||||||
|
DEFAULT_SENSOR_INDOOR_GPIO
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Heating temp",
|
||||||
|
Sensors::Purpose::HEATING_TEMP,
|
||||||
|
Sensors::Type::OT_HEATING_TEMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Heating return temp",
|
||||||
|
Sensors::Purpose::HEATING_RETURN_TEMP,
|
||||||
|
Sensors::Type::OT_HEATING_RETURN_TEMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Heating setpoint temp",
|
||||||
|
Sensors::Purpose::TEMPERATURE,
|
||||||
|
Sensors::Type::HEATING_SETPOINT_TEMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"DHW temp",
|
||||||
|
Sensors::Purpose::DHW_TEMP,
|
||||||
|
Sensors::Type::OT_DHW_TEMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"DHW flow rate",
|
||||||
|
Sensors::Purpose::DHW_FLOW_RATE,
|
||||||
|
Sensors::Type::OT_DHW_FLOW_RATE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Exhaust temp",
|
||||||
|
Sensors::Purpose::EXHAUST_TEMP,
|
||||||
|
Sensors::Type::OT_EXHAUST_TEMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Pressure",
|
||||||
|
Sensors::Purpose::PRESSURE,
|
||||||
|
Sensors::Type::OT_PRESSURE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Modulation level",
|
||||||
|
Sensors::Purpose::MODULATION_LEVEL,
|
||||||
|
Sensors::Type::OT_MODULATION_LEVEL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
true,
|
||||||
|
"Power",
|
||||||
|
Sensors::Purpose::CURRENT_POWER,
|
||||||
|
Sensors::Type::OT_CURRENT_POWER,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Variables {
|
struct Variables {
|
||||||
struct {
|
struct {
|
||||||
bool otStatus = false;
|
bool connected = false;
|
||||||
bool emergency = false;
|
|
||||||
bool heating = false;
|
|
||||||
bool dhw = false;
|
|
||||||
bool flame = false;
|
|
||||||
bool fault = false;
|
|
||||||
bool diagnostic = false;
|
|
||||||
bool externalPump = false;
|
|
||||||
bool mqtt = false;
|
|
||||||
} states;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
float modulation = 0.0f;
|
|
||||||
float pressure = 0.0f;
|
|
||||||
float dhwFlowRate = 0.0f;
|
|
||||||
byte faultCode = 0;
|
|
||||||
unsigned short diagnosticCode = 0;
|
|
||||||
int8_t rssi = 0;
|
int8_t rssi = 0;
|
||||||
|
} network;
|
||||||
struct {
|
|
||||||
bool connected = false;
|
|
||||||
int8_t rssi = 0;
|
|
||||||
float battery = 0.0f;
|
|
||||||
float humidity = 0.0f;
|
|
||||||
} outdoor;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool connected = false;
|
|
||||||
int8_t rssi = 0;
|
|
||||||
float battery = 0.0f;
|
|
||||||
float humidity = 0.0f;
|
|
||||||
} indoor;
|
|
||||||
} sensors;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
float indoor = 0.0f;
|
bool connected = false;
|
||||||
float outdoor = 0.0f;
|
} mqtt;
|
||||||
float heating = 0.0f;
|
|
||||||
float heatingReturn = 0.0f;
|
|
||||||
float dhw = 0.0f;
|
|
||||||
float exhaust = 0.0f;
|
|
||||||
} temperatures;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool heatingEnabled = false;
|
bool state = false;
|
||||||
byte heatingMinTemp = DEFAULT_HEATING_MIN_TEMP;
|
} emergency;
|
||||||
byte heatingMaxTemp = DEFAULT_HEATING_MAX_TEMP;
|
|
||||||
float heatingSetpoint = 0;
|
struct {
|
||||||
unsigned long extPumpLastEnableTime = 0;
|
bool state = false;
|
||||||
byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP;
|
unsigned long lastEnabledTime = 0;
|
||||||
byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP;
|
} externalPump;
|
||||||
byte maxModulation = 0;
|
|
||||||
uint8_t slaveMemberId = 0;
|
struct {
|
||||||
uint8_t slaveFlags = 0;
|
bool input = false;
|
||||||
uint8_t slaveType = 0;
|
bool output = false;
|
||||||
uint8_t slaveVersion = 0;
|
} cascadeControl;
|
||||||
float slaveOtVersion = 0.0f;
|
|
||||||
uint8_t masterMemberId = 0;
|
struct {
|
||||||
uint8_t masterFlags = 0;
|
uint8_t memberId = 0;
|
||||||
uint8_t masterType = 0;
|
uint8_t flags = 0;
|
||||||
uint8_t masterVersion = 0;
|
uint8_t type = 0;
|
||||||
float masterOtVersion = 0;
|
uint8_t appVersion = 0;
|
||||||
} parameters;
|
float protocolVersion = 0.0f;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool blocking = false;
|
||||||
|
bool enabled = false;
|
||||||
|
bool indoorTempControl = false;
|
||||||
|
float targetTemp = 0.0f;
|
||||||
|
float currentTemp = 0.0f;
|
||||||
|
float returnTemp = 0.0f;
|
||||||
|
float indoorTemp = 0.0f;
|
||||||
|
float outdoorTemp = 0.0f;
|
||||||
|
float minTemp = 0.0f;
|
||||||
|
float maxTemp = 0.0f;
|
||||||
|
} heating;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
float targetTemp = 0.0f;
|
||||||
|
float currentTemp = 0.0f;
|
||||||
|
float returnTemp = 0.0f;
|
||||||
|
} dhw;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
float targetTemp = 0.0f;
|
||||||
|
} ch2;
|
||||||
|
} master;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t memberId = 0;
|
||||||
|
uint8_t flags = 0;
|
||||||
|
uint8_t type = 0;
|
||||||
|
uint8_t appVersion = 0;
|
||||||
|
float protocolVersion = 0.0f;
|
||||||
|
|
||||||
|
bool connected = false;
|
||||||
|
bool flame = false;
|
||||||
|
float pressure = 0.0f;
|
||||||
|
float exhaustTemp = 0.0f;
|
||||||
|
float heatExchangerTemp = 0.0f;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool active = false;
|
||||||
|
uint8_t code = 0;
|
||||||
|
} fault;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool active = false;
|
||||||
|
uint16_t code = 0;
|
||||||
|
} diag;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t current = 0;
|
||||||
|
uint8_t min = 0;
|
||||||
|
uint8_t max = 100;
|
||||||
|
} modulation;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float current = 0.0f;
|
||||||
|
float min = 0.0f;
|
||||||
|
float max = 0.0f;
|
||||||
|
} power;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool active = false;
|
||||||
|
bool enabled = false;
|
||||||
|
float targetTemp = 0.0f;
|
||||||
|
float currentTemp = 0.0f;
|
||||||
|
float returnTemp = 0.0f;
|
||||||
|
float indoorTemp = 0.0f;
|
||||||
|
float outdoorTemp = 0.0f;
|
||||||
|
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
|
||||||
|
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||||
|
} heating;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool active = false;
|
||||||
|
bool enabled = false;
|
||||||
|
float targetTemp = 0.0f;
|
||||||
|
float currentTemp = 0.0f;
|
||||||
|
float currentTemp2 = 0.0f;
|
||||||
|
float returnTemp = 0.0f;
|
||||||
|
float flowRate = 0.0f;
|
||||||
|
uint8_t minTemp = DEFAULT_DHW_MIN_TEMP;
|
||||||
|
uint8_t maxTemp = DEFAULT_DHW_MAX_TEMP;
|
||||||
|
} dhw;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled = false;
|
||||||
|
float targetTemp = 0.0f;
|
||||||
|
float currentTemp = 0.0f;
|
||||||
|
float indoorTemp = 0.0f;
|
||||||
|
} ch2;
|
||||||
|
} slave;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool restart = false;
|
bool restart = false;
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
||||||
|
|
||||||
#define MQTT_RECONNECT_INTERVAL 15000
|
#define MQTT_RECONNECT_INTERVAL 15000
|
||||||
|
|
||||||
#define EXT_SENSORS_INTERVAL 5000
|
|
||||||
#define EXT_SENSORS_FILTER_K 0.15
|
|
||||||
|
|
||||||
#define CONFIG_URL "http://%s/"
|
#define CONFIG_URL "http://%s/"
|
||||||
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
|
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
|
||||||
#define GPIO_IS_NOT_CONFIGURED 0xff
|
#define GPIO_IS_NOT_CONFIGURED 0xff
|
||||||
@@ -22,6 +18,14 @@
|
|||||||
#define THERMOSTAT_INDOOR_MIN_TEMP 5
|
#define THERMOSTAT_INDOOR_MIN_TEMP 5
|
||||||
#define THERMOSTAT_INDOOR_MAX_TEMP 30
|
#define THERMOSTAT_INDOOR_MAX_TEMP 30
|
||||||
|
|
||||||
|
#define DEFAULT_NTC_NOMINAL_RESISTANCE 10000.0f
|
||||||
|
#define DEFAULT_NTC_NOMINAL_TEMP 25.0f
|
||||||
|
#define DEFAULT_NTC_REF_RESISTANCE 10000.0f
|
||||||
|
#define DEFAULT_NTC_BETA_FACTOR 3950.0f
|
||||||
|
#define DEFAULT_NTC_VREF 3300.0f
|
||||||
|
#define DEFAULT_NTC_VLOW_TRESHOLD 25.0f
|
||||||
|
#define DEFAULT_NTC_VHIGH_TRESHOLD 3298.0f
|
||||||
|
|
||||||
#ifndef BUILD_VERSION
|
#ifndef BUILD_VERSION
|
||||||
#define BUILD_VERSION "0.0.0"
|
#define BUILD_VERSION "0.0.0"
|
||||||
#endif
|
#endif
|
||||||
@@ -30,12 +34,20 @@
|
|||||||
#define BUILD_ENV "undefined"
|
#define BUILD_ENV "undefined"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef USE_SERIAL
|
#ifndef DEFAULT_SERIAL_ENABLED
|
||||||
#define USE_SERIAL true
|
#define DEFAULT_SERIAL_ENABLED true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef USE_TELNET
|
#ifndef DEFAULT_SERIAL_BAUD
|
||||||
#define USE_TELNET true
|
#define DEFAULT_SERIAL_BAUD 115200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEFAULT_TELNET_ENABLED
|
||||||
|
#define DEFAULT_TELNET_ENABLED true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEFAULT_TELNET_PORT
|
||||||
|
#define DEFAULT_TELNET_PORT 23
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef USE_BLE
|
#ifndef USE_BLE
|
||||||
@@ -62,8 +74,8 @@
|
|||||||
#define DEFAULT_STA_PASSWORD ""
|
#define DEFAULT_STA_PASSWORD ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEBUG_BY_DEFAULT
|
#ifndef DEFAULT_LOG_LEVEL
|
||||||
#define DEBUG_BY_DEFAULT false
|
#define DEFAULT_LOG_LEVEL TinyLogger::Level::VERBOSE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_STATUS_LED_GPIO
|
#ifndef DEFAULT_STATUS_LED_GPIO
|
||||||
@@ -78,6 +90,10 @@
|
|||||||
#define DEFAULT_PORTAL_PASSWORD ""
|
#define DEFAULT_PORTAL_PASSWORD ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEFAULT_MQTT_ENABLED
|
||||||
|
#define DEFAULT_MQTT_ENABLED false
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_MQTT_SERVER
|
#ifndef DEFAULT_MQTT_SERVER
|
||||||
#define DEFAULT_MQTT_SERVER ""
|
#define DEFAULT_MQTT_SERVER ""
|
||||||
#endif
|
#endif
|
||||||
@@ -122,6 +138,10 @@
|
|||||||
#define DEFAULT_SENSOR_INDOOR_GPIO GPIO_IS_NOT_CONFIGURED
|
#define DEFAULT_SENSOR_INDOOR_GPIO GPIO_IS_NOT_CONFIGURED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SENSORS_AMOUNT
|
||||||
|
#define SENSORS_AMOUNT 20
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_EXT_PUMP_GPIO
|
#ifndef DEFAULT_EXT_PUMP_GPIO
|
||||||
#define DEFAULT_EXT_PUMP_GPIO GPIO_IS_NOT_CONFIGURED
|
#define DEFAULT_EXT_PUMP_GPIO GPIO_IS_NOT_CONFIGURED
|
||||||
#endif
|
#endif
|
||||||
@@ -133,21 +153,14 @@
|
|||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
#elif !defined(GPIO_IS_VALID_GPIO)
|
#elif !defined(GPIO_IS_VALID_GPIO)
|
||||||
#define GPIO_IS_VALID_GPIO(gpioNum) (gpioNum >= 0 && gpioNum <= 16)
|
#define GPIO_IS_VALID_GPIO(gpioNum) (gpioNum >= 0 && gpioNum <= 17)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
|
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
|
||||||
|
|
||||||
enum class SensorType : byte {
|
enum class UnitSystem : uint8_t {
|
||||||
BOILER,
|
METRIC = 0,
|
||||||
MANUAL,
|
IMPERIAL = 1
|
||||||
DS18B20,
|
|
||||||
BLUETOOTH
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class UnitSystem : byte {
|
|
||||||
METRIC,
|
|
||||||
IMPERIAL
|
|
||||||
};
|
};
|
||||||
|
|
||||||
char buffer[255];
|
char buffer[255];
|
||||||
101
src/main.cpp
101
src/main.cpp
@@ -1,12 +1,18 @@
|
|||||||
|
#define ARDUINOJSON_USE_DOUBLE 0
|
||||||
|
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "defines.h"
|
|
||||||
#include "strings.h"
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <FileData.h>
|
#include <FileData.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include <ESPTelnetStream.h>
|
#include <ESPTelnetStream.h>
|
||||||
#include <TinyLogger.h>
|
#include <TinyLogger.h>
|
||||||
#include <NetworkMgr.h>
|
#include <NetworkMgr.h>
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "strings.h"
|
||||||
|
#include "CrashRecorder.h"
|
||||||
|
#include "Sensors.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
@@ -30,10 +36,13 @@
|
|||||||
using namespace NetworkUtils;
|
using namespace NetworkUtils;
|
||||||
|
|
||||||
// Vars
|
// Vars
|
||||||
FileData fsNetworkSettings(&LittleFS, "/network.conf", 'n', &networkSettings, sizeof(networkSettings), 1000);
|
|
||||||
FileData fsSettings(&LittleFS, "/settings.conf", 's', &settings, sizeof(settings), 60000);
|
|
||||||
ESPTelnetStream* telnetStream = nullptr;
|
ESPTelnetStream* telnetStream = nullptr;
|
||||||
NetworkMgr* network = nullptr;
|
NetworkMgr* network = nullptr;
|
||||||
|
Sensors::Result sensorsResults[SENSORS_AMOUNT];
|
||||||
|
|
||||||
|
FileData fsNetworkSettings(&LittleFS, "/network.conf", 'n', &networkSettings, sizeof(networkSettings), 1000);
|
||||||
|
FileData fsSettings(&LittleFS, "/settings.conf", 's', &settings, sizeof(settings), 60000);
|
||||||
|
FileData fsSensorsSettings(&LittleFS, "/sensors.conf", 'e', &sensorsSettings, sizeof(sensorsSettings), 60000);
|
||||||
|
|
||||||
// Tasks
|
// Tasks
|
||||||
MqttTask* tMqtt;
|
MqttTask* tMqtt;
|
||||||
@@ -45,6 +54,10 @@ MainTask* tMain;
|
|||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
CrashRecorder::init();
|
||||||
|
Sensors::setMaxSensors(SENSORS_AMOUNT);
|
||||||
|
Sensors::settings = sensorsSettings;
|
||||||
|
Sensors::results = sensorsResults;
|
||||||
LittleFS.begin();
|
LittleFS.begin();
|
||||||
|
|
||||||
Log.setLevel(TinyLogger::Level::VERBOSE);
|
Log.setLevel(TinyLogger::Level::VERBOSE);
|
||||||
@@ -62,10 +75,14 @@ void setup() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
#if ARDUINO_USB_MODE
|
||||||
|
Serial.setTxBufferSize(512);
|
||||||
|
#endif
|
||||||
Log.addStream(&Serial);
|
Log.addStream(&Serial);
|
||||||
Log.print("\n\n\r");
|
Log.print("\n\n\r");
|
||||||
|
|
||||||
// network settings
|
//
|
||||||
|
// Network settings
|
||||||
switch (fsNetworkSettings.read()) {
|
switch (fsNetworkSettings.read()) {
|
||||||
case FD_FS_ERR:
|
case FD_FS_ERR:
|
||||||
Log.swarningln(FPSTR(L_NETWORK_SETTINGS), F("Filesystem error, load default"));
|
Log.swarningln(FPSTR(L_NETWORK_SETTINGS), F("Filesystem error, load default"));
|
||||||
@@ -84,7 +101,27 @@ void setup() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// settings
|
network = (new NetworkMgr)
|
||||||
|
->setHostname(networkSettings.hostname)
|
||||||
|
->setStaCredentials(
|
||||||
|
strlen(networkSettings.sta.ssid) ? networkSettings.sta.ssid : nullptr,
|
||||||
|
strlen(networkSettings.sta.password) ? networkSettings.sta.password : nullptr,
|
||||||
|
networkSettings.sta.channel
|
||||||
|
)->setApCredentials(
|
||||||
|
strlen(networkSettings.ap.ssid) ? networkSettings.ap.ssid : nullptr,
|
||||||
|
strlen(networkSettings.ap.password) ? networkSettings.ap.password : nullptr,
|
||||||
|
networkSettings.ap.channel
|
||||||
|
)
|
||||||
|
->setUseDhcp(networkSettings.useDhcp)
|
||||||
|
->setStaticConfig(
|
||||||
|
networkSettings.staticConfig.ip,
|
||||||
|
networkSettings.staticConfig.gateway,
|
||||||
|
networkSettings.staticConfig.subnet,
|
||||||
|
networkSettings.staticConfig.dns
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Settings
|
||||||
switch (fsSettings.read()) {
|
switch (fsSettings.read()) {
|
||||||
case FD_FS_ERR:
|
case FD_FS_ERR:
|
||||||
Log.swarningln(FPSTR(L_SETTINGS), F("Filesystem error, load default"));
|
Log.swarningln(FPSTR(L_SETTINGS), F("Filesystem error, load default"));
|
||||||
@@ -110,8 +147,8 @@ void setup() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// logs
|
// Logs settings
|
||||||
if (!settings.system.serial.enable) {
|
if (!settings.system.serial.enabled) {
|
||||||
Serial.end();
|
Serial.end();
|
||||||
Log.clearStreams();
|
Log.clearStreams();
|
||||||
|
|
||||||
@@ -123,42 +160,44 @@ void setup() {
|
|||||||
Log.addStream(&Serial);
|
Log.addStream(&Serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.system.telnet.enable) {
|
if (settings.system.telnet.enabled) {
|
||||||
telnetStream = new ESPTelnetStream;
|
telnetStream = new ESPTelnetStream;
|
||||||
telnetStream->setKeepAliveInterval(500);
|
telnetStream->setKeepAliveInterval(500);
|
||||||
Log.addStream(telnetStream);
|
Log.addStream(telnetStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.setLevel(settings.system.debug ? TinyLogger::Level::VERBOSE : TinyLogger::Level::INFO);
|
if (settings.system.logLevel >= TinyLogger::Level::SILENT && settings.system.logLevel <= TinyLogger::Level::VERBOSE) {
|
||||||
|
Log.setLevel(static_cast<TinyLogger::Level>(settings.system.logLevel));
|
||||||
|
}
|
||||||
|
|
||||||
// network
|
//
|
||||||
network = (new NetworkMgr)
|
// Sensors settings
|
||||||
->setHostname(networkSettings.hostname)
|
switch (fsSensorsSettings.read()) {
|
||||||
->setStaCredentials(
|
case FD_FS_ERR:
|
||||||
strlen(networkSettings.sta.ssid) ? networkSettings.sta.ssid : nullptr,
|
Log.swarningln(FPSTR(L_SENSORS), F("Filesystem error, load default"));
|
||||||
strlen(networkSettings.sta.password) ? networkSettings.sta.password : nullptr,
|
break;
|
||||||
networkSettings.sta.channel
|
case FD_FILE_ERR:
|
||||||
)->setApCredentials(
|
Log.swarningln(FPSTR(L_SENSORS), F("Bad data, load default"));
|
||||||
strlen(networkSettings.ap.ssid) ? networkSettings.ap.ssid : nullptr,
|
break;
|
||||||
strlen(networkSettings.ap.password) ? networkSettings.ap.password : nullptr,
|
case FD_WRITE:
|
||||||
networkSettings.ap.channel
|
Log.sinfoln(FPSTR(L_SENSORS), F("Not found, load default"));
|
||||||
)
|
break;
|
||||||
->setUseDhcp(networkSettings.useDhcp)
|
case FD_ADD:
|
||||||
->setStaticConfig(
|
case FD_READ:
|
||||||
networkSettings.staticConfig.ip,
|
Log.sinfoln(FPSTR(L_SENSORS), F("Loaded"));
|
||||||
networkSettings.staticConfig.gateway,
|
default:
|
||||||
networkSettings.staticConfig.subnet,
|
break;
|
||||||
networkSettings.staticConfig.dns
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// tasks
|
//
|
||||||
|
// Make tasks
|
||||||
tMqtt = new MqttTask(false, 500);
|
tMqtt = new MqttTask(false, 500);
|
||||||
Scheduler.start(tMqtt);
|
Scheduler.start(tMqtt);
|
||||||
|
|
||||||
tOt = new OpenThermTask(true, 750);
|
tOt = new OpenThermTask(true, 750);
|
||||||
Scheduler.start(tOt);
|
Scheduler.start(tOt);
|
||||||
|
|
||||||
tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL);
|
tSensors = new SensorsTask(true, 1000);
|
||||||
Scheduler.start(tSensors);
|
Scheduler.start(tSensors);
|
||||||
|
|
||||||
tRegulator = new RegulatorTask(true, 10000);
|
tRegulator = new RegulatorTask(true, 10000);
|
||||||
|
|||||||
207
src/strings.h
207
src/strings.h
@@ -3,22 +3,191 @@
|
|||||||
#define PROGMEM
|
#define PROGMEM
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char L_SETTINGS[] PROGMEM = "SETTINGS";
|
const char L_SETTINGS[] PROGMEM = "SETTINGS";
|
||||||
const char L_NETWORK[] PROGMEM = "NETWORK";
|
const char L_SETTINGS_OT[] PROGMEM = "SETTINGS.OT";
|
||||||
const char L_NETWORK_SETTINGS[] PROGMEM = "NETWORK.SETTINGS";
|
const char L_SETTINGS_DHW[] PROGMEM = "SETTINGS.DHW";
|
||||||
const char L_PORTAL_WEBSERVER[] PROGMEM = "PORTAL.WEBSERVER";
|
const char L_SETTINGS_HEATING[] PROGMEM = "SETTINGS.HEATING";
|
||||||
const char L_PORTAL_DNSSERVER[] PROGMEM = "PORTAL.DNSSERVER";
|
const char L_NETWORK[] PROGMEM = "NETWORK";
|
||||||
const char L_PORTAL_CAPTIVE[] PROGMEM = "PORTAL.CAPTIVE";
|
const char L_NETWORK_SETTINGS[] PROGMEM = "NETWORK.SETTINGS";
|
||||||
const char L_PORTAL_OTA[] PROGMEM = "PORTAL.OTA";
|
const char L_PORTAL_WEBSERVER[] PROGMEM = "PORTAL.WEBSERVER";
|
||||||
const char L_MAIN[] PROGMEM = "MAIN";
|
const char L_PORTAL_DNSSERVER[] PROGMEM = "PORTAL.DNSSERVER";
|
||||||
const char L_MQTT[] PROGMEM = "MQTT";
|
const char L_PORTAL_CAPTIVE[] PROGMEM = "PORTAL.CAPTIVE";
|
||||||
const char L_MQTT_MSG[] PROGMEM = "MQTT.MSG";
|
const char L_PORTAL_OTA[] PROGMEM = "PORTAL.OTA";
|
||||||
const char L_OT[] PROGMEM = "OT";
|
const char L_MAIN[] PROGMEM = "MAIN";
|
||||||
const char L_OT_DHW[] PROGMEM = "OT.DHW";
|
const char L_MQTT[] PROGMEM = "MQTT";
|
||||||
const char L_OT_HEATING[] PROGMEM = "OT.HEATING";
|
const char L_MQTT_HA[] PROGMEM = "MQTT.HA";
|
||||||
const char L_SENSORS_OUTDOOR[] PROGMEM = "SENSORS.OUTDOOR";
|
const char L_MQTT_MSG[] PROGMEM = "MQTT.MSG";
|
||||||
const char L_SENSORS_INDOOR[] PROGMEM = "SENSORS.INDOOR";
|
const char L_OT[] PROGMEM = "OT";
|
||||||
const char L_SENSORS_BLE[] PROGMEM = "SENSORS.BLE";
|
const char L_OT_DHW[] PROGMEM = "OT.DHW";
|
||||||
const char L_REGULATOR[] PROGMEM = "REGULATOR";
|
const char L_OT_HEATING[] PROGMEM = "OT.HEATING";
|
||||||
const char L_REGULATOR_PID[] PROGMEM = "REGULATOR.PID";
|
const char L_OT_CH2[] PROGMEM = "OT.CH2";
|
||||||
const char L_REGULATOR_EQUITHERM[] PROGMEM = "REGULATOR.EQUITHERM";
|
const char L_SENSORS[] PROGMEM = "SENSORS";
|
||||||
|
const char L_SENSORS_SETTINGS[] PROGMEM = "SENSORS.SETTINGS";
|
||||||
|
const char L_SENSORS_DALLAS[] PROGMEM = "SENSORS.DALLAS";
|
||||||
|
const char L_SENSORS_NTC[] PROGMEM = "SENSORS.NTC";
|
||||||
|
const char L_SENSORS_BLE[] PROGMEM = "SENSORS.BLE";
|
||||||
|
const char L_REGULATOR[] PROGMEM = "REGULATOR";
|
||||||
|
const char L_REGULATOR_PID[] PROGMEM = "REGULATOR.PID";
|
||||||
|
const char L_REGULATOR_EQUITHERM[] PROGMEM = "REGULATOR.EQUITHERM";
|
||||||
|
const char L_CASCADE_INPUT[] PROGMEM = "CASCADE.INPUT";
|
||||||
|
const char L_CASCADE_OUTPUT[] PROGMEM = "CASCADE.OUTPUT";
|
||||||
|
const char L_EXTPUMP[] PROGMEM = "EXTPUMP";
|
||||||
|
|
||||||
|
|
||||||
|
const char S_ACTIONS[] PROGMEM = "actions";
|
||||||
|
const char S_ACTIVE[] PROGMEM = "active";
|
||||||
|
const char S_ADDRESS[] PROGMEM = "address";
|
||||||
|
const char S_ANTI_STUCK_INTERVAL[] PROGMEM = "antiStuckInterval";
|
||||||
|
const char S_ANTI_STUCK_TIME[] PROGMEM = "antiStuckTime";
|
||||||
|
const char S_AP[] PROGMEM = "ap";
|
||||||
|
const char S_APP_VERSION[] PROGMEM = "appVersion";
|
||||||
|
const char S_AUTH[] PROGMEM = "auth";
|
||||||
|
const char S_BACKTRACE[] PROGMEM = "backtrace";
|
||||||
|
const char S_BATTERY[] PROGMEM = "battery";
|
||||||
|
const char S_BAUDRATE[] PROGMEM = "baudrate";
|
||||||
|
const char S_BLOCKING[] PROGMEM = "blocking";
|
||||||
|
const char S_BSSID[] PROGMEM = "bssid";
|
||||||
|
const char S_BUILD[] PROGMEM = "build";
|
||||||
|
const char S_CASCADE_CONTROL[] PROGMEM = "cascadeControl";
|
||||||
|
const char S_CHANNEL[] PROGMEM = "channel";
|
||||||
|
const char S_CHIP[] PROGMEM = "chip";
|
||||||
|
const char S_CODE[] PROGMEM = "code";
|
||||||
|
const char S_CONNECTED[] PROGMEM = "connected";
|
||||||
|
const char S_CONTINUES[] PROGMEM = "continues";
|
||||||
|
const char S_CORE[] PROGMEM = "core";
|
||||||
|
const char S_CORES[] PROGMEM = "cores";
|
||||||
|
const char S_CRASH[] PROGMEM = "crash";
|
||||||
|
const char S_CURRENT_TEMP[] PROGMEM = "currentTemp";
|
||||||
|
const char S_DATA[] PROGMEM = "data";
|
||||||
|
const char S_DATE[] PROGMEM = "date";
|
||||||
|
const char S_DHW[] PROGMEM = "dhw";
|
||||||
|
const char S_DHW_BLOCKING[] PROGMEM = "dhwBlocking";
|
||||||
|
const char S_DHW_PRESENT[] PROGMEM = "dhwPresent";
|
||||||
|
const char S_DHW_TO_CH2[] PROGMEM = "dhwToCh2";
|
||||||
|
const char S_DIAG[] PROGMEM = "diag";
|
||||||
|
const char S_DNS[] PROGMEM = "dns";
|
||||||
|
const char S_DT[] PROGMEM = "dt";
|
||||||
|
const char S_D_FACTOR[] PROGMEM = "d_factor";
|
||||||
|
const char S_EMERGENCY[] PROGMEM = "emergency";
|
||||||
|
const char S_ENABLED[] PROGMEM = "enabled";
|
||||||
|
const char S_ENV[] PROGMEM = "env";
|
||||||
|
const char S_EPC[] PROGMEM = "epc";
|
||||||
|
const char S_EQUITHERM[] PROGMEM = "equitherm";
|
||||||
|
const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump";
|
||||||
|
const char S_FACTOR[] PROGMEM = "factor";
|
||||||
|
const char S_FAULT[] PROGMEM = "fault";
|
||||||
|
const char S_FILTERING[] PROGMEM = "filtering";
|
||||||
|
const char S_FILTERING_FACTOR[] PROGMEM = "filteringFactor";
|
||||||
|
const char S_FLAGS[] PROGMEM = "flags";
|
||||||
|
const char S_FLAME[] PROGMEM = "flame";
|
||||||
|
const char S_FLASH[] PROGMEM = "flash";
|
||||||
|
const char S_FREE[] PROGMEM = "free";
|
||||||
|
const char S_FREQ[] PROGMEM = "freq";
|
||||||
|
const char S_GATEWAY[] PROGMEM = "gateway";
|
||||||
|
const char S_GET_MIN_MAX_TEMP[] PROGMEM = "getMinMaxTemp";
|
||||||
|
const char S_GPIO[] PROGMEM = "gpio";
|
||||||
|
const char S_HEAP[] PROGMEM = "heap";
|
||||||
|
const char S_HEATING[] PROGMEM = "heating";
|
||||||
|
const char S_HEATING_CH1_TO_CH2[] PROGMEM = "heatingCh1ToCh2";
|
||||||
|
const char S_HEATING_CH2_ENABLED[] PROGMEM = "heatingCh2Enabled";
|
||||||
|
const char S_HIDDEN[] PROGMEM = "hidden";
|
||||||
|
const char S_HOME_ASSISTANT_DISCOVERY[] PROGMEM = "homeAssistantDiscovery";
|
||||||
|
const char S_HOSTNAME[] PROGMEM = "hostname";
|
||||||
|
const char S_HUMIDITY[] PROGMEM = "humidity";
|
||||||
|
const char S_HYSTERESIS[] PROGMEM = "hysteresis";
|
||||||
|
const char S_ID[] PROGMEM = "id";
|
||||||
|
const char S_IMMERGAS_FIX[] PROGMEM = "immergasFix";
|
||||||
|
const char S_INDOOR_TEMP[] PROGMEM = "indoorTemp";
|
||||||
|
const char S_INDOOR_TEMP_CONTROL[] PROGMEM = "indoorTempControl";
|
||||||
|
const char S_IN_GPIO[] PROGMEM = "inGpio";
|
||||||
|
const char S_INPUT[] PROGMEM = "input";
|
||||||
|
const char S_INTERVAL[] PROGMEM = "interval";
|
||||||
|
const char S_INVERT_STATE[] PROGMEM = "invertState";
|
||||||
|
const char S_IP[] PROGMEM = "ip";
|
||||||
|
const char S_I_FACTOR[] PROGMEM = "i_factor";
|
||||||
|
const char S_K_FACTOR[] PROGMEM = "k_factor";
|
||||||
|
const char S_LOGIN[] PROGMEM = "login";
|
||||||
|
const char S_LOG_LEVEL[] PROGMEM = "logLevel";
|
||||||
|
const char S_MAC[] PROGMEM = "mac";
|
||||||
|
const char S_MASTER[] PROGMEM = "master";
|
||||||
|
const char S_MAX[] PROGMEM = "max";
|
||||||
|
const char S_MAX_FREE_BLOCK[] PROGMEM = "maxFreeBlock";
|
||||||
|
const char S_MAX_MODULATION[] PROGMEM = "maxModulation";
|
||||||
|
const char S_MAX_POWER[] PROGMEM = "maxPower";
|
||||||
|
const char S_MAX_TEMP[] PROGMEM = "maxTemp";
|
||||||
|
const char S_MEMBER_ID[] PROGMEM = "memberId";
|
||||||
|
const char S_MIN[] PROGMEM = "min";
|
||||||
|
const char S_MIN_FREE[] PROGMEM = "minFree";
|
||||||
|
const char S_MIN_MAX_FREE_BLOCK[] PROGMEM = "minMaxFreeBlock";
|
||||||
|
const char S_MIN_POWER[] PROGMEM = "minPower";
|
||||||
|
const char S_MIN_TEMP[] PROGMEM = "minTemp";
|
||||||
|
const char S_MODEL[] PROGMEM = "model";
|
||||||
|
const char S_MODULATION[] PROGMEM = "modulation";
|
||||||
|
const char S_MODULATION_SYNC_WITH_HEATING[] PROGMEM = "modulationSyncWithHeating";
|
||||||
|
const char S_MQTT[] PROGMEM = "mqtt";
|
||||||
|
const char S_NAME[] PROGMEM = "name";
|
||||||
|
const char S_NATIVE_HEATING_CONTROL[] PROGMEM = "nativeHeatingControl";
|
||||||
|
const char S_NETWORK[] PROGMEM = "network";
|
||||||
|
const char S_N_FACTOR[] PROGMEM = "n_factor";
|
||||||
|
const char S_OFFSET[] PROGMEM = "offset";
|
||||||
|
const char S_ON_ENABLED_HEATING[] PROGMEM = "onEnabledHeating";
|
||||||
|
const char S_ON_FAULT[] PROGMEM = "onFault";
|
||||||
|
const char S_ON_LOSS_CONNECTION[] PROGMEM = "onLossConnection";
|
||||||
|
const char S_OPENTHERM[] PROGMEM = "opentherm";
|
||||||
|
const char S_OUTDOOR_TEMP[] PROGMEM = "outdoorTemp";
|
||||||
|
const char S_OUT_GPIO[] PROGMEM = "outGpio";
|
||||||
|
const char S_OUTPUT[] PROGMEM = "output";
|
||||||
|
const char S_PASSWORD[] PROGMEM = "password";
|
||||||
|
const char S_PID[] PROGMEM = "pid";
|
||||||
|
const char S_PORT[] PROGMEM = "port";
|
||||||
|
const char S_PORTAL[] PROGMEM = "portal";
|
||||||
|
const char S_POST_CIRCULATION_TIME[] PROGMEM = "postCirculationTime";
|
||||||
|
const char S_POWER[] PROGMEM = "power";
|
||||||
|
const char S_PREFIX[] PROGMEM = "prefix";
|
||||||
|
const char S_PROTOCOL_VERSION[] PROGMEM = "protocolVersion";
|
||||||
|
const char S_PURPOSE[] PROGMEM = "purpose";
|
||||||
|
const char S_P_FACTOR[] PROGMEM = "p_factor";
|
||||||
|
const char S_REAL_SIZE[] PROGMEM = "realSize";
|
||||||
|
const char S_REASON[] PROGMEM = "reason";
|
||||||
|
const char S_RESET_DIAGNOSTIC[] PROGMEM = "resetDiagnostic";
|
||||||
|
const char S_RESET_FAULT[] PROGMEM = "resetFault";
|
||||||
|
const char S_RESET_REASON[] PROGMEM = "resetReason";
|
||||||
|
const char S_RESTART[] PROGMEM = "restart";
|
||||||
|
const char S_RETURN_TEMP[] PROGMEM = "returnTemp";
|
||||||
|
const char S_REV[] PROGMEM = "rev";
|
||||||
|
const char S_RSSI[] PROGMEM = "rssi";
|
||||||
|
const char S_RX_LED_GPIO[] PROGMEM = "rxLedGpio";
|
||||||
|
const char S_SDK[] PROGMEM = "sdk";
|
||||||
|
const char S_SENSORS[] PROGMEM = "sensors";
|
||||||
|
const char S_SERIAL[] PROGMEM = "serial";
|
||||||
|
const char S_SERVER[] PROGMEM = "server";
|
||||||
|
const char S_SETTINGS[] PROGMEM = "settings";
|
||||||
|
const char S_SIGNAL_QUALITY[] PROGMEM = "signalQuality";
|
||||||
|
const char S_SIZE[] PROGMEM = "size";
|
||||||
|
const char S_SLAVE[] PROGMEM = "slave";
|
||||||
|
const char S_SSID[] PROGMEM = "ssid";
|
||||||
|
const char S_STA[] PROGMEM = "sta";
|
||||||
|
const char S_STATE[] PROGMEM = "state";
|
||||||
|
const char S_STATIC_CONFIG[] PROGMEM = "staticConfig";
|
||||||
|
const char S_STATUS_LED_GPIO[] PROGMEM = "statusLedGpio";
|
||||||
|
const char S_SUBNET[] PROGMEM = "subnet";
|
||||||
|
const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode";
|
||||||
|
const char S_SYSTEM[] PROGMEM = "system";
|
||||||
|
const char S_TARGET[] PROGMEM = "target";
|
||||||
|
const char S_TARGET_TEMP[] PROGMEM = "targetTemp";
|
||||||
|
const char S_TELNET[] PROGMEM = "telnet";
|
||||||
|
const char S_TEMPERATURE[] PROGMEM = "temperature";
|
||||||
|
const char S_THRESHOLD_TIME[] PROGMEM = "thresholdTime";
|
||||||
|
const char S_TOTAL[] PROGMEM = "total";
|
||||||
|
const char S_TRESHOLD_TIME[] PROGMEM = "tresholdTime";
|
||||||
|
const char S_TURBO[] PROGMEM = "turbo";
|
||||||
|
const char S_TURBO_FACTOR[] PROGMEM = "turboFactor";
|
||||||
|
const char S_TYPE[] PROGMEM = "type";
|
||||||
|
const char S_T_FACTOR[] PROGMEM = "t_factor";
|
||||||
|
const char S_UNIT_SYSTEM[] PROGMEM = "unitSystem";
|
||||||
|
const char S_UPTIME[] PROGMEM = "uptime";
|
||||||
|
const char S_USE[] PROGMEM = "use";
|
||||||
|
const char S_USE_DHCP[] PROGMEM = "useDhcp";
|
||||||
|
const char S_USER[] PROGMEM = "user";
|
||||||
|
const char S_VALUE[] PROGMEM = "value";
|
||||||
|
const char S_VERSION[] PROGMEM = "version";
|
||||||
|
|||||||
1654
src/utils.h
1654
src/utils.h
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,13 @@
|
|||||||
"releases": "Releases"
|
"releases": "Releases"
|
||||||
},
|
},
|
||||||
"dbm": "dBm",
|
"dbm": "dBm",
|
||||||
|
"kw": "kW",
|
||||||
|
"time": {
|
||||||
|
"days": "d.",
|
||||||
|
"hours": "h.",
|
||||||
|
"min": "min.",
|
||||||
|
"sec": "sec."
|
||||||
|
},
|
||||||
|
|
||||||
"button": {
|
"button": {
|
||||||
"upgrade": "Upgrade",
|
"upgrade": "Upgrade",
|
||||||
@@ -38,7 +45,8 @@
|
|||||||
"title": "Build",
|
"title": "Build",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"sdk": "Core/SDK"
|
"core": "Core",
|
||||||
|
"sdk": "SDK"
|
||||||
},
|
},
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"memory": {
|
"memory": {
|
||||||
@@ -66,8 +74,9 @@
|
|||||||
|
|
||||||
"section": {
|
"section": {
|
||||||
"control": "Control",
|
"control": "Control",
|
||||||
"states": "States and sensors",
|
"states": "States",
|
||||||
"otDiag": "OpenTherm diagnostic"
|
"sensors": "Sensors",
|
||||||
|
"diag": "OpenTherm diagnostic"
|
||||||
},
|
},
|
||||||
|
|
||||||
"thermostat": {
|
"thermostat": {
|
||||||
@@ -78,36 +87,35 @@
|
|||||||
"turbo": "Turbo mode"
|
"turbo": "Turbo mode"
|
||||||
},
|
},
|
||||||
|
|
||||||
"state": {
|
"states": {
|
||||||
"ot": "OpenTherm connected",
|
"mNetworkConnected": "Network connection",
|
||||||
"mqtt": "MQTT connected",
|
"mMqttConnected": "MQTT connection",
|
||||||
"emergency": "Emergency",
|
"mEmergencyState": "Emergency mode",
|
||||||
"heating": "Heating",
|
"mExtPumpState": "External pump",
|
||||||
"dhw": "DHW",
|
"mCascadeControlInput": "Cascade control (input)",
|
||||||
"flame": "Flame",
|
"mCascadeControlOutput": "Cascade control (output)",
|
||||||
"fault": "Fault",
|
|
||||||
"diag": "Diagnostic",
|
"sConnected": "OpenTherm connection",
|
||||||
"extpump": "External pump",
|
"sFlame": "Flame",
|
||||||
"outdoorSensorConnected": "Outdoor sensor connected",
|
"sFaultActive": "Fault",
|
||||||
"outdoorSensorRssi": "Outdoor sensor RSSI",
|
"sFaultCode": "Faul code",
|
||||||
"outdoorSensorHumidity": "Outdoor sensor humidity",
|
"sDiagActive": "Diagnostic",
|
||||||
"outdoorSensorBattery": "Outdoor sensor battery",
|
"sDiagCode": "Diagnostic code",
|
||||||
"indoorSensorConnected": "Indoor sensor connected",
|
|
||||||
"indoorSensorRssi": "Indoor sensor RSSI",
|
"mHeatEnabled": "Heating enabled",
|
||||||
"indoorSensorHumidity": "Indoor sensor humidity",
|
"mHeatBlocking": "Heating blocked",
|
||||||
"indoorSensorBattery": "Indoor sensor battery",
|
"sHeatActive": "Heating active",
|
||||||
"modulation": "Modulation",
|
"mHeatTargetTemp": "Heating setpoint temp",
|
||||||
"pressure": "Pressure",
|
"mHeatCurrTemp": "Heating current temp",
|
||||||
"dhwFlowRate": "DHW flow rate",
|
"mHeatRetTemp": "Heating return temp",
|
||||||
"faultCode": "Fault code",
|
"mHeatIndoorTemp": "Heating, indoor temp",
|
||||||
"diagCode": "Diagnostic code",
|
"mHeatOutdoorTemp": "Heating, outdoor temp",
|
||||||
"indoorTemp": "Indoor temp",
|
|
||||||
"outdoorTemp": "Outdoor temp",
|
"mDhwEnabled": "DHW enabled",
|
||||||
"heatingTemp": "Heating temp",
|
"sDhwActive": "DHW active",
|
||||||
"heatingSetpointTemp": "Heating setpoint temp",
|
"mDhwTargetTemp": "DHW setpoint temp",
|
||||||
"heatingReturnTemp": "Heating return temp",
|
"mDhwCurrTemp": "DHW current temp",
|
||||||
"dhwTemp": "DHW temp",
|
"mDhwRetTemp": "DHW return temp"
|
||||||
"exhaustTemp": "Exhaust temp"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -150,6 +158,76 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"sensors": {
|
||||||
|
"title": "Sensors settings - OpenTherm Gateway",
|
||||||
|
"name": "Sensors settings",
|
||||||
|
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"sensorName": {
|
||||||
|
"title": "Sensor name",
|
||||||
|
"note": "May only contain: a-z, A-Z, 0-9, _ and space"
|
||||||
|
},
|
||||||
|
"purpose": "Purpose",
|
||||||
|
"purposes": {
|
||||||
|
"outdoorTemp": "Outdoor temperature",
|
||||||
|
"indoorTemp": "Indoor temperature",
|
||||||
|
"heatTemp": "Heating, temperature",
|
||||||
|
"heatRetTemp": "Heating, return temperature",
|
||||||
|
"dhwTemp": "DHW, temperature",
|
||||||
|
"dhwRetTemp": "DHW, return temperature",
|
||||||
|
"dhwFlowRate": "DHW, flow rate",
|
||||||
|
"exhaustTemp": "Exhaust temperature",
|
||||||
|
"modLevel": "Modulation level (in percents)",
|
||||||
|
"currentPower": "Current power (in kWt)",
|
||||||
|
"pressure": "Pressure",
|
||||||
|
"humidity": "Humidity",
|
||||||
|
"temperature": "Temperature",
|
||||||
|
"notConfigured": "Not configured"
|
||||||
|
},
|
||||||
|
"type": "Type/source",
|
||||||
|
"types": {
|
||||||
|
"otOutdoorTemp": "OpenTherm, outdoor temp",
|
||||||
|
"otHeatTemp": "OpenTherm, heating, temp",
|
||||||
|
"otHeatRetTemp": "OpenTherm, heating, return temp",
|
||||||
|
"otDhwTemp": "OpenTherm, DHW, temperature",
|
||||||
|
"otDhwTemp2": "OpenTherm, DHW, temperature 2",
|
||||||
|
"otDhwFlowRate": "OpenTherm, DHW, flow rate",
|
||||||
|
"otCh2Temp": "OpenTherm, channel 2, temp",
|
||||||
|
"otExhaustTemp": "OpenTherm, exhaust temp",
|
||||||
|
"otHeatExchangerTemp": "OpenTherm, heat exchanger temp",
|
||||||
|
"otPressure": "OpenTherm, pressure",
|
||||||
|
"otModLevel": "OpenTherm, modulation level",
|
||||||
|
"otCurrentPower": "OpenTherm, current power",
|
||||||
|
"ntcTemp": "NTC sensor",
|
||||||
|
"dallasTemp": "DALLAS sensor",
|
||||||
|
"bluetooth": "BLE sensor",
|
||||||
|
"heatSetpointTemp": "Heating, setpoint temp",
|
||||||
|
"manual": "Manual via MQTT/API",
|
||||||
|
"notConfigured": "Not configured"
|
||||||
|
},
|
||||||
|
"gpio": "GPIO",
|
||||||
|
"address": {
|
||||||
|
"title": "Sensor address",
|
||||||
|
"note": "For auto detection of DALLAS sensors leave it at default, for BLE devices need a MAC address"
|
||||||
|
},
|
||||||
|
"correction": {
|
||||||
|
"desc": "Correction of values",
|
||||||
|
"offset": "Compensation (offset)",
|
||||||
|
"factor": "Multiplier"
|
||||||
|
},
|
||||||
|
"filtering": {
|
||||||
|
"desc": "Filtering values",
|
||||||
|
"enabled": {
|
||||||
|
"title": "Enabled filtering",
|
||||||
|
"note": "It can be useful if there is a lot of sharp noise on the charts. The filter used is \"Running Average\"."
|
||||||
|
},
|
||||||
|
"factor": {
|
||||||
|
"title": "Filtration factor",
|
||||||
|
"note": "The lower the value, the smoother and <u>longer</u> the change in numeric values."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings - OpenTherm Gateway",
|
"title": "Settings - OpenTherm Gateway",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
@@ -161,16 +239,12 @@
|
|||||||
"heating": "Heating settings",
|
"heating": "Heating settings",
|
||||||
"dhw": "DHW settings",
|
"dhw": "DHW settings",
|
||||||
"emergency": "Emergency mode settings",
|
"emergency": "Emergency mode settings",
|
||||||
"emergency.events": "Events",
|
|
||||||
"emergency.regulators": "Using regulators",
|
|
||||||
"equitherm": "Equitherm settings",
|
"equitherm": "Equitherm settings",
|
||||||
"pid": "PID settings",
|
"pid": "PID settings",
|
||||||
"ot": "OpenTherm settings",
|
"ot": "OpenTherm settings",
|
||||||
"ot.options": "Options",
|
|
||||||
"mqtt": "MQTT settings",
|
"mqtt": "MQTT settings",
|
||||||
"outdorSensor": "Outdoor sensor settings",
|
"extPump": "External pump settings",
|
||||||
"indoorSensor": "Indoor sensor settings",
|
"cascadeControl": "Cascade control settings"
|
||||||
"extPump": "External pump settings"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"enable": "Enable",
|
"enable": "Enable",
|
||||||
@@ -196,16 +270,13 @@
|
|||||||
"metric": "Metric <small>(celsius, liters, bar)</small>",
|
"metric": "Metric <small>(celsius, liters, bar)</small>",
|
||||||
"imperial": "Imperial <small>(fahrenheit, gallons, psi)</small>",
|
"imperial": "Imperial <small>(fahrenheit, gallons, psi)</small>",
|
||||||
"statusLedGpio": "Status LED GPIO",
|
"statusLedGpio": "Status LED GPIO",
|
||||||
"debug": "Debug mode",
|
"logLevel": "Log level",
|
||||||
"serial": {
|
"serial": {
|
||||||
"enable": "Enable Serial port",
|
"enable": "Enabled Serial port",
|
||||||
"baud": {
|
"baud": "Serial port baud rate"
|
||||||
"title": "Serial port baud rate",
|
|
||||||
"note": "Available: 9600, 19200, 38400, 57600, 74880, 115200"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"telnet": {
|
"telnet": {
|
||||||
"enable": "Enable Telnet",
|
"enable": "Enabled Telnet",
|
||||||
"port": {
|
"port": {
|
||||||
"title": "Telnet port",
|
"title": "Telnet port",
|
||||||
"note": "Default: 23"
|
"note": "Default: 23"
|
||||||
@@ -215,29 +286,17 @@
|
|||||||
|
|
||||||
"heating": {
|
"heating": {
|
||||||
"hyst": "Hysteresis <small>(in degrees)</small>",
|
"hyst": "Hysteresis <small>(in degrees)</small>",
|
||||||
"maxMod": "Max modulation level"
|
"turboFactor": "Turbo mode coeff."
|
||||||
},
|
},
|
||||||
|
|
||||||
"emergency": {
|
"emergency": {
|
||||||
"desc": "<b>!</b> Emergency mode can be useful <u>only</u> when using Equitherm and/or PID (when normal work) and when reporting indoor/outdoor temperature via MQTT/API/BLE. In this mode, sensor values that are reported via MQTT/API/BLE are not used.",
|
"desc": "Emergency mode is activated automatically when «PID» or «Equitherm» cannot calculate the heat carrier setpoint:<br />- if «Equitherm» is enabled and the outdoor temperature sensor is disconnected;<br />- if «PID» or OT option <i>«Native heating control»</i> is enabled and the indoor temperature sensor is disconnected.<br /><b>Note:</b> On network fault or MQTT fault, sensors with <i>«Manual via MQTT/API»</i> type will be in DISCONNECTED state.",
|
||||||
|
|
||||||
"target": {
|
"target": {
|
||||||
"title": "Target temperature",
|
"title": "Target temperature",
|
||||||
"note": "<u>Indoor temperature</u> if Equitherm or PID is <b>enabled</b><br /><u>Heat carrier temperature</u> if Equitherm and PID <b>is disabled</b>"
|
"note": "<b>Important:</b> <u>Target indoor temperature</u> if OT option <i>«Native heating control»</i> is enabled.<br />In all other cases, the <u>target heat carrier temperature</u>."
|
||||||
},
|
},
|
||||||
"treshold": "Treshold time <small>(sec)</small>",
|
"treshold": "Treshold time <small>(sec)</small>"
|
||||||
|
|
||||||
"events": {
|
|
||||||
"network": "On network fault",
|
|
||||||
"mqtt": "On MQTT fault",
|
|
||||||
"indoorSensorDisconnect": "On loss connection with indoor sensor",
|
|
||||||
"outdoorSensorDisconnect": "On loss connection with outdoor sensor"
|
|
||||||
},
|
|
||||||
|
|
||||||
"regulators": {
|
|
||||||
"equitherm": "Equitherm <small>(requires at least an external (DS18B20) or boiler <u>outdoor</u> sensor)</small>",
|
|
||||||
"pid": "PID <small>(requires at least an external (DS18B20) <u>indoor</u> sensor)</small>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"equitherm": {
|
"equitherm": {
|
||||||
@@ -253,35 +312,29 @@
|
|||||||
"p": "P factor",
|
"p": "P factor",
|
||||||
"i": "I factor",
|
"i": "I factor",
|
||||||
"d": "D factor",
|
"d": "D factor",
|
||||||
"dt": "DT <small>in seconds</small>"
|
"dt": "DT <small>in seconds</small>",
|
||||||
|
"noteMinMaxTemp": "<b>Important:</b> When using «Equitherm» and «PID» at the same time, the min and max temperatures limit the influence on the «Equitherm» result temperature.<br />Thus, if the min temperature is set to -15 and the max temperature is set to 15, then the final heat carrier setpoint will be from <code>equitherm_result - 15</code> to <code>equitherm_result + 15</code>."
|
||||||
},
|
},
|
||||||
|
|
||||||
"ot": {
|
"ot": {
|
||||||
|
"advanced": "Advanced Settings",
|
||||||
"inGpio": "In GPIO",
|
"inGpio": "In GPIO",
|
||||||
"outGpio": "Out GPIO",
|
"outGpio": "Out GPIO",
|
||||||
"ledGpio": "RX LED GPIO",
|
"ledGpio": "RX LED GPIO",
|
||||||
"memberIdCode": "Master MemberID code",
|
"memberId": "Master member ID",
|
||||||
"pressureFactor": {
|
"flags": "Master flags",
|
||||||
"title": "Coeff. pressure correction",
|
"maxMod": "Max modulation level",
|
||||||
"note": "If the pressure displayed is <b>X10</b> from the real one, set the <b>0.1</b>."
|
"minPower": {
|
||||||
|
"title": "Min boiler power <small>(kW)</small>",
|
||||||
|
"note": "This value is at 0-1% boiler modulation level. Typically found in the boiler specification as \"minimum useful heat output\"."
|
||||||
},
|
},
|
||||||
"dhwFlowRateFactor": {
|
"maxPower": {
|
||||||
"title": "Coeff. DHW flow rate correction",
|
"title": "Max boiler power <small>(kW)</small>",
|
||||||
"note": "If the DHW flow rate displayed is <b>X10</b> from the real one, set the <b>0.1</b>."
|
"note": "<b>0</b> - try detect automatically. Typically found in the boiler specification as \"maximum useful heat output\"."
|
||||||
},
|
|
||||||
"fnv": {
|
|
||||||
"title": "Filtering numeric values",
|
|
||||||
"enable": {
|
|
||||||
"title": "Enable filtering",
|
|
||||||
"note": "It can be useful if there is a lot of sharp noise on the charts. The filter used is \"Running Average\"."
|
|
||||||
},
|
|
||||||
"factor": {
|
|
||||||
"title": "Filtration coeff.",
|
|
||||||
"note": "The lower the value, the smoother and <u>longer</u> the change in numeric values."
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"options": {
|
"options": {
|
||||||
|
"desc": "Options",
|
||||||
"dhwPresent": "DHW present",
|
"dhwPresent": "DHW present",
|
||||||
"summerWinterMode": "Summer/winter mode",
|
"summerWinterMode": "Summer/winter mode",
|
||||||
"heatingCh2Enabled": "Heating CH2 always enabled",
|
"heatingCh2Enabled": "Heating CH2 always enabled",
|
||||||
@@ -293,15 +346,9 @@
|
|||||||
"immergasFix": "Fix for Immergas boilers"
|
"immergasFix": "Fix for Immergas boilers"
|
||||||
},
|
},
|
||||||
|
|
||||||
"faultState": {
|
|
||||||
"gpio": "Fault state GPIO",
|
|
||||||
"note": "Can be useful to switch on another boiler <u>via relay</u>. Blank - not use.",
|
|
||||||
"invert": "Invert fault state"
|
|
||||||
},
|
|
||||||
|
|
||||||
"nativeHeating": {
|
"nativeHeating": {
|
||||||
"title": "Native heating control (boiler)",
|
"title": "Native heating control (boiler)",
|
||||||
"note": "Works <u>ONLY</u> if the boiler requires the desired room temperature and regulates the temperature of the coolant itself. Not compatible with PID and Equitherm regulators and hysteresis in firmware."
|
"note": "Works <u>ONLY</u> if the boiler requires the desired room temperature and regulates the temperature of the coolant itself. Not compatible with PID and Equitherm regulators in firmware."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -315,25 +362,35 @@
|
|||||||
"interval": "Publish interval <small>(sec)</small>"
|
"interval": "Publish interval <small>(sec)</small>"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tempSensor": {
|
|
||||||
"source": {
|
|
||||||
"type": "Source type",
|
|
||||||
"boiler": "From boiler via OpenTherm",
|
|
||||||
"manual": "Manual via MQTT/API",
|
|
||||||
"ext": "External (DS18B20)",
|
|
||||||
"ble": "BLE device"
|
|
||||||
},
|
|
||||||
"gpio": "GPIO",
|
|
||||||
"offset": "Temp offset <small>(calibration)</small>",
|
|
||||||
"bleAddress": "BLE device MAC address"
|
|
||||||
},
|
|
||||||
|
|
||||||
"extPump": {
|
"extPump": {
|
||||||
"use": "Use external pump",
|
"use": "Use external pump",
|
||||||
"gpio": "Relay GPIO",
|
"gpio": "Relay GPIO",
|
||||||
"postCirculationTime": "Post circulation time <small>(min)</small>",
|
"postCirculationTime": "Post circulation time <small>(min)</small>",
|
||||||
"antiStuckInterval": "Anti stuck interval <small>(days)</small>",
|
"antiStuckInterval": "Anti stuck interval <small>(days)</small>",
|
||||||
"antiStuckTime": "Anti stuck time <small>(min)</small>"
|
"antiStuckTime": "Anti stuck time <small>(min)</small>"
|
||||||
|
},
|
||||||
|
|
||||||
|
"cascadeControl": {
|
||||||
|
"input": {
|
||||||
|
"desc": "Can be used to turn on the heating only if another boiler is faulty. The other boiler controller must change the state of the GPIO input in the event of a fault.",
|
||||||
|
"enable": "Enabled input",
|
||||||
|
"gpio": "GPIO",
|
||||||
|
"invertState": "Invert GPIO state",
|
||||||
|
"thresholdTime": "State change threshold time <small>(sec)</small>"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"desc": "Can be used to switch on another boiler <u>via relay</u>.",
|
||||||
|
"enable": "Enabled output",
|
||||||
|
"gpio": "GPIO",
|
||||||
|
"invertState": "Invert GPIO state",
|
||||||
|
"thresholdTime": "State change threshold time <small>(sec)</small>",
|
||||||
|
"events": {
|
||||||
|
"desc": "Events",
|
||||||
|
"onFault": "If the fault state is active",
|
||||||
|
"onLossConnection": "If the connection via Opentherm is lost",
|
||||||
|
"onEnabledHeating": "If heating is enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,13 @@
|
|||||||
"releases": "Релизы"
|
"releases": "Релизы"
|
||||||
},
|
},
|
||||||
"dbm": "дБм",
|
"dbm": "дБм",
|
||||||
|
"kw": "кВт",
|
||||||
|
"time": {
|
||||||
|
"days": "д.",
|
||||||
|
"hours": "ч.",
|
||||||
|
"min": "мин.",
|
||||||
|
"sec": "сек."
|
||||||
|
},
|
||||||
|
|
||||||
"button": {
|
"button": {
|
||||||
"upgrade": "Обновить",
|
"upgrade": "Обновить",
|
||||||
@@ -38,7 +45,8 @@
|
|||||||
"title": "Билд",
|
"title": "Билд",
|
||||||
"version": "Версия",
|
"version": "Версия",
|
||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"sdk": "Ядро/SDK"
|
"core": "Ядро",
|
||||||
|
"sdk": "SDK"
|
||||||
},
|
},
|
||||||
"uptime": "Аптайм",
|
"uptime": "Аптайм",
|
||||||
"memory": {
|
"memory": {
|
||||||
@@ -66,8 +74,9 @@
|
|||||||
|
|
||||||
"section": {
|
"section": {
|
||||||
"control": "Управление",
|
"control": "Управление",
|
||||||
"states": "Состояние и сенсоры",
|
"states": "Состояние",
|
||||||
"otDiag": "Диагностика OpenTherm"
|
"sensors": "Сенсоры",
|
||||||
|
"diag": "Диагностика OpenTherm"
|
||||||
},
|
},
|
||||||
|
|
||||||
"thermostat": {
|
"thermostat": {
|
||||||
@@ -78,36 +87,35 @@
|
|||||||
"turbo": "Турбо"
|
"turbo": "Турбо"
|
||||||
},
|
},
|
||||||
|
|
||||||
"state": {
|
"states": {
|
||||||
"ot": "OpenTherm подключение",
|
"mNetworkConnected": "Подключение к сети",
|
||||||
"mqtt": "MQTT подключение",
|
"mMqttConnected": "Подключение к MQTT",
|
||||||
"emergency": "Аварийный режим",
|
"mEmergencyState": "Аварийный режим",
|
||||||
"heating": "Отопление",
|
"mExtPumpState": "Внешний насос",
|
||||||
"dhw": "ГВС",
|
"mCascadeControlInput": "Каскадное управление (вход)",
|
||||||
"flame": "Пламя",
|
"mCascadeControlOutput": "Каскадное управление (выход)",
|
||||||
"fault": "Ошибка",
|
|
||||||
"diag": "Диагностика",
|
"sConnected": "Подключение к OpenTherm",
|
||||||
"extpump": "Внешний насос",
|
"sFlame": "Пламя",
|
||||||
"outdoorSensorConnected": "Датчик наруж. темп.",
|
"sFaultActive": "Ошибка",
|
||||||
"outdoorSensorRssi": "RSSI датчика наруж. темп.",
|
"sFaultCode": "Код ошибки",
|
||||||
"outdoorSensorHumidity": "Влажность с наруж. датчика темп.",
|
"sDiagActive": "Диагностика",
|
||||||
"outdoorSensorBattery": "Заряд наруж. датчика темп.",
|
"sDiagCode": "Диагностический код",
|
||||||
"indoorSensorConnected": "Датчик внутр. темп.",
|
|
||||||
"indoorSensorRssi": "RSSI датчика внутр. темп.",
|
"mHeatEnabled": "Отопление",
|
||||||
"indoorSensorHumidity": "Влажность с внутр. датчика темп.",
|
"mHeatBlocking": "Блокировка отопления",
|
||||||
"indoorSensorBattery": "Заряд внутр. датчика темп.",
|
"sHeatActive": "Активность отопления",
|
||||||
"modulation": "Уровень модуляции",
|
"mHeatTargetTemp": "Отопление, целевая температура",
|
||||||
"pressure": "Давление",
|
"mHeatCurrTemp": "Отопление, текущая температура",
|
||||||
"dhwFlowRate": "Расход ГВС",
|
"mHeatRetTemp": "Отопление, температура обратки",
|
||||||
"faultCode": "Код ошибки",
|
"mHeatIndoorTemp": "Отопление, внутренняя темп.",
|
||||||
"diagCode": "Диагностический код",
|
"mHeatOutdoorTemp": "Отопление, наружная темп.",
|
||||||
"indoorTemp": "Внутренняя темп.",
|
|
||||||
"outdoorTemp": "Наружная темп.",
|
"mDhwEnabled": "ГВС",
|
||||||
"heatingTemp": "Темп. отопления",
|
"sDhwActive": "Активность ГВС",
|
||||||
"heatingSetpointTemp": "Уставка темп. отопления",
|
"mDhwTargetTemp": "ГВС, целевая температура",
|
||||||
"heatingReturnTemp": "Темп. обратки отопления",
|
"mDhwCurrTemp": "ГВС, текущая температура",
|
||||||
"dhwTemp": "Темп. ГВС",
|
"mDhwRetTemp": "ГВС, температура обратки"
|
||||||
"exhaustTemp": "Темп. выхлопных газов"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -150,6 +158,76 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"sensors": {
|
||||||
|
"title": "Настройки сенсоров - OpenTherm Gateway",
|
||||||
|
"name": "Настройки сенсоров",
|
||||||
|
|
||||||
|
"enabled": "Включить и использовать",
|
||||||
|
"sensorName": {
|
||||||
|
"title": "Имя сенсора",
|
||||||
|
"note": "Может содержать только: a-z, A-Z, 0-9, _ и пробел"
|
||||||
|
},
|
||||||
|
"purpose": "Назначение",
|
||||||
|
"purposes": {
|
||||||
|
"outdoorTemp": "Внешняя температура",
|
||||||
|
"indoorTemp": "Внутреняя температура",
|
||||||
|
"heatTemp": "Отопление, температура",
|
||||||
|
"heatRetTemp": "Отопление, температура обратки",
|
||||||
|
"dhwTemp": "ГВС, температура",
|
||||||
|
"dhwRetTemp": "ГВС, температура обратки",
|
||||||
|
"dhwFlowRate": "ГВС, расход/скорость потока",
|
||||||
|
"exhaustTemp": "Температура выхлопных газов",
|
||||||
|
"modLevel": "Уровень модуляции (в процентах)",
|
||||||
|
"currentPower": "Текущая мощность (в кВт)",
|
||||||
|
"pressure": "Давление",
|
||||||
|
"humidity": "Влажность",
|
||||||
|
"temperature": "Температура",
|
||||||
|
"notConfigured": "Не сконфигурировано"
|
||||||
|
},
|
||||||
|
"type": "Тип/источник",
|
||||||
|
"types": {
|
||||||
|
"otOutdoorTemp": "OpenTherm, внешняя температура",
|
||||||
|
"otHeatTemp": "OpenTherm, отопление, температура",
|
||||||
|
"otHeatRetTemp": "OpenTherm, отопление, температура обратки",
|
||||||
|
"otDhwTemp": "OpenTherm, ГВС, температура",
|
||||||
|
"otDhwTemp2": "OpenTherm, ГВС, температура 2",
|
||||||
|
"otDhwFlowRate": "OpenTherm, ГВС, расход/скорость потока",
|
||||||
|
"otCh2Temp": "OpenTherm, канал 2, температура",
|
||||||
|
"otExhaustTemp": "OpenTherm, температура выхлопных газов",
|
||||||
|
"otHeatExchangerTemp": "OpenTherm, температура теплообменника",
|
||||||
|
"otPressure": "OpenTherm, давление",
|
||||||
|
"otModLevel": "OpenTherm, уровень модуляции",
|
||||||
|
"otCurrentPower": "OpenTherm, текущая мощность",
|
||||||
|
"ntcTemp": "NTC датчик",
|
||||||
|
"dallasTemp": "DALLAS датчик",
|
||||||
|
"bluetooth": "BLE датчик",
|
||||||
|
"heatSetpointTemp": "Отопление, температура уставки",
|
||||||
|
"manual": "Вручную через MQTT/API",
|
||||||
|
"notConfigured": "Не сконфигурировано"
|
||||||
|
},
|
||||||
|
"gpio": "GPIO датчика",
|
||||||
|
"address": {
|
||||||
|
"title": "Адрес датчика",
|
||||||
|
"note": "Для DALLAS датчиков оставьте по умолчанию для автоопределения, для BLE устройств необходимо указать MAC адрес"
|
||||||
|
},
|
||||||
|
"correction": {
|
||||||
|
"desc": "Коррекция показаний",
|
||||||
|
"offset": "Компенсация (смещение)",
|
||||||
|
"factor": "Множитель"
|
||||||
|
},
|
||||||
|
"filtering": {
|
||||||
|
"desc": "Фильтрация показаний",
|
||||||
|
"enabled": {
|
||||||
|
"title": "Включить фильтрацию",
|
||||||
|
"note": "Может быть полезно, если на графиках много резкого шума. В качестве фильтра используется \"бегущее среднее\"."
|
||||||
|
},
|
||||||
|
"factor": {
|
||||||
|
"title": "Коэфф. фильтрации",
|
||||||
|
"note": "Чем меньше коэф., тем плавнее и <u>дольше</u> изменение числовых значений."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Настройки - OpenTherm Gateway",
|
"title": "Настройки - OpenTherm Gateway",
|
||||||
"name": "Настройки",
|
"name": "Настройки",
|
||||||
@@ -161,16 +239,12 @@
|
|||||||
"heating": "Настройки отопления",
|
"heating": "Настройки отопления",
|
||||||
"dhw": "Настройки ГВС",
|
"dhw": "Настройки ГВС",
|
||||||
"emergency": "Настройки аварийного режима",
|
"emergency": "Настройки аварийного режима",
|
||||||
"emergency.events": "События",
|
|
||||||
"emergency.regulators": "Используемые регуляторы",
|
|
||||||
"equitherm": "Настройки ПЗА",
|
"equitherm": "Настройки ПЗА",
|
||||||
"pid": "Настройки ПИД",
|
"pid": "Настройки ПИД",
|
||||||
"ot": "Настройки OpenTherm",
|
"ot": "Настройки OpenTherm",
|
||||||
"ot.options": "Опции",
|
|
||||||
"mqtt": "Настройки MQTT",
|
"mqtt": "Настройки MQTT",
|
||||||
"outdorSensor": "Настройки наружного датчика температуры",
|
"extPump": "Настройки дополнительного насоса",
|
||||||
"indoorSensor": "Настройки внутреннего датчика температуры",
|
"cascadeControl": "Настройки каскадного управления"
|
||||||
"extPump": "Настройки дополнительного насоса"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"enable": "Вкл",
|
"enable": "Вкл",
|
||||||
@@ -196,13 +270,10 @@
|
|||||||
"metric": "Метрическая <small>(цильсии, литры, бары)</small>",
|
"metric": "Метрическая <small>(цильсии, литры, бары)</small>",
|
||||||
"imperial": "Imperial <small>(фаренгейты, галлоны, psi)</small>",
|
"imperial": "Imperial <small>(фаренгейты, галлоны, psi)</small>",
|
||||||
"statusLedGpio": "Статус LED GPIO",
|
"statusLedGpio": "Статус LED GPIO",
|
||||||
"debug": "Отладка",
|
"logLevel": "Уровень логирования",
|
||||||
"serial": {
|
"serial": {
|
||||||
"enable": "Вкл. Serial порт",
|
"enable": "Вкл. Serial порт",
|
||||||
"baud": {
|
"baud": "Скорость Serial порта"
|
||||||
"title": "Скорость Serial порта",
|
|
||||||
"note": "Доступно: 9600, 19200, 38400, 57600, 74880, 115200"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"telnet": {
|
"telnet": {
|
||||||
"enable": "Вкл. Telnet",
|
"enable": "Вкл. Telnet",
|
||||||
@@ -215,29 +286,17 @@
|
|||||||
|
|
||||||
"heating": {
|
"heating": {
|
||||||
"hyst": "Гистерезис <small>(в градусах)</small>",
|
"hyst": "Гистерезис <small>(в градусах)</small>",
|
||||||
"maxMod": "Макс. уровень модуляции"
|
"turboFactor": "Коэфф. турбо режима"
|
||||||
},
|
},
|
||||||
|
|
||||||
"emergency": {
|
"emergency": {
|
||||||
"desc": "<b>!</b> Аварийный режим может быть полезен <u>только</u> при использовании ПЗА и/или ПИД и при передачи наружной/внутренней температуры через MQTT/API/BLE. В этом режиме значения датчиков, передаваемые через MQTT/API/BLE, не используются.",
|
"desc": "Аварийный режим активируется автоматически, если «ПИД» или «ПЗА» не могут рассчитать уставку теплоносителя:<br />- если «ПЗА» включен и датчик наружной температуры отключен;<br />- если включен «ПИД» или OT опция <i>«Передать управление отоплением котлу»</i> и датчик внутренней температуры отключен.<br /><b>Примечание:</b> При сбое сети или MQTT датчики с типом <i>«Вручную через MQTT/API»</i> будут находиться в состоянии ОТКЛЮЧЕН.",
|
||||||
|
|
||||||
"target": {
|
"target": {
|
||||||
"title": "Целевая температура",
|
"title": "Целевая температура",
|
||||||
"note": "Целевая <u>внутренняя температура</u> если ПЗА и/или ПИД <b>включены</b><br />Целевая <u>температура теплоносителя</u> если ПЗА и ПИД <b>выключены</b>"
|
"note": "<b>Важно:</b> <u>Целевая температура в помещении</u>, если включена ОТ опция <i>«Передать управление отоплением котлу»</i>.<br />Во всех остальных случаях <u>целевая температура теплоносителя</u>."
|
||||||
},
|
},
|
||||||
"treshold": "Пороговое время включения <small>(сек)</small>",
|
"treshold": "Пороговое время включения <small>(сек)</small>"
|
||||||
|
|
||||||
"events": {
|
|
||||||
"network": "При отключении сети",
|
|
||||||
"mqtt": "При отключении MQTT",
|
|
||||||
"indoorSensorDisconnect": "При потере связи с датчиком внутренней темп.",
|
|
||||||
"outdoorSensorDisconnect": "При потере связи с датчиком наружной темп."
|
|
||||||
},
|
|
||||||
|
|
||||||
"regulators": {
|
|
||||||
"equitherm": "ПЗА <small>(требуется внешний (DS18B20) или подключенный к котлу датчик <u>наружной</u> температуры)</small>",
|
|
||||||
"pid": "ПИД <small>(требуется внешний (DS18B20) датчик <u>внутренней</u> температуры)</small>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"equitherm": {
|
"equitherm": {
|
||||||
@@ -253,35 +312,29 @@
|
|||||||
"p": "Коэффициент P",
|
"p": "Коэффициент P",
|
||||||
"i": "Коэффициент I",
|
"i": "Коэффициент I",
|
||||||
"d": "Коэффициент D",
|
"d": "Коэффициент D",
|
||||||
"dt": "DT <small>(сек)</small>"
|
"dt": "DT <small>(сек)</small>",
|
||||||
|
"noteMinMaxTemp": "<b>Важно:</b> При использовании «ПЗА» и «ПИД» одновременно, мин. и макс. температура ограничивает влияние на расчётную температуру «ПЗА».<br />Таким образом, если мин. температура задана как -15, а макс. как 15, то конечная температура теплоносителя будет от <code>equitherm_result - 15</code> до <code>equitherm_result + 15</code>."
|
||||||
},
|
},
|
||||||
|
|
||||||
"ot": {
|
"ot": {
|
||||||
|
"advanced": "Дополнительные настройки",
|
||||||
"inGpio": "Вход GPIO",
|
"inGpio": "Вход GPIO",
|
||||||
"outGpio": "Выход GPIO",
|
"outGpio": "Выход GPIO",
|
||||||
"ledGpio": "RX LED GPIO",
|
"ledGpio": "RX LED GPIO",
|
||||||
"memberIdCode": "Master MemberID код",
|
"memberId": "Master member ID",
|
||||||
"pressureFactor": {
|
"flags": "Master flags",
|
||||||
"title": "Коэфф. коррекции давления",
|
"maxMod": "Макс. уровень модуляции",
|
||||||
"note": "Если давление отображается <b>Х10</b> от реального, установите значение <b>0.1</b>."
|
"minPower": {
|
||||||
|
"title": "Мин. мощность котла <small>(кВт)</small>",
|
||||||
|
"note": "Это значение соответствует уровню модуляции котла 0–1%. Обычно можно найти в спецификации котла как \"минимальная полезная тепловая мощность\"."
|
||||||
},
|
},
|
||||||
"dhwFlowRateFactor": {
|
"maxPower": {
|
||||||
"title": "Коэфф. коррекции потока ГВС",
|
"title": "Макс. мощность котла <small>(кВт)</small>",
|
||||||
"note": "Если поток ГВС отображается <b>Х10</b> от реального, установите значение <b>0.1</b>."
|
"note": "<b>0</b> - попробовать определить автоматически. Обычно можно найти в спецификации котла как \"максимальная полезная тепловая мощность\"."
|
||||||
},
|
|
||||||
"fnv": {
|
|
||||||
"title": "Фильтрация числовых значений",
|
|
||||||
"enable": {
|
|
||||||
"title": "Включить фильтрацию",
|
|
||||||
"note": "Может быть полезно, если на графиках много резкого шума. В качестве фильтра используется \"бегущее среднее\"."
|
|
||||||
},
|
|
||||||
"factor": {
|
|
||||||
"title": "Коэфф. фильтрации",
|
|
||||||
"note": "Чем меньше коэф., тем плавнее и <u>дольше</u> изменение числовых значений."
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"options": {
|
"options": {
|
||||||
|
"desc": "Опции",
|
||||||
"dhwPresent": "Контур ГВС",
|
"dhwPresent": "Контур ГВС",
|
||||||
"summerWinterMode": "Летний/зимний режим",
|
"summerWinterMode": "Летний/зимний режим",
|
||||||
"heatingCh2Enabled": "Канал 2 отопления всегда вкл.",
|
"heatingCh2Enabled": "Канал 2 отопления всегда вкл.",
|
||||||
@@ -293,15 +346,9 @@
|
|||||||
"immergasFix": "Фикс для котлов Immergas"
|
"immergasFix": "Фикс для котлов Immergas"
|
||||||
},
|
},
|
||||||
|
|
||||||
"faultState": {
|
|
||||||
"gpio": "Fault state GPIO",
|
|
||||||
"note": "Can be useful to switch on another boiler <u>via relay</u>. Blank - not use.",
|
|
||||||
"invert": "Invert fault state"
|
|
||||||
},
|
|
||||||
|
|
||||||
"nativeHeating": {
|
"nativeHeating": {
|
||||||
"title": "Передать управление отоплением котлу",
|
"title": "Передать управление отоплением котлу",
|
||||||
"note": "Работает <u>ТОЛЬКО</u> если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД, ПЗА и гистерезисом."
|
"note": "Работает <u>ТОЛЬКО</u> если котел требует и принимает целевую температуру в помещении и сам регулирует температуру теплоносителя на основе встроенного режима кривых. Несовместимо с ПИД и ПЗА."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -315,25 +362,35 @@
|
|||||||
"interval": "Интервал публикации <small>(сек)</small>"
|
"interval": "Интервал публикации <small>(сек)</small>"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tempSensor": {
|
|
||||||
"source": {
|
|
||||||
"type": "Источник данных",
|
|
||||||
"boiler": "От котла через OpenTherm",
|
|
||||||
"manual": "Вручную через MQTT/API",
|
|
||||||
"ext": "Внешний датчик (DS18B20)",
|
|
||||||
"ble": "BLE устройство"
|
|
||||||
},
|
|
||||||
"gpio": "GPIO",
|
|
||||||
"offset": "Смещение температуры <small>(калибровка)</small>",
|
|
||||||
"bleAddress": "MAC адрес BLE устройства"
|
|
||||||
},
|
|
||||||
|
|
||||||
"extPump": {
|
"extPump": {
|
||||||
"use": "Использовать доп. насос",
|
"use": "Использовать доп. насос",
|
||||||
"gpio": "GPIO реле",
|
"gpio": "GPIO реле",
|
||||||
"postCirculationTime": "Время постциркуляции <small>(в минутах)</small>",
|
"postCirculationTime": "Время постциркуляции <small>(в минутах)</small>",
|
||||||
"antiStuckInterval": "Интервал защиты от блокировки <small>(в днях)</small>",
|
"antiStuckInterval": "Интервал защиты от блокировки <small>(в днях)</small>",
|
||||||
"antiStuckTime": "Время работы насоса <small>(в минутах)</small>"
|
"antiStuckTime": "Время работы насоса <small>(в минутах)</small>"
|
||||||
|
},
|
||||||
|
|
||||||
|
"cascadeControl": {
|
||||||
|
"input": {
|
||||||
|
"desc": "Может использоваться для включения отопления только при неисправности другого котла. Контроллер другого котла должен изменить состояние входа GPIO в случае неисправности.",
|
||||||
|
"enable": "Включить вход",
|
||||||
|
"gpio": "GPIO",
|
||||||
|
"invertState": "Инвертировать состояние GPIO",
|
||||||
|
"thresholdTime": "Пороговое время изменения состояния <small>(сек)</small>"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"desc": "Может использоваться для включения другого котла <u>через реле</u>.",
|
||||||
|
"enable": "Включить выход",
|
||||||
|
"gpio": "GPIO",
|
||||||
|
"invertState": "Инвертировать состояние GPIO",
|
||||||
|
"thresholdTime": "Пороговое время изменения состояния <small>(сек)</small>",
|
||||||
|
"events": {
|
||||||
|
"desc": "События",
|
||||||
|
"onFault": "Если состояние fault (ошибки) активно",
|
||||||
|
"onLossConnection": "Если соединение по OpenTherm потеряно",
|
||||||
|
"onEnabledHeating": "Если отопление включено"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -42,31 +42,31 @@
|
|||||||
<div class="thermostat" id="thermostat-heating">
|
<div class="thermostat" id="thermostat-heating">
|
||||||
<div class="thermostat-header" data-i18n>dashboard.thermostat.heating</div>
|
<div class="thermostat-header" data-i18n>dashboard.thermostat.heating</div>
|
||||||
<div class="thermostat-temp">
|
<div class="thermostat-temp">
|
||||||
<div class="thermostat-temp-target"><span id="thermostat-heating-target"></span> <span class="temp-unit"></span></div>
|
<div class="thermostat-temp-target"><span id="tHeatTargetTemp"></span> <span class="tempUnit"></span></div>
|
||||||
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="thermostat-heating-current"></span> <span class="temp-unit"></span></div>
|
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="tHeatCurrentTemp"></span> <span class="tempUnit"></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="thermostat-minus"><button id="thermostat-heating-minus" class="outline"><i class="icons-down"></i></button></div>
|
<div class="thermostat-minus"><button id="tHeatActionMinus" class="outline"><i class="icons-down"></i></button></div>
|
||||||
<div class="thermostat-plus"><button id="thermostat-heating-plus" class="outline"><i class="icons-up"></i></button></div>
|
<div class="thermostat-plus"><button id="tHeatActionPlus" class="outline"><i class="icons-up"></i></button></div>
|
||||||
<div class="thermostat-control">
|
<div class="thermostat-control">
|
||||||
<input type="checkbox" role="switch" id="thermostat-heating-enabled" value="true">
|
<input type="checkbox" role="switch" id="tHeatEnabled" value="true">
|
||||||
<label htmlFor="thermostat-heating-enabled" data-i18n>dashboard.thermostat.enable</label>
|
<label htmlFor="tHeatEnabled" data-i18n>dashboard.thermostat.enable</label>
|
||||||
|
|
||||||
<input type="checkbox" role="switch" id="thermostat-heating-turbo" value="true">
|
<input type="checkbox" role="switch" id="tHeatTurbo" value="true">
|
||||||
<label htmlFor="thermostat-heating-turbo" data-i18n>dashboard.thermostat.turbo</label>
|
<label htmlFor="tHeatTurbo" data-i18n>dashboard.thermostat.turbo</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="thermostat" id="thermostat-dhw">
|
<div class="thermostat" id="thermostat-dhw">
|
||||||
<div class="thermostat-header" data-i18n>dashboard.thermostat.dhw</div>
|
<div class="thermostat-header" data-i18n>dashboard.thermostat.dhw</div>
|
||||||
<div class="thermostat-temp">
|
<div class="thermostat-temp">
|
||||||
<div class="thermostat-temp-target"><span id="thermostat-dhw-target"></span> <span class="temp-unit"></span></div>
|
<div class="thermostat-temp-target"><span id="tDhwTargetTemp"></span> <span class="tempUnit"></span></div>
|
||||||
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="thermostat-dhw-current"></span> <span class="temp-unit"></span></div>
|
<div class="thermostat-temp-current"><span data-i18n>dashboard.thermostat.temp.current</span>: <span id="tDhwCurrentTemp"></span> <span class="tempUnit"></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="thermostat-minus"><button class="outline" id="thermostat-dhw-minus"><i class="icons-down"></i></button></div>
|
<div class="thermostat-minus"><button class="outline" id="tDhwActionMinus"><i class="icons-down"></i></button></div>
|
||||||
<div class="thermostat-plus"><button class="outline" id="thermostat-dhw-plus"><i class="icons-up"></i></button></div>
|
<div class="thermostat-plus"><button class="outline" id="tDhwActionPlus"><i class="icons-up"></i></button></div>
|
||||||
<div class="thermostat-control">
|
<div class="thermostat-control">
|
||||||
<input type="checkbox" role="switch" id="thermostat-dhw-enabled" value="true">
|
<input type="checkbox" role="switch" id="tDhwEnabled" value="true">
|
||||||
<label htmlFor="thermostat-dhw-enabled" data-i18n>dashboard.thermostat.enable</label>
|
<label htmlFor="tDhwEnabled" data-i18n>dashboard.thermostat.enable</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,120 +79,112 @@
|
|||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.ot</th>
|
<th scope="row" data-i18n>dashboard.states.mNetworkConnected</th>
|
||||||
<td><input type="radio" id="ot-connected" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" class="mNetworkConnected" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.mqtt</th>
|
<th scope="row" data-i18n>dashboard.states.mMqttConnected</th>
|
||||||
<td><input type="radio" id="mqtt-connected" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" class="mMqttConnected" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.emergency</th>
|
<th scope="row" data-i18n>dashboard.states.mEmergencyState</th>
|
||||||
<td><input type="radio" id="ot-emergency" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" class="mEmergencyState" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.heating</th>
|
<th scope="row" data-i18n>dashboard.states.mExtPumpState</th>
|
||||||
<td><input type="radio" id="ot-heating" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" class="mExtPumpState" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.dhw</th>
|
<th scope="row" data-i18n>dashboard.states.mCascadeControlInput</th>
|
||||||
<td><input type="radio" id="ot-dhw" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" id="mCascadeControlInput" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.flame</th>
|
<th scope="row" data-i18n>dashboard.states.mCascadeControlOutput</th>
|
||||||
<td><input type="radio" id="ot-flame" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" id="mCascadeControlOutput" aria-invalid="false" checked disabled /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.states.sConnected</th>
|
||||||
|
<td><input type="radio" class="sConnected" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.fault</th>
|
<th scope="row" data-i18n>dashboard.states.sFlame</th>
|
||||||
<td><input type="radio" id="ot-fault" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" class="sFlame" aria-invalid="false" checked disabled /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.states.sFaultActive</th>
|
||||||
|
<td><input type="radio" class="sFaultActive" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.diag</th>
|
<th scope="row" data-i18n>dashboard.states.sFaultCode</th>
|
||||||
<td><input type="radio" id="ot-diagnostic" aria-invalid="false" checked disabled /></td>
|
<td><b class="sFaultCode"></b></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.extpump</th>
|
<th scope="row" data-i18n>dashboard.states.sDiagActive</th>
|
||||||
<td><input type="radio" id="ot-external-pump" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" class="sDiagActive" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.outdoorSensorConnected</th>
|
<th scope="row" data-i18n>dashboard.states.sDiagCode</th>
|
||||||
<td><input type="radio" id="outdoor-sensor-connected" aria-invalid="false" checked disabled /></td>
|
<td><b class="sDiagCode"></b></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.states.mHeatEnabled</th>
|
||||||
|
<td><input type="radio" class="mHeatEnabled" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.outdoorSensorRssi</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatBlocking</th>
|
||||||
<td><b id="outdoor-sensor-rssi"></b> <span data-i18n>dbm</span></td>
|
<td><input type="radio" class="mHeatBlocking" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.outdoorSensorHumidity</th>
|
<th scope="row" data-i18n>dashboard.states.sHeatActive</th>
|
||||||
<td><b id="outdoor-sensor-humidity"></b> %</td>
|
<td><input type="radio" class="sHeatActive" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.outdoorSensorBattery</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatTargetTemp</th>
|
||||||
<td><b id="outdoor-sensor-battery"></b> %</td>
|
<td><b class="mHeatTargetTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.indoorSensorConnected</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatCurrTemp</th>
|
||||||
<td><input type="radio" id="indoor-sensor-connected" aria-invalid="false" checked disabled /></td>
|
<td><b class="mHeatCurrTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.indoorSensorRssi</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatRetTemp</th>
|
||||||
<td><b id="indoor-sensor-rssi"></b> <span data-i18n>dbm</span></td>
|
<td><b class="mHeatRetTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.indoorSensorHumidity</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatIndoorTemp</th>
|
||||||
<td><b id="indoor-sensor-humidity"></b> %</td>
|
<td><b class="mHeatIndoorTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.indoorSensorBattery</th>
|
<th scope="row" data-i18n>dashboard.states.mHeatOutdoorTemp</th>
|
||||||
<td><b id="indoor-sensor-battery"></b> %</td>
|
<td><b class="mHeatOutdoorTemp"></b> <span class="tempUnit"></span></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.states.mDhwEnabled</th>
|
||||||
|
<td><input type="radio" class="mDhwEnabled" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.modulation</th>
|
<th scope="row" data-i18n>dashboard.states.sDhwActive</th>
|
||||||
<td><b id="ot-modulation"></b> %</td>
|
<td><input type="radio" class="sDhwActive" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.pressure</th>
|
<th scope="row" data-i18n>dashboard.states.mDhwTargetTemp</th>
|
||||||
<td><b id="ot-pressure"></b> <span class="pressure-unit"></span></td>
|
<td><b class="mDhwTargetTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.dhwFlowRate</th>
|
<th scope="row" data-i18n>dashboard.states.mDhwCurrTemp</th>
|
||||||
<td><b id="ot-dhw-flow-rate"></b> <span class="volume-unit"></span>/min</td>
|
<td><b class="mDhwCurrTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.faultCode</th>
|
<th scope="row" data-i18n>dashboard.states.mDhwRetTemp</th>
|
||||||
<td><b id="ot-fault-code"></b></td>
|
<td><b class="mDhwRetTemp"></b> <span class="tempUnit"></span></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.diagCode</th>
|
|
||||||
<td><b id="ot-diag-code"></b></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.indoorTemp</th>
|
|
||||||
<td><b id="indoor-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.outdoorTemp</th>
|
|
||||||
<td><b id="outdoor-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.heatingTemp</th>
|
|
||||||
<td><b id="heating-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.heatingSetpointTemp</th>
|
|
||||||
<td><b id="heating-setpoint-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.heatingReturnTemp</th>
|
|
||||||
<td><b id="heating-return-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.dhwTemp</th>
|
|
||||||
<td><b id="dhw-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" data-i18n>dashboard.state.exhaustTemp</th>
|
|
||||||
<td><b id="exhaust-temp"></b> <span class="temp-unit"></span></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -201,15 +193,17 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b data-i18n>dashboard.section.otDiag</b></summary>
|
<summary><b data-i18n>dashboard.section.diag</b></summary>
|
||||||
<pre><b>Vendor:</b> <span id="slave-vendor"></span>
|
<pre><b>Vendor:</b> <span class="sVendor"></span>
|
||||||
<b>Member ID:</b> <span id="slave-member-id"></span>
|
<b>Member ID:</b> <span class="sMemberId"></span>
|
||||||
<b>Flags:</b> <span id="slave-flags"></span>
|
<b>Flags:</b> <span class="sFlags"></span>
|
||||||
<b>Type:</b> <span id="slave-type"></span>
|
<b>Type:</b> <span class="sType"></span>
|
||||||
<b>Version:</b> <span id="slave-version"></span>
|
<b>AppVersion:</b> <span class="sAppVersion"></span>
|
||||||
<b>OT version:</b> <span id="slave-ot-version"></span>
|
<b>OT version:</b> <span class="sProtocolVersion"></span>
|
||||||
<b>Heating limits:</b> <span id="heating-min-temp"></span>...<span id="heating-max-temp"></span> <span class="temp-unit"></span>
|
<b>Modulation limits:</b> <span class="sModMin"></span>...<span class="sModMax"></span> %
|
||||||
<b>DHW limits:</b> <span id="dhw-min-temp"></span>...<span id="dhw-max-temp"></span> <span class="temp-unit"></span></pre>
|
<b>Power limits:</b> <span class="sPowerMin"></span>...<span class="sPowerMax"></span> kW
|
||||||
|
<b>Heating limits:</b> <span class="sHeatMinTemp"></span>...<span class="sHeatMaxTemp"></span> <span class="tempUnit"></span>
|
||||||
|
<b>DHW limits:</b> <span class="sDhwMinTemp"></span>...<span class="sDhwMaxTemp"></span> <span class="tempUnit"></span></pre>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -247,7 +241,7 @@
|
|||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
lang.build();
|
lang.build();
|
||||||
|
|
||||||
document.querySelector('#thermostat-heating-minus').addEventListener('click', (event) => {
|
document.querySelector('#tHeatActionMinus').addEventListener('click', (event) => {
|
||||||
if (!prevSettings) {
|
if (!prevSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -266,10 +260,10 @@
|
|||||||
newSettings.heating.target = minTemp;
|
newSettings.heating.target = minTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue('#thermostat-heating-target', newSettings.heating.target);
|
setValue('#tHeatTargetTemp', newSettings.heating.target);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#thermostat-heating-plus').addEventListener('click', (event) => {
|
document.querySelector('#tHeatActionPlus').addEventListener('click', (event) => {
|
||||||
if (!prevSettings) {
|
if (!prevSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -288,10 +282,10 @@
|
|||||||
newSettings.heating.target = maxTemp;
|
newSettings.heating.target = maxTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue('#thermostat-heating-target', newSettings.heating.target);
|
setValue('#tHeatTargetTemp', newSettings.heating.target);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#thermostat-dhw-minus').addEventListener('click', (event) => {
|
document.querySelector('#tDhwActionMinus').addEventListener('click', (event) => {
|
||||||
if (!prevSettings) {
|
if (!prevSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -303,10 +297,10 @@
|
|||||||
newSettings.dhw.target = prevSettings.dhw.minTemp;
|
newSettings.dhw.target = prevSettings.dhw.minTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue('#thermostat-dhw-target', newSettings.dhw.target);
|
setValue('#tDhwTargetTemp', newSettings.dhw.target);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#thermostat-dhw-plus').addEventListener('click', (event) => {
|
document.querySelector('#tDhwActionPlus').addEventListener('click', (event) => {
|
||||||
if (!prevSettings) {
|
if (!prevSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -318,22 +312,22 @@
|
|||||||
newSettings.dhw.target = prevSettings.dhw.maxTemp;
|
newSettings.dhw.target = prevSettings.dhw.maxTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue('#thermostat-dhw-target', newSettings.dhw.target);
|
setValue('#tDhwTargetTemp', newSettings.dhw.target);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#thermostat-heating-enabled').addEventListener('change', (event) => {
|
document.querySelector('#tHeatEnabled').addEventListener('change', (event) => {
|
||||||
modifiedTime = Date.now();
|
modifiedTime = Date.now();
|
||||||
newSettings.heating.enable = event.currentTarget.checked;
|
newSettings.heating.enabled = event.currentTarget.checked;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#thermostat-heating-turbo').addEventListener('change', (event) => {
|
document.querySelector('#tHeatTurbo').addEventListener('change', (event) => {
|
||||||
modifiedTime = Date.now();
|
modifiedTime = Date.now();
|
||||||
newSettings.heating.turbo = event.currentTarget.checked;
|
newSettings.heating.turbo = event.currentTarget.checked;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#thermostat-dhw-enabled').addEventListener('change', (event) => {
|
document.querySelector('#tDhwEnabled').addEventListener('change', (event) => {
|
||||||
modifiedTime = Date.now();
|
modifiedTime = Date.now();
|
||||||
newSettings.dhw.enable = event.currentTarget.checked;
|
newSettings.dhw.enabled = event.currentTarget.checked;
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(async function onLoadPage() {
|
setTimeout(async function onLoadPage() {
|
||||||
@@ -349,10 +343,10 @@
|
|||||||
// settings
|
// settings
|
||||||
try {
|
try {
|
||||||
let modified = prevSettings && (
|
let modified = prevSettings && (
|
||||||
(prevSettings.heating.enable != newSettings.heating.enable)
|
(prevSettings.heating.enabled != newSettings.heating.enabled)
|
||||||
|| (prevSettings.heating.turbo != newSettings.heating.turbo)
|
|| (prevSettings.heating.turbo != newSettings.heating.turbo)
|
||||||
|| (prevSettings.heating.target != newSettings.heating.target)
|
|| (prevSettings.heating.target != newSettings.heating.target)
|
||||||
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.enable != newSettings.dhw.enable)
|
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.enabled != newSettings.dhw.enabled)
|
||||||
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.target != newSettings.dhw.target)
|
|| (prevSettings.opentherm.dhwPresent && prevSettings.dhw.target != newSettings.dhw.target)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -372,12 +366,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
noRegulators = !result.opentherm.nativeHeatingControl && !result.equitherm.enable && !result.pid.enable;
|
noRegulators = !result.opentherm.nativeHeatingControl && !result.equitherm.enabled && !result.pid.enabled;
|
||||||
prevSettings = result;
|
prevSettings = result;
|
||||||
newSettings.heating.enable = result.heating.enable;
|
newSettings.heating.enabled = result.heating.enabled;
|
||||||
newSettings.heating.turbo = result.heating.turbo;
|
newSettings.heating.turbo = result.heating.turbo;
|
||||||
newSettings.heating.target = result.heating.target;
|
newSettings.heating.target = result.heating.target;
|
||||||
newSettings.dhw.enable = result.dhw.enable;
|
newSettings.dhw.enabled = result.dhw.enabled;
|
||||||
newSettings.dhw.target = result.dhw.target;
|
newSettings.dhw.target = result.dhw.target;
|
||||||
|
|
||||||
if (result.opentherm.dhwPresent) {
|
if (result.opentherm.dhwPresent) {
|
||||||
@@ -386,16 +380,16 @@
|
|||||||
hide('#thermostat-dhw');
|
hide('#thermostat-dhw');
|
||||||
}
|
}
|
||||||
|
|
||||||
setCheckboxValue('#thermostat-heating-enabled', result.heating.enable);
|
setCheckboxValue('#tHeatEnabled', result.heating.enabled);
|
||||||
setCheckboxValue('#thermostat-heating-turbo', result.heating.turbo);
|
setCheckboxValue('#tHeatTurbo', result.heating.turbo);
|
||||||
setValue('#thermostat-heating-target', result.heating.target);
|
setValue('#tHeatTargetTemp', result.heating.target);
|
||||||
|
|
||||||
setCheckboxValue('#thermostat-dhw-enabled', result.dhw.enable);
|
setCheckboxValue('#tDhwEnabled', result.dhw.enabled);
|
||||||
setValue('#thermostat-dhw-target', result.dhw.target);
|
setValue('#tDhwTargetTemp', result.dhw.target);
|
||||||
|
|
||||||
setValue('.temp-unit', temperatureUnit(result.system.unitSystem));
|
setValue('.tempUnit', temperatureUnit(result.system.unitSystem));
|
||||||
setValue('.pressure-unit', pressureUnit(result.system.unitSystem));
|
setValue('.pressureUnit', pressureUnit(result.system.unitSystem));
|
||||||
setValue('.volume-unit', volumeUnit(result.system.unitSystem));
|
setValue('.volumeUnit', volumeUnit(result.system.unitSystem));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -409,64 +403,83 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
setValue('#thermostat-heating-current', noRegulators ? result.temperatures.heating : result.temperatures.indoor);
|
|
||||||
setValue('#thermostat-dhw-current', result.temperatures.dhw);
|
// Graph
|
||||||
|
setValue('#tHeatCurrentTemp', result.master.indoorTempControl
|
||||||
setState('#ot-connected', result.states.otStatus);
|
? result.master.heating.indoorTemp
|
||||||
setState('#mqtt-connected', result.states.mqtt);
|
: result.master.heating.currentTemp
|
||||||
setState('#ot-emergency', result.states.emergency);
|
|
||||||
setState('#ot-heating', result.states.heating);
|
|
||||||
setState('#ot-dhw', result.states.dhw);
|
|
||||||
setState('#ot-flame', result.states.flame);
|
|
||||||
setState('#ot-fault', result.states.fault);
|
|
||||||
setState('#ot-diagnostic', result.states.diagnostic);
|
|
||||||
setState('#ot-external-pump', result.states.externalPump);
|
|
||||||
setState('#outdoor-sensor-connected', result.sensors.outdoor.connected);
|
|
||||||
setState('#indoor-sensor-connected', result.sensors.indoor.connected);
|
|
||||||
|
|
||||||
setValue('#outdoor-sensor-rssi', result.sensors.outdoor.rssi);
|
|
||||||
setValue('#outdoor-sensor-humidity', result.sensors.outdoor.humidity);
|
|
||||||
setValue('#outdoor-sensor-battery', result.sensors.outdoor.battery);
|
|
||||||
setValue('#indoor-sensor-rssi', result.sensors.indoor.rssi);
|
|
||||||
setValue('#indoor-sensor-humidity', result.sensors.indoor.humidity);
|
|
||||||
setValue('#indoor-sensor-battery', result.sensors.indoor.battery);
|
|
||||||
|
|
||||||
setValue('#ot-modulation', result.sensors.modulation);
|
|
||||||
setValue('#ot-pressure', result.sensors.pressure);
|
|
||||||
setValue('#ot-dhw-flow-rate', result.sensors.dhwFlowRate);
|
|
||||||
setValue(
|
|
||||||
'#ot-fault-code',
|
|
||||||
result.sensors.faultCode
|
|
||||||
? (result.sensors.faultCode + " (0x" + dec2hex(result.sensors.faultCode) + ")")
|
|
||||||
: "-"
|
|
||||||
);
|
);
|
||||||
|
setValue('#tDhwCurrentTemp', result.master.dhw.currentTemp);
|
||||||
|
|
||||||
|
|
||||||
|
// SLAVE
|
||||||
|
setValue('.sMemberId', result.slave.memberId);
|
||||||
|
setValue('.sVendor', memberIdToVendor(result.slave.memberId));
|
||||||
|
setValue('.sFlags', result.slave.flags);
|
||||||
|
setValue('.sType', result.slave.type);
|
||||||
|
setValue('.sAppVersion', result.slave.appVersion);
|
||||||
|
setValue('.sProtocolVersion', result.slave.protocolVersion);
|
||||||
|
|
||||||
|
setState('.sConnected', result.slave.connected);
|
||||||
|
setState('.sFlame', result.slave.flame);
|
||||||
|
|
||||||
|
setValue('.sModMin', result.slave.modulation.min);
|
||||||
|
setValue('.sModMax', result.slave.modulation.max);
|
||||||
|
|
||||||
|
setValue('.sPowerMin', result.slave.power.min);
|
||||||
|
setValue('.sPowerMax', result.slave.power.max);
|
||||||
|
|
||||||
|
setState('.sHeatActive', result.slave.heating.active);
|
||||||
|
setValue('.sHeatMinTemp', result.slave.heating.minTemp);
|
||||||
|
setValue('.sHeatMaxTemp', result.slave.heating.maxTemp);
|
||||||
|
|
||||||
|
setState('.sDhwActive', result.slave.dhw.active);
|
||||||
|
setValue('.sDhwMinTemp', result.slave.dhw.minTemp);
|
||||||
|
setValue('.sDhwMaxTemp', result.slave.dhw.maxTemp);
|
||||||
|
|
||||||
|
setState('.sFaultActive', result.slave.fault.active);
|
||||||
setValue(
|
setValue(
|
||||||
'#ot-diag-code',
|
'.sFaultCode',
|
||||||
result.sensors.diagnosticCode
|
result.slave.fault.active
|
||||||
? (result.sensors.diagnosticCode + " (0x" + dec2hex(result.sensors.diagnosticCode) + ")")
|
? (result.slave.fault.code + " (0x" + dec2hex(result.slave.fault.code) + ")")
|
||||||
: "-"
|
: "-"
|
||||||
);
|
);
|
||||||
|
|
||||||
setValue('#indoor-temp', result.temperatures.indoor);
|
setState('.sDiagActive', result.slave.diag.active);
|
||||||
setValue('#outdoor-temp', result.temperatures.outdoor);
|
setValue(
|
||||||
setValue('#heating-temp', result.temperatures.heating);
|
'.sDiagCode',
|
||||||
setValue('#heating-return-temp', result.temperatures.heatingReturn);
|
result.slave.diag.active
|
||||||
setValue('#dhw-temp', result.temperatures.dhw);
|
? (result.slave.diag.code + " (0x" + dec2hex(result.slave.diag.code) + ")")
|
||||||
setValue('#exhaust-temp', result.temperatures.exhaust);
|
: "-"
|
||||||
|
);
|
||||||
|
|
||||||
setValue('#heating-min-temp', result.parameters.heatingMinTemp);
|
|
||||||
setValue('#heating-max-temp', result.parameters.heatingMaxTemp);
|
|
||||||
setValue('#heating-setpoint-temp', result.parameters.heatingSetpoint);
|
|
||||||
setValue('#dhw-min-temp', result.parameters.dhwMinTemp);
|
|
||||||
setValue('#dhw-max-temp', result.parameters.dhwMaxTemp);
|
|
||||||
|
|
||||||
setValue('#slave-member-id', result.parameters.slaveMemberId);
|
// MASTER
|
||||||
setValue('#slave-vendor', memberIdToVendor(result.parameters.slaveMemberId));
|
setState('.mHeatEnabled', result.master.heating.enabled);
|
||||||
|
setState('.mHeatBlocking', result.master.heating.blocking);
|
||||||
|
setState('.mHeatIndoorTempControl', result.master.heating.indoorTempControl);
|
||||||
|
setValue('.mHeatTargetTemp', result.master.heating.targetTemp);
|
||||||
|
setValue('.mHeatCurrTemp', result.master.heating.currentTemp);
|
||||||
|
setValue('.mHeatRetTemp', result.master.heating.returnTemp);
|
||||||
|
setValue('.mHeatIndoorTemp', result.master.heating.indoorTemp);
|
||||||
|
setValue('.mHeatOutdoorTemp', result.master.heating.outdoorTemp);
|
||||||
|
setValue('.mHeatMinTemp', result.master.heating.minTemp);
|
||||||
|
setValue('.mHeatMaxTemp', result.master.heating.maxTemp);
|
||||||
|
|
||||||
|
setState('.mDhwEnabled', result.master.dhw.enabled);
|
||||||
|
setValue('.mDhwTargetTemp', result.master.dhw.targetTemp);
|
||||||
|
setValue('.mDhwCurrTemp', result.master.dhw.currentTemp);
|
||||||
|
setValue('.mDhwRetTemp', result.master.dhw.returnTemp);
|
||||||
|
setValue('.mDhwMinTemp', result.master.dhw.minTemp);
|
||||||
|
setValue('.mDhwMaxTemp', result.master.dhw.maxTemp);
|
||||||
|
|
||||||
|
setState('.mNetworkConnected', result.master.network.connected);
|
||||||
|
setState('.mMqttConnected', result.master.mqtt.connected);
|
||||||
|
setState('.mEmergencyState', result.master.emergency.state);
|
||||||
|
setState('.mExtPumpState', result.master.externalPump.state);
|
||||||
|
setState('.mCascadeControlInput', result.master.cascadeControl.input);
|
||||||
|
setState('.mCascadeControlOutput', result.master.cascadeControl.output);
|
||||||
|
|
||||||
setValue('#slave-flags', result.parameters.slaveFlags);
|
|
||||||
setValue('#slave-type', result.parameters.slaveType);
|
|
||||||
setValue('#slave-version', result.parameters.slaveVersion);
|
|
||||||
setValue('#slave-ot-version', result.parameters.slaveOtVersion);
|
|
||||||
setBusy('#dashboard-busy', '#dashboard-container', false);
|
setBusy('#dashboard-busy', '#dashboard-container', false);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -101,43 +101,48 @@
|
|||||||
<td>
|
<td>
|
||||||
Env: <b id="build-env"></b><br />
|
Env: <b id="build-env"></b><br />
|
||||||
<span data-i18n>index.system.build.date</span>: <b id="build-date"></b><br />
|
<span data-i18n>index.system.build.date</span>: <b id="build-date"></b><br />
|
||||||
<span data-i18n>index.system.build.sdk</span>: <b id="core-version"></b>
|
<span data-i18n>index.system.build.core</span>: <b id="build-core"></b><br />
|
||||||
|
<span data-i18n>index.system.build.sdk</span>: <b id="build-sdk"></b>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>index.system.uptime</th>
|
<th scope="row" data-i18n>index.system.uptime</th>
|
||||||
<td>
|
<td>
|
||||||
<b id="uptime-days"></b> days,
|
<b id="uptime-days"></b> <span data-i18n>time.days</span>,
|
||||||
<b id="uptime-hours"></b> hours,
|
<b id="uptime-hours"></b> <span data-i18n>time.hours</span>,
|
||||||
<b id="uptime-min"></b> min.,
|
<b id="uptime-min"></b> <span data-i18n>time.min</span>,
|
||||||
<b id="uptime-sec"></b> sec.
|
<b id="uptime-sec"></b> <span data-i18n>time.sec</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>index.system.memory.title</th>
|
<th scope="row" data-i18n>index.system.memory.title</th>
|
||||||
<td>
|
<td>
|
||||||
<b id="free-heap"></b> of <b id="total-heap"></b> bytes (<span data-i18n>index.system.memory.min</span>: <b id="min-free-heap"></b> bytes)<br />
|
<b id="heap-free"></b> of <b id="heap-total"></b> bytes (<span data-i18n>index.system.memory.min</span>: <b id="heap-min-free"></b> bytes)<br />
|
||||||
<span data-i18n>index.system.memory.maxFreeBlock</span>: <b id="max-free-block-heap"></b> bytes (<span data-i18n>index.system.memory.min</span>: <b id="min-max-free-block-heap"></b> bytes)
|
<span data-i18n>index.system.memory.maxFreeBlock</span>: <b id="heap-max-free-block"></b> bytes (<span data-i18n>index.system.memory.min</span>: <b id="heap-min-max-free-block"></b> bytes)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>index.system.board</th>
|
<th scope="row" data-i18n>index.system.board</th>
|
||||||
<td>
|
<td>
|
||||||
<span data-i18n>index.system.chip.model</span>: <b id="chip-model"></b> (rev. <span id="chip-revision"></span>)<br />
|
<span data-i18n>index.system.chip.model</span>: <b id="chip-model"></b> (rev. <span id="chip-rev"></span>)<br />
|
||||||
<span data-i18n>index.system.chip.cores</span>: <b id="chip-cores"></b>, <span data-i18n>index.system.chip.freq</span>: <b id="cpu-freq"></b> mHz<br />
|
<span data-i18n>index.system.chip.cores</span>: <b id="chip-cores"></b>, <span data-i18n>index.system.chip.freq</span>: <b id="chip-freq"></b> mHz<br />
|
||||||
<span data-i18n>index.system.flash.size</span>: <b id="flash-size"></b> MB (<span data-i18n>index.system.flash.realSize</span>: <b id="flash-real-size"></b> MB)
|
<span data-i18n>index.system.flash.size</span>: <b id="flash-size"></b> MB (<span data-i18n>index.system.flash.realSize</span>: <b id="flash-real-size"></b> MB)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>index.system.lastResetReason</th>
|
<th scope="row" data-i18n>index.system.lastResetReason</th>
|
||||||
<td><b id="reset-reason"></b></td>
|
<td>
|
||||||
|
<b id="reset-reason"></b><br />
|
||||||
|
<a href="/api/debug" target="_blank"><small>Save debug data</small></a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid" style="grid-template-columns: repeat(auto-fit,minmax(12rem,1fr)) !important;">
|
||||||
<a href="/dashboard.html" role="button" data-i18n>dashboard.name</a>
|
<a href="/dashboard.html" role="button" data-i18n>dashboard.name</a>
|
||||||
<a href="/settings.html" role="button" data-i18n>settings.name</a>
|
<a href="/settings.html" role="button" data-i18n>settings.name</a>
|
||||||
|
<a href="/sensors.html" role="button" data-i18n>sensors.name</a>
|
||||||
<a href="/upgrade.html" role="button" data-i18n>upgrade.name</a>
|
<a href="/upgrade.html" role="button" data-i18n>upgrade.name</a>
|
||||||
<a href="/restart.html" role="button" class="secondary restart" data-i18n>button.restart</a>
|
<a href="/restart.html" role="button" class="secondary restart" data-i18n>button.restart</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,6 +175,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
setValue('#reset-reason', result.system.resetReason);
|
||||||
|
setValue('#uptime', result.system.uptime);
|
||||||
|
setValue('#uptime-days', Math.floor(result.system.uptime / 86400));
|
||||||
|
setValue('#uptime-hours', Math.floor(result.system.uptime % 86400 / 3600));
|
||||||
|
setValue('#uptime-min', Math.floor(result.system.uptime % 3600 / 60));
|
||||||
|
setValue('#uptime-sec', Math.floor(result.system.uptime % 60));
|
||||||
|
|
||||||
setValue('#network-hostname', result.network.hostname);
|
setValue('#network-hostname', result.network.hostname);
|
||||||
setValue('#network-mac', result.network.mac);
|
setValue('#network-mac', result.network.mac);
|
||||||
setState('#network-connected', result.network.connected);
|
setState('#network-connected', result.network.connected);
|
||||||
@@ -181,28 +193,24 @@
|
|||||||
setValue('#network-dns', result.network.dns);
|
setValue('#network-dns', result.network.dns);
|
||||||
setBusy('#main-busy', '#main-table', false);
|
setBusy('#main-busy', '#main-table', false);
|
||||||
|
|
||||||
setValue('#build-version', result.system.buildVersion);
|
setValue('#build-version', result.build.version);
|
||||||
setValue('#build-date', result.system.buildDate);
|
setValue('#build-date', result.build.date);
|
||||||
setValue('#build-env', result.system.buildEnv);
|
setValue('#build-env', result.build.env);
|
||||||
setValue('#uptime', result.system.uptime);
|
setValue('#build-core', result.build.core);
|
||||||
setValue('#uptime-days', Math.floor(result.system.uptime / 86400));
|
setValue('#build-sdk', result.build.sdk);
|
||||||
setValue('#uptime-hours', Math.floor(result.system.uptime % 86400 / 3600));
|
|
||||||
setValue('#uptime-min', Math.floor(result.system.uptime % 3600 / 60));
|
|
||||||
setValue('#uptime-sec', Math.floor(result.system.uptime % 60));
|
|
||||||
setValue('#total-heap', result.system.totalHeap);
|
|
||||||
setValue('#free-heap', result.system.freeHeap);
|
|
||||||
setValue('#min-free-heap', result.system.minFreeHeap);
|
|
||||||
setValue('#max-free-block-heap', result.system.maxFreeBlockHeap);
|
|
||||||
setValue('#min-max-free-block-heap', result.system.minMaxFreeBlockHeap);
|
|
||||||
setValue('#reset-reason', result.system.resetReason);
|
|
||||||
|
|
||||||
setValue('#chip-model', result.system.chipModel);
|
setValue('#heap-total', result.heap.total);
|
||||||
setValue('#chip-revision', result.system.chipRevision);
|
setValue('#heap-free', result.heap.free);
|
||||||
setValue('#chip-cores', result.system.chipCores);
|
setValue('#heap-min-free', result.heap.minFree);
|
||||||
setValue('#cpu-freq', result.system.cpuFreq);
|
setValue('#heap-max-free-block', result.heap.maxFreeBlock);
|
||||||
setValue('#core-version', result.system.coreVersion);
|
setValue('#heap-min-max-free-block', result.heap.minMaxFreeBlock);
|
||||||
setValue('#flash-size', result.system.flashSize / 1024 / 1024);
|
|
||||||
setValue('#flash-real-size', result.system.flashRealSize / 1024 / 1024);
|
setValue('#chip-model', result.chip.model);
|
||||||
|
setValue('#chip-rev', result.chip.rev);
|
||||||
|
setValue('#chip-cores', result.chip.cores);
|
||||||
|
setValue('#chip-freq', result.chip.freq);
|
||||||
|
setValue('#flash-size', result.flash.size / 1024 / 1024);
|
||||||
|
setValue('#flash-real-size', result.flash.realSize / 1024 / 1024);
|
||||||
|
|
||||||
setBusy('#system-busy', '#system-table', false);
|
setBusy('#system-busy', '#system-table', false);
|
||||||
|
|
||||||
|
|||||||
283
src_data/pages/sensors.html
Normal file
283
src_data/pages/sensors.html
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title data-i18n>sensors.title</title>
|
||||||
|
<link rel="stylesheet" href="/static/app.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="container">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">
|
||||||
|
<div class="logo" data-i18n>logo</div>
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<!--<li><a href="https://github.com/Laxilef/OTGateway/wiki" role="button" class="secondary" target="_blank">Help</a></li>-->
|
||||||
|
<li>
|
||||||
|
<select id="lang" aria-label="Lang">
|
||||||
|
<option value="en" selected>EN</option>
|
||||||
|
<option value="ru">RU</option>
|
||||||
|
</select>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<article>
|
||||||
|
<hgroup>
|
||||||
|
<h2 data-i18n>sensors.name</h2>
|
||||||
|
<p></p>
|
||||||
|
</hgroup>
|
||||||
|
|
||||||
|
<details id="template" class="sensor hidden" data-id="" data-preloaded="0">
|
||||||
|
<summary><b>#<span class="id"></span>: <span class="name"></span></b></summary>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="form-busy" aria-busy="true"></div>
|
||||||
|
<form action="/api/sensor?id={id}" class="hidden">
|
||||||
|
<fieldset>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" role="switch" name="enabled" value="true">
|
||||||
|
<span data-i18n>sensors.enabled</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.sensorName.title</span>
|
||||||
|
<input type="text" name="name" maxlength="32" required>
|
||||||
|
<small data-i18n>sensors.sensorName.note</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.purpose</span>
|
||||||
|
<select name="purpose" required>
|
||||||
|
<option value="0" data-i18n>sensors.purposes.outdoorTemp</option>
|
||||||
|
<option value="1" data-i18n>sensors.purposes.indoorTemp</option>
|
||||||
|
<option value="2" data-i18n>sensors.purposes.heatTemp</option>
|
||||||
|
<option value="3" data-i18n>sensors.purposes.heatRetTemp</option>
|
||||||
|
<option value="4" data-i18n>sensors.purposes.dhwTemp</option>
|
||||||
|
<option value="5" data-i18n>sensors.purposes.dhwRetTemp</option>
|
||||||
|
<option value="6" data-i18n>sensors.purposes.dhwFlowRate</option>
|
||||||
|
<option value="7" data-i18n>sensors.purposes.exhaustTemp</option>
|
||||||
|
<option value="8" data-i18n>sensors.purposes.modLevel</option>
|
||||||
|
<option value="9" data-i18n>sensors.purposes.currentPower</option>
|
||||||
|
<option value="252" data-i18n>sensors.purposes.pressure</option>
|
||||||
|
<option value="253" data-i18n>sensors.purposes.humidity</option>
|
||||||
|
<option value="254" data-i18n>sensors.purposes.temperature</option>
|
||||||
|
<option value="255" data-i18n>sensors.purposes.notConfigured</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.type</span>
|
||||||
|
<select name="type" required>
|
||||||
|
<option value="0" data-i18n>sensors.types.otOutdoorTemp</option>
|
||||||
|
<option value="1" data-i18n>sensors.types.otHeatTemp</option>
|
||||||
|
<option value="2" data-i18n>sensors.types.otHeatRetTemp</option>
|
||||||
|
<option value="3" data-i18n>sensors.types.otDhwTemp</option>
|
||||||
|
<option value="4" data-i18n>sensors.types.otDhwTemp2</option>
|
||||||
|
<option value="5" data-i18n>sensors.types.otDhwFlowRate</option>
|
||||||
|
<option value="6" data-i18n>sensors.types.otCh2Temp</option>
|
||||||
|
<option value="7" data-i18n>sensors.types.otExhaustTemp</option>
|
||||||
|
<option value="8" data-i18n>sensors.types.otHeatExchangerTemp</option>
|
||||||
|
<option value="9" data-i18n>sensors.types.otPressure</option>
|
||||||
|
<option value="10" data-i18n>sensors.types.otModLevel</option>
|
||||||
|
<option value="11" data-i18n>sensors.types.otCurrentPower</option>
|
||||||
|
<option value="50" data-i18n>sensors.types.ntcTemp</option>
|
||||||
|
<option value="51" data-i18n>sensors.types.dallasTemp</option>
|
||||||
|
<option value="52" data-i18n>sensors.types.bluetooth</option>
|
||||||
|
<option value="253" data-i18n>sensors.types.heatSetpointTemp</option>
|
||||||
|
<option value="254" data-i18n>sensors.types.manual</option>
|
||||||
|
<option value="255" data-i18n>sensors.types.notConfigured</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.gpio</span>
|
||||||
|
<input type="number" outputmode="numeric" name="gpio" min="0" max="254" step="1">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.address.title</span>
|
||||||
|
<input type="text" name="address">
|
||||||
|
<small data-i18n>sensors.address.note</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend><b data-i18n>sensors.correction.desc</b></legend>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.correction.offset</span>
|
||||||
|
<input type="number" inputmode="numeric" name="offset" min="-20" max="20" step="0.01" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.correction.factor</span>
|
||||||
|
<input type="number" inputmode="numeric" name="factor" min="0.01" max="10" step="0.01" required>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend><b data-i18n>sensors.filtering.desc</b></legend>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="filtering" value="true">
|
||||||
|
<span data-i18n>sensors.filtering.enabled.title</span>
|
||||||
|
<br />
|
||||||
|
<small data-i18n>sensors.filtering.enabled.note</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<span data-i18n>sensors.filtering.factor.title</span>
|
||||||
|
<input type="number" inputmode="numeric" name="filteringFactor" min="0.01" max="1" step="0.01">
|
||||||
|
<small data-i18n>sensors.filtering.factor.note</small>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<button type="submit" data-i18n>button.save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="container">
|
||||||
|
<small>
|
||||||
|
<b>Made by Laxilef</b>
|
||||||
|
• <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary" data-i18n>nav.license</a>
|
||||||
|
• <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary" data-i18n>nav.source</a>
|
||||||
|
• <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary" data-i18n>nav.help</a>
|
||||||
|
• <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary" data-i18n>nav.issues</a>
|
||||||
|
• <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary" data-i18n>nav.releases</a>
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="/static/app.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
const lang = new Lang(document.getElementById("lang"));
|
||||||
|
lang.build();
|
||||||
|
|
||||||
|
const container = document.querySelector("article");
|
||||||
|
const templateNode = container.querySelector("#template");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/sensors", { cache: "no-cache" });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Response not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
for (const sensorId in result) {
|
||||||
|
const sensorNode = templateNode.cloneNode(true);
|
||||||
|
sensorNode.removeAttribute("id");
|
||||||
|
sensorNode.classList.remove("hidden");
|
||||||
|
sensorNode.dataset.id = sensorId;
|
||||||
|
setValue(".id", sensorId, sensorNode);
|
||||||
|
setValue(".name", result[sensorId], sensorNode);
|
||||||
|
|
||||||
|
container.appendChild(sensorNode);
|
||||||
|
container.appendChild(document.createElement("hr"));
|
||||||
|
|
||||||
|
const sensorForm = sensorNode.querySelector("form");
|
||||||
|
const fillData = (data) => {
|
||||||
|
setCheckboxValue("[name='enabled']", data.enabled, sensorForm);
|
||||||
|
setInputValue("[name='name']", data.name, {}, sensorForm);
|
||||||
|
setSelectValue("[name='purpose']", data.purpose, sensorForm);
|
||||||
|
setSelectValue("[name='type']", data.type, sensorForm);
|
||||||
|
setInputValue("[name='gpio']", data.gpio < 255 ? data.gpio : "", {}, sensorForm);
|
||||||
|
setInputValue("[name='address']", data.address, {}, sensorForm);
|
||||||
|
setInputValue("[name='offset']", data.offset, {}, sensorForm);
|
||||||
|
setInputValue("[name='factor']", data.factor, {}, sensorForm);
|
||||||
|
setCheckboxValue("[name='filtering']", data.filtering, sensorForm);
|
||||||
|
setInputValue("[name='filteringFactor']", data.filteringFactor, {}, sensorForm);
|
||||||
|
|
||||||
|
sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
setBusy(".form-busy", "form", false, sensorNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
sensorForm.action = sensorForm.action.replace("{id}", sensorId);
|
||||||
|
sensorForm.querySelector("[name='type']").addEventListener("change", async (event) => {
|
||||||
|
const gpio = sensorForm.querySelector("[name='gpio']");
|
||||||
|
const address = sensorForm.querySelector("[name='address']");
|
||||||
|
const parentGpio = gpio.parentElement;
|
||||||
|
const parentAddress = address.parentElement;
|
||||||
|
|
||||||
|
switch(parseInt(event.target.value)) {
|
||||||
|
// ntc
|
||||||
|
case 50:
|
||||||
|
parentGpio.classList.remove("hidden");
|
||||||
|
parentAddress.classList.add("hidden");
|
||||||
|
address.removeAttribute("pattern");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// dallas
|
||||||
|
case 51:
|
||||||
|
parentGpio.classList.remove("hidden");
|
||||||
|
parentAddress.classList.remove("hidden");
|
||||||
|
address.setAttribute("pattern", "([A-Fa-f0-9]{2}:){7}[A-Fa-f0-9]{2}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ble
|
||||||
|
case 52:
|
||||||
|
parentGpio.classList.add("hidden");
|
||||||
|
parentAddress.classList.remove("hidden");
|
||||||
|
address.setAttribute("pattern", "([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// other
|
||||||
|
default:
|
||||||
|
parentGpio.classList.add("hidden");
|
||||||
|
parentAddress.classList.add("hidden");
|
||||||
|
address.removeAttribute("pattern");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sensorNode.addEventListener("click", async (event) => {
|
||||||
|
if (parseInt(sensorNode.dataset.preloaded)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(sensorForm.action, { cache: "no-cache" });
|
||||||
|
if (response.status != 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
fillData(result);
|
||||||
|
sensorNode.dataset.preloaded = 1;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setupForm(".sensor[data-id='" + sensorId + "'] form", fillData, ['address']);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -94,26 +94,41 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend data-i18n>settings.section.diag</legend>
|
<legend data-i18n>settings.section.diag</legend>
|
||||||
|
|
||||||
<label for="system-debug">
|
|
||||||
<input type="checkbox" id="system-debug" name="system[debug]" value="true">
|
|
||||||
<span data-i18n>settings.system.debug</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="system-serial-enable">
|
<label for="system-serial-enable">
|
||||||
<input type="checkbox" id="system-serial-enable" name="system[serial][enable]" value="true">
|
<input type="checkbox" id="system-serial-enable" name="system[serial][enabled]" value="true">
|
||||||
<span data-i18n>settings.system.serial.enable</span>
|
<span data-i18n>settings.system.serial.enable</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="system-telnet-enable">
|
<label for="system-telnet-enable">
|
||||||
<input type="checkbox" id="system-telnet-enable" name="system[telnet][enable]" value="true">
|
<input type="checkbox" id="system-telnet-enable" name="system[telnet][enabled]" value="true">
|
||||||
<span data-i18n>settings.system.telnet.enable</span>
|
<span data-i18n>settings.system.telnet.enable</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label for="system-log-level">
|
||||||
|
<span data-i18n>settings.system.logLevel</span>
|
||||||
|
<select id="system-log-level" name="system[logLevel]">
|
||||||
|
<option value="0">SILENT</option>
|
||||||
|
<option value="1">FATAL</option>
|
||||||
|
<option value="2">ERROR</option>
|
||||||
|
<option value="3">WARNING</option>
|
||||||
|
<option value="4">INFO</option>
|
||||||
|
<option value="5">NOTICE</option>
|
||||||
|
<option value="6">TRACE</option>
|
||||||
|
<option value="7">VERBOSE</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<label for="system-serial-baudrate">
|
<label for="system-serial-baudrate">
|
||||||
<span data-i18n>settings.system.serial.baud.title</span>
|
<span data-i18n>settings.system.serial.baud</span>
|
||||||
<input type="number" inputmode="numeric" id="system-serial-baudrate" name="system[serial][baudrate]" min="9600" max="115200" step="1" required>
|
<select id="system-serial-baudrate" name="system[serial][baudrate]" required>
|
||||||
<small data-i18n>settings.system.serial.baud.note</small>
|
<option value="9600">9600</option>
|
||||||
|
<option value="19200">19200</option>
|
||||||
|
<option value="38400">38400</option>
|
||||||
|
<option value="57600">57600</option>
|
||||||
|
<option value="74880">74880</option>
|
||||||
|
<option value="115200">115200</option>
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="system-telnet-port">
|
<label for="system-telnet-port">
|
||||||
@@ -157,9 +172,9 @@
|
|||||||
<input type="number" inputmode="numeric" id="heating-hysteresis" name="heating[hysteresis]" min="0" max="5" step="0.05" required>
|
<input type="number" inputmode="numeric" id="heating-hysteresis" name="heating[hysteresis]" min="0" max="5" step="0.05" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="heating-max-modulation">
|
<label for="heating-turbo-factor">
|
||||||
<span data-i18n>settings.heating.maxMod</span>
|
<span data-i18n>settings.heating.turboFactor</span>
|
||||||
<input type="number" inputmode="numeric" id="heating-max-modulation" name="heating[maxModulation]" min="1" max="100" step="1" required>
|
<input type="number" inputmode="numeric" id="heating-turbo-factor" name="heating[turboFactor]" min="1.5" max="10" step="0.1" required>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -200,11 +215,6 @@
|
|||||||
<div id="emergency-settings-busy" aria-busy="true"></div>
|
<div id="emergency-settings-busy" aria-busy="true"></div>
|
||||||
<form action="/api/settings" id="emergency-settings" class="hidden">
|
<form action="/api/settings" id="emergency-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="emergency-enable">
|
|
||||||
<input type="checkbox" id="emergency-enable" name="emergency[enable]" value="true">
|
|
||||||
<span data-i18n>settings.enable</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<small data-i18n>settings.emergency.desc</small>
|
<small data-i18n>settings.emergency.desc</small>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -221,43 +231,6 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend data-i18n>settings.section.emergency.events</legend>
|
|
||||||
|
|
||||||
<label for="emergency-on-network-fault">
|
|
||||||
<input type="checkbox" id="emergency-on-network-fault" name="emergency[onNetworkFault]" value="true">
|
|
||||||
<span data-i18n>settings.emergency.events.network</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="emergency-on-mqtt-fault">
|
|
||||||
<input type="checkbox" id="emergency-on-mqtt-fault" name="emergency[onMqttFault]" value="true">
|
|
||||||
<span data-i18n>settings.emergency.events.mqtt</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="emergency-on-indoor-sensor-disconnect">
|
|
||||||
<input type="checkbox" id="emergency-on-indoor-sensor-disconnect" name="emergency[onIndoorSensorDisconnect]" value="true">
|
|
||||||
<span data-i18n>settings.emergency.events.indoorSensorDisconnect</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="emergency-on-outdoor-sensor-disconnect">
|
|
||||||
<input type="checkbox" id="emergency-on-outdoor-sensor-disconnect" name="emergency[onOutdoorSensorDisconnect]" value="true">
|
|
||||||
<span data-i18n>settings.emergency.events.outdoorSensorDisconnect</span>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend data-i18n>settings.section.emergency.regulators</legend>
|
|
||||||
|
|
||||||
<label for="emergency-use-equitherm">
|
|
||||||
<input type="checkbox" id="emergency-use-equitherm" name="emergency[useEquitherm]" value="true">
|
|
||||||
<span data-i18n>settings.emergency.regulators.equitherm</span>
|
|
||||||
</label>
|
|
||||||
<label for="emergency-use-pid">
|
|
||||||
<input type="checkbox" id="emergency-use-pid" name="emergency[usePid]" value="true">
|
|
||||||
<span data-i18n>settings.emergency.regulators.pid</span>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<button type="submit" data-i18n>button.save</button>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,7 +245,7 @@
|
|||||||
<form action="/api/settings" id="equitherm-settings" class="hidden">
|
<form action="/api/settings" id="equitherm-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="equitherm-enable">
|
<label for="equitherm-enable">
|
||||||
<input type="checkbox" id="equitherm-enable" name="equitherm[enable]" value="true">
|
<input type="checkbox" id="equitherm-enable" name="equitherm[enabled]" value="true">
|
||||||
<span data-i18n>settings.enable</span>
|
<span data-i18n>settings.enable</span>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -309,7 +282,7 @@
|
|||||||
<form action="/api/settings" id="pid-settings" class="hidden">
|
<form action="/api/settings" id="pid-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="pid-enable">
|
<label for="pid-enable">
|
||||||
<input type="checkbox" id="pid-enable" name="pid[enable]" value="true">
|
<input type="checkbox" id="pid-enable" name="pid[enabled]" value="true">
|
||||||
<span data-i18n>settings.enable</span>
|
<span data-i18n>settings.enable</span>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -317,7 +290,7 @@
|
|||||||
<div class="grid">
|
<div class="grid">
|
||||||
<label for="pid-p-factor">
|
<label for="pid-p-factor">
|
||||||
<span data-i18n>settings.pid.p</span>
|
<span data-i18n>settings.pid.p</span>
|
||||||
<input type="number" inputmode="numeric" id="pid-p-factor" name="pid[p_factor]" min="0.1" max="1000" step="0.1" required>
|
<input type="number" inputmode="numeric" id="pid-p-factor" name="pid[p_factor]" min="0.1" max="1000" step="0.01" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="pid-i-factor">
|
<label for="pid-i-factor">
|
||||||
@@ -327,13 +300,13 @@
|
|||||||
|
|
||||||
<label for="pid-d-factor">
|
<label for="pid-d-factor">
|
||||||
<span data-i18n>settings.pid.d</span>
|
<span data-i18n>settings.pid.d</span>
|
||||||
<input type="number" inputmode="numeric" id="pid-d-factor" name="pid[d_factor]" min="0" max="100000" step="1" required>
|
<input type="number" inputmode="numeric" id="pid-d-factor" name="pid[d_factor]" min="0" max="100000" step="0.1" required>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label for="pid-dt">
|
<label for="pid-dt">
|
||||||
<span data-i18n>settings.pid.dt</span>
|
<span data-i18n>settings.pid.dt</span>
|
||||||
<input type="number" inputmode="numeric" id="pid-dt" name="pid[dt]" min="30" max="600" step="1" required>
|
<input type="number" inputmode="numeric" id="pid-dt" name="pid[dt]" min="30" max="1800" step="1" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
@@ -350,6 +323,8 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<small data-i18n>settings.pid.noteMinMaxTemp</small>
|
||||||
|
|
||||||
<button type="submit" data-i18n>button.save</button>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -386,37 +361,48 @@
|
|||||||
<span data-i18n>settings.ot.outGpio</span>
|
<span data-i18n>settings.ot.outGpio</span>
|
||||||
<input type="number" inputmode="numeric" id="opentherm-out-gpio" name="opentherm[outGpio]" min="0" max="254" step="1">
|
<input type="number" inputmode="numeric" id="opentherm-out-gpio" name="opentherm[outGpio]" min="0" max="254" step="1">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid">
|
|
||||||
<label for="opentherm-rx-led-gpio">
|
<label for="opentherm-rx-led-gpio">
|
||||||
<span data-i18n>settings.ot.ledGpio</span>
|
<span data-i18n>settings.ot.ledGpio</span>
|
||||||
<input type="number" inputmode="numeric" id="opentherm-rx-led-gpio" name="opentherm[rxLedGpio]" min="0" max="254" step="1">
|
<input type="number" inputmode="numeric" id="opentherm-rx-led-gpio" name="opentherm[rxLedGpio]" min="0" max="254" step="1">
|
||||||
<small data-i18n>settings.note.blankNotUse</small>
|
<small data-i18n>settings.note.blankNotUse</small>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
<label for="opentherm-member-id-code">
|
<label for="opentherm-member-id-code">
|
||||||
<span data-i18n>settings.ot.memberIdCode</span>
|
<span data-i18n>settings.ot.memberId</span>
|
||||||
<input type="number" inputmode="numeric" id="opentherm-member-id-code" name="opentherm[memberIdCode]" min="0" max="65535" step="1" required>
|
<input type="number" inputmode="numeric" id="opentherm-member-id" name="opentherm[memberId]" min="0" max="255" step="1" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="opentherm-flags">
|
||||||
|
<span data-i18n>settings.ot.flags</span>
|
||||||
|
<input type="number" inputmode="numeric" id="opentherm-flags" name="opentherm[flags]" min="0" max="255" step="1" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="opentherm-max-modulation">
|
||||||
|
<span data-i18n>settings.ot.maxMod</span>
|
||||||
|
<input type="number" inputmode="numeric" id="opentherm-max-modulation" name="opentherm[maxModulation]" min="1" max="100" step="1" required>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<label for="opentherm-pressure-factor">
|
<label for="opentherm-min-power">
|
||||||
<span data-i18n>settings.ot.pressureFactor.title</span>
|
<span data-i18n>settings.ot.minPower.title</span>
|
||||||
<input type="number" inputmode="numeric" id="opentherm-pressure-factor" name="opentherm[pressureFactor]" min="0.1" max="100" step="0.01">
|
<input type="number" inputmode="numeric" id="opentherm-min-power" name="opentherm[minPower]" min="0" max="1000" step="0.1">
|
||||||
<small data-i18n>settings.ot.pressureFactor.note</small>
|
<small data-i18n>settings.ot.minPower.note</small>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="opentherm-dhw-fr-factor">
|
<label for="opentherm-max-power">
|
||||||
<span data-i18n>settings.ot.dhwFlowRateFactor.title</span>
|
<span data-i18n>settings.ot.maxPower.title</span>
|
||||||
<input type="number" inputmode="numeric" id="opentherm-dhw-fr-factor" name="opentherm[dhwFlowRateFactor]" min="0.1" max="100" step="0.01">
|
<input type="number" inputmode="numeric" id="opentherm-max-power" name="opentherm[maxPower]" min="0" max="1000" step="0.1">
|
||||||
<small data-i18n>settings.ot.dhwFlowRateFactor.note</small>
|
<small data-i18n>settings.ot.maxPower.note</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend data-i18n>settings.section.ot.options</legend>
|
<legend data-i18n>settings.ot.options.desc</legend>
|
||||||
|
|
||||||
<label for="opentherm-dhw-present">
|
<label for="opentherm-dhw-present">
|
||||||
<input type="checkbox" id="opentherm-dhw-present" name="opentherm[dhwPresent]" value="true">
|
<input type="checkbox" id="opentherm-dhw-present" name="opentherm[dhwPresent]" value="true">
|
||||||
<span data-i18n>settings.ot.options.dhwPresent</span>
|
<span data-i18n>settings.ot.options.dhwPresent</span>
|
||||||
@@ -462,40 +448,6 @@
|
|||||||
<span data-i18n>settings.ot.options.immergasFix</span>
|
<span data-i18n>settings.ot.options.immergasFix</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<hr />
|
|
||||||
<fieldset>
|
|
||||||
<legend>
|
|
||||||
<span data-i18n>settings.ot.fnv.title</span>
|
|
||||||
</legend>
|
|
||||||
|
|
||||||
<label for="opentherm-fnv-enable">
|
|
||||||
<input type="checkbox" id="opentherm-fnv-enable" name="opentherm[filterNumValues][enable]" value="true">
|
|
||||||
<span data-i18n>settings.ot.fnv.enable.title</span>
|
|
||||||
<br>
|
|
||||||
<small data-i18n>settings.ot.fnv.enable.note</small>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="opentherm-fnv-factor">
|
|
||||||
<span data-i18n>settings.ot.fnv.factor.title</span>
|
|
||||||
<input type="number" inputmode="numeric" id="opentherm-fnv-factor" name="opentherm[filterNumValues][factor]" min="0.01" max="1" step="0.01">
|
|
||||||
<small data-i18n>settings.ot.fnv.factor.note</small>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<fieldset>
|
|
||||||
<label for="opentherm-fault-state-gpio">
|
|
||||||
<span data-i18n>settings.ot.faultState.gpio</span>
|
|
||||||
<input type="number" inputmode="numeric" id="opentherm-fault-state-gpio" name="opentherm[faultStateGpio]" min="0" max="254" step="1">
|
|
||||||
<small data-i18n>settings.ot.faultState.note</small>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="opentherm-invert-fault-state">
|
|
||||||
<input type="checkbox" id="opentherm-invert-fault-state" name="opentherm[invertFaultState]" value="true">
|
|
||||||
<span data-i18n>settings.ot.faultState.invert</span>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<label for="opentherm-native-heating-control">
|
<label for="opentherm-native-heating-control">
|
||||||
<input type="checkbox" id="opentherm-native-heating-control" name="opentherm[nativeHeatingControl]" value="true">
|
<input type="checkbox" id="opentherm-native-heating-control" name="opentherm[nativeHeatingControl]" value="true">
|
||||||
@@ -518,7 +470,7 @@
|
|||||||
<form action="/api/settings" id="mqtt-settings" class="hidden">
|
<form action="/api/settings" id="mqtt-settings" class="hidden">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="mqtt-enable">
|
<label for="mqtt-enable">
|
||||||
<input type="checkbox" id="mqtt-enable" name="mqtt[enable]" value="true">
|
<input type="checkbox" id="mqtt-enable" name="mqtt[enabled]" value="true">
|
||||||
<span data-i18n>settings.enable</span>
|
<span data-i18n>settings.enable</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -571,115 +523,6 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b data-i18n>settings.section.outdorSensor</b></summary>
|
|
||||||
<div>
|
|
||||||
<div id="outdoor-sensor-settings-busy" aria-busy="true"></div>
|
|
||||||
<form action="/api/settings" id="outdoor-sensor-settings" class="hidden">
|
|
||||||
<fieldset>
|
|
||||||
<legend data-i18n>settings.tempSensor.source.type</legend>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="0" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.boiler</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="1" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.manual</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="2" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.ext</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="3" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.ble</span>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div class="grid">
|
|
||||||
<label for="outdoor-sensor-gpio">
|
|
||||||
<span data-i18n>settings.tempSensor.gpio</span>
|
|
||||||
<input type="number" inputmode="numeric" id="outdoor-sensor-gpio" name="sensors[outdoor][gpio]" min="0" max="254" step="1">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="outdoor-sensor-ble-addresss">
|
|
||||||
<span data-i18n>settings.tempSensor.bleAddress</span>
|
|
||||||
<input type="text" id="outdoor-sensor-ble-addresss" name="sensors[outdoor][bleAddress]" pattern="([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label for="outdoor-sensor-offset">
|
|
||||||
<span data-i18n>settings.tempSensor.offset</span>
|
|
||||||
<input type="number" inputmode="numeric" id="outdoor-sensor-offset" name="sensors[outdoor][offset]" min="-10" max="10" step="0.01" required>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<mark data-i18n>settings.note.bleDevice</mark>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<button type="submit" data-i18n>button.save</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b data-i18n>settings.section.indoorSensor</b></summary>
|
|
||||||
<div>
|
|
||||||
<div id="indoor-sensor-settings-busy" aria-busy="true"></div>
|
|
||||||
<form action="/api/settings" id="indoor-sensor-settings" class="hidden">
|
|
||||||
<fieldset>
|
|
||||||
<legend data-i18n>settings.tempSensor.source.type</legend>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="indoor-sensor-type" name="sensors[indoor][type]" value="1" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.manual</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="indoor-sensor-type" name="sensors[indoor][type]" value="2" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.ext</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" class="indoor-sensor-type" name="sensors[indoor][type]" value="3" />
|
|
||||||
<span data-i18n>settings.tempSensor.source.ble</span>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div class="grid">
|
|
||||||
<label for="indoor-sensor-gpio">
|
|
||||||
<span data-i18n>settings.tempSensor.gpio</span>
|
|
||||||
<input type="number" inputmode="numeric" id="indoor-sensor-gpio" name="sensors[indoor][gpio]" min="0" max="254" step="1">
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="indoor-sensor-ble-addresss">
|
|
||||||
<span data-i18n>settings.tempSensor.bleAddress</span>
|
|
||||||
<input type="text" id="indoor-sensor-ble-addresss" name="sensors[indoor][bleAddress]" pattern="([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label for="indoor-sensor-offset">
|
|
||||||
<span data-i18n>settings.tempSensor.offset</span>
|
|
||||||
<input type="number" inputmode="numeric" id="indoor-sensor-offset" name="sensors[indoor][offset]" min="-10" max="10" step="0.01" required>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<mark data-i18n>settings.note.bleDevice</mark>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<button type="submit" data-i18n>button.save</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b data-i18n>settings.section.extPump</b></summary>
|
<summary><b data-i18n>settings.section.extPump</b></summary>
|
||||||
<div>
|
<div>
|
||||||
@@ -720,6 +563,91 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b data-i18n>settings.section.cascadeControl</b></summary>
|
||||||
|
<div>
|
||||||
|
<div id="cc-settings-busy" aria-busy="true"></div>
|
||||||
|
<form action="/api/settings" id="cc-settings" class="hidden">
|
||||||
|
<fieldset>
|
||||||
|
<label for="cc-input-enable">
|
||||||
|
<input type="checkbox" id="cc-input-enable" name="cascadeControl[input][enabled]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.input.enable</span>
|
||||||
|
<br />
|
||||||
|
<small data-i18n>settings.cascadeControl.input.desc</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="cc-input-invert-state">
|
||||||
|
<input type="checkbox" id="cc-input-invert-state" name="cascadeControl[input][invertState]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.input.invertState</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<label for="cc-input-gpio">
|
||||||
|
<span data-i18n>settings.cascadeControl.input.gpio</span>
|
||||||
|
<input type="number" inputmode="numeric" id="cc-input-gpio" name="cascadeControl[input][gpio]" min="0" max="254" step="1">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="cc-input-tt">
|
||||||
|
<span data-i18n>settings.cascadeControl.input.thresholdTime</span>
|
||||||
|
<input type="number" inputmode="numeric" id="cc-input-tt" name="cascadeControl[input][thresholdTime]" min="5" max="600" step="1" required>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label for="cc-output-enable">
|
||||||
|
<input type="checkbox" id="cc-output-enable" name="cascadeControl[output][enabled]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.enable</span>
|
||||||
|
<br />
|
||||||
|
<small data-i18n>settings.cascadeControl.output.desc</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="cc-output-invert-state">
|
||||||
|
<input type="checkbox" id="cc-output-invert-state" name="cascadeControl[output][invertState]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.invertState</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<label for="cc-output-gpio">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.gpio</span>
|
||||||
|
<input type="number" outputmode="numeric" id="cc-output-gpio" name="cascadeControl[output][gpio]" min="0" max="254" step="1">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="cc-output-tt">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.thresholdTime</span>
|
||||||
|
<input type="number" outputmode="numeric" id="cc-output-tt" name="cascadeControl[output][thresholdTime]" min="5" max="600" step="1" required>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend data-i18n>settings.cascadeControl.output.events.desc</legend>
|
||||||
|
|
||||||
|
<label for="cc-on-fault">
|
||||||
|
<input type="checkbox" id="cc-on-fault" name="cascadeControl[output][onFault]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.events.onFault</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="cc-on-loss-conn">
|
||||||
|
<input type="checkbox" id="cc-on-loss-conn" name="cascadeControl[output][onLossConnection]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.events.onLossConnection</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="cc-on-enabled-heating">
|
||||||
|
<input type="checkbox" id="cc-on-enabled-heating" name="cascadeControl[output][onEnabledHeating]" value="true">
|
||||||
|
<span data-i18n>settings.cascadeControl.output.events.onEnabledHeating</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<button type="submit" data-i18n>button.save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -740,13 +668,12 @@
|
|||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
lang.build();
|
lang.build();
|
||||||
|
|
||||||
|
|
||||||
const fillData = (data) => {
|
const fillData = (data) => {
|
||||||
// System
|
// System
|
||||||
setCheckboxValue('#system-debug', data.system.debug);
|
setSelectValue('#system-log-level', data.system.logLevel);
|
||||||
setCheckboxValue('#system-serial-enable', data.system.serial.enable);
|
setCheckboxValue('#system-serial-enable', data.system.serial.enabled);
|
||||||
setInputValue('#system-serial-baudrate', data.system.serial.baudrate);
|
setSelectValue('#system-serial-baudrate', data.system.serial.baudrate);
|
||||||
setCheckboxValue('#system-telnet-enable', data.system.telnet.enable);
|
setCheckboxValue('#system-telnet-enable', data.system.telnet.enabled);
|
||||||
setInputValue('#system-telnet-port', data.system.telnet.port);
|
setInputValue('#system-telnet-port', data.system.telnet.port);
|
||||||
setRadioValue('.system-unit-system', data.system.unitSystem);
|
setRadioValue('.system-unit-system', data.system.unitSystem);
|
||||||
setInputValue('#system-status-led-gpio', data.system.statusLedGpio < 255 ? data.system.statusLedGpio : '');
|
setInputValue('#system-status-led-gpio', data.system.statusLedGpio < 255 ? data.system.statusLedGpio : '');
|
||||||
@@ -763,11 +690,11 @@
|
|||||||
setInputValue('#opentherm-in-gpio', data.opentherm.inGpio < 255 ? data.opentherm.inGpio : '');
|
setInputValue('#opentherm-in-gpio', data.opentherm.inGpio < 255 ? data.opentherm.inGpio : '');
|
||||||
setInputValue('#opentherm-out-gpio', data.opentherm.outGpio < 255 ? data.opentherm.outGpio : '');
|
setInputValue('#opentherm-out-gpio', data.opentherm.outGpio < 255 ? data.opentherm.outGpio : '');
|
||||||
setInputValue('#opentherm-rx-led-gpio', data.opentherm.rxLedGpio < 255 ? data.opentherm.rxLedGpio : '');
|
setInputValue('#opentherm-rx-led-gpio', data.opentherm.rxLedGpio < 255 ? data.opentherm.rxLedGpio : '');
|
||||||
setInputValue('#opentherm-fault-state-gpio', data.opentherm.faultStateGpio < 255 ? data.opentherm.faultStateGpio : '');
|
setInputValue('#opentherm-member-id', data.opentherm.memberId);
|
||||||
setCheckboxValue('#opentherm-invert-fault-state', data.opentherm.invertFaultState);
|
setInputValue('#opentherm-flags', data.opentherm.flags);
|
||||||
setInputValue('#opentherm-member-id-code', data.opentherm.memberIdCode);
|
setInputValue('#opentherm-max-modulation', data.opentherm.maxModulation);
|
||||||
setInputValue('#opentherm-pressure-factor', data.opentherm.pressureFactor);
|
setInputValue('#opentherm-min-power', data.opentherm.minPower);
|
||||||
setInputValue('#opentherm-dhw-fr-factor', data.opentherm.dhwFlowRateFactor);
|
setInputValue('#opentherm-max-power', data.opentherm.maxPower);
|
||||||
setCheckboxValue('#opentherm-dhw-present', data.opentherm.dhwPresent);
|
setCheckboxValue('#opentherm-dhw-present', data.opentherm.dhwPresent);
|
||||||
setCheckboxValue('#opentherm-sw-mode', data.opentherm.summerWinterMode);
|
setCheckboxValue('#opentherm-sw-mode', data.opentherm.summerWinterMode);
|
||||||
setCheckboxValue('#opentherm-heating-ch2-enabled', data.opentherm.heatingCh2Enabled);
|
setCheckboxValue('#opentherm-heating-ch2-enabled', data.opentherm.heatingCh2Enabled);
|
||||||
@@ -778,12 +705,10 @@
|
|||||||
setCheckboxValue('#opentherm-get-min-max-temp', data.opentherm.getMinMaxTemp);
|
setCheckboxValue('#opentherm-get-min-max-temp', data.opentherm.getMinMaxTemp);
|
||||||
setCheckboxValue('#opentherm-native-heating-control', data.opentherm.nativeHeatingControl);
|
setCheckboxValue('#opentherm-native-heating-control', data.opentherm.nativeHeatingControl);
|
||||||
setCheckboxValue('#opentherm-immergas-fix', data.opentherm.immergasFix);
|
setCheckboxValue('#opentherm-immergas-fix', data.opentherm.immergasFix);
|
||||||
setCheckboxValue('#opentherm-fnv-enable', data.opentherm.filterNumValues.enable);
|
|
||||||
setInputValue('#opentherm-fnv-factor', data.opentherm.filterNumValues.factor);
|
|
||||||
setBusy('#opentherm-settings-busy', '#opentherm-settings', false);
|
setBusy('#opentherm-settings-busy', '#opentherm-settings', false);
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
setCheckboxValue('#mqtt-enable', data.mqtt.enable);
|
setCheckboxValue('#mqtt-enable', data.mqtt.enabled);
|
||||||
setCheckboxValue('#mqtt-ha-discovery', data.mqtt.homeAssistantDiscovery);
|
setCheckboxValue('#mqtt-ha-discovery', data.mqtt.homeAssistantDiscovery);
|
||||||
setInputValue('#mqtt-server', data.mqtt.server);
|
setInputValue('#mqtt-server', data.mqtt.server);
|
||||||
setInputValue('#mqtt-port', data.mqtt.port);
|
setInputValue('#mqtt-port', data.mqtt.port);
|
||||||
@@ -793,20 +718,6 @@
|
|||||||
setInputValue('#mqtt-interval', data.mqtt.interval);
|
setInputValue('#mqtt-interval', data.mqtt.interval);
|
||||||
setBusy('#mqtt-settings-busy', '#mqtt-settings', false);
|
setBusy('#mqtt-settings-busy', '#mqtt-settings', false);
|
||||||
|
|
||||||
// Outdoor sensor
|
|
||||||
setRadioValue('.outdoor-sensor-type', data.sensors.outdoor.type);
|
|
||||||
setInputValue('#outdoor-sensor-gpio', data.sensors.outdoor.gpio < 255 ? data.sensors.outdoor.gpio : '');
|
|
||||||
setInputValue('#outdoor-sensor-offset', data.sensors.outdoor.offset);
|
|
||||||
setInputValue('#outdoor-sensor-ble-addresss', data.sensors.outdoor.bleAddress);
|
|
||||||
setBusy('#outdoor-sensor-settings-busy', '#outdoor-sensor-settings', false);
|
|
||||||
|
|
||||||
// Indoor sensor
|
|
||||||
setRadioValue('.indoor-sensor-type', data.sensors.indoor.type);
|
|
||||||
setInputValue('#indoor-sensor-gpio', data.sensors.indoor.gpio < 255 ? data.sensors.indoor.gpio : '');
|
|
||||||
setInputValue('#indoor-sensor-offset', data.sensors.indoor.offset);
|
|
||||||
setInputValue('#indoor-sensor-ble-addresss', data.sensors.indoor.bleAddress);
|
|
||||||
setBusy('#indoor-sensor-settings-busy', '#indoor-sensor-settings', false);
|
|
||||||
|
|
||||||
// Extpump
|
// Extpump
|
||||||
setCheckboxValue('#extpump-use', data.externalPump.use);
|
setCheckboxValue('#extpump-use', data.externalPump.use);
|
||||||
setInputValue('#extpump-gpio', data.externalPump.gpio < 255 ? data.externalPump.gpio : '');
|
setInputValue('#extpump-gpio', data.externalPump.gpio < 255 ? data.externalPump.gpio : '');
|
||||||
@@ -815,6 +726,21 @@
|
|||||||
setInputValue('#extpump-as-time', data.externalPump.antiStuckTime);
|
setInputValue('#extpump-as-time', data.externalPump.antiStuckTime);
|
||||||
setBusy('#extpump-settings-busy', '#extpump-settings', false);
|
setBusy('#extpump-settings-busy', '#extpump-settings', false);
|
||||||
|
|
||||||
|
// Cascade control
|
||||||
|
setCheckboxValue('#cc-input-enable', data.cascadeControl.input.enabled);
|
||||||
|
setInputValue('#cc-input-gpio', data.cascadeControl.input.gpio < 255 ? data.cascadeControl.input.gpio : '');
|
||||||
|
setCheckboxValue('#cc-input-invert-state', data.cascadeControl.input.invertState);
|
||||||
|
setInputValue('#cc-input-tt', data.cascadeControl.input.thresholdTime);
|
||||||
|
|
||||||
|
setCheckboxValue('#cc-output-enable', data.cascadeControl.output.enabled);
|
||||||
|
setInputValue('#cc-output-gpio', data.cascadeControl.output.gpio < 255 ? data.cascadeControl.output.gpio : '');
|
||||||
|
setCheckboxValue('#cc-output-invert-state', data.cascadeControl.output.invertState);
|
||||||
|
setInputValue('#cc-output-tt', data.cascadeControl.output.thresholdTime);
|
||||||
|
setCheckboxValue('#cc-on-fault', data.cascadeControl.output.onFault);
|
||||||
|
setCheckboxValue('#cc-on-loss-conn', data.cascadeControl.output.onLossConnection);
|
||||||
|
setCheckboxValue('#cc-on-enabled-heating', data.cascadeControl.output.onEnabledHeating);
|
||||||
|
setBusy('#cc-settings-busy', '#cc-settings', false);
|
||||||
|
|
||||||
// Heating
|
// Heating
|
||||||
setInputValue('#heating-min-temp', data.heating.minTemp, {
|
setInputValue('#heating-min-temp', data.heating.minTemp, {
|
||||||
"min": data.system.unitSystem == 0 ? 0 : 32,
|
"min": data.system.unitSystem == 0 ? 0 : 32,
|
||||||
@@ -825,7 +751,7 @@
|
|||||||
"max": data.system.unitSystem == 0 ? 100 : 212
|
"max": data.system.unitSystem == 0 ? 100 : 212
|
||||||
});
|
});
|
||||||
setInputValue('#heating-hysteresis', data.heating.hysteresis);
|
setInputValue('#heating-hysteresis', data.heating.hysteresis);
|
||||||
setInputValue('#heating-max-modulation', data.heating.maxModulation);
|
setInputValue('#heating-turbo-factor', data.heating.turboFactor);
|
||||||
setBusy('#heating-settings-busy', '#heating-settings', false);
|
setBusy('#heating-settings-busy', '#heating-settings', false);
|
||||||
|
|
||||||
// DHW
|
// DHW
|
||||||
@@ -840,40 +766,42 @@
|
|||||||
setBusy('#dhw-settings-busy', '#dhw-settings', false);
|
setBusy('#dhw-settings-busy', '#dhw-settings', false);
|
||||||
|
|
||||||
// Emergency mode
|
// Emergency mode
|
||||||
setCheckboxValue('#emergency-enable', data.emergency.enable);
|
|
||||||
setInputValue('#emergency-treshold-time', data.emergency.tresholdTime);
|
setInputValue('#emergency-treshold-time', data.emergency.tresholdTime);
|
||||||
setCheckboxValue('#emergency-use-equitherm', data.emergency.useEquitherm);
|
if (data.opentherm.nativeHeatingControl) {
|
||||||
setCheckboxValue('#emergency-use-pid', data.emergency.usePid);
|
setInputValue('#emergency-target', data.emergency.target, {
|
||||||
setCheckboxValue('#emergency-on-network-fault', data.emergency.onNetworkFault);
|
"min": data.system.unitSystem == 0 ? 5 : 41,
|
||||||
setCheckboxValue('#emergency-on-mqtt-fault', data.emergency.onMqttFault);
|
"max": data.system.unitSystem == 0 ? 30 : 86
|
||||||
setCheckboxValue('#emergency-on-indoor-sensor-disconnect', data.emergency.onIndoorSensorDisconnect);
|
});
|
||||||
setCheckboxValue('#emergency-on-outdoor-sensor-disconnect', data.emergency.onOutdoorSensorDisconnect);
|
|
||||||
setInputValue('#emergency-target', data.emergency.target, {
|
} else {
|
||||||
"min": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.minTemp : 10,
|
setInputValue('#emergency-target', data.emergency.target, {
|
||||||
"max": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.maxTemp : 30,
|
"min": data.heating.minTemp,
|
||||||
});
|
"max": data.heating.maxTemp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setBusy('#emergency-settings-busy', '#emergency-settings', false);
|
setBusy('#emergency-settings-busy', '#emergency-settings', false);
|
||||||
|
|
||||||
// Equitherm
|
// Equitherm
|
||||||
setCheckboxValue('#equitherm-enable', data.equitherm.enable);
|
setCheckboxValue('#equitherm-enable', data.equitherm.enabled);
|
||||||
setInputValue('#equitherm-n-factor', data.equitherm.n_factor);
|
setInputValue('#equitherm-n-factor', data.equitherm.n_factor);
|
||||||
setInputValue('#equitherm-k-factor', data.equitherm.k_factor);
|
setInputValue('#equitherm-k-factor', data.equitherm.k_factor);
|
||||||
setInputValue('#equitherm-t-factor', data.equitherm.t_factor);
|
setInputValue('#equitherm-t-factor', data.equitherm.t_factor);
|
||||||
setBusy('#equitherm-settings-busy', '#equitherm-settings', false);
|
setBusy('#equitherm-settings-busy', '#equitherm-settings', false);
|
||||||
|
|
||||||
// PID
|
// PID
|
||||||
setCheckboxValue('#pid-enable', data.pid.enable);
|
setCheckboxValue('#pid-enable', data.pid.enabled);
|
||||||
setInputValue('#pid-p-factor', data.pid.p_factor);
|
setInputValue('#pid-p-factor', data.pid.p_factor);
|
||||||
setInputValue('#pid-i-factor', data.pid.i_factor);
|
setInputValue('#pid-i-factor', data.pid.i_factor);
|
||||||
setInputValue('#pid-d-factor', data.pid.d_factor);
|
setInputValue('#pid-d-factor', data.pid.d_factor);
|
||||||
setInputValue('#pid-dt', data.pid.dt);
|
setInputValue('#pid-dt', data.pid.dt);
|
||||||
setInputValue('#pid-min-temp', data.pid.minTemp, {
|
setInputValue('#pid-min-temp', data.pid.minTemp, {
|
||||||
"min": 0,
|
"min": data.equitherm.enabled ? (data.system.unitSystem == 0 ? -100 : -146) : (data.system.unitSystem == 0 ? 0 : 32),
|
||||||
"max": data.system.unitSystem == 0 ? 99 : 211
|
"max": (data.system.unitSystem == 0 ? 99 : 211)
|
||||||
});
|
});
|
||||||
setInputValue('#pid-max-temp', data.pid.maxTemp, {
|
setInputValue('#pid-max-temp', data.pid.maxTemp, {
|
||||||
"min": 1,
|
"min": (data.system.unitSystem == 0 ? 0 : 33),
|
||||||
"max": data.system.unitSystem == 0 ? 100 : 212
|
"max": (data.system.unitSystem == 0 ? 100 : 212)
|
||||||
});
|
});
|
||||||
setBusy('#pid-settings-busy', '#pid-settings', false);
|
setBusy('#pid-settings-busy', '#pid-settings', false);
|
||||||
};
|
};
|
||||||
@@ -896,9 +824,8 @@
|
|||||||
setupForm('#pid-settings', fillData);
|
setupForm('#pid-settings', fillData);
|
||||||
setupForm('#opentherm-settings', fillData);
|
setupForm('#opentherm-settings', fillData);
|
||||||
setupForm('#mqtt-settings', fillData, ['mqtt.user', 'mqtt.password', 'mqtt.prefix']);
|
setupForm('#mqtt-settings', fillData, ['mqtt.user', 'mqtt.password', 'mqtt.prefix']);
|
||||||
setupForm('#outdoor-sensor-settings', fillData);
|
|
||||||
setupForm('#indoor-sensor-settings', fillData, ['sensors.indoor.bleAddress']);
|
|
||||||
setupForm('#extpump-settings', fillData);
|
setupForm('#extpump-settings', fillData);
|
||||||
|
setupForm('#cc-settings', fillData);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
function setupForm(formSelector, onResultCallback = null, noCastItems = []) {
|
const setupForm = (formSelector, onResultCallback = null, noCastItems = []) => {
|
||||||
const form = document.querySelector(formSelector);
|
const form = document.querySelector(formSelector);
|
||||||
if (!form) {
|
if (!form) {
|
||||||
return;
|
return;
|
||||||
@@ -10,13 +10,13 @@ function setupForm(formSelector, onResultCallback = null, noCastItems = []) {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = form.action;
|
|
||||||
let button = form.querySelector('button[type="submit"]');
|
|
||||||
let defaultText;
|
|
||||||
|
|
||||||
form.addEventListener('submit', async (event) => {
|
form.addEventListener('submit', async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
const url = form.action;
|
||||||
|
let button = form.querySelector('button[type="submit"]');
|
||||||
|
let defaultText;
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
defaultText = button.textContent;
|
defaultText = button.textContent;
|
||||||
button.textContent = i18n("button.wait");
|
button.textContent = i18n("button.wait");
|
||||||
@@ -86,7 +86,7 @@ function setupForm(formSelector, onResultCallback = null, noCastItems = []) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupNetworkScanForm(formSelector, tableSelector) {
|
const setupNetworkScanForm = (formSelector, tableSelector) => {
|
||||||
const form = document.querySelector(formSelector);
|
const form = document.querySelector(formSelector);
|
||||||
if (!form) {
|
if (!form) {
|
||||||
console.error("form not found");
|
console.error("form not found");
|
||||||
@@ -132,7 +132,7 @@ function setupNetworkScanForm(formSelector, tableSelector) {
|
|||||||
let row = tbody.insertRow(-1);
|
let row = tbody.insertRow(-1);
|
||||||
row.classList.add("network");
|
row.classList.add("network");
|
||||||
row.setAttribute('data-ssid', result[i].hidden ? '' : result[i].ssid);
|
row.setAttribute('data-ssid', result[i].hidden ? '' : result[i].ssid);
|
||||||
row.onclick = function () {
|
row.onclick = () => {
|
||||||
const input = document.querySelector('input#sta-ssid');
|
const input = document.querySelector('input#sta-ssid');
|
||||||
const ssid = this.getAttribute('data-ssid');
|
const ssid = this.getAttribute('data-ssid');
|
||||||
if (!input || !ssid) {
|
if (!input || !ssid) {
|
||||||
@@ -246,7 +246,7 @@ function setupNetworkScanForm(formSelector, tableSelector) {
|
|||||||
onSubmitFn();
|
onSubmitFn();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupRestoreBackupForm(formSelector) {
|
const setupRestoreBackupForm = (formSelector) => {
|
||||||
const form = document.querySelector(formSelector);
|
const form = document.querySelector(formSelector);
|
||||||
if (!form) {
|
if (!form) {
|
||||||
return;
|
return;
|
||||||
@@ -266,7 +266,7 @@ function setupRestoreBackupForm(formSelector) {
|
|||||||
button.setAttribute('aria-busy', true);
|
button.setAttribute('aria-busy', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = () => {
|
||||||
if (button) {
|
if (button) {
|
||||||
button.textContent = i18n('button.restored');
|
button.textContent = i18n('button.restored');
|
||||||
button.classList.add('success');
|
button.classList.add('success');
|
||||||
@@ -280,7 +280,7 @@ function setupRestoreBackupForm(formSelector) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFailed = (response) => {
|
const onFailed = () => {
|
||||||
if (button) {
|
if (button) {
|
||||||
button.textContent = i18n('button.error');
|
button.textContent = i18n('button.error');
|
||||||
button.classList.add('failed');
|
button.classList.add('failed');
|
||||||
@@ -302,35 +302,79 @@ function setupRestoreBackupForm(formSelector) {
|
|||||||
|
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.readAsText(files[0]);
|
reader.readAsText(files[0]);
|
||||||
reader.onload = async function () {
|
reader.onload = async (event) => {
|
||||||
try {
|
try {
|
||||||
let response = await fetch(url, {
|
const data = JSON.parse(event.target.result);
|
||||||
method: 'POST',
|
console.log("Backup: ", data);
|
||||||
cache: 'no-cache',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: reader.result
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (data.settings != undefined) {
|
||||||
onSuccess(response);
|
let response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({"settings": data.settings})
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
if (!response.ok) {
|
||||||
onFailed(response);
|
onFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.sensors != undefined) {
|
||||||
|
for (const sensorId in data.sensors) {
|
||||||
|
const payload = {
|
||||||
|
"sensors": {}
|
||||||
|
};
|
||||||
|
payload["sensors"][sensorId] = data.sensors[sensorId];
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
onFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.network != undefined) {
|
||||||
|
let response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({"network": data.network})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
onFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccess();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
onFailed(false);
|
onFailed();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.onerror = function () {
|
reader.onerror = () => {
|
||||||
console.log(reader.error);
|
console.log(reader.error);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupUpgradeForm(formSelector) {
|
const setupUpgradeForm = (formSelector) => {
|
||||||
const form = document.querySelector(formSelector);
|
const form = document.querySelector(formSelector);
|
||||||
if (!form) {
|
if (!form) {
|
||||||
return;
|
return;
|
||||||
@@ -471,19 +515,23 @@ function setupUpgradeForm(formSelector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function setBusy(busySelector, contentSelector, value) {
|
const setBusy = (busySelector, contentSelector, value, parent = undefined) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
hide(busySelector);
|
hide(busySelector, parent);
|
||||||
show(contentSelector);
|
show(contentSelector, parent);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
show(busySelector);
|
show(busySelector, parent);
|
||||||
hide(contentSelector);
|
hide(contentSelector, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setState(selector, value) {
|
const setState = (selector, value, parent = undefined) => {
|
||||||
let item = document.querySelector(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = parent.querySelector(selector);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -491,8 +539,12 @@ function setState(selector, value) {
|
|||||||
item.setAttribute('aria-invalid', !value);
|
item.setAttribute('aria-invalid', !value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setValue(selector, value) {
|
const setValue = (selector, value, parent = undefined) => {
|
||||||
let items = document.querySelectorAll(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = parent.querySelectorAll(selector);
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -502,8 +554,12 @@ function setValue(selector, value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCheckboxValue(selector, value) {
|
const setCheckboxValue = (selector, value, parent = undefined) => {
|
||||||
let item = document.querySelector(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = parent.querySelector(selector);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -511,8 +567,12 @@ function setCheckboxValue(selector, value) {
|
|||||||
item.checked = value;
|
item.checked = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRadioValue(selector, value) {
|
const setRadioValue = (selector, value, parent = undefined) => {
|
||||||
let items = document.querySelectorAll(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = parent.querySelectorAll(selector);
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -522,8 +582,12 @@ function setRadioValue(selector, value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInputValue(selector, value, attrs = {}) {
|
const setInputValue = (selector, value, attrs = {}, parent = undefined) => {
|
||||||
let items = document.querySelectorAll(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = parent.querySelectorAll(selector);
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -539,8 +603,27 @@ function setInputValue(selector, value, attrs = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function show(selector) {
|
const setSelectValue = (selector, value, parent = undefined) => {
|
||||||
let items = document.querySelectorAll(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = parent.querySelector(selector);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let option of item.options) {
|
||||||
|
option.selected = option.value == value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = (selector, parent = undefined) => {
|
||||||
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = parent.querySelectorAll(selector);
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -552,8 +635,12 @@ function show(selector) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide(selector) {
|
const hide = (selector, parent = undefined) => {
|
||||||
let items = document.querySelectorAll(selector);
|
if (parent == undefined) {
|
||||||
|
parent = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = parent.querySelectorAll(selector);
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -571,34 +658,34 @@ function unit2str(unitSystem, units = {}, defaultValue = '?') {
|
|||||||
: defaultValue;
|
: defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function temperatureUnit(unitSystem) {
|
const temperatureUnit = (unitSystem) => {
|
||||||
return unit2str(unitSystem, {
|
return unit2str(unitSystem, {
|
||||||
0: "°C",
|
0: "°C",
|
||||||
1: "°F"
|
1: "°F"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function pressureUnit(unitSystem) {
|
const pressureUnit = (unitSystem) => {
|
||||||
return unit2str(unitSystem, {
|
return unit2str(unitSystem, {
|
||||||
0: "bar",
|
0: "bar",
|
||||||
1: "psi"
|
1: "psi"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function volumeUnit(unitSystem) {
|
const volumeUnit = (unitSystem) => {
|
||||||
return unit2str(unitSystem, {
|
return unit2str(unitSystem, {
|
||||||
0: "L",
|
0: "L",
|
||||||
1: "gal"
|
1: "gal"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function memberIdToVendor(memberId) {
|
const memberIdToVendor = (memberId) => {
|
||||||
// https://github.com/Jeroen88/EasyOpenTherm/blob/main/src/EasyOpenTherm.h
|
// https://github.com/Jeroen88/EasyOpenTherm/blob/main/src/EasyOpenTherm.h
|
||||||
// https://github.com/Evgen2/SmartTherm/blob/v0.7/src/Web.cpp
|
// https://github.com/Evgen2/SmartTherm/blob/v0.7/src/Web.cpp
|
||||||
const vendorList = {
|
const vendorList = {
|
||||||
1: "Baxi Fourtech/Luna 3",
|
1: "Baxi",
|
||||||
2: "AWB/Brink",
|
2: "AWB/Brink/Viessmann",
|
||||||
4: "ATAG/Brötje/ELCO/GEMINOX",
|
4: "ATAG/Baxi/Brötje/ELCO/GEMINOX",
|
||||||
5: "Itho Daalderop",
|
5: "Itho Daalderop",
|
||||||
6: "IDEAL",
|
6: "IDEAL",
|
||||||
8: "Buderus/Bosch/Hoval",
|
8: "Buderus/Bosch/Hoval",
|
||||||
@@ -610,7 +697,7 @@ function memberIdToVendor(memberId) {
|
|||||||
29: "Itho Daalderop",
|
29: "Itho Daalderop",
|
||||||
33: "Viessmann",
|
33: "Viessmann",
|
||||||
41: "Italtherm/Radiant",
|
41: "Italtherm/Radiant",
|
||||||
56: "Baxi Luna Duo-Tec",
|
56: "Baxi",
|
||||||
131: "Nefit",
|
131: "Nefit",
|
||||||
148: "Navien",
|
148: "Navien",
|
||||||
173: "Intergas",
|
173: "Intergas",
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ Import("env")
|
|||||||
|
|
||||||
def post_build(source, target, env):
|
def post_build(source, target, env):
|
||||||
copy_to_build_dir({
|
copy_to_build_dir({
|
||||||
source[0].get_abspath(): "firmware_%s_%s.bin" % (env["PIOENV"], env.GetProjectOption("version")),
|
source[0].get_abspath(): "firmware_%s_%s.bin" % (env["PIOENV"], env.GetProjectOption("version")),
|
||||||
env.subst("$BUILD_DIR/${PROGNAME}.factory.bin"): "firmware_%s_%s.factory.bin" % (env["PIOENV"], env.GetProjectOption("version")),
|
env.subst("$BUILD_DIR/${PROGNAME}.factory.bin"): "firmware_%s_%s.factory.bin" % (env["PIOENV"], env.GetProjectOption("version")),
|
||||||
|
env.subst("$BUILD_DIR/${PROGNAME}.elf"): "firmware_%s_%s.elf" % (env["PIOENV"], env.GetProjectOption("version"))
|
||||||
}, os.path.join(env["PROJECT_DIR"], "build"));
|
}, os.path.join(env["PROJECT_DIR"], "build"));
|
||||||
|
|
||||||
env.Execute("pio run --target buildfs --environment %s" % env["PIOENV"]);
|
env.Execute("pio run --target buildfs --environment %s" % env["PIOENV"]);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user