mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-12 19:24:27 +05:00
Compare commits
9 Commits
ff89c6d478
...
platformio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4338b10d5 | ||
|
|
9d77256051 | ||
|
|
d3e7a13e1f | ||
|
|
14826c10cd | ||
|
|
28a5218b7c | ||
|
|
f3ba43adbd | ||
|
|
062c6714d1 | ||
|
|
502f73db0e | ||
|
|
c2ee30d1ab |
@@ -72,7 +72,7 @@ All available information and instructions can be found in the wiki:
|
|||||||
* [Leds on board](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#leds-on-board)
|
* [Leds on board](https://github.com/Laxilef/OTGateway/wiki/OT-adapters#leds-on-board)
|
||||||
|
|
||||||
## Gratitude
|
## Gratitude
|
||||||
* To the developers of the libraries used: [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library), [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler), [ArduinoJson](https://github.com/bblanchon/ArduinoJson), [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino), [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient), [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet), [FileData](https://github.com/GyverLibs/FileData), [GyverPID](https://github.com/GyverLibs/GyverPID), [GyverBlinker](https://github.com/GyverLibs/GyverBlinker), [FileData](https://github.com/GyverLibs/FileData), [OneWireNg](https://github.com/pstolarz/OneWireNg) & [OneWire](https://github.com/PaulStoffregen/OneWire)
|
* To the developers of the libraries used: [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library), [ESP8266Scheduler](https://github.com/nrwiersma/ESP8266Scheduler), [ArduinoJson](https://github.com/bblanchon/ArduinoJson), [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino), [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient), [ESPTelnet](https://github.com/LennartHennigs/ESPTelnet), [FileData](https://github.com/GyverLibs/FileData), [GyverPID](https://github.com/GyverLibs/GyverPID), [GyverBlinker](https://github.com/GyverLibs/GyverBlinker), [OneWireNg](https://github.com/pstolarz/OneWireNg) & [OneWire](https://github.com/PaulStoffregen/OneWire)
|
||||||
* To the [PlatformIO](https://platformio.org/) Team
|
* To the [PlatformIO](https://platformio.org/) Team
|
||||||
* To the team and contributors of the [pioarduino](https://github.com/pioarduino/platform-espressif32) project
|
* To the team and contributors of the [pioarduino](https://github.com/pioarduino/platform-espressif32) project
|
||||||
* To the [BrowserStack](https://www.browserstack.com/) team. This project is tested with BrowserStack.
|
* To the [BrowserStack](https://www.browserstack.com/) team. This project is tested with BrowserStack.
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ let paths = {
|
|||||||
'src_data/scripts/i18n.min.js',
|
'src_data/scripts/i18n.min.js',
|
||||||
'src_data/scripts/lang.js',
|
'src_data/scripts/lang.js',
|
||||||
'src_data/scripts/utils.js'
|
'src_data/scripts/utils.js'
|
||||||
],
|
|
||||||
'chart.js': [
|
|
||||||
'src_data/scripts/chart.js'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
class CustomOpenTherm : public OpenTherm {
|
class CustomOpenTherm : public OpenTherm {
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(unsigned int)> DelayCallback;
|
typedef std::function<void(unsigned int)> DelayCallback;
|
||||||
typedef std::function<void(unsigned long, byte)> BeforeSendRequestCallback;
|
typedef std::function<void(unsigned long, uint8_t)> BeforeSendRequestCallback;
|
||||||
typedef std::function<void(unsigned long, unsigned long, OpenThermResponseStatus, byte)> AfterSendRequestCallback;
|
typedef std::function<void(unsigned long, unsigned long, OpenThermResponseStatus, uint8_t)> AfterSendRequestCallback;
|
||||||
|
|
||||||
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false, bool alwaysReceive = false) : OpenTherm(inPin, outPin, isSlave, alwaysReceive) {}
|
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false, bool alwaysReceive = false) : OpenTherm(inPin, outPin, isSlave, alwaysReceive) {}
|
||||||
~CustomOpenTherm() {}
|
~CustomOpenTherm() {}
|
||||||
@@ -106,15 +106,14 @@ public:
|
|||||||
return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest);
|
return isValidResponse(response) && isValidResponseId(response, OpenThermMessageID::RemoteRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isCh2Active(unsigned long response)
|
static bool isCh2Active(unsigned long response) {
|
||||||
{
|
|
||||||
return response & 0x20;
|
return response & 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isValidResponseId(unsigned long response, OpenThermMessageID id) {
|
static bool isValidResponseId(unsigned long response, OpenThermMessageID id) {
|
||||||
byte responseId = (response >> 16) & 0xFF;
|
uint8_t responseId = (response >> 16) & 0xFF;
|
||||||
|
|
||||||
return (byte)id == responseId;
|
return (uint8_t)id == responseId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t getResponseMessageTypeId(unsigned long response) {
|
static uint8_t getResponseMessageTypeId(unsigned long response) {
|
||||||
|
|||||||
63
lib/Equitherm/Equitherm.h
Normal file
63
lib/Equitherm/Equitherm.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#if defined(EQUITHERM_INTEGER)
|
||||||
|
// расчёты с целыми числами
|
||||||
|
typedef int datatype;
|
||||||
|
#else
|
||||||
|
// расчёты с float числами
|
||||||
|
typedef float datatype;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Equitherm {
|
||||||
|
public:
|
||||||
|
datatype targetTemp = 0;
|
||||||
|
datatype indoorTemp = 0;
|
||||||
|
datatype outdoorTemp = 0;
|
||||||
|
float Kn = 0.0;
|
||||||
|
float Kk = 0.0;
|
||||||
|
float Kt = 0.0;
|
||||||
|
|
||||||
|
Equitherm() = default;
|
||||||
|
|
||||||
|
// kn, kk, kt
|
||||||
|
Equitherm(float new_kn, float new_kk, float new_kt) {
|
||||||
|
Kn = new_kn;
|
||||||
|
Kk = new_kk;
|
||||||
|
Kt = new_kt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// лимит выходной величины
|
||||||
|
void setLimits(unsigned short min_output, unsigned short max_output) {
|
||||||
|
_minOut = min_output;
|
||||||
|
_maxOut = max_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// возвращает новое значение при вызове
|
||||||
|
datatype getResult() {
|
||||||
|
datatype output = getResultN() + getResultK() + getResultT();
|
||||||
|
output = constrain(output, _minOut, _maxOut); // ограничиваем выход
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned short _minOut = 20, _maxOut = 90;
|
||||||
|
|
||||||
|
// температура контура отопления в зависимости от наружной температуры
|
||||||
|
datatype getResultN() {
|
||||||
|
float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06
|
||||||
|
float b = (6.04 * Kn) + 1.98; // b = 6,04k + 1,98
|
||||||
|
float c = (-5.06 * Kn) + 18.06; // с = -5,06k + 18,06
|
||||||
|
float x = (-0.2 * outdoorTemp) + 5; // x = -0.2*t1 + 5
|
||||||
|
return (a * x * x) + (b * x) + c; // Tn = ax2 + bx + c
|
||||||
|
}
|
||||||
|
|
||||||
|
// поправка на желаемую комнатную температуру
|
||||||
|
datatype getResultK() {
|
||||||
|
return (targetTemp - 20) * Kk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Расчет поправки (ошибки) термостата
|
||||||
|
datatype getResultT() {
|
||||||
|
return constrain((targetTemp - indoorTemp), -3, 3) * Kt;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -35,7 +35,7 @@ namespace NetworkUtils {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkMgr* setApCredentials(const char* ssid, const char* password = nullptr, byte channel = 0) {
|
NetworkMgr* setApCredentials(const char* ssid, const char* password = nullptr, uint8_t channel = 0) {
|
||||||
this->apName = ssid;
|
this->apName = ssid;
|
||||||
this->apPassword = password;
|
this->apPassword = password;
|
||||||
this->apChannel = channel;
|
this->apChannel = channel;
|
||||||
@@ -43,7 +43,7 @@ namespace NetworkUtils {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkMgr* setStaCredentials(const char* ssid = nullptr, const char* password = nullptr, byte channel = 0) {
|
NetworkMgr* setStaCredentials(const char* ssid = nullptr, const char* password = nullptr, uint8_t channel = 0) {
|
||||||
this->staSsid = ssid;
|
this->staSsid = ssid;
|
||||||
this->staPassword = password;
|
this->staPassword = password;
|
||||||
this->staChannel = channel;
|
this->staChannel = channel;
|
||||||
@@ -140,7 +140,7 @@ namespace NetworkUtils {
|
|||||||
return this->staPassword;
|
return this->staPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte getStaChannel() {
|
uint8_t getStaChannel() {
|
||||||
return this->staChannel;
|
return this->staChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +377,7 @@ namespace NetworkUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte rssiToSignalQuality(short int rssi) {
|
static uint8_t rssiToSignalQuality(short int rssi) {
|
||||||
return constrain(map(rssi, -100, -50, 0, 100), 0, 100);
|
return constrain(map(rssi, -100, -50, 0, 100), 0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,11 +397,11 @@ namespace NetworkUtils {
|
|||||||
const char* hostname = "esp";
|
const char* hostname = "esp";
|
||||||
const char* apName = "ESP";
|
const char* apName = "ESP";
|
||||||
const char* apPassword = nullptr;
|
const char* apPassword = nullptr;
|
||||||
byte apChannel = 1;
|
uint8_t apChannel = 1;
|
||||||
|
|
||||||
const char* staSsid = nullptr;
|
const char* staSsid = nullptr;
|
||||||
const char* staPassword = nullptr;
|
const char* staPassword = nullptr;
|
||||||
byte staChannel = 0;
|
uint8_t staChannel = 0;
|
||||||
|
|
||||||
bool useDhcp = true;
|
bool useDhcp = true;
|
||||||
IPAddress staticIp;
|
IPAddress staticIp;
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ core_dir = .pio
|
|||||||
version = 1.5.6
|
version = 1.5.6
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@^7.3.0
|
bblanchon/ArduinoJson@^7.4.2
|
||||||
;ihormelnyk/OpenTherm Library@^1.1.5
|
;ihormelnyk/OpenTherm Library@^1.1.5
|
||||||
https://github.com/Laxilef/opentherm_library#esp32_timer
|
https://github.com/Laxilef/opentherm_library#esp32_timer
|
||||||
arduino-libraries/ArduinoMqttClient@^0.1.8
|
arduino-libraries/ArduinoMqttClient@^0.1.8
|
||||||
lennarthennigs/ESP Telnet@^2.2
|
lennarthennigs/ESP Telnet@^2.2.3
|
||||||
gyverlibs/FileData@^1.0.2
|
gyverlibs/FileData@^1.0.3
|
||||||
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
|
||||||
@@ -92,13 +92,13 @@ check_flags = ${env.check_flags}
|
|||||||
;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/55.03.30-2/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31/platform-espressif32.zip
|
||||||
platform_packages = ${env.platform_packages}
|
platform_packages = ${env.platform_packages}
|
||||||
board_build.partitions = esp32_partitions.csv
|
board_build.partitions = esp32_partitions.csv
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
laxilef/ESP32Scheduler@^1.0.1
|
laxilef/ESP32Scheduler@^1.0.1
|
||||||
nimble_lib = h2zero/NimBLE-Arduino@^2.1.0
|
nimble_lib = h2zero/NimBLE-Arduino@2.3.6
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
post:tools/esp32.py
|
post:tools/esp32.py
|
||||||
@@ -294,6 +294,10 @@ build_flags =
|
|||||||
check_tool = ${esp32_defaults.check_tool}
|
check_tool = ${esp32_defaults.check_tool}
|
||||||
check_flags = ${esp32_defaults.check_flags}
|
check_flags = ${esp32_defaults.check_flags}
|
||||||
|
|
||||||
|
[env:nodemcu_32_160mhz]
|
||||||
|
extends = env:nodemcu_32
|
||||||
|
board_build.f_cpu = 160000000L ; set frequency to 160MHz
|
||||||
|
|
||||||
[env:d1_mini32]
|
[env:d1_mini32]
|
||||||
platform = ${esp32_defaults.platform}
|
platform = ${esp32_defaults.platform}
|
||||||
platform_packages = ${esp32_defaults.platform_packages}
|
platform_packages = ${esp32_defaults.platform_packages}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
class HaHelper : public HomeAssistantHelper {
|
class HaHelper : public HomeAssistantHelper {
|
||||||
public:
|
public:
|
||||||
static const byte TEMP_SOURCE_HEATING = 0;
|
static const uint8_t TEMP_SOURCE_HEATING = 0;
|
||||||
static const byte TEMP_SOURCE_INDOOR = 1;
|
static const uint8_t TEMP_SOURCE_INDOOR = 1;
|
||||||
static const char AVAILABILITY_OT_CONN[];
|
static const char AVAILABILITY_OT_CONN[];
|
||||||
static const char AVAILABILITY_SENSOR_CONN[];
|
static const char AVAILABILITY_SENSOR_CONN[];
|
||||||
|
|
||||||
@@ -840,19 +840,19 @@ public:
|
|||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SWITCH), F("equitherm")).c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SWITCH), F("equitherm")).c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishInputEquithermSlope(bool enabledByDefault = true) {
|
bool publishInputEquithermFactorN(bool enabledByDefault = true) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_slope"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_n"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_NAME)] = F("Equitherm slope");
|
doc[FPSTR(HA_NAME)] = F("Equitherm factor N");
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:slope-uphill");
|
doc[FPSTR(HA_ICON)] = F("mdi:alpha-n-circle-outline");
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.slope|float(0)|round(3) }}");
|
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.n_factor|float(0)|round(3) }}");
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"slope\" : {{ value }}}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"n_factor\" : {{ value }}}}");
|
||||||
doc[FPSTR(HA_MIN)] = 0.001f;
|
doc[FPSTR(HA_MIN)] = 0.001f;
|
||||||
doc[FPSTR(HA_MAX)] = 10;
|
doc[FPSTR(HA_MAX)] = 10;
|
||||||
doc[FPSTR(HA_STEP)] = 0.001f;
|
doc[FPSTR(HA_STEP)] = 0.001f;
|
||||||
@@ -860,80 +860,56 @@ public:
|
|||||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_slope")).c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_n_factor")).c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishInputEquithermExponent(bool enabledByDefault = true) {
|
bool publishInputEquithermFactorK(bool enabledByDefault = true) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_exponent"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_k"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_NAME)] = F("Equitherm exponent");
|
doc[FPSTR(HA_NAME)] = F("Equitherm factor K");
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:exponent");
|
doc[FPSTR(HA_ICON)] = F("mdi:alpha-k-circle-outline");
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.exponent|float(0)|round(3) }}");
|
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.k_factor|float(0)|round(2) }}");
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"exponent\" : {{ value }}}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"k_factor\" : {{ value }}}}");
|
||||||
doc[FPSTR(HA_MIN)] = 0.1;
|
doc[FPSTR(HA_MIN)] = 0;
|
||||||
doc[FPSTR(HA_MAX)] = 2;
|
doc[FPSTR(HA_MAX)] = 10;
|
||||||
doc[FPSTR(HA_STEP)] = 0.001f;
|
|
||||||
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
|
||||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
|
||||||
doc.shrinkToFit();
|
|
||||||
|
|
||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_exponent")).c_str(), doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool publishInputEquithermShift(bool enabledByDefault = true) {
|
|
||||||
JsonDocument doc;
|
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_shift"));
|
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
|
||||||
doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE);
|
|
||||||
doc[FPSTR(HA_NAME)] = F("Equitherm shift");
|
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:chart-areaspline");
|
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.shift|float(0)|round(2) }}");
|
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"shift\" : {{ value }}}}");
|
|
||||||
doc[FPSTR(HA_MIN)] = -15;
|
|
||||||
doc[FPSTR(HA_MAX)] = 15;
|
|
||||||
doc[FPSTR(HA_STEP)] = 0.01f;
|
doc[FPSTR(HA_STEP)] = 0.01f;
|
||||||
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
||||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_shift")).c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_k_factor")).c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishInputEquithermTargetDiffFactor(bool enabledByDefault = true) {
|
bool publishInputEquithermFactorT(bool enabledByDefault = true) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->settingsTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->settingsTopic.c_str();
|
||||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.pid.enabled, 'offline', 'online') }}");
|
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.pid.enabled, 'offline', 'online') }}");
|
||||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_target_diff_factor"));
|
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("equitherm_t"));
|
||||||
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)];
|
||||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG);
|
||||||
doc[FPSTR(HA_NAME)] = F("Equitherm target diff factor");
|
doc[FPSTR(HA_NAME)] = F("Equitherm factor T");
|
||||||
doc[FPSTR(HA_ICON)] = F("mdi:chart-timeline-variant-shimmer");
|
doc[FPSTR(HA_ICON)] = F("mdi:alpha-t-circle-outline");
|
||||||
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
doc[FPSTR(HA_STATE_TOPIC)] = this->settingsTopic.c_str();
|
||||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.targetDiffFactor|float(0)|round(3) }}");
|
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.equitherm.t_factor|float(0)|round(2) }}");
|
||||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
doc[FPSTR(HA_COMMAND_TOPIC)] = this->setSettingsTopic.c_str();
|
||||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"targetDiffFactor\" : {{ value }}}}");
|
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"equitherm\": {\"t_factor\" : {{ value }}}}");
|
||||||
doc[FPSTR(HA_MIN)] = 0;
|
doc[FPSTR(HA_MIN)] = 0;
|
||||||
doc[FPSTR(HA_MAX)] = 10;
|
doc[FPSTR(HA_MAX)] = 10;
|
||||||
doc[FPSTR(HA_STEP)] = 0.001f;
|
doc[FPSTR(HA_STEP)] = 0.01f;
|
||||||
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX);
|
||||||
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter;
|
||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
|
|
||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_target_diff_factor")).c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_NUMBER), F("equitherm_t_factor")).c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1194,7 +1170,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool publishClimateHeating(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) {
|
bool publishClimateHeating(UnitSystem unit = UnitSystem::METRIC, uint8_t minTemp = 20, uint8_t maxTemp = 90, bool enabledByDefault = true) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
@@ -1246,7 +1222,7 @@ public:
|
|||||||
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_CLIMATE), F("heating"), '_').c_str(), doc);
|
return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_CLIMATE), F("heating"), '_').c_str(), doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishClimateDhw(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
|
bool publishClimateDhw(UnitSystem unit = UnitSystem::METRIC, uint8_t minTemp = 40, uint8_t maxTemp = 60, bool enabledByDefault = true) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str();
|
||||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||||
|
|||||||
@@ -502,10 +502,9 @@ protected:
|
|||||||
|
|
||||||
// equitherm
|
// equitherm
|
||||||
this->haHelper->publishSwitchEquitherm();
|
this->haHelper->publishSwitchEquitherm();
|
||||||
this->haHelper->publishInputEquithermSlope(false);
|
this->haHelper->publishInputEquithermFactorN(false);
|
||||||
this->haHelper->publishInputEquithermExponent(false);
|
this->haHelper->publishInputEquithermFactorK(false);
|
||||||
this->haHelper->publishInputEquithermShift(false);
|
this->haHelper->publishInputEquithermFactorT(false);
|
||||||
this->haHelper->publishInputEquithermTargetDiffFactor(false);
|
|
||||||
|
|
||||||
// states
|
// states
|
||||||
this->haHelper->publishStatusState();
|
this->haHelper->publishStatusState();
|
||||||
@@ -558,7 +557,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool publishNonStaticHaEntities(bool force = false) {
|
bool publishNonStaticHaEntities(bool force = false) {
|
||||||
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0;
|
static uint8_t _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0;
|
||||||
static bool _indoorTempControl, _dhwSupport = false;
|
static bool _indoorTempControl, _dhwSupport = false;
|
||||||
|
|
||||||
bool published = false;
|
bool published = false;
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ protected:
|
|||||||
|
|
||||||
CustomOpenTherm* instance = nullptr;
|
CustomOpenTherm* instance = nullptr;
|
||||||
unsigned long instanceCreatedTime = 0;
|
unsigned long instanceCreatedTime = 0;
|
||||||
byte instanceInGpio = 0;
|
uint8_t instanceInGpio = 0;
|
||||||
byte instanceOutGpio = 0;
|
uint8_t instanceOutGpio = 0;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
unsigned long connectedTime = 0;
|
unsigned long connectedTime = 0;
|
||||||
unsigned long disconnectedTime = 0;
|
unsigned long disconnectedTime = 0;
|
||||||
@@ -31,7 +31,7 @@ protected:
|
|||||||
unsigned long heatingSetTempTime = 0;
|
unsigned long heatingSetTempTime = 0;
|
||||||
unsigned long dhwSetTempTime = 0;
|
unsigned long dhwSetTempTime = 0;
|
||||||
unsigned long ch2SetTempTime = 0;
|
unsigned long ch2SetTempTime = 0;
|
||||||
byte configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED;
|
uint8_t configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
const char* getTaskName() override {
|
const char* getTaskName() override {
|
||||||
@@ -90,7 +90,7 @@ protected:
|
|||||||
|
|
||||||
Log.sinfoln(FPSTR(L_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inGpio, settings.opentherm.outGpio);
|
Log.sinfoln(FPSTR(L_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inGpio, settings.opentherm.outGpio);
|
||||||
|
|
||||||
this->instance->setAfterSendRequestCallback([this](unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) {
|
this->instance->setAfterSendRequestCallback([this](unsigned long request, unsigned long response, OpenThermResponseStatus status, uint8_t attempt) {
|
||||||
Log.sverboseln(
|
Log.sverboseln(
|
||||||
FPSTR(L_OT),
|
FPSTR(L_OT),
|
||||||
F("ID: %4d Request: %8lx Response: %8lx Msg type: %s Attempt: %2d Status: %s"),
|
F("ID: %4d Request: %8lx Response: %8lx Msg type: %s Attempt: %2d Status: %s"),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
#include <Equitherm.h>
|
||||||
#include <GyverPID.h>
|
#include <GyverPID.h>
|
||||||
|
|
||||||
|
Equitherm etRegulator;
|
||||||
GyverPID pidRegulator(0, 0, 0);
|
GyverPID pidRegulator(0, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
@@ -144,32 +146,39 @@ protected:
|
|||||||
|
|
||||||
// if use equitherm
|
// if use equitherm
|
||||||
if (settings.equitherm.enabled) {
|
if (settings.equitherm.enabled) {
|
||||||
float tempDelta = settings.heating.target - vars.master.heating.outdoorTemp;
|
unsigned short minTemp = settings.heating.minTemp;
|
||||||
float maxPoint = settings.heating.target - (
|
unsigned short maxTemp = settings.heating.maxTemp;
|
||||||
settings.heating.maxTemp - settings.heating.target
|
float targetTemp = settings.heating.target;
|
||||||
) / settings.equitherm.slope;
|
float indoorTemp = vars.master.heating.indoorTemp;
|
||||||
|
float outdoorTemp = vars.master.heating.outdoorTemp;
|
||||||
|
|
||||||
float sf = (settings.heating.maxTemp - settings.heating.target) / pow(
|
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||||
settings.heating.target - maxPoint,
|
minTemp = f2c(minTemp);
|
||||||
1.0f / settings.equitherm.exponent
|
maxTemp = f2c(maxTemp);
|
||||||
);
|
targetTemp = f2c(targetTemp);
|
||||||
float etResult = settings.heating.target + settings.equitherm.shift + sf * (
|
indoorTemp = f2c(indoorTemp);
|
||||||
tempDelta >= 0
|
outdoorTemp = f2c(outdoorTemp);
|
||||||
? pow(tempDelta, 1.0f / settings.equitherm.exponent)
|
|
||||||
: -(pow(-(tempDelta), 1.0f / settings.equitherm.exponent))
|
|
||||||
);
|
|
||||||
|
|
||||||
// add diff
|
|
||||||
if (this->indoorSensorsConnected && !settings.pid.enabled && !settings.heating.turbo) {
|
|
||||||
etResult += constrain(
|
|
||||||
settings.heating.target - vars.master.heating.indoorTemp,
|
|
||||||
-3.0f,
|
|
||||||
3.0f
|
|
||||||
) * settings.equitherm.targetDiffFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// limit
|
if (!this->indoorSensorsConnected || settings.pid.enabled) {
|
||||||
etResult = constrain(etResult, settings.heating.minTemp, settings.heating.maxTemp);
|
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) {
|
if (fabsf(prevEtResult - etResult) > 0.09f) {
|
||||||
prevEtResult = etResult;
|
prevEtResult = etResult;
|
||||||
@@ -204,7 +213,8 @@ protected:
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
float error = pidRegulator.setpoint - pidRegulator.input;
|
float error = pidRegulator.setpoint - pidRegulator.input;
|
||||||
bool hasDeadband = (error > -(settings.pid.deadband.thresholdHigh))
|
bool hasDeadband = settings.pid.deadband.enabled
|
||||||
|
&& (error > -(settings.pid.deadband.thresholdHigh))
|
||||||
&& (error < settings.pid.deadband.thresholdLow);
|
&& (error < settings.pid.deadband.thresholdLow);
|
||||||
|
|
||||||
if (hasDeadband) {
|
if (hasDeadband) {
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ public:
|
|||||||
|
|
||||||
uint8_t valueId = (uint8_t) valueType;
|
uint8_t valueId = (uint8_t) valueType;
|
||||||
if (!isValidValueId(valueId)) {
|
if (!isValidValueId(valueId)) {
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float value = 0.0f;
|
float value = 0.0f;
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ protected:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float sensorResistance = value > 0.001f
|
const float sensorResistance = value > 1
|
||||||
? DEFAULT_NTC_REF_RESISTANCE / (DEFAULT_NTC_VREF / (float) value - 1.0f)
|
? DEFAULT_NTC_REF_RESISTANCE / (DEFAULT_NTC_VREF / (float) value - 1.0f)
|
||||||
: 0.0f;
|
: 0.0f;
|
||||||
const float rawTemp = 1.0f / (
|
const float rawTemp = 1.0f / (
|
||||||
@@ -405,7 +405,6 @@ protected:
|
|||||||
|
|
||||||
#if USE_BLE
|
#if USE_BLE
|
||||||
void cleanBleInstances() {
|
void cleanBleInstances() {
|
||||||
#if USE_BLE
|
|
||||||
if (!NimBLEDevice::isInitialized()) {
|
if (!NimBLEDevice::isInitialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -442,10 +441,13 @@ protected:
|
|||||||
NimBLEDevice::deleteClient(client);
|
NimBLEDevice::deleteClient(client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pollingBleSensors() {
|
void pollingBleSensors() {
|
||||||
|
if (!Sensors::getAmountByType(Sensors::Type::BLUETOOTH, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!NimBLEDevice::isInitialized() && millis() > 5000) {
|
if (!NimBLEDevice::isInitialized() && millis() > 5000) {
|
||||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Initialized"));
|
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Initialized"));
|
||||||
BLEDevice::init("");
|
BLEDevice::init("");
|
||||||
@@ -577,7 +579,7 @@ protected:
|
|||||||
|
|
||||||
bool subscribeToBleDevice(const uint8_t sensorId, NimBLEClient* pClient) {
|
bool subscribeToBleDevice(const uint8_t sensorId, NimBLEClient* pClient) {
|
||||||
auto& sSensor = Sensors::settings[sensorId];
|
auto& sSensor = Sensors::settings[sensorId];
|
||||||
auto pAddress = pClient->getPeerAddress().toString().c_str();
|
auto pAddress = pClient->getPeerAddress().toString();
|
||||||
|
|
||||||
NimBLERemoteService* pService = nullptr;
|
NimBLERemoteService* pService = nullptr;
|
||||||
NimBLERemoteCharacteristic* pChar = nullptr;
|
NimBLERemoteCharacteristic* pChar = nullptr;
|
||||||
@@ -588,13 +590,13 @@ protected:
|
|||||||
if (!pService) {
|
if (!pService) {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to find env service (%s) on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to find env service (%s) on device %s"),
|
||||||
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found env service (%s) on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found env service (%s) on device %s"),
|
||||||
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
// 0x2A6E - Notify temperature x0.01C (pvvx)
|
// 0x2A6E - Notify temperature x0.01C (pvvx)
|
||||||
@@ -606,7 +608,7 @@ protected:
|
|||||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found temp char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found temp char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
pChar->unsubscribe();
|
pChar->unsubscribe();
|
||||||
@@ -661,14 +663,14 @@ protected:
|
|||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to temp char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to temp char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to temp char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to temp char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,7 +685,7 @@ protected:
|
|||||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found temp char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found temp char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
pChar->unsubscribe();
|
pChar->unsubscribe();
|
||||||
@@ -738,14 +740,14 @@ protected:
|
|||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to temp char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to temp char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to temp char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to temp char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,7 +756,7 @@ protected:
|
|||||||
if (!tempNotifyCreated) {
|
if (!tempNotifyCreated) {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': not found supported temp chars in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': not found supported temp chars in env service on device %s"),
|
||||||
sensorId, sSensor.name, pAddress
|
sensorId, sSensor.name, pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
pClient->disconnect();
|
pClient->disconnect();
|
||||||
@@ -772,7 +774,7 @@ protected:
|
|||||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found humidity char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found humidity char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
pChar->unsubscribe();
|
pChar->unsubscribe();
|
||||||
@@ -827,14 +829,14 @@ protected:
|
|||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to humidity char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to humidity char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to humidity char (%s) in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to humidity char (%s) in env service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -843,7 +845,7 @@ protected:
|
|||||||
if (!humidityNotifyCreated) {
|
if (!humidityNotifyCreated) {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': not found supported humidity chars in env service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': not found supported humidity chars in env service on device %s"),
|
||||||
sensorId, sSensor.name, pAddress
|
sensorId, sSensor.name, pAddress.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -857,13 +859,13 @@ protected:
|
|||||||
if (!pService) {
|
if (!pService) {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to find battery service (%s) on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to find battery service (%s) on device %s"),
|
||||||
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found battery service (%s) on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found battery service (%s) on device %s"),
|
||||||
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, serviceUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
// 0x2A19 - Notify the battery charge level 0..99% (pvvx)
|
// 0x2A19 - Notify the battery charge level 0..99% (pvvx)
|
||||||
@@ -875,7 +877,7 @@ protected:
|
|||||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found battery char (%s) in battery service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found battery char (%s) in battery service on device %s"),
|
||||||
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress
|
sensorId, sSensor.name, charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
pChar->unsubscribe();
|
pChar->unsubscribe();
|
||||||
@@ -930,14 +932,14 @@ protected:
|
|||||||
Log.straceln(
|
Log.straceln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to battery char (%s) in battery service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': subscribed to battery char (%s) in battery service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to battery char (%s) in battery service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': failed to subscribe to battery char (%s) in battery service on device %s"),
|
||||||
sensorId, sSensor.name,
|
sensorId, sSensor.name,
|
||||||
charUuid.toString().c_str(), pAddress
|
charUuid.toString().c_str(), pAddress.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -946,7 +948,7 @@ protected:
|
|||||||
if (!batteryNotifyCreated) {
|
if (!batteryNotifyCreated) {
|
||||||
Log.swarningln(
|
Log.swarningln(
|
||||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': not found supported battery chars in battery service on device %s"),
|
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': not found supported battery chars in battery service on device %s"),
|
||||||
sensorId, sSensor.name, pAddress
|
sensorId, sSensor.name, pAddress.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ struct NetworkSettings {
|
|||||||
struct {
|
struct {
|
||||||
char ssid[33] = DEFAULT_AP_SSID;
|
char ssid[33] = DEFAULT_AP_SSID;
|
||||||
char password[65] = DEFAULT_AP_PASSWORD;
|
char password[65] = DEFAULT_AP_PASSWORD;
|
||||||
byte channel = 6;
|
uint8_t channel = 6;
|
||||||
} ap;
|
} ap;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
char ssid[33] = DEFAULT_STA_SSID;
|
char ssid[33] = DEFAULT_STA_SSID;
|
||||||
char password[65] = DEFAULT_STA_PASSWORD;
|
char password[65] = DEFAULT_STA_PASSWORD;
|
||||||
byte channel = 0;
|
uint8_t channel = 0;
|
||||||
} sta;
|
} sta;
|
||||||
} networkSettings;
|
} networkSettings;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ struct Settings {
|
|||||||
} ntp;
|
} ntp;
|
||||||
|
|
||||||
UnitSystem unitSystem = UnitSystem::METRIC;
|
UnitSystem unitSystem = UnitSystem::METRIC;
|
||||||
byte statusLedGpio = DEFAULT_STATUS_LED_GPIO;
|
uint8_t statusLedGpio = DEFAULT_STATUS_LED_GPIO;
|
||||||
} system;
|
} system;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -54,9 +54,9 @@ struct Settings {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
UnitSystem unitSystem = UnitSystem::METRIC;
|
UnitSystem unitSystem = UnitSystem::METRIC;
|
||||||
byte inGpio = DEFAULT_OT_IN_GPIO;
|
uint8_t inGpio = DEFAULT_OT_IN_GPIO;
|
||||||
byte outGpio = DEFAULT_OT_OUT_GPIO;
|
uint8_t outGpio = DEFAULT_OT_OUT_GPIO;
|
||||||
byte rxLedGpio = DEFAULT_OT_RX_LED_GPIO;
|
uint8_t rxLedGpio = DEFAULT_OT_RX_LED_GPIO;
|
||||||
uint8_t memberId = 0;
|
uint8_t memberId = 0;
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
float minPower = 0.0f;
|
float minPower = 0.0f;
|
||||||
@@ -105,8 +105,8 @@ struct Settings {
|
|||||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||||
float hysteresis = 0.5f;
|
float hysteresis = 0.5f;
|
||||||
float turboFactor = 7.5f;
|
float turboFactor = 7.5f;
|
||||||
byte minTemp = DEFAULT_HEATING_MIN_TEMP;
|
uint8_t minTemp = DEFAULT_HEATING_MIN_TEMP;
|
||||||
byte maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
uint8_t maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||||
uint8_t maxModulation = 100;
|
uint8_t maxModulation = 100;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -123,8 +123,8 @@ struct Settings {
|
|||||||
struct {
|
struct {
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
float target = DEFAULT_DHW_TARGET_TEMP;
|
float target = DEFAULT_DHW_TARGET_TEMP;
|
||||||
byte minTemp = DEFAULT_DHW_MIN_TEMP;
|
uint8_t minTemp = DEFAULT_DHW_MIN_TEMP;
|
||||||
byte maxTemp = DEFAULT_DHW_MAX_TEMP;
|
uint8_t maxTemp = DEFAULT_DHW_MAX_TEMP;
|
||||||
uint8_t maxModulation = 100;
|
uint8_t maxModulation = 100;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -154,15 +154,14 @@ struct Settings {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
float slope = 0.7f;
|
float n_factor = 0.7f;
|
||||||
float exponent = 1.3f;
|
float k_factor = 3.0f;
|
||||||
float shift = 0.0f;
|
float t_factor = 2.0f;
|
||||||
float targetDiffFactor = 2.0f;
|
|
||||||
} equitherm;
|
} equitherm;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool use = false;
|
bool use = false;
|
||||||
byte gpio = DEFAULT_EXT_PUMP_GPIO;
|
uint8_t gpio = DEFAULT_EXT_PUMP_GPIO;
|
||||||
unsigned short postCirculationTime = 600;
|
unsigned short postCirculationTime = 600;
|
||||||
unsigned int antiStuckInterval = 2592000;
|
unsigned int antiStuckInterval = 2592000;
|
||||||
unsigned short antiStuckTime = 300;
|
unsigned short antiStuckTime = 300;
|
||||||
@@ -171,15 +170,15 @@ struct Settings {
|
|||||||
struct {
|
struct {
|
||||||
struct {
|
struct {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
byte gpio = GPIO_IS_NOT_CONFIGURED;
|
uint8_t gpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
byte invertState = false;
|
bool invertState = false;
|
||||||
unsigned short thresholdTime = 60;
|
unsigned short thresholdTime = 60;
|
||||||
} input;
|
} input;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
byte gpio = GPIO_IS_NOT_CONFIGURED;
|
uint8_t gpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
byte invertState = false;
|
bool invertState = false;
|
||||||
unsigned short thresholdTime = 60;
|
unsigned short thresholdTime = 60;
|
||||||
bool onFault = true;
|
bool onFault = true;
|
||||||
bool onLossConnection = true;
|
bool onLossConnection = true;
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ const char S_ENABLED[] PROGMEM = "enabled";
|
|||||||
const char S_ENV[] PROGMEM = "env";
|
const char S_ENV[] PROGMEM = "env";
|
||||||
const char S_EPC[] PROGMEM = "epc";
|
const char S_EPC[] PROGMEM = "epc";
|
||||||
const char S_EQUITHERM[] PROGMEM = "equitherm";
|
const char S_EQUITHERM[] PROGMEM = "equitherm";
|
||||||
const char S_EXPONENT[] PROGMEM = "exponent";
|
|
||||||
const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump";
|
const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump";
|
||||||
const char S_FACTOR[] PROGMEM = "factor";
|
const char S_FACTOR[] PROGMEM = "factor";
|
||||||
const char S_FAULT[] PROGMEM = "fault";
|
const char S_FAULT[] PROGMEM = "fault";
|
||||||
@@ -118,6 +117,7 @@ const char S_INVERT_STATE[] PROGMEM = "invertState";
|
|||||||
const char S_IP[] PROGMEM = "ip";
|
const char S_IP[] PROGMEM = "ip";
|
||||||
const char S_I_FACTOR[] PROGMEM = "i_factor";
|
const char S_I_FACTOR[] PROGMEM = "i_factor";
|
||||||
const char S_I_MULTIPLIER[] PROGMEM = "i_multiplier";
|
const char S_I_MULTIPLIER[] PROGMEM = "i_multiplier";
|
||||||
|
const char S_K_FACTOR[] PROGMEM = "k_factor";
|
||||||
const char S_LOGIN[] PROGMEM = "login";
|
const char S_LOGIN[] PROGMEM = "login";
|
||||||
const char S_LOG_LEVEL[] PROGMEM = "logLevel";
|
const char S_LOG_LEVEL[] PROGMEM = "logLevel";
|
||||||
const char S_LOW_TEMP[] PROGMEM = "lowTemp";
|
const char S_LOW_TEMP[] PROGMEM = "lowTemp";
|
||||||
@@ -143,6 +143,7 @@ const char S_NAME[] PROGMEM = "name";
|
|||||||
const char S_NATIVE_HEATING_CONTROL[] PROGMEM = "nativeHeatingControl";
|
const char S_NATIVE_HEATING_CONTROL[] PROGMEM = "nativeHeatingControl";
|
||||||
const char S_NETWORK[] PROGMEM = "network";
|
const char S_NETWORK[] PROGMEM = "network";
|
||||||
const char S_NTP[] PROGMEM = "ntp";
|
const char S_NTP[] PROGMEM = "ntp";
|
||||||
|
const char S_N_FACTOR[] PROGMEM = "n_factor";
|
||||||
const char S_OFFSET[] PROGMEM = "offset";
|
const char S_OFFSET[] PROGMEM = "offset";
|
||||||
const char S_ON_ENABLED_HEATING[] PROGMEM = "onEnabledHeating";
|
const char S_ON_ENABLED_HEATING[] PROGMEM = "onEnabledHeating";
|
||||||
const char S_ON_FAULT[] PROGMEM = "onFault";
|
const char S_ON_FAULT[] PROGMEM = "onFault";
|
||||||
@@ -181,11 +182,9 @@ const char S_SERIAL[] PROGMEM = "serial";
|
|||||||
const char S_SERVER[] PROGMEM = "server";
|
const char S_SERVER[] PROGMEM = "server";
|
||||||
const char S_SETTINGS[] PROGMEM = "settings";
|
const char S_SETTINGS[] PROGMEM = "settings";
|
||||||
const char S_SET_DATE_AND_TIME[] PROGMEM = "setDateAndTime";
|
const char S_SET_DATE_AND_TIME[] PROGMEM = "setDateAndTime";
|
||||||
const char S_SHIFT[] PROGMEM = "shift";
|
|
||||||
const char S_SIGNAL_QUALITY[] PROGMEM = "signalQuality";
|
const char S_SIGNAL_QUALITY[] PROGMEM = "signalQuality";
|
||||||
const char S_SIZE[] PROGMEM = "size";
|
const char S_SIZE[] PROGMEM = "size";
|
||||||
const char S_SLAVE[] PROGMEM = "slave";
|
const char S_SLAVE[] PROGMEM = "slave";
|
||||||
const char S_SLOPE[] PROGMEM = "slope";
|
|
||||||
const char S_SSID[] PROGMEM = "ssid";
|
const char S_SSID[] PROGMEM = "ssid";
|
||||||
const char S_STA[] PROGMEM = "sta";
|
const char S_STA[] PROGMEM = "sta";
|
||||||
const char S_STATE[] PROGMEM = "state";
|
const char S_STATE[] PROGMEM = "state";
|
||||||
@@ -196,7 +195,6 @@ const char S_SUBNET[] PROGMEM = "subnet";
|
|||||||
const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode";
|
const char S_SUMMER_WINTER_MODE[] PROGMEM = "summerWinterMode";
|
||||||
const char S_SYSTEM[] PROGMEM = "system";
|
const char S_SYSTEM[] PROGMEM = "system";
|
||||||
const char S_TARGET[] PROGMEM = "target";
|
const char S_TARGET[] PROGMEM = "target";
|
||||||
const char S_TARGET_DIFF_FACTOR[] PROGMEM = "targetDiffFactor";
|
|
||||||
const char S_TARGET_TEMP[] PROGMEM = "targetTemp";
|
const char S_TARGET_TEMP[] PROGMEM = "targetTemp";
|
||||||
const char S_TELNET[] PROGMEM = "telnet";
|
const char S_TELNET[] PROGMEM = "telnet";
|
||||||
const char S_TEMPERATURE[] PROGMEM = "temperature";
|
const char S_TEMPERATURE[] PROGMEM = "temperature";
|
||||||
@@ -209,6 +207,7 @@ const char S_TRESHOLD_TIME[] PROGMEM = "tresholdTime";
|
|||||||
const char S_TURBO[] PROGMEM = "turbo";
|
const char S_TURBO[] PROGMEM = "turbo";
|
||||||
const char S_TURBO_FACTOR[] PROGMEM = "turboFactor";
|
const char S_TURBO_FACTOR[] PROGMEM = "turboFactor";
|
||||||
const char S_TYPE[] PROGMEM = "type";
|
const char S_TYPE[] PROGMEM = "type";
|
||||||
|
const char S_T_FACTOR[] PROGMEM = "t_factor";
|
||||||
const char S_UNIT_SYSTEM[] PROGMEM = "unitSystem";
|
const char S_UNIT_SYSTEM[] PROGMEM = "unitSystem";
|
||||||
const char S_UPTIME[] PROGMEM = "uptime";
|
const char S_UPTIME[] PROGMEM = "uptime";
|
||||||
const char S_USE[] PROGMEM = "use";
|
const char S_USE[] PROGMEM = "use";
|
||||||
|
|||||||
48
src/utils.h
48
src/utils.h
@@ -72,7 +72,7 @@ time_t mkgmtime(const struct tm *ptm) {
|
|||||||
|
|
||||||
inline bool isDigit(const char* ptr) {
|
inline bool isDigit(const char* ptr) {
|
||||||
char* endPtr;
|
char* endPtr;
|
||||||
strtol(ptr, &endPtr, 10);
|
auto tmp = strtol(ptr, &endPtr, 10);
|
||||||
return *endPtr == 0;
|
return *endPtr == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,10 +517,9 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
|||||||
|
|
||||||
auto equitherm = dst[FPSTR(S_EQUITHERM)].to<JsonObject>();
|
auto equitherm = dst[FPSTR(S_EQUITHERM)].to<JsonObject>();
|
||||||
equitherm[FPSTR(S_ENABLED)] = src.equitherm.enabled;
|
equitherm[FPSTR(S_ENABLED)] = src.equitherm.enabled;
|
||||||
equitherm[FPSTR(S_SLOPE)] = roundf(src.equitherm.slope, 3);
|
equitherm[FPSTR(S_N_FACTOR)] = roundf(src.equitherm.n_factor, 3);
|
||||||
equitherm[FPSTR(S_EXPONENT)] = roundf(src.equitherm.exponent, 3);
|
equitherm[FPSTR(S_K_FACTOR)] = roundf(src.equitherm.k_factor, 3);
|
||||||
equitherm[FPSTR(S_SHIFT)] = roundf(src.equitherm.shift, 2);
|
equitherm[FPSTR(S_T_FACTOR)] = roundf(src.equitherm.t_factor, 3);
|
||||||
equitherm[FPSTR(S_TARGET_DIFF_FACTOR)] = roundf(src.equitherm.targetDiffFactor, 3);
|
|
||||||
|
|
||||||
auto pid = dst[FPSTR(S_PID)].to<JsonObject>();
|
auto pid = dst[FPSTR(S_PID)].to<JsonObject>();
|
||||||
pid[FPSTR(S_ENABLED)] = src.pid.enabled;
|
pid[FPSTR(S_ENABLED)] = src.pid.enabled;
|
||||||
@@ -1127,38 +1126,29 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].isNull()) {
|
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_N_FACTOR)].isNull()) {
|
||||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SLOPE)].as<float>();
|
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_N_FACTOR)].as<float>();
|
||||||
|
|
||||||
if (value > 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.slope) > 0.0001f) {
|
if (value > 0 && value <= 10 && fabsf(value - dst.equitherm.n_factor) > 0.0001f) {
|
||||||
dst.equitherm.slope = roundf(value, 3);
|
dst.equitherm.n_factor = roundf(value, 3);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].isNull()) {
|
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_K_FACTOR)].isNull()) {
|
||||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_EXPONENT)].as<float>();
|
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_K_FACTOR)].as<float>();
|
||||||
|
|
||||||
if (value > 0.0f && value <= 2.0f && fabsf(value - dst.equitherm.exponent) > 0.0001f) {
|
if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.k_factor) > 0.0001f) {
|
||||||
dst.equitherm.exponent = roundf(value, 3);
|
dst.equitherm.k_factor = roundf(value, 3);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].isNull()) {
|
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].isNull()) {
|
||||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_SHIFT)].as<float>();
|
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_T_FACTOR)].as<float>();
|
||||||
|
|
||||||
if (value >= -15.0f && value <= 15.0f && fabsf(value - dst.equitherm.shift) > 0.0001f) {
|
if (value >= 0 && value <= 10 && fabsf(value - dst.equitherm.t_factor) > 0.0001f) {
|
||||||
dst.equitherm.shift = roundf(value, 2);
|
dst.equitherm.t_factor = roundf(value, 3);
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].isNull()) {
|
|
||||||
float value = src[FPSTR(S_EQUITHERM)][FPSTR(S_TARGET_DIFF_FACTOR)].as<float>();
|
|
||||||
|
|
||||||
if (value >= 0.0f && value <= 10.0f && fabsf(value - dst.equitherm.targetDiffFactor) > 0.0001f) {
|
|
||||||
dst.equitherm.targetDiffFactor = roundf(value, 3);
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1334,7 +1324,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
if (!src[FPSTR(S_HEATING)][FPSTR(S_MIN_TEMP)].isNull()) {
|
if (!src[FPSTR(S_HEATING)][FPSTR(S_MIN_TEMP)].isNull()) {
|
||||||
unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_MIN_TEMP)].as<unsigned char>();
|
unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_MIN_TEMP)].as<unsigned char>();
|
||||||
|
|
||||||
if (value != dst.heating.minTemp && value >= vars.slave.heating.minTemp && value < vars.slave.heating.maxTemp && value != dst.heating.minTemp) {
|
if (value != dst.heating.minTemp && value >= vars.slave.heating.minTemp && value < vars.slave.heating.maxTemp && value != dst.heating.maxTemp) {
|
||||||
dst.heating.minTemp = value;
|
dst.heating.minTemp = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@@ -1343,7 +1333,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
if (!src[FPSTR(S_HEATING)][FPSTR(S_MAX_TEMP)].isNull()) {
|
if (!src[FPSTR(S_HEATING)][FPSTR(S_MAX_TEMP)].isNull()) {
|
||||||
unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_MAX_TEMP)].as<unsigned char>();
|
unsigned char value = src[FPSTR(S_HEATING)][FPSTR(S_MAX_TEMP)].as<unsigned char>();
|
||||||
|
|
||||||
if (value != dst.heating.maxTemp && value > vars.slave.heating.minTemp && value <= vars.slave.heating.maxTemp && value != dst.heating.maxTemp) {
|
if (value != dst.heating.maxTemp && value > vars.slave.heating.minTemp && value <= vars.slave.heating.maxTemp && value != dst.heating.minTemp) {
|
||||||
dst.heating.maxTemp = value;
|
dst.heating.maxTemp = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@@ -1904,7 +1894,7 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se
|
|||||||
|
|
||||||
// gpio
|
// gpio
|
||||||
if (!src[FPSTR(S_GPIO)].isNull()) {
|
if (!src[FPSTR(S_GPIO)].isNull()) {
|
||||||
if (dst.type != Sensors::Type::DALLAS_TEMP && dst.type == Sensors::Type::BLUETOOTH && dst.type == Sensors::Type::NTC_10K_TEMP) {
|
if (dst.type != Sensors::Type::DALLAS_TEMP && dst.type != Sensors::Type::NTC_10K_TEMP) {
|
||||||
if (dst.gpio != GPIO_IS_NOT_CONFIGURED) {
|
if (dst.gpio != GPIO_IS_NOT_CONFIGURED) {
|
||||||
dst.gpio = GPIO_IS_NOT_CONFIGURED;
|
dst.gpio = GPIO_IS_NOT_CONFIGURED;
|
||||||
changed = true;
|
changed = true;
|
||||||
|
|||||||
@@ -370,26 +370,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"equitherm": {
|
"equitherm": {
|
||||||
"slope": {
|
"n": "N factor",
|
||||||
"title": "Slope",
|
"k": "K factor",
|
||||||
"note": "Heat loss compensation. Main tuning parameter."
|
"t": {
|
||||||
},
|
|
||||||
"exponent": {
|
|
||||||
"title": "Exponent",
|
|
||||||
"note": "Radiator efficiency. Typical values: <code>1.1</code> - Floor heating, <code>1.2</code> - Cast iron, <code>1.3</code> - Panel radiators, <code>1.4</code> - Convectors."
|
|
||||||
},
|
|
||||||
"shift": {
|
|
||||||
"title": "Shift",
|
|
||||||
"note": "Compensates for additional heat losses (e.g., in pipes) or extra heat sources."
|
|
||||||
},
|
|
||||||
"targetDiffFactor": {
|
|
||||||
"title": "T factor",
|
"title": "T factor",
|
||||||
"note": "Not used if PID is enabled. Adds to the setpoint the difference between the target and current indoor temp: <code>setpoint = setpoint + ((target - indoor) * T)</code>."
|
"note": "Not used if PID is enabled"
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"targetTemp": "Target indoor temperature",
|
|
||||||
"setpointTemp": "Heat carrier temperature",
|
|
||||||
"outdoorTemp": "Outdoor temperature"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -370,26 +370,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"equitherm": {
|
"equitherm": {
|
||||||
"slope": {
|
"n": "Fattore N",
|
||||||
"title": "Pendenza",
|
"k": "Fattore K",
|
||||||
"note": "Compensazione delle perdite di calore. Principale parametro di regolazione."
|
"t": {
|
||||||
},
|
|
||||||
"exponent": {
|
|
||||||
"title": "Esponente",
|
|
||||||
"note": "Efficienza del radiatore. Valori tipici: <code>1.1</code> - Riscaldamento a pavimento, <code>1.2</code> - Radiatori in ghisa, <code>1.3</code> - Radiatori a pannelli, <code>1.4</code> - Convetttori."
|
|
||||||
},
|
|
||||||
"shift": {
|
|
||||||
"title": "Spostare",
|
|
||||||
"note": "Compensa le perdite di calore aggiuntive (ad esempio, nelle tubature) o fonti di calore extra."
|
|
||||||
},
|
|
||||||
"targetDiffFactor": {
|
|
||||||
"title": "Fattore T",
|
"title": "Fattore T",
|
||||||
"note": "Non usato se PID è attivato. Aggiunge al setpoint la differenza tra la temperatura interna target e quella attuale: <code>setpoint = setpoint + ((target - indoor) * T)</code>."
|
"note": "Non usato se PID è attivato"
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"targetTemp": "Temperatura interna target",
|
|
||||||
"setpointTemp": "Temperatura del portatore di calore",
|
|
||||||
"outdoorTemp": "Temperatura esterna"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -370,26 +370,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"equitherm": {
|
"equitherm": {
|
||||||
"slope": {
|
"n": "Коэффициент N",
|
||||||
"title": "Наклон",
|
"k": "Коэффициент K",
|
||||||
"note": "Компенсация теплопотерь. Основной параметр настройки."
|
"t": {
|
||||||
},
|
|
||||||
"exponent": {
|
|
||||||
"title": "Экспонента",
|
|
||||||
"note": "Эффективность радиатора. Типичные значения: <code>1.1</code> - Тёплый пол, <code>1.2</code> - Чугунные радиаторы, <code>1.3</code> - Панельные радиаторы, <code>1.4</code> - Конвекторы."
|
|
||||||
},
|
|
||||||
"shift": {
|
|
||||||
"title": "Смещение",
|
|
||||||
"note": "Компенсирует дополнительные теплопотери (например, в трубах) или дополнительные источники тепла."
|
|
||||||
},
|
|
||||||
"targetDiffFactor": {
|
|
||||||
"title": "Коэффициент T",
|
"title": "Коэффициент T",
|
||||||
"note": "Не используется, если ПИД включен. Добавляет разницу между целевой и текущей температурой в помещении: <code>setpoint = setpoint + ((target - indoor) * T)</code>."
|
"note": "Не используется, если ПИД включен"
|
||||||
},
|
|
||||||
"chart": {
|
|
||||||
"targetTemp": "Целевая внутренняя температура",
|
|
||||||
"setpointTemp": "Температура теплоносителя",
|
|
||||||
"outdoorTemp": "Наружная температура"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -240,9 +240,7 @@
|
|||||||
setCheckboxValue("[name='filtering']", data.filtering, sensorForm);
|
setCheckboxValue("[name='filtering']", data.filtering, sensorForm);
|
||||||
setInputValue("[name='filteringFactor']", data.filteringFactor, {}, sensorForm);
|
setInputValue("[name='filteringFactor']", data.filteringFactor, {}, sensorForm);
|
||||||
|
|
||||||
setTimeout(() => {
|
sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change"));
|
||||||
sensorForm.querySelector("[name='type']").dispatchEvent(new Event("change"));
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
setBusy(".form-busy", "form", false, sensorNode);
|
setBusy(".form-busy", "form", false, sensorNode);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -105,7 +105,7 @@
|
|||||||
<option disabled selected data-i18n>settings.system.ntp.timezonePresets</option>
|
<option disabled selected data-i18n>settings.system.ntp.timezonePresets</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -353,44 +353,21 @@
|
|||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<canvas id="etChart"></canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<div>
|
|
||||||
<span data-i18n>settings.equitherm.chart.targetTemp</span>: <b class="etChartTargetTempValue"></b>°
|
|
||||||
</div>
|
|
||||||
<input class="etChartTargetTemp" type="range" value="0" min="0" max="0" step="0.5">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.equitherm.slope.title</span>
|
<span data-i18n>settings.equitherm.n</span>
|
||||||
<input type="number" inputmode="decimal" name="equitherm[slope]" min="0.001" max="10" step="0.001" required>
|
<input type="number" inputmode="decimal" name="equitherm[n_factor]" min="0.001" max="10" step="0.001" required>
|
||||||
<small data-i18n>settings.equitherm.slope.note</small>
|
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.equitherm.exponent.title</span>
|
<span data-i18n>settings.equitherm.k</span>
|
||||||
<input type="number" inputmode="decimal" name="equitherm[exponent]" min="0.1" max="2" step="0.001" required>
|
<input type="number" inputmode="decimal" name="equitherm[k_factor]" min="0" max="10" step="0.01" required>
|
||||||
<small data-i18n>settings.equitherm.exponent.note</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid">
|
|
||||||
<label>
|
|
||||||
<span data-i18n>settings.equitherm.shift.title</span>
|
|
||||||
<input type="number" inputmode="decimal" name="equitherm[shift]" min="-15" max="15" step="0.01" required>
|
|
||||||
<small data-i18n>settings.equitherm.shift.note</small>
|
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.equitherm.targetDiffFactor.title</span>
|
<span data-i18n>settings.equitherm.t.title</span>
|
||||||
<input type="number" inputmode="decimal" name="equitherm[targetDiffFactor]" min="0" max="10" step="0.01" required>
|
<input type="number" inputmode="decimal" name="equitherm[t_factor]" min="0" max="10" step="0.01" required>
|
||||||
<small data-i18n>settings.equitherm.targetDiffFactor.note</small>
|
<small data-i18n>settings.equitherm.t.note</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -446,7 +423,7 @@
|
|||||||
<span data-i18n>settings.temp.min</span>
|
<span data-i18n>settings.temp.min</span>
|
||||||
<input type="number" inputmode="decimal" name="pid[minTemp]" min="0" max="0" step="1" required>
|
<input type="number" inputmode="decimal" name="pid[minTemp]" min="0" max="0" step="1" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.temp.max</span>
|
<span data-i18n>settings.temp.max</span>
|
||||||
<input type="number" inputmode="numeric" name="pid[maxTemp]" min="0" max="0" step="1" required>
|
<input type="number" inputmode="numeric" name="pid[maxTemp]" min="0" max="0" step="1" required>
|
||||||
@@ -475,12 +452,12 @@
|
|||||||
<span data-i18n>settings.pid.deadband.p_multiplier</span>
|
<span data-i18n>settings.pid.deadband.p_multiplier</span>
|
||||||
<input type="number" inputmode="decimal" name="pid[deadband][p_multiplier]" min="0" max="5" step="0.001" required>
|
<input type="number" inputmode="decimal" name="pid[deadband][p_multiplier]" min="0" max="5" step="0.001" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.pid.deadband.i_multiplier</span>
|
<span data-i18n>settings.pid.deadband.i_multiplier</span>
|
||||||
<input type="number" inputmode="decimal" name="pid[deadband][i_multiplier]" min="0" max="1" step="0.001" required>
|
<input type="number" inputmode="decimal" name="pid[deadband][i_multiplier]" min="0" max="1" step="0.001" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.pid.deadband.d_multiplier</span>
|
<span data-i18n>settings.pid.deadband.d_multiplier</span>
|
||||||
<input type="number" inputmode="decimal" name="pid[deadband][d_multiplier]" min="0" max="1" step="0.001" required>
|
<input type="number" inputmode="decimal" name="pid[deadband][d_multiplier]" min="0" max="1" step="0.001" required>
|
||||||
@@ -492,7 +469,7 @@
|
|||||||
<span data-i18n>settings.pid.deadband.thresholdHigh</span>
|
<span data-i18n>settings.pid.deadband.thresholdHigh</span>
|
||||||
<input type="number" inputmode="decimal" name="pid[deadband][thresholdHigh]" min="0" max="5" step="0.01" required>
|
<input type="number" inputmode="decimal" name="pid[deadband][thresholdHigh]" min="0" max="5" step="0.01" required>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span data-i18n>settings.pid.deadband.thresholdLow</span>
|
<span data-i18n>settings.pid.deadband.thresholdLow</span>
|
||||||
<input type="number" inputmode="decimal" name="pid[deadband][thresholdLow]" min="0" max="5" step="0.01" required>
|
<input type="number" inputmode="decimal" name="pid[deadband][thresholdLow]" min="0" max="5" step="0.01" required>
|
||||||
@@ -674,7 +651,7 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" data-i18n>button.save</button>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -883,163 +860,11 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/app.js?{BUILD_TIME}"></script>
|
<script src="/static/app.js?{BUILD_TIME}"></script>
|
||||||
<script src="/static/chart.js?{BUILD_TIME}"></script>
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const lang = new Lang(document.getElementById('lang'));
|
const lang = new Lang(document.getElementById('lang'));
|
||||||
lang.build();
|
lang.build();
|
||||||
|
|
||||||
let etChart = null;
|
|
||||||
let etChartConfig = {
|
|
||||||
slope: null,
|
|
||||||
exponent: null,
|
|
||||||
shift: null,
|
|
||||||
unitSystem: null,
|
|
||||||
targetTemp: null,
|
|
||||||
minTemp: null,
|
|
||||||
maxTemp: null,
|
|
||||||
decimated: false
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasNeedDecimationChart = () => {
|
|
||||||
return window.innerWidth <= 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeEquithermChart = () => {
|
|
||||||
if (etChart == null) {
|
|
||||||
const ctx = document.getElementById('etChart').getContext('2d');
|
|
||||||
|
|
||||||
try {
|
|
||||||
etChart = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [{
|
|
||||||
borderColor: (context) => {
|
|
||||||
const chart = context.chart;
|
|
||||||
const { ctx, chartArea } = chart;
|
|
||||||
|
|
||||||
if (!chartArea) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gradient = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
|
|
||||||
gradient.addColorStop(0, 'rgba(1, 114, 173, 1)');
|
|
||||||
gradient.addColorStop(0.5, 'rgba(255, 99, 132, 1)');
|
|
||||||
|
|
||||||
return gradient;
|
|
||||||
},
|
|
||||||
borderWidth: 3,
|
|
||||||
fill: false,
|
|
||||||
tension: 0.1,
|
|
||||||
pointRadius: 2,
|
|
||||||
pointHoverRadius: 4,
|
|
||||||
indexAxis: "x",
|
|
||||||
data: []
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
resizeDelay: 500,
|
|
||||||
parsing: false,
|
|
||||||
interaction: {
|
|
||||||
mode: 'nearest',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
tooltip: {
|
|
||||||
enabled: true,
|
|
||||||
position: 'nearest',
|
|
||||||
displayColors: false,
|
|
||||||
callbacks: {
|
|
||||||
title: (items) => {
|
|
||||||
return `${i18n("settings.equitherm.chart.outdoorTemp")}: ${items[0].label}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
display: true,
|
|
||||||
type: "linear",
|
|
||||||
reverse: true,
|
|
||||||
title: {
|
|
||||||
display: true
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
stepSize: 1,
|
|
||||||
format: {
|
|
||||||
style: "unit",
|
|
||||||
unit: "degree",
|
|
||||||
unitDisplay: "narrow"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
display: true,
|
|
||||||
title: {
|
|
||||||
display: true
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
format: {
|
|
||||||
style: "unit",
|
|
||||||
unit: "degree",
|
|
||||||
unitDisplay: "narrow"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!etChart) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = [];
|
|
||||||
etChartConfig.decimated = hasNeedDecimationChart();
|
|
||||||
for (let value = 30; value >= -30; value -= etChartConfig.decimated ? 2 : 1) {
|
|
||||||
const outdoorTemp = etChartConfig.unitSystem == 0 ? value : c2f(value);
|
|
||||||
|
|
||||||
data.push({
|
|
||||||
x: parseFloat(outdoorTemp.toFixed(1)),
|
|
||||||
y: parseFloat(calculateEquithermTemp(outdoorTemp).toFixed(1))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
etChart.data.datasets[0].data = data;
|
|
||||||
etChart.data.datasets[0].label = i18n("settings.equitherm.chart.setpointTemp");
|
|
||||||
etChart.options.scales.x.title.text = i18n("settings.equitherm.chart.outdoorTemp");
|
|
||||||
etChart.options.scales.y.title.text = i18n("settings.equitherm.chart.setpointTemp");
|
|
||||||
etChart.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateEquithermTemp = (outdoorTemp) => {
|
|
||||||
const tempDelta = etChartConfig.targetTemp - outdoorTemp;
|
|
||||||
const maxPoint = etChartConfig.targetTemp - (
|
|
||||||
etChartConfig.maxTemp - etChartConfig.targetTemp
|
|
||||||
) / etChartConfig.slope;
|
|
||||||
|
|
||||||
const sf = (etChartConfig.maxTemp - etChartConfig.targetTemp) / Math.pow(
|
|
||||||
etChartConfig.targetTemp - maxPoint,
|
|
||||||
1 / etChartConfig.exponent
|
|
||||||
);
|
|
||||||
const result = etChartConfig.targetTemp + etChartConfig.shift + sf * (
|
|
||||||
tempDelta >= 0
|
|
||||||
? Math.pow(tempDelta, 1 / etChartConfig.exponent)
|
|
||||||
: -(Math.pow(-(tempDelta), 1 / etChartConfig.exponent))
|
|
||||||
);
|
|
||||||
|
|
||||||
return Math.max(Math.min(result, etChartConfig.maxTemp), etChartConfig.minTemp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fillData = (data) => {
|
const fillData = (data) => {
|
||||||
// System
|
// System
|
||||||
setSelectValue("[name='system[logLevel]']", data.system.logLevel);
|
setSelectValue("[name='system[logLevel]']", data.system.logLevel);
|
||||||
@@ -1187,10 +1012,9 @@
|
|||||||
|
|
||||||
// Equitherm
|
// Equitherm
|
||||||
setCheckboxValue("[name='equitherm[enabled]']", data.equitherm.enabled);
|
setCheckboxValue("[name='equitherm[enabled]']", data.equitherm.enabled);
|
||||||
setInputValue("[name='equitherm[slope]']", data.equitherm.slope);
|
setInputValue("[name='equitherm[n_factor]']", data.equitherm.n_factor);
|
||||||
setInputValue("[name='equitherm[exponent]']", data.equitherm.exponent);
|
setInputValue("[name='equitherm[k_factor]']", data.equitherm.k_factor);
|
||||||
setInputValue("[name='equitherm[shift]']", data.equitherm.shift);
|
setInputValue("[name='equitherm[t_factor]']", data.equitherm.t_factor);
|
||||||
setInputValue("[name='equitherm[targetDiffFactor]']", data.equitherm.targetDiffFactor);
|
|
||||||
setBusy('#equitherm-settings-busy', '#equitherm-settings', false);
|
setBusy('#equitherm-settings-busy', '#equitherm-settings', false);
|
||||||
|
|
||||||
// PID
|
// PID
|
||||||
@@ -1214,24 +1038,6 @@
|
|||||||
setInputValue("[name='pid[deadband][thresholdHigh]']", data.pid.deadband.thresholdHigh);
|
setInputValue("[name='pid[deadband][thresholdHigh]']", data.pid.deadband.thresholdHigh);
|
||||||
setInputValue("[name='pid[deadband][thresholdLow]']", data.pid.deadband.thresholdLow);
|
setInputValue("[name='pid[deadband][thresholdLow]']", data.pid.deadband.thresholdLow);
|
||||||
setBusy('#pid-settings-busy', '#pid-settings', false);
|
setBusy('#pid-settings-busy', '#pid-settings', false);
|
||||||
|
|
||||||
const etMinTemp = parseInt(data.system.unitSystem == 0 ? 5 : 41);
|
|
||||||
const etMaxTemp = parseInt(data.system.unitSystem == 0 ? 30 : 86);
|
|
||||||
const etTargetTemp = constrain(parseFloat(data.heating.target), etMinTemp, etMaxTemp);
|
|
||||||
|
|
||||||
setInputValue(".etChartTargetTemp", etTargetTemp.toFixed(1), {
|
|
||||||
"min": etMinTemp,
|
|
||||||
"max": etMaxTemp
|
|
||||||
});
|
|
||||||
|
|
||||||
etChartConfig.slope = data.equitherm.slope;
|
|
||||||
etChartConfig.exponent = data.equitherm.exponent;
|
|
||||||
etChartConfig.shift = data.equitherm.shift;
|
|
||||||
etChartConfig.unitSystem = data.system.unitSystem;
|
|
||||||
etChartConfig.minTemp = data.heating.minTemp;
|
|
||||||
etChartConfig.maxTemp = data.heating.maxTemp;
|
|
||||||
|
|
||||||
makeEquithermChart();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1261,7 +1067,7 @@
|
|||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
credentials: "include"
|
credentials: "include"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Response not valid');
|
throw new Error('Response not valid');
|
||||||
}
|
}
|
||||||
@@ -1284,57 +1090,6 @@
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector(".etChartTargetTemp").addEventListener("input", async (event) => {
|
|
||||||
setValue('.etChartTargetTempValue', parseFloat(event.target.value).toFixed(1));
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector(".etChartTargetTemp").addEventListener("change", async (event) => {
|
|
||||||
if (!event.target.checkValidity()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
etChartConfig.targetTemp = parseFloat(event.target.value);
|
|
||||||
setValue('.etChartTargetTempValue', etChartConfig.targetTemp.toFixed(1));
|
|
||||||
makeEquithermChart();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector("[name='equitherm[slope]']").addEventListener("change", async (event) => {
|
|
||||||
if (!event.target.checkValidity()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
etChartConfig.slope = parseFloat(event.target.value);
|
|
||||||
makeEquithermChart();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector("[name='equitherm[exponent]']").addEventListener("change", async (event) => {
|
|
||||||
if (!event.target.checkValidity()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
etChartConfig.exponent = parseFloat(event.target.value);
|
|
||||||
makeEquithermChart();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector("[name='equitherm[shift]']").addEventListener("change", async (event) => {
|
|
||||||
if (!event.target.checkValidity()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
etChartConfig.shift = parseFloat(event.target.value);
|
|
||||||
makeEquithermChart();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('resize', async (event) => {
|
|
||||||
if (etChart) {
|
|
||||||
etChart.resize();
|
|
||||||
|
|
||||||
if (etChartConfig.decimated != hasNeedDecimationChart()) {
|
|
||||||
makeEquithermChart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,13 +5,8 @@ const setupForm = (formSelector, onResultCallback = null, noCastItems = []) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.querySelectorAll('input').forEach(item => {
|
form.querySelectorAll('input').forEach(item => {
|
||||||
item.addEventListener('change', (event) => {
|
item.addEventListener('change', (e) => {
|
||||||
if (!event.target.checkValidity()) {
|
e.target.setAttribute('aria-invalid', !e.target.checkValidity());
|
||||||
event.target.setAttribute('aria-invalid', true);
|
|
||||||
|
|
||||||
} else if (event.target.hasAttribute('aria-invalid')) {
|
|
||||||
event.target.removeAttribute('aria-invalid');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -635,10 +630,6 @@ const setCheckboxValue = (selector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.checked = value;
|
item.checked = value;
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
item.dispatchEvent(new Event("change"));
|
|
||||||
}, 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const setRadioValue = (selector, value, parent = undefined) => {
|
const setRadioValue = (selector, value, parent = undefined) => {
|
||||||
@@ -652,14 +643,7 @@ const setRadioValue = (selector, value, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
const checked = item.value == value;
|
item.checked = item.value == value;
|
||||||
if (item.checked != checked) {
|
|
||||||
item.checked = checked;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
item.dispatchEvent(new Event("change"));
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,17 +658,13 @@ const setInputValue = (selector, value, attrs = {}, parent = undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
|
item.value = value;
|
||||||
|
|
||||||
if (attrs instanceof Object) {
|
if (attrs instanceof Object) {
|
||||||
for (let attrKey of Object.keys(attrs)) {
|
for (let attrKey of Object.keys(attrs)) {
|
||||||
item.setAttribute(attrKey, attrs[attrKey]);
|
item.setAttribute(attrKey, attrs[attrKey]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item.value = value;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
item.dispatchEvent(new Event("change"));
|
|
||||||
}, 10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,12 +853,4 @@ function dec2hex(i) {
|
|||||||
|
|
||||||
function constrain(amt, low, high) {
|
function constrain(amt, low, high) {
|
||||||
return ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)));
|
return ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)));
|
||||||
}
|
|
||||||
|
|
||||||
function c2f(value) {
|
|
||||||
return (9 / 5) * value + 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
function f2c(value) {
|
|
||||||
return (value - 32) * (5 / 9);
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user