mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +05:00
Compare commits
38 Commits
1.4.0-rc.1
...
1.4.0-rc.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b89f61ed58 | ||
|
|
ab1566bd45 | ||
|
|
86734ab622 | ||
|
|
0a8dd2a076 | ||
|
|
a7a561622e | ||
|
|
b0e0f6fd7d | ||
|
|
53eaa1d7f1 | ||
|
|
a7d796e0cc | ||
|
|
4490b38130 | ||
|
|
0ede2240a2 | ||
|
|
0cff35ee12 | ||
|
|
14aef20234 | ||
|
|
560f8fbd51 | ||
|
|
946414ad31 | ||
|
|
39a29042e1 | ||
|
|
f544f01caa | ||
|
|
41cca76bfa | ||
|
|
942bc53043 | ||
|
|
1bad689b6b | ||
|
|
2f4dbcc205 | ||
|
|
9e3ef7a465 | ||
|
|
a5f6749101 | ||
|
|
b07dd46f55 | ||
|
|
07ab121788 | ||
|
|
7cbc52a8b0 | ||
|
|
e090be380c | ||
|
|
0bf49d2249 | ||
|
|
a83d94d361 | ||
|
|
358980da4c | ||
|
|
f91e39d067 | ||
|
|
1d53f21d46 | ||
|
|
c225e7c2a8 | ||
|
|
6831c4331f | ||
|
|
8fb62ce8ae | ||
|
|
e829a00355 | ||
|
|
bee720386a | ||
|
|
c4b6eadb81 | ||
|
|
a5d2b9fcfa |
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||
<br>
|
||||
[](https://github.com/Laxilef/OTGateway/releases)
|
||||
[](https://github.com/Laxilef/OTGateway/releases)
|
||||
[](https://github.com/Laxilef/OTGateway/releases/latest)
|
||||
[](LICENSE.txt)
|
||||
[](https://t.me/otgateway)
|
||||
@@ -16,7 +16,7 @@
|
||||
- PID
|
||||
- Equithermic curves - adjusts the temperature based on indoor and outdoor temperatures
|
||||
- Hysteresis setting (for accurate maintenance of room temperature)
|
||||
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](#compatible-temperature-sensors))
|
||||
- Ability to connect an external sensors to monitor outdoor and indoor temperature ([compatible sensors](https://github.com/Laxilef/OTGateway/wiki/Compatibility#temperature-sensors))
|
||||
- Emergency mode. If the Wi-Fi connection is lost or the gateway cannot connect to the MQTT server, the mode will turn on. This mode will automatically maintain the set temperature and prevent your home from freezing. In this mode it is also possible to use equithermal curves (weather-compensated control).
|
||||
- Automatic error reset (not with all boilers)
|
||||
- Diagnostics:
|
||||
@@ -72,7 +72,7 @@ All available information and instructions can be found in the wiki:
|
||||
- [ESP32Scheduler](https://github.com/laxilef/ESP32Scheduler) (for ESP32)
|
||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
||||
- [OpenTherm Library](https://github.com/ihormelnyk/opentherm_library)
|
||||
- [ArduinoMqttClient](https://github.com/arduino-libraries/ArduinoMqttClient) /
|
||||
- [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)
|
||||
|
||||
@@ -168,11 +168,11 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Pressure:</th>
|
||||
<td><b class="ot-pressure"></b> bar</td>
|
||||
<td><b class="ot-pressure"></b> <span class="pressure-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">DHW flow rate:</th>
|
||||
<td><b class="ot-dhw-flow-rate"></b> l/min</td>
|
||||
<td><b class="ot-dhw-flow-rate"></b> <span class="volume-unit"></span>/min</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Fault code:</th>
|
||||
@@ -180,23 +180,31 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Indoor temp:</th>
|
||||
<td><b class="indoor-temp"></b> C</td>
|
||||
<td><b class="indoor-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Outdoor temp:</th>
|
||||
<td><b class="outdoor-temp"></b> C</td>
|
||||
<td><b class="outdoor-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Heating temp:</th>
|
||||
<td><b class="heating-temp"></b> C</td>
|
||||
<td><b class="heating-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Heating setpoint temp:</th>
|
||||
<td><b class="heating-setpoint-temp"></b> C</td>
|
||||
<td><b class="heating-setpoint-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Heating return temp:</th>
|
||||
<td><b class="heating-return-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">DHW temp:</th>
|
||||
<td><b class="dhw-temp"></b> C</td>
|
||||
<td><b class="dhw-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Exhaust temp:</th>
|
||||
<td><b class="exhaust-temp"></b> <span class="temp-unit"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -210,7 +218,7 @@
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -227,4 +235,4 @@
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -163,4 +163,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -62,14 +62,26 @@
|
||||
|
||||
<div id="opentherm-settings-busy" aria-busy="true"></div>
|
||||
<form action="/api/settings" id="opentherm-settings" class="hidden">
|
||||
<div class="grid">
|
||||
<label for="opentherm-in-pin">
|
||||
In GPIO
|
||||
<input type="number" inputmode="numeric" class="opentherm-in-pin" name="opentherm[inPin]" min="0" max="99" step="1" required>
|
||||
<fieldset>
|
||||
<legend>Unit system</legend>
|
||||
<label>
|
||||
<input type="radio" class="opentherm-unit-system" name="opentherm[unitSystem]" value="0" />
|
||||
Metric (celsius)
|
||||
</label>
|
||||
<label for="opentherm-in-pin">
|
||||
<label>
|
||||
<input type="radio" class="opentherm-unit-system" name="opentherm[unitSystem]" value="1" />
|
||||
Imperial (fahrenheit)
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div class="grid">
|
||||
<label for="opentherm-in-gpio">
|
||||
In GPIO
|
||||
<input type="number" inputmode="numeric" class="opentherm-in-gpio" name="opentherm[inGpio]" min="0" max="254" step="1">
|
||||
</label>
|
||||
<label for="opentherm-in-gpio">
|
||||
Out GPIO
|
||||
<input type="number" inputmode="numeric" class="opentherm-out-pin" name="opentherm[outPin]" min="0" max="99" step="1" required>
|
||||
<input type="number" inputmode="numeric" class="opentherm-out-gpio" name="opentherm[outGpio]" min="0" max="254" step="1">
|
||||
</label>
|
||||
<label for="opentherm-member-id-code">
|
||||
Master MemberID code
|
||||
@@ -77,10 +89,6 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<mark>After changing GPIO, the ESP must be restarted for the changes to take effect.</mark>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Options</legend>
|
||||
<label for="opentherm-dhw-present">
|
||||
@@ -111,6 +119,10 @@
|
||||
<input type="checkbox" class="opentherm-sync-modulation-with-heating" name="opentherm[modulationSyncWithHeating]" value="true">
|
||||
Sync modulation with heating
|
||||
</label>
|
||||
<label for="opentherm-get-min-max-temp">
|
||||
<input type="checkbox" class="opentherm-get-min-max-temp" name="opentherm[getMinMaxTemp]" value="true">
|
||||
Get min/max temp from boiler
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<button type="submit">Save</button>
|
||||
@@ -190,9 +202,9 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<label for="outdoor-sensor-pin">
|
||||
<label for="outdoor-sensor-gpio">
|
||||
GPIO
|
||||
<input type="number" inputmode="numeric" class="outdoor-sensor-pin" name="sensors[outdoor][pin]" min="0" max="99" step="1" required>
|
||||
<input type="number" inputmode="numeric" class="outdoor-sensor-gpio" name="sensors[outdoor][gpio]" min="0" max="254" step="1">
|
||||
</label>
|
||||
<label for="outdoor-sensor-offset">
|
||||
Temp offset (calibration)
|
||||
@@ -229,9 +241,9 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<label for="indoor-sensor-pin">
|
||||
<label for="indoor-sensor-gpio">
|
||||
GPIO
|
||||
<input type="number" inputmode="numeric" class="indoor-sensor-pin" name="sensors[indoor][pin]" min="0" max="99" step="1" required>
|
||||
<input type="number" inputmode="numeric" class="indoor-sensor-gpio" name="sensors[indoor][gpio]" min="0" max="254" step="1">
|
||||
</label>
|
||||
|
||||
<div class="grid">
|
||||
@@ -267,9 +279,9 @@
|
||||
<br>
|
||||
|
||||
<div class="grid">
|
||||
<label for="extpump-pin">
|
||||
<label for="extpump-gpio">
|
||||
Relay GPIO
|
||||
<input type="number" inputmode="numeric" class="extpump-pin" name="externalPump[pin]" min="0" max="99" step="1" required>
|
||||
<input type="number" inputmode="numeric" class="extpump-gpio" name="externalPump[gpio]" min="0" max="254" step="1">
|
||||
</label>
|
||||
<label for="extpump-pc-time">
|
||||
Post circulation time <small>(min)</small>
|
||||
@@ -302,6 +314,18 @@
|
||||
|
||||
<div id="system-settings-busy" aria-busy="true"></div>
|
||||
<form action="/api/settings" id="system-settings" class="hidden">
|
||||
<fieldset>
|
||||
<legend>Unit system</legend>
|
||||
<label>
|
||||
<input type="radio" class="system-unit-system" name="system[unitSystem]" value="0" />
|
||||
Metric (celsius)
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" class="system-unit-system" name="system[unitSystem]" value="1" />
|
||||
Imperial (fahrenheit)
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="system-debug">
|
||||
<input type="checkbox" class="system-debug" name="system[debug]" value="true">
|
||||
@@ -333,7 +357,7 @@
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -354,4 +378,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
@@ -91,7 +91,7 @@
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/LICENSE" target="_blank" class="secondary">License</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/blob/master/" target="_blank" class="secondary">Source code</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/wiki" target="_blank" class="secondary">Help</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issue" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/issues" target="_blank" class="secondary">Issue & questions</a>
|
||||
• <a href="https://github.com/Laxilef/OTGateway/releases" target="_blank" class="secondary">Releases</a>
|
||||
</small>
|
||||
</footer>
|
||||
@@ -105,4 +105,4 @@
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -8,6 +8,7 @@ public:
|
||||
typedef std::function<void(unsigned long, unsigned long, OpenThermResponseStatus, byte)> AfterSendRequestCallback;
|
||||
|
||||
CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {}
|
||||
~CustomOpenTherm() {}
|
||||
|
||||
CustomOpenTherm* setYieldCallback(YieldCallback callback = nullptr) {
|
||||
this->yieldCallback = callback;
|
||||
@@ -46,7 +47,7 @@ public:
|
||||
|
||||
unsigned long _response;
|
||||
OpenThermResponseStatus _responseStatus = OpenThermResponseStatus::NONE;
|
||||
if (!this->sendRequestAync(request)) {
|
||||
if (!this->sendRequestAsync(request)) {
|
||||
_response = 0;
|
||||
|
||||
} else {
|
||||
@@ -88,7 +89,7 @@ public:
|
||||
| (dhwBlocking << 6);
|
||||
data <<= 8;
|
||||
|
||||
return this->sendRequest(this->buildRequest(
|
||||
return this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::READ_DATA,
|
||||
OpenThermMessageID::Status,
|
||||
data
|
||||
@@ -96,30 +97,30 @@ public:
|
||||
}
|
||||
|
||||
bool setHeatingCh1Temp(float temperature) {
|
||||
unsigned long response = this->sendRequest(this->buildRequest(
|
||||
unsigned long response = this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::TSet,
|
||||
this->temperatureToData(temperature)
|
||||
temperatureToData(temperature)
|
||||
));
|
||||
|
||||
return isValidResponse(response);
|
||||
}
|
||||
|
||||
bool setHeatingCh2Temp(float temperature) {
|
||||
unsigned long response = this->sendRequest(this->buildRequest(
|
||||
unsigned long response = this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::TsetCH2,
|
||||
this->temperatureToData(temperature)
|
||||
temperatureToData(temperature)
|
||||
));
|
||||
|
||||
return isValidResponse(response);
|
||||
}
|
||||
|
||||
bool setDhwTemp(float temperature) {
|
||||
unsigned long response = this->sendRequest(this->buildRequest(
|
||||
unsigned long response = this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::TdhwSet,
|
||||
this->temperatureToData(temperature)
|
||||
temperatureToData(temperature)
|
||||
));
|
||||
|
||||
return isValidResponse(response);
|
||||
@@ -128,9 +129,9 @@ public:
|
||||
bool sendBoilerReset() {
|
||||
unsigned int data = 1;
|
||||
data <<= 8;
|
||||
unsigned long response = this->sendRequest(this->buildRequest(
|
||||
unsigned long response = this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::Command,
|
||||
OpenThermMessageID::RemoteRequest,
|
||||
data
|
||||
));
|
||||
|
||||
@@ -140,9 +141,9 @@ public:
|
||||
bool sendServiceReset() {
|
||||
unsigned int data = 10;
|
||||
data <<= 8;
|
||||
unsigned long response = this->sendRequest(this->buildRequest(
|
||||
unsigned long response = this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::Command,
|
||||
OpenThermMessageID::RemoteRequest,
|
||||
data
|
||||
));
|
||||
|
||||
@@ -152,9 +153,9 @@ public:
|
||||
bool sendWaterFilling() {
|
||||
unsigned int data = 2;
|
||||
data <<= 8;
|
||||
unsigned long response = this->sendRequest(this->buildRequest(
|
||||
unsigned long response = this->sendRequest(buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::Command,
|
||||
OpenThermMessageID::RemoteRequest,
|
||||
data
|
||||
));
|
||||
|
||||
@@ -162,24 +163,13 @@ public:
|
||||
}
|
||||
|
||||
// converters
|
||||
float fromF88(unsigned long response) {
|
||||
const byte valueLB = response & 0xFF;
|
||||
const byte valueHB = (response >> 8) & 0xFF;
|
||||
|
||||
float value = (int8_t)valueHB;
|
||||
return value + (float)valueLB / 256.0;
|
||||
}
|
||||
|
||||
template <class T> unsigned int toF88(T val) {
|
||||
template <class T>
|
||||
static unsigned int toFloat(const T val) {
|
||||
return (unsigned int)(val * 256);
|
||||
}
|
||||
|
||||
int16_t fromS16(unsigned long response) {
|
||||
const byte valueLB = response & 0xFF;
|
||||
const byte valueHB = (response >> 8) & 0xFF;
|
||||
|
||||
int16_t value = valueHB;
|
||||
return ((value << 8) + valueLB);
|
||||
static short getInt(const unsigned long response) {
|
||||
return response & 0xffff;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
float Kk = 0.0;
|
||||
float Kt = 0.0;
|
||||
|
||||
Equitherm() {}
|
||||
Equitherm() = default;
|
||||
|
||||
// kn, kk, kt
|
||||
Equitherm(float new_kn, float new_kk, float new_kt) {
|
||||
|
||||
@@ -6,7 +6,7 @@ class HomeAssistantHelper {
|
||||
public:
|
||||
typedef std::function<void(const char*, bool)> PublishEventCallback;
|
||||
|
||||
HomeAssistantHelper() {}
|
||||
HomeAssistantHelper() = default;
|
||||
|
||||
void setWriter() {
|
||||
this->writer = nullptr;
|
||||
|
||||
@@ -33,6 +33,8 @@ const char HA_AVAILABILITY_MODE[] PROGMEM = "availability_mode";
|
||||
const char HA_TOPIC[] PROGMEM = "topic";
|
||||
const char HA_DEVICE_CLASS[] PROGMEM = "device_class";
|
||||
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_F[] PROGMEM = "°F";
|
||||
const char HA_ICON[] PROGMEM = "icon";
|
||||
const char HA_MIN[] PROGMEM = "min";
|
||||
const char HA_MAX[] PROGMEM = "max";
|
||||
@@ -50,6 +52,7 @@ const char HA_TEMPERATURE_COMMAND_TOPIC[] PROGMEM = "temperature_command_t
|
||||
const char HA_TEMPERATURE_COMMAND_TEMPLATE[] PROGMEM = "temperature_command_template";
|
||||
const char HA_TEMPERATURE_STATE_TOPIC[] PROGMEM = "temperature_state_topic";
|
||||
const char HA_TEMPERATURE_STATE_TEMPLATE[] PROGMEM = "temperature_state_template";
|
||||
const char HA_TEMPERATURE_UNIT[] PROGMEM = "temperature_unit";
|
||||
const char HA_MODE_COMMAND_TOPIC[] PROGMEM = "mode_command_topic";
|
||||
const char HA_MODE_COMMAND_TEMPLATE[] PROGMEM = "mode_command_template";
|
||||
const char HA_MODE_STATE_TOPIC[] PROGMEM = "mode_state_topic";
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Network {
|
||||
return this;
|
||||
}
|
||||
|
||||
Manager* setStaticConfig(IPAddress &ip, IPAddress &gateway, IPAddress &subnet, IPAddress &dns) {
|
||||
Manager* setStaticConfig(IPAddress& ip, IPAddress& gateway, IPAddress& subnet, IPAddress& dns) {
|
||||
this->staticIp = ip;
|
||||
this->staticGateway = gateway;
|
||||
this->staticSubnet = subnet;
|
||||
@@ -181,7 +181,12 @@ namespace Network {
|
||||
wifi_station_dhcpc_set_maxtry(5);
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// Nothing. Because memory leaks when turn off WiFi on ESP32, bug?
|
||||
return true;
|
||||
#else
|
||||
return WiFi.mode(WIFI_OFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
@@ -328,12 +333,12 @@ namespace Network {
|
||||
} else if (!this->isConnecting() && this->hasStaCredentials() && (!this->prevReconnectingTime || millis() - this->prevReconnectingTime > this->reconnectInterval)) {
|
||||
Log.sinfoln(FPSTR(L_NETWORK), F("Try connect..."));
|
||||
|
||||
this->reconnectFlag = false;
|
||||
this->reconnectFlag = false;
|
||||
Connection::reset();
|
||||
if (!this->connect(true, this->connectionTimeout)) {
|
||||
Log.straceln(FPSTR(L_NETWORK), F("Connection failed. Status: %d, reason: %d"), Connection::getStatus(), Connection::getDisconnectReason());
|
||||
}
|
||||
|
||||
|
||||
this->prevReconnectingTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ public:
|
||||
|
||||
} else {
|
||||
sizeArgName = length - size_t(argStartPos - currentBuf) - 1;
|
||||
Serial.printf("sizeArgName: %d\r\n", sizeArgName);
|
||||
|
||||
// send all content if arg len > space
|
||||
if (sizeArgName >= sizeof(argName)) {
|
||||
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
Log.serrorln(
|
||||
FPSTR(L_PORTAL_OTA),
|
||||
F("File '%s', on writing %d bytes: %s"),
|
||||
upload.filename.c_str(), upload.totalSize, result->error
|
||||
upload.filename.c_str(), upload.totalSize, result->error.c_str()
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
137
platformio.ini
137
platformio.ini
@@ -15,9 +15,9 @@ extra_configs = secrets.default.ini
|
||||
[env]
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@^7.0.3
|
||||
;ihormelnyk/OpenTherm Library@^1.1.4
|
||||
https://github.com/Laxilef/opentherm_library/archive/refs/heads/fix_start_bit.zip
|
||||
bblanchon/ArduinoJson@^7.0.4
|
||||
;ihormelnyk/OpenTherm Library@^1.1.5
|
||||
https://github.com/Laxilef/opentherm_library/archive/refs/heads/fix_lambda.zip
|
||||
arduino-libraries/ArduinoMqttClient@^0.1.8
|
||||
lennarthennigs/ESP Telnet@^2.2
|
||||
gyverlibs/FileData@^1.0.2
|
||||
@@ -30,44 +30,43 @@ build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
|
||||
-mtext-section-literals
|
||||
-D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1
|
||||
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_PORT=Serial
|
||||
;-D DEBUG_ESP_CORE -D DEBUG_ESP_WIFI -D DEBUG_ESP_HTTP_SERVER -D DEBUG_ESP_PORT=Serial
|
||||
-D USE_SERIAL=${secrets.use_serial}
|
||||
-D USE_TELNET=${secrets.use_telnet}
|
||||
-D DEBUG_BY_DEFAULT=${secrets.debug}
|
||||
-D HOSTNAME_DEFAULT='"${secrets.hostname}"'
|
||||
-D AP_SSID_DEFAULT='"${secrets.ap_ssid}"'
|
||||
-D AP_PASSWORD_DEFAULT='"${secrets.ap_password}"'
|
||||
-D STA_SSID_DEFAULT='"${secrets.sta_ssid}"'
|
||||
-D STA_PASSWORD_DEFAULT='"${secrets.sta_password}"'
|
||||
-D PORTAL_LOGIN_DEFAULT='"${secrets.portal_login}"'
|
||||
-D PORTAL_PASSWORD_DEFAULT='"${secrets.portal_password}"'
|
||||
-D MQTT_SERVER_DEFAULT='"${secrets.mqtt_server}"'
|
||||
-D MQTT_PORT_DEFAULT=${secrets.mqtt_port}
|
||||
-D MQTT_USER_DEFAULT='"${secrets.mqtt_user}"'
|
||||
-D MQTT_PASSWORD_DEFAULT='"${secrets.mqtt_password}"'
|
||||
-D MQTT_PREFIX_DEFAULT='"${secrets.mqtt_prefix}"'
|
||||
-D DEFAULT_HOSTNAME='"${secrets.hostname}"'
|
||||
-D DEFAULT_AP_SSID='"${secrets.ap_ssid}"'
|
||||
-D DEFAULT_AP_PASSWORD='"${secrets.ap_password}"'
|
||||
-D DEFAULT_STA_SSID='"${secrets.sta_ssid}"'
|
||||
-D DEFAULT_STA_PASSWORD='"${secrets.sta_password}"'
|
||||
-D DEFAULT_PORTAL_LOGIN='"${secrets.portal_login}"'
|
||||
-D DEFAULT_PORTAL_PASSWORD='"${secrets.portal_password}"'
|
||||
-D DEFAULT_MQTT_SERVER='"${secrets.mqtt_server}"'
|
||||
-D DEFAULT_MQTT_PORT=${secrets.mqtt_port}
|
||||
-D DEFAULT_MQTT_USER='"${secrets.mqtt_user}"'
|
||||
-D DEFAULT_MQTT_PASSWORD='"${secrets.mqtt_password}"'
|
||||
-D DEFAULT_MQTT_PREFIX='"${secrets.mqtt_prefix}"'
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
monitor_filters = direct
|
||||
board_build.flash_mode = dio
|
||||
board_build.filesystem = littlefs
|
||||
version = 1.4.0-rc.14
|
||||
version = 1.4.0-rc.20
|
||||
|
||||
; Defaults
|
||||
[esp8266_defaults]
|
||||
platform = espressif8266
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
nrwiersma/ESP8266Scheduler@^1.1
|
||||
nrwiersma/ESP8266Scheduler@^1.2
|
||||
lib_ignore =
|
||||
extra_scripts =
|
||||
post:tools/build.py
|
||||
build_flags = ${env.build_flags}
|
||||
board_build.ldscript = eagle.flash.1m256.ld
|
||||
;board_build.ldscript = eagle.flash.4m1m.ld
|
||||
board_build.ldscript = eagle.flash.4m1m.ld
|
||||
|
||||
[esp32_defaults]
|
||||
platform = espressif32
|
||||
platform = espressif32@^6.6
|
||||
board_build.partitions = esp32_partitions.csv
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
@@ -91,12 +90,12 @@ extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||
build_flags =
|
||||
${esp8266_defaults.build_flags}
|
||||
-D OT_IN_PIN_DEFAULT=4
|
||||
-D OT_OUT_PIN_DEFAULT=5
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=12
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=14
|
||||
-D LED_STATUS_PIN=13
|
||||
-D LED_OT_RX_PIN=15
|
||||
-D DEFAULT_OT_IN_GPIO=4
|
||||
-D DEFAULT_OT_OUT_GPIO=5
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=12
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=14
|
||||
-D LED_STATUS_GPIO=13
|
||||
-D LED_OT_RX_GPIO=15
|
||||
|
||||
[env:d1_mini_lite]
|
||||
platform = ${esp8266_defaults.platform}
|
||||
@@ -107,12 +106,12 @@ extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||
build_flags =
|
||||
${esp8266_defaults.build_flags}
|
||||
-D OT_IN_PIN_DEFAULT=4
|
||||
-D OT_OUT_PIN_DEFAULT=5
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=12
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=14
|
||||
-D LED_STATUS_PIN=13
|
||||
-D LED_OT_RX_PIN=15
|
||||
-D DEFAULT_OT_IN_GPIO=4
|
||||
-D DEFAULT_OT_OUT_GPIO=5
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=12
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=14
|
||||
-D LED_STATUS_GPIO=13
|
||||
-D LED_OT_RX_GPIO=15
|
||||
|
||||
[env:d1_mini_pro]
|
||||
platform = ${esp8266_defaults.platform}
|
||||
@@ -123,12 +122,12 @@ extra_scripts = ${esp8266_defaults.extra_scripts}
|
||||
board_build.ldscript = ${esp8266_defaults.board_build.ldscript}
|
||||
build_flags =
|
||||
${esp8266_defaults.build_flags}
|
||||
-D OT_IN_PIN_DEFAULT=4
|
||||
-D OT_OUT_PIN_DEFAULT=5
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=12
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=14
|
||||
-D LED_STATUS_PIN=13
|
||||
-D LED_OT_RX_PIN=15
|
||||
-D DEFAULT_OT_IN_GPIO=4
|
||||
-D DEFAULT_OT_OUT_GPIO=5
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=12
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=14
|
||||
-D LED_STATUS_GPIO=13
|
||||
-D LED_OT_RX_GPIO=15
|
||||
|
||||
[env:s2_mini]
|
||||
platform = ${esp32_defaults.platform}
|
||||
@@ -139,12 +138,12 @@ lib_ignore = ${esp32_defaults.lib_ignore}
|
||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D OT_IN_PIN_DEFAULT=33
|
||||
-D OT_OUT_PIN_DEFAULT=35
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=9
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=7
|
||||
-D LED_STATUS_PIN=11
|
||||
-D LED_OT_RX_PIN=12
|
||||
-D DEFAULT_OT_IN_GPIO=33
|
||||
-D DEFAULT_OT_OUT_GPIO=35
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=9
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=7
|
||||
-D LED_STATUS_GPIO=11
|
||||
-D LED_OT_RX_GPIO=12
|
||||
|
||||
[env:s3_mini]
|
||||
platform = ${esp32_defaults.platform}
|
||||
@@ -158,12 +157,12 @@ extra_scripts = ${esp32_defaults.extra_scripts}
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D USE_BLE=1
|
||||
-D OT_IN_PIN_DEFAULT=35
|
||||
-D OT_OUT_PIN_DEFAULT=36
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=13
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=12
|
||||
-D LED_STATUS_PIN=11
|
||||
-D LED_OT_RX_PIN=10
|
||||
-D DEFAULT_OT_IN_GPIO=35
|
||||
-D DEFAULT_OT_OUT_GPIO=36
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=13
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=12
|
||||
-D LED_STATUS_GPIO=11
|
||||
-D LED_OT_RX_GPIO=10
|
||||
|
||||
[env:c3_mini]
|
||||
platform = ${esp32_defaults.platform}
|
||||
@@ -179,12 +178,12 @@ build_unflags =
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D USE_BLE=1
|
||||
-D OT_IN_PIN_DEFAULT=8
|
||||
-D OT_OUT_PIN_DEFAULT=10
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=0
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=1
|
||||
-D LED_STATUS_PIN=4
|
||||
-D LED_OT_RX_PIN=5
|
||||
-D DEFAULT_OT_IN_GPIO=8
|
||||
-D DEFAULT_OT_OUT_GPIO=10
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=0
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=1
|
||||
-D LED_STATUS_GPIO=4
|
||||
-D LED_OT_RX_GPIO=5
|
||||
|
||||
[env:nodemcu_32s]
|
||||
platform = ${esp32_defaults.platform}
|
||||
@@ -198,12 +197,12 @@ extra_scripts = ${esp32_defaults.extra_scripts}
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D USE_BLE=1
|
||||
-D OT_IN_PIN_DEFAULT=21
|
||||
-D OT_OUT_PIN_DEFAULT=22
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=12
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=13
|
||||
-D LED_STATUS_PIN=2 ; 18
|
||||
-D LED_OT_RX_PIN=19
|
||||
-D DEFAULT_OT_IN_GPIO=21
|
||||
-D DEFAULT_OT_OUT_GPIO=22
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=12
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=13
|
||||
-D LED_STATUS_GPIO=2 ; 18
|
||||
-D LED_OT_RX_GPIO=19
|
||||
;-D WOKWI=1
|
||||
|
||||
[env:d1_mini32]
|
||||
@@ -218,9 +217,9 @@ extra_scripts = ${esp32_defaults.extra_scripts}
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D USE_BLE=1
|
||||
-D OT_IN_PIN_DEFAULT=21
|
||||
-D OT_OUT_PIN_DEFAULT=22
|
||||
-D SENSOR_OUTDOOR_PIN_DEFAULT=12
|
||||
-D SENSOR_INDOOR_PIN_DEFAULT=18
|
||||
-D LED_STATUS_PIN=2
|
||||
-D LED_OT_RX_PIN=19
|
||||
-D DEFAULT_OT_IN_GPIO=21
|
||||
-D DEFAULT_OT_OUT_GPIO=22
|
||||
-D DEFAULT_SENSOR_OUTDOOR_GPIO=12
|
||||
-D DEFAULT_SENSOR_INDOOR_GPIO=18
|
||||
-D LED_STATUS_GPIO=2
|
||||
-D LED_OT_RX_GPIO=19
|
||||
|
||||
524
src/HaHelper.h
524
src/HaHelper.h
@@ -6,59 +6,6 @@ public:
|
||||
static const byte TEMP_SOURCE_HEATING = 0;
|
||||
static const byte TEMP_SOURCE_INDOOR = 1;
|
||||
|
||||
bool publishNumberOutdoorSensorOffset(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_sensor_offset"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_sensor_offset"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
doc[FPSTR(HA_NAME)] = F("Outdoor sensor offset");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.sensors.outdoor.offset|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"outdoor\" : {\"offset\" : {{ value }}}}}");
|
||||
doc[FPSTR(HA_MIN)] = -10;
|
||||
doc[FPSTR(HA_MAX)] = 10;
|
||||
doc[FPSTR(HA_STEP)] = 0.1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
doc.shrinkToFit();
|
||||
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("outdoor_sensor_offset")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberIndoorSensorOffset(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.indoor.type != 1, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_sensor_offset"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_sensor_offset"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
doc[FPSTR(HA_NAME)] = F("Indoor sensor offset");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.sensors.indoor.offset|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"indoor\" : {\"offset\" : {{ value }}}}}");
|
||||
doc[FPSTR(HA_MIN)] = -10;
|
||||
doc[FPSTR(HA_MAX)] = 10;
|
||||
doc[FPSTR(HA_STEP)] = 0.1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
doc.shrinkToFit();
|
||||
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("indoor_sensor_offset")).c_str(), doc);
|
||||
}
|
||||
|
||||
|
||||
bool publishSwitchEmergency(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -80,22 +27,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("emergency")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberEmergencyTarget(bool enabledByDefault = true) {
|
||||
bool publishNumberEmergencyTarget(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("emergency_target"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("emergency_target"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 5;
|
||||
doc[FPSTR(HA_MAX)] = 50;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 41;
|
||||
doc[FPSTR(HA_MAX)] = 122;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Emergency target temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-alert");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.emergency.target|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"emergency\": {\"target\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 5;
|
||||
doc[FPSTR(HA_MAX)] = 50;
|
||||
doc[FPSTR(HA_STEP)] = 0.5;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -195,7 +151,7 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("heating_turbo")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberHeatingTarget(byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) {
|
||||
bool publishNumberHeatingTarget(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -203,7 +159,14 @@ public:
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_target"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating target");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:radiator");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
@@ -220,14 +183,21 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_target")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberHeatingHysteresis(bool enabledByDefault = true) {
|
||||
bool publishNumberHeatingHysteresis(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_hysteresis"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_hysteresis"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating hysteresis");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:altimeter");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
@@ -244,7 +214,7 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_hysteresis")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorHeatingSetpoint(bool enabledByDefault = true) {
|
||||
bool publishSensorHeatingSetpoint(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -253,7 +223,14 @@ public:
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating setpoint");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:coolant-temperature");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -264,16 +241,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_setpoint")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorBoilerHeatingMinTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorBoilerHeatingMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_heating_min_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_heating_min_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Boiler heating min temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -284,16 +271,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_heating_min_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorBoilerHeatingMaxTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorBoilerHeatingMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_heating_max_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_heating_max_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Boiler heating max temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -304,22 +301,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_heating_max_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberHeatingMinTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberHeatingMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_min_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_min_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 32;
|
||||
doc[FPSTR(HA_MAX)] = 211;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating min temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.minTemp|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"minTemp\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -328,22 +334,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("heating_min_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberHeatingMaxTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberHeatingMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_max_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_max_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 100;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 33;
|
||||
doc[FPSTR(HA_MAX)] = 212;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating max temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.heating.maxTemp|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"heating\": {\"maxTemp\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 100;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -399,7 +414,7 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SWITCH), F("dhw")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberDhwTarget(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
|
||||
bool publishNumberDhwTarget(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -407,7 +422,14 @@ public:
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_target"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("DHW target");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:water-pump");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
@@ -415,7 +437,7 @@ public:
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}");
|
||||
doc[FPSTR(HA_MIN)] = minTemp;
|
||||
doc[FPSTR(HA_MAX)] = maxTemp <= minTemp ? maxTemp : maxTemp;
|
||||
doc[FPSTR(HA_MAX)] = maxTemp > minTemp ? maxTemp : minTemp;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -424,16 +446,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("dhw_target")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorBoilerDhwMinTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorBoilerDhwMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_dhw_min_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_dhw_min_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Boiler DHW min temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -444,16 +476,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_dhw_min_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorBoilerDhwMaxTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorBoilerDhwMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("boiler_dhw_max_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("boiler_dhw_max_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Boiler DHW max temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -464,22 +506,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("boiler_dhw_max_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberDhwMinTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberDhwMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_min_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_min_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 32;
|
||||
doc[FPSTR(HA_MAX)] = 211;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("DHW min temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.dhw.minTemp|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"minTemp\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -488,22 +539,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("dhw_min_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberDhwMaxTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberDhwMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_max_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_max_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 100;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 33;
|
||||
doc[FPSTR(HA_MAX)] = 212;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("DHW max temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.dhw.maxTemp|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"dhw\": {\"maxTemp\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 100;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -620,22 +680,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("pid_dt")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberPidMinTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberPidMinTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pid_min_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pid_min_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 211;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("PID min temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-down");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.pid.minTemp|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"pid\": {\"minTemp\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 0;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -644,22 +713,31 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("pid_min_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberPidMaxTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberPidMaxTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pid_max_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pid_max_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 100;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 212;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("PID max temp");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:thermometer-chevron-up");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.pid.maxTemp|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"pid\": {\"maxTemp\" : {{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = 1;
|
||||
doc[FPSTR(HA_MAX)] = 100;
|
||||
doc[FPSTR(HA_STEP)] = 1;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -780,7 +858,6 @@ public:
|
||||
bool publishSelectTuningRegulator(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"tuning\": {\"regulator\": {% if value == 'Equitherm' %}0{% elif value == 'PID' %}1{% endif %}}}");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -835,7 +912,10 @@ public:
|
||||
|
||||
bool publishBinSensorHeating(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating"));
|
||||
@@ -853,7 +933,10 @@ public:
|
||||
|
||||
bool publishBinSensorDhw(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw"));
|
||||
@@ -871,7 +954,10 @@ public:
|
||||
|
||||
bool publishBinSensorFlame(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("flame"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("flame"));
|
||||
@@ -889,8 +975,10 @@ public:
|
||||
|
||||
bool publishBinSensorFault(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault"));
|
||||
@@ -908,7 +996,10 @@ public:
|
||||
|
||||
bool publishBinSensorDiagnostic(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("diagnostic"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("diagnostic"));
|
||||
@@ -926,8 +1017,10 @@ public:
|
||||
|
||||
bool publishSensorFaultCode(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus and value_json.states.fault, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("fault_code"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("fault_code"));
|
||||
@@ -985,7 +1078,10 @@ public:
|
||||
|
||||
bool publishSensorModulation(bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("modulation_level"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("modulation_level"));
|
||||
@@ -1003,16 +1099,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("modulation")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorPressure(bool enabledByDefault = true) {
|
||||
bool publishSensorPressure(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("pressure"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("pressure"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("pressure");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("bar");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("bar");
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("psi");
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Pressure");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:gauge");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -1023,16 +1129,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("pressure")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorDhwFlowRate(bool enabledByDefault = true) {
|
||||
bool publishSensorDhwFlowRate(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_flow_rate"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_flow_rate"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("volume_flow_rate");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("L/min");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("L/min");
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("gal/min");
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("DHW flow rate");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:water-pump");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -1044,21 +1160,30 @@ public:
|
||||
}
|
||||
|
||||
|
||||
bool publishNumberIndoorTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberIndoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = -99;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = -147;
|
||||
doc[FPSTR(HA_MAX)] = 211;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Indoor temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"temperatures\": {\"indoor\":{{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = -99;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
doc[FPSTR(HA_STEP)] = 0.01;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -1067,17 +1192,23 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("indoor_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorIndoorTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorIndoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any");
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("indoor_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("indoor_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Indoor temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -1088,21 +1219,30 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("indoor_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishNumberOutdoorTemp(bool enabledByDefault = true) {
|
||||
bool publishNumberOutdoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
doc[FPSTR(HA_MIN)] = -99;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
doc[FPSTR(HA_MIN)] = -147;
|
||||
doc[FPSTR(HA_MAX)] = 211;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Outdoor temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer-outline");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}");
|
||||
doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("state/set"));
|
||||
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"temperatures\": {\"outdoor\":{{ value }}}}");
|
||||
doc[FPSTR(HA_MIN)] = -99;
|
||||
doc[FPSTR(HA_MAX)] = 99;
|
||||
doc[FPSTR(HA_STEP)] = 0.01;
|
||||
doc[FPSTR(HA_MODE)] = "box";
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
@@ -1111,17 +1251,23 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_NUMBER), F("outdoor_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorOutdoorTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorOutdoorTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any");
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("outdoor_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("outdoor_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Outdoor temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:home-thermometer-outline");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -1132,16 +1278,26 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("outdoor_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorHeatingTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorHeatingTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:radiator");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -1152,16 +1308,56 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorDhwTemp(bool enabledByDefault = true) {
|
||||
bool publishSensorHeatingReturnTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("heating_return_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("heating_return_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Heating return temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:radiator");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.heatingReturn|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
doc.shrinkToFit();
|
||||
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_return_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorDhwTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("dhw_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("dhw_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("DHW temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:water-pump");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
@@ -1172,8 +1368,38 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("dhw_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishSensorExhaustTemp(UnitSystem unit = UnitSystem::METRIC, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}");
|
||||
doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all");
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectId(F("exhaust_temp"));
|
||||
doc[FPSTR(HA_OBJECT_ID)] = this->getObjectId(F("exhaust_temp"));
|
||||
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("diagnostic");
|
||||
doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature");
|
||||
doc[FPSTR(HA_STATE_CLASS)] = F("measurement");
|
||||
|
||||
bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) {
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C);
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_F);
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_NAME)] = F("Exhaust temperature");
|
||||
doc[FPSTR(HA_ICON)] = F("mdi:smoke");
|
||||
doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("state"));
|
||||
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.temperatures.exhaust|float(0)|round(2) }}");
|
||||
doc[FPSTR(HA_EXPIRE_AFTER)] = 120;
|
||||
doc.shrinkToFit();
|
||||
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("exhaust_temp")).c_str(), doc);
|
||||
}
|
||||
|
||||
|
||||
bool publishClimateHeating(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -1199,6 +1425,13 @@ public:
|
||||
doc[FPSTR(HA_TEMPERATURE_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_TEMPERATURE_STATE_TEMPLATE)] = F("{{ value_json.heating.target|float(0)|round(1) }}");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "C";
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "F";
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_MODE_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'heat' %}{\"heating\": {\"enable\" : true}}"
|
||||
"{% elif value == 'off' %}{\"heating\": {\"enable\" : false}}{% endif %}");
|
||||
@@ -1226,7 +1459,7 @@ public:
|
||||
return this->publish(this->getTopic(FPSTR(HA_ENTITY_CLIMATE), F("heating"), '_').c_str(), doc);
|
||||
}
|
||||
|
||||
bool publishClimateDhw(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
|
||||
bool publishClimateDhw(UnitSystem unit = UnitSystem::METRIC, byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) {
|
||||
JsonDocument doc;
|
||||
doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
|
||||
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
|
||||
@@ -1244,6 +1477,13 @@ public:
|
||||
doc[FPSTR(HA_TEMPERATURE_STATE_TOPIC)] = this->getDeviceTopic(F("settings"));
|
||||
doc[FPSTR(HA_TEMPERATURE_STATE_TEMPLATE)] = F("{{ value_json.dhw.target|int(0) }}");
|
||||
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "C";
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
doc[FPSTR(HA_TEMPERATURE_UNIT)] = "F";
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_MODE_COMMAND_TOPIC)] = this->getDeviceTopic(F("settings/set"));
|
||||
doc[FPSTR(HA_MODE_COMMAND_TEMPLATE)] = F("{% if value == 'heat' %}{\"dhw\": {\"enable\" : true}}"
|
||||
"{% elif value == 'off' %}{\"dhw\": {\"enable\" : false}}{% endif %}");
|
||||
|
||||
@@ -20,14 +20,11 @@ public:
|
||||
}
|
||||
|
||||
~MainTask() {
|
||||
if (this->blinker != nullptr) {
|
||||
delete this->blinker;
|
||||
}
|
||||
delete this->blinker;
|
||||
}
|
||||
|
||||
protected:
|
||||
const static byte REASON_PUMP_START_HEATING = 1;
|
||||
const static byte REASON_PUMP_START_ANTISTUCK = 2;
|
||||
enum class PumpStartReason {NONE, HEATING, ANTISTUCK};
|
||||
|
||||
Blinker* blinker = nullptr;
|
||||
bool blinkerInitialized = false;
|
||||
@@ -38,7 +35,7 @@ protected:
|
||||
unsigned long restartSignalTime = 0;
|
||||
bool heatingEnabled = false;
|
||||
unsigned long heatingDisabledTime = 0;
|
||||
byte externalPumpStartReason;
|
||||
PumpStartReason extPumpStartReason = PumpStartReason::NONE;
|
||||
unsigned long externalPumpStartTime = 0;
|
||||
bool telnetStarted = false;
|
||||
|
||||
@@ -55,14 +52,14 @@ protected:
|
||||
}
|
||||
|
||||
void setup() {
|
||||
#ifdef LED_STATUS_PIN
|
||||
pinMode(LED_STATUS_PIN, OUTPUT);
|
||||
digitalWrite(LED_STATUS_PIN, false);
|
||||
#ifdef LED_STATUS_GPIO
|
||||
pinMode(LED_STATUS_GPIO, OUTPUT);
|
||||
digitalWrite(LED_STATUS_GPIO, LOW);
|
||||
#endif
|
||||
|
||||
if (settings.externalPump.pin != 0) {
|
||||
pinMode(settings.externalPump.pin, OUTPUT);
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
if (GPIO_IS_VALID(settings.externalPump.gpio)) {
|
||||
pinMode(settings.externalPump.gpio, OUTPUT);
|
||||
digitalWrite(settings.externalPump.gpio, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +89,6 @@ protected:
|
||||
Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec."));
|
||||
}
|
||||
|
||||
if (!tOt->isEnabled() && settings.opentherm.inPin > 0 && settings.opentherm.outPin > 0 && settings.opentherm.inPin != settings.opentherm.outPin) {
|
||||
tOt->enable();
|
||||
}
|
||||
|
||||
if (network->isConnected()) {
|
||||
vars.sensors.rssi = WiFi.RSSI();
|
||||
|
||||
@@ -143,8 +136,8 @@ protected:
|
||||
this->yield();
|
||||
|
||||
|
||||
#ifdef LED_STATUS_PIN
|
||||
this->ledStatus(LED_STATUS_PIN);
|
||||
#ifdef LED_STATUS_GPIO
|
||||
this->ledStatus(LED_STATUS_GPIO);
|
||||
#endif
|
||||
this->externalPump();
|
||||
this->yield();
|
||||
@@ -214,7 +207,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void ledStatus(uint8_t ledPin) {
|
||||
void ledStatus(uint8_t gpio) {
|
||||
uint8_t errors[4];
|
||||
uint8_t errCount = 0;
|
||||
static uint8_t errPos = 0;
|
||||
@@ -222,7 +215,7 @@ protected:
|
||||
static bool ledOn = false;
|
||||
|
||||
if (!this->blinkerInitialized) {
|
||||
this->blinker->init(ledPin);
|
||||
this->blinker->init(gpio);
|
||||
this->blinkerInitialized = true;
|
||||
}
|
||||
|
||||
@@ -250,14 +243,14 @@ protected:
|
||||
if (!this->blinker->running() && millis() - endBlinkTime >= 5000) {
|
||||
if (errCount == 0) {
|
||||
if (!ledOn) {
|
||||
digitalWrite(ledPin, true);
|
||||
digitalWrite(gpio, HIGH);
|
||||
ledOn = true;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
} else if (ledOn) {
|
||||
digitalWrite(ledPin, false);
|
||||
digitalWrite(gpio, LOW);
|
||||
ledOn = false;
|
||||
endBlinkTime = millis();
|
||||
return;
|
||||
@@ -286,10 +279,10 @@ protected:
|
||||
this->heatingEnabled = true;
|
||||
}
|
||||
|
||||
if (!settings.externalPump.use || settings.externalPump.pin == 0) {
|
||||
if (!settings.externalPump.use || !GPIO_IS_VALID(settings.externalPump.gpio)) {
|
||||
if (vars.states.externalPump) {
|
||||
if (settings.externalPump.pin != 0) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
if (GPIO_IS_VALID(settings.externalPump.gpio)) {
|
||||
digitalWrite(settings.externalPump.gpio, LOW);
|
||||
}
|
||||
|
||||
vars.states.externalPump = false;
|
||||
@@ -302,16 +295,16 @@ protected:
|
||||
}
|
||||
|
||||
if (vars.states.externalPump && !this->heatingEnabled) {
|
||||
if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
if (this->extPumpStartReason == MainTask::PumpStartReason::HEATING && millis() - this->heatingDisabledTime > (settings.externalPump.postCirculationTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.gpio, LOW);
|
||||
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Disabled: expired post circulation time"));
|
||||
|
||||
} else if (this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.pin, false);
|
||||
} else if (this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK && millis() - this->externalPumpStartTime >= (settings.externalPump.antiStuckTime * 1000u)) {
|
||||
digitalWrite(settings.externalPump.gpio, LOW);
|
||||
|
||||
vars.states.externalPump = false;
|
||||
vars.parameters.extPumpLastEnableTime = millis();
|
||||
@@ -319,24 +312,24 @@ protected:
|
||||
Log.sinfoln("EXTPUMP", F("Disabled: expired anti stuck time"));
|
||||
}
|
||||
|
||||
} else if (vars.states.externalPump && this->heatingEnabled && this->externalPumpStartReason == MainTask::REASON_PUMP_START_ANTISTUCK) {
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING;
|
||||
} else if (vars.states.externalPump && this->heatingEnabled && this->extPumpStartReason == MainTask::PumpStartReason::ANTISTUCK) {
|
||||
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
||||
|
||||
} else if (!vars.states.externalPump && this->heatingEnabled) {
|
||||
vars.states.externalPump = true;
|
||||
this->externalPumpStartTime = millis();
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_HEATING;
|
||||
this->extPumpStartReason = MainTask::PumpStartReason::HEATING;
|
||||
|
||||
digitalWrite(settings.externalPump.pin, true);
|
||||
digitalWrite(settings.externalPump.gpio, HIGH);
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Enabled: heating on"));
|
||||
|
||||
} else if (!vars.states.externalPump && (vars.parameters.extPumpLastEnableTime == 0 || millis() - vars.parameters.extPumpLastEnableTime >= (settings.externalPump.antiStuckInterval * 1000ul))) {
|
||||
vars.states.externalPump = true;
|
||||
this->externalPumpStartTime = millis();
|
||||
this->externalPumpStartReason = MainTask::REASON_PUMP_START_ANTISTUCK;
|
||||
this->extPumpStartReason = MainTask::PumpStartReason::ANTISTUCK;
|
||||
|
||||
digitalWrite(settings.externalPump.pin, true);
|
||||
digitalWrite(settings.externalPump.gpio, HIGH);
|
||||
|
||||
Log.sinfoln("EXTPUMP", F("Enabled: anti stuck"));
|
||||
}
|
||||
|
||||
105
src/MqttTask.h
105
src/MqttTask.h
@@ -15,9 +15,7 @@ public:
|
||||
}
|
||||
|
||||
~MqttTask() {
|
||||
if (this->haHelper != nullptr) {
|
||||
delete this->haHelper;
|
||||
}
|
||||
delete this->haHelper;
|
||||
|
||||
if (this->client != nullptr) {
|
||||
if (this->client->connected()) {
|
||||
@@ -27,13 +25,8 @@ public:
|
||||
delete this->client;
|
||||
}
|
||||
|
||||
if (this->writer != nullptr) {
|
||||
delete this->writer;
|
||||
}
|
||||
|
||||
if (this->wifiClient != nullptr) {
|
||||
delete this->wifiClient;
|
||||
}
|
||||
delete this->writer;
|
||||
delete this->wifiClient;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
@@ -58,6 +51,7 @@ protected:
|
||||
MqttClient* client = nullptr;
|
||||
HaHelper* haHelper = nullptr;
|
||||
MqttWriter* writer = nullptr;
|
||||
UnitSystem currentUnitSystem = UnitSystem::METRIC;
|
||||
unsigned short readyForSendTime = 15000;
|
||||
unsigned long lastReconnectTime = 0;
|
||||
unsigned long connectedTime = 0;
|
||||
@@ -76,7 +70,7 @@ protected:
|
||||
}*/
|
||||
|
||||
int getTaskPriority() {
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool isReadyForSend() {
|
||||
@@ -208,12 +202,7 @@ protected:
|
||||
|
||||
// publish variables and status
|
||||
if (this->newConnection || millis() - this->prevPubVarsTime > (settings.mqtt.interval * 1000u)) {
|
||||
this->writer->publish(
|
||||
this->haHelper->getDeviceTopic("status").c_str(),
|
||||
!vars.states.otStatus ? "offline" : vars.states.fault ? "fault" : "online",
|
||||
true
|
||||
);
|
||||
|
||||
this->writer->publish(this->haHelper->getDeviceTopic("status").c_str(), "online", false);
|
||||
this->publishVariables(this->haHelper->getDeviceTopic("state").c_str());
|
||||
this->prevPubVarsTime = millis();
|
||||
}
|
||||
@@ -225,10 +214,11 @@ protected:
|
||||
}
|
||||
|
||||
// publish ha entities if not published
|
||||
if (this->newConnection) {
|
||||
if (this->newConnection || this->currentUnitSystem != settings.system.unitSystem) {
|
||||
this->publishHaEntities();
|
||||
this->publishNonStaticHaEntities(true);
|
||||
this->newConnection = false;
|
||||
this->currentUnitSystem = settings.system.unitSystem;
|
||||
|
||||
} else {
|
||||
// publish non static ha entities
|
||||
@@ -328,25 +318,21 @@ protected:
|
||||
}
|
||||
|
||||
void publishHaEntities() {
|
||||
// main
|
||||
this->haHelper->publishNumberOutdoorSensorOffset(false);
|
||||
this->haHelper->publishNumberIndoorSensorOffset(false);
|
||||
|
||||
// emergency
|
||||
this->haHelper->publishSwitchEmergency();
|
||||
this->haHelper->publishNumberEmergencyTarget();
|
||||
this->haHelper->publishNumberEmergencyTarget(settings.system.unitSystem);
|
||||
this->haHelper->publishSwitchEmergencyUseEquitherm();
|
||||
this->haHelper->publishSwitchEmergencyUsePid();
|
||||
|
||||
// heating
|
||||
this->haHelper->publishSwitchHeating(false);
|
||||
this->haHelper->publishSwitchHeatingTurbo();
|
||||
this->haHelper->publishNumberHeatingHysteresis();
|
||||
this->haHelper->publishSensorHeatingSetpoint(false);
|
||||
this->haHelper->publishSensorBoilerHeatingMinTemp(false);
|
||||
this->haHelper->publishSensorBoilerHeatingMaxTemp(false);
|
||||
this->haHelper->publishNumberHeatingMinTemp(false);
|
||||
this->haHelper->publishNumberHeatingMaxTemp(false);
|
||||
this->haHelper->publishNumberHeatingHysteresis(settings.system.unitSystem);
|
||||
this->haHelper->publishSensorHeatingSetpoint(settings.system.unitSystem, false);
|
||||
this->haHelper->publishSensorBoilerHeatingMinTemp(settings.system.unitSystem, false);
|
||||
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
|
||||
@@ -355,8 +341,8 @@ protected:
|
||||
this->haHelper->publishNumberPidFactorI();
|
||||
this->haHelper->publishNumberPidFactorD();
|
||||
this->haHelper->publishNumberPidDt(false);
|
||||
this->haHelper->publishNumberPidMinTemp(false);
|
||||
this->haHelper->publishNumberPidMaxTemp(false);
|
||||
this->haHelper->publishNumberPidMinTemp(settings.system.unitSystem, false);
|
||||
this->haHelper->publishNumberPidMaxTemp(settings.system.unitSystem, false);
|
||||
|
||||
// equitherm
|
||||
this->haHelper->publishSwitchEquitherm();
|
||||
@@ -378,14 +364,16 @@ protected:
|
||||
|
||||
// sensors
|
||||
this->haHelper->publishSensorModulation(false);
|
||||
this->haHelper->publishSensorPressure(false);
|
||||
this->haHelper->publishSensorPressure(settings.system.unitSystem, false);
|
||||
this->haHelper->publishSensorFaultCode();
|
||||
this->haHelper->publishSensorRssi(false);
|
||||
this->haHelper->publishSensorUptime(false);
|
||||
|
||||
// temperatures
|
||||
this->haHelper->publishNumberIndoorTemp();
|
||||
this->haHelper->publishSensorHeatingTemp();
|
||||
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
|
||||
this->haHelper->publishButtonRestart(false);
|
||||
@@ -399,23 +387,36 @@ protected:
|
||||
|
||||
bool published = false;
|
||||
bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable;
|
||||
byte heatingMinTemp = isStupidMode ? settings.heating.minTemp : 10;
|
||||
byte heatingMaxTemp = isStupidMode ? settings.heating.maxTemp : 30;
|
||||
bool editableOutdoorTemp = settings.sensors.outdoor.type == 1;
|
||||
bool editableIndoorTemp = settings.sensors.indoor.type == 1;
|
||||
byte heatingMinTemp = 0;
|
||||
byte heatingMaxTemp = 0;
|
||||
bool editableOutdoorTemp = settings.sensors.outdoor.type == SensorType::MANUAL;
|
||||
bool editableIndoorTemp = settings.sensors.indoor.type == SensorType::MANUAL;
|
||||
|
||||
if (isStupidMode) {
|
||||
heatingMinTemp = settings.heating.minTemp;
|
||||
heatingMaxTemp = settings.heating.maxTemp;
|
||||
|
||||
} else if (settings.system.unitSystem == UnitSystem::METRIC) {
|
||||
heatingMinTemp = 5;
|
||||
heatingMaxTemp = 30;
|
||||
|
||||
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||
heatingMinTemp = 41;
|
||||
heatingMaxTemp = 86;
|
||||
}
|
||||
|
||||
if (force || _dhwPresent != settings.opentherm.dhwPresent) {
|
||||
_dhwPresent = settings.opentherm.dhwPresent;
|
||||
|
||||
if (_dhwPresent) {
|
||||
this->haHelper->publishSwitchDhw(false);
|
||||
this->haHelper->publishSensorBoilerDhwMinTemp(false);
|
||||
this->haHelper->publishSensorBoilerDhwMaxTemp(false);
|
||||
this->haHelper->publishNumberDhwMinTemp(false);
|
||||
this->haHelper->publishNumberDhwMaxTemp(false);
|
||||
this->haHelper->publishSensorBoilerDhwMinTemp(settings.system.unitSystem, false);
|
||||
this->haHelper->publishSensorBoilerDhwMaxTemp(settings.system.unitSystem, false);
|
||||
this->haHelper->publishNumberDhwMinTemp(settings.system.unitSystem, false);
|
||||
this->haHelper->publishNumberDhwMaxTemp(settings.system.unitSystem, false);
|
||||
this->haHelper->publishBinSensorDhw();
|
||||
this->haHelper->publishSensorDhwTemp();
|
||||
this->haHelper->publishSensorDhwFlowRate(false);
|
||||
this->haHelper->publishSensorDhwTemp(settings.system.unitSystem);
|
||||
this->haHelper->publishSensorDhwFlowRate(settings.system.unitSystem, false);
|
||||
|
||||
} else {
|
||||
this->haHelper->deleteSwitchDhw();
|
||||
@@ -442,8 +443,9 @@ protected:
|
||||
_heatingMaxTemp = heatingMaxTemp;
|
||||
_isStupidMode = isStupidMode;
|
||||
|
||||
this->haHelper->publishNumberHeatingTarget(heatingMinTemp, heatingMaxTemp, false);
|
||||
this->haHelper->publishNumberHeatingTarget(settings.system.unitSystem, heatingMinTemp, heatingMaxTemp, false);
|
||||
this->haHelper->publishClimateHeating(
|
||||
settings.system.unitSystem,
|
||||
heatingMinTemp,
|
||||
heatingMaxTemp,
|
||||
isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR
|
||||
@@ -454,6 +456,7 @@ protected:
|
||||
} else if (_isStupidMode != isStupidMode) {
|
||||
_isStupidMode = isStupidMode;
|
||||
this->haHelper->publishClimateHeating(
|
||||
settings.system.unitSystem,
|
||||
heatingMinTemp,
|
||||
heatingMaxTemp,
|
||||
isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR
|
||||
@@ -466,8 +469,8 @@ protected:
|
||||
_dhwMinTemp = settings.dhw.minTemp;
|
||||
_dhwMaxTemp = settings.dhw.maxTemp;
|
||||
|
||||
this->haHelper->publishNumberDhwTarget(settings.dhw.minTemp, settings.dhw.maxTemp, false);
|
||||
this->haHelper->publishClimateDhw(settings.dhw.minTemp, 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);
|
||||
|
||||
published = true;
|
||||
}
|
||||
@@ -477,10 +480,10 @@ protected:
|
||||
|
||||
if (editableOutdoorTemp) {
|
||||
this->haHelper->deleteSensorOutdoorTemp();
|
||||
this->haHelper->publishNumberOutdoorTemp();
|
||||
this->haHelper->publishNumberOutdoorTemp(settings.system.unitSystem);
|
||||
} else {
|
||||
this->haHelper->deleteNumberOutdoorTemp();
|
||||
this->haHelper->publishSensorOutdoorTemp();
|
||||
this->haHelper->publishSensorOutdoorTemp(settings.system.unitSystem);
|
||||
}
|
||||
|
||||
published = true;
|
||||
@@ -491,10 +494,10 @@ protected:
|
||||
|
||||
if (editableIndoorTemp) {
|
||||
this->haHelper->deleteSensorIndoorTemp();
|
||||
this->haHelper->publishNumberIndoorTemp();
|
||||
this->haHelper->publishNumberIndoorTemp(settings.system.unitSystem);
|
||||
} else {
|
||||
this->haHelper->deleteNumberIndoorTemp();
|
||||
this->haHelper->publishSensorIndoorTemp();
|
||||
this->haHelper->publishSensorIndoorTemp(settings.system.unitSystem);
|
||||
}
|
||||
|
||||
published = true;
|
||||
|
||||
@@ -1,31 +1,35 @@
|
||||
#include <CustomOpenTherm.h>
|
||||
|
||||
CustomOpenTherm* ot;
|
||||
extern FileData fsSettings;
|
||||
|
||||
class OpenThermTask : public Task {
|
||||
public:
|
||||
OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {
|
||||
ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin);
|
||||
}
|
||||
OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {}
|
||||
|
||||
static void IRAM_ATTR handleInterrupt() {
|
||||
ot->handleInterrupt();
|
||||
~OpenThermTask() {
|
||||
delete this->instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
unsigned short readyTime = 60000;
|
||||
unsigned short dhwSetTempInterval = 60000;
|
||||
unsigned short heatingSetTempInterval = 60000;
|
||||
const unsigned short readyTime = 60000;
|
||||
const unsigned short dhwSetTempInterval = 60000;
|
||||
const unsigned short heatingSetTempInterval = 60000;
|
||||
const unsigned int initializingInterval = 3600000;
|
||||
|
||||
CustomOpenTherm* instance = nullptr;
|
||||
unsigned long instanceCreatedTime = 0;
|
||||
byte instanceInGpio = 0;
|
||||
byte instanceOutGpio = 0;
|
||||
bool isInitialized = false;
|
||||
unsigned long initializedTime = 0;
|
||||
unsigned int initializedMemberIdCode = 0;
|
||||
byte dhwFlowRateMultiplier = 1;
|
||||
byte pressureMultiplier = 1;
|
||||
bool pump = true;
|
||||
unsigned long lastSuccessResponse = 0;
|
||||
unsigned long prevUpdateNonEssentialVars = 0;
|
||||
unsigned long startupTime = millis();
|
||||
unsigned long dhwSetTempTime = 0;
|
||||
unsigned long heatingSetTempTime = 0;
|
||||
byte dhwFlowRateMultiplier = 1;
|
||||
byte pressureMultiplier = 1;
|
||||
|
||||
|
||||
const char* getTaskName() {
|
||||
return "OpenTherm";
|
||||
@@ -36,85 +40,88 @@ protected:
|
||||
}
|
||||
|
||||
int getTaskPriority() {
|
||||
return 2;
|
||||
return 5;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Log.sinfoln(FPSTR(L_OT), F("Started. GPIO IN: %hhu, GPIO OUT: %hhu"), settings.opentherm.inPin, settings.opentherm.outPin);
|
||||
if (settings.system.unitSystem != UnitSystem::METRIC) {
|
||||
vars.parameters.heatingMinTemp = convertTemp(vars.parameters.heatingMinTemp, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
vars.parameters.heatingMaxTemp = convertTemp(vars.parameters.heatingMaxTemp, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
vars.parameters.dhwMinTemp = convertTemp(vars.parameters.dhwMinTemp, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
vars.parameters.dhwMaxTemp = convertTemp(vars.parameters.dhwMaxTemp, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
}
|
||||
|
||||
#ifdef LED_OT_RX_GPIO
|
||||
pinMode(LED_OT_RX_GPIO, OUTPUT);
|
||||
digitalWrite(LED_OT_RX_GPIO, LOW);
|
||||
#endif
|
||||
|
||||
ot->setAfterSendRequestCallback([this](unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) {
|
||||
// delete instance
|
||||
if (this->instance != nullptr) {
|
||||
delete this->instance;
|
||||
this->instance = nullptr;
|
||||
Log.sinfoln(FPSTR(L_OT), F("Stopped"));
|
||||
}
|
||||
|
||||
if (!GPIO_IS_VALID(settings.opentherm.inGpio) || !GPIO_IS_VALID(settings.opentherm.outGpio)) {
|
||||
Log.swarningln(FPSTR(L_OT), F("Not started. GPIO IN: %hhu or GPIO OUT: %hhu is not valid"), settings.opentherm.inGpio, settings.opentherm.outGpio);
|
||||
return;
|
||||
}
|
||||
|
||||
// create instance
|
||||
this->instance = new CustomOpenTherm(settings.opentherm.inGpio, settings.opentherm.outGpio);
|
||||
|
||||
// flags
|
||||
this->instanceCreatedTime = millis();
|
||||
this->instanceInGpio = settings.opentherm.inGpio;
|
||||
this->instanceOutGpio = settings.opentherm.outGpio;
|
||||
this->isInitialized = false;
|
||||
|
||||
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) {
|
||||
Log.straceln(
|
||||
FPSTR(L_OT),
|
||||
F("ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s"),
|
||||
ot->getDataID(request), request, response, attempt, ot->statusToString(status)
|
||||
CustomOpenTherm::getDataID(request), request, response, attempt, CustomOpenTherm::statusToString(status)
|
||||
);
|
||||
|
||||
if (status == OpenThermResponseStatus::SUCCESS) {
|
||||
this->lastSuccessResponse = millis();
|
||||
|
||||
#ifdef LED_OT_RX_PIN
|
||||
#ifdef LED_OT_RX_GPIO
|
||||
{
|
||||
digitalWrite(LED_OT_RX_PIN, true);
|
||||
digitalWrite(LED_OT_RX_GPIO, HIGH);
|
||||
delayMicroseconds(2000);
|
||||
digitalWrite(LED_OT_RX_PIN, false);
|
||||
digitalWrite(LED_OT_RX_GPIO, LOW);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
ot->setYieldCallback([this]() {
|
||||
this->instance->setYieldCallback([this]() {
|
||||
this->delay(25);
|
||||
});
|
||||
|
||||
ot->setMinWaitTimeForStartBit(20000);
|
||||
ot->begin(OpenThermTask::handleInterrupt);
|
||||
|
||||
#ifdef LED_OT_RX_PIN
|
||||
pinMode(LED_OT_RX_PIN, OUTPUT);
|
||||
digitalWrite(LED_OT_RX_PIN, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void initBoiler() {
|
||||
this->dhwFlowRateMultiplier = 1;
|
||||
this->pressureMultiplier = 1;
|
||||
|
||||
// Not all boilers support these, only try once when the boiler becomes connected
|
||||
if (updateSlaveVersion()) {
|
||||
Log.straceln(FPSTR(L_OT), F("Slave version: %u, type: %u"), vars.parameters.slaveVersion, vars.parameters.slaveType);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Get slave version failed"));
|
||||
}
|
||||
|
||||
// 0x013F
|
||||
if (setMasterVersion(0x3F, 0x01)) {
|
||||
Log.straceln(FPSTR(L_OT), F("Master version: %u, type: %u"), vars.parameters.masterVersion, vars.parameters.masterType);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Set master version failed"));
|
||||
}
|
||||
|
||||
if (updateSlaveConfig()) {
|
||||
Log.straceln(FPSTR(L_OT), F("Slave member id: %u, flags: %u"), vars.parameters.slaveMemberId, vars.parameters.slaveFlags);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Get slave config failed"));
|
||||
}
|
||||
|
||||
if (setMasterConfig(settings.opentherm.memberIdCode & 0xFF, (settings.opentherm.memberIdCode & 0xFFFF) >> 8)) {
|
||||
Log.straceln(FPSTR(L_OT), F("Master member id: %u, flags: %u"), vars.parameters.masterMemberId, vars.parameters.masterFlags);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Set master config failed"));
|
||||
}
|
||||
this->instance->begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static byte currentHeatingTemp, currentDhwTemp = 0;
|
||||
unsigned long localResponse;
|
||||
|
||||
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && isReady();
|
||||
if (this->instanceInGpio != settings.opentherm.inGpio || this->instanceOutGpio != settings.opentherm.outGpio) {
|
||||
this->setup();
|
||||
|
||||
} else if (this->initializedMemberIdCode != settings.opentherm.memberIdCode || millis() - this->initializedTime > this->initializingInterval) {
|
||||
this->isInitialized = false;
|
||||
}
|
||||
|
||||
if (this->instance == nullptr) {
|
||||
this->delay(5000);
|
||||
return;
|
||||
}
|
||||
|
||||
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && this->isReady();
|
||||
bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled;
|
||||
if (settings.opentherm.heatingCh1ToCh2) {
|
||||
heatingCh2Enabled = heatingEnabled;
|
||||
@@ -123,7 +130,7 @@ protected:
|
||||
heatingCh2Enabled = settings.opentherm.dhwPresent && settings.dhw.enable;
|
||||
}
|
||||
|
||||
localResponse = ot->setBoilerStatus(
|
||||
unsigned long response = this->instance->setBoilerStatus(
|
||||
heatingEnabled,
|
||||
settings.opentherm.dhwPresent && settings.dhw.enable,
|
||||
false,
|
||||
@@ -133,20 +140,20 @@ protected:
|
||||
settings.opentherm.dhwBlocking
|
||||
);
|
||||
|
||||
if (!ot->isValidResponse(localResponse)) {
|
||||
Log.swarningln(FPSTR(L_OT), F("Invalid response after setBoilerStatus: %s"), ot->statusToString(ot->getLastResponseStatus()));
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
Log.swarningln(FPSTR(L_OT), F("Invalid response after setBoilerStatus: %s"), CustomOpenTherm::statusToString(this->instance->getLastResponseStatus()));
|
||||
}
|
||||
|
||||
if (!vars.states.otStatus && millis() - this->lastSuccessResponse < 1150) {
|
||||
Log.sinfoln(FPSTR(L_OT), F("Connected. Initializing"));
|
||||
Log.sinfoln(FPSTR(L_OT), F("Connected"));
|
||||
|
||||
vars.states.otStatus = true;
|
||||
this->initBoiler();
|
||||
|
||||
} else if (vars.states.otStatus && millis() - this->lastSuccessResponse > 1150) {
|
||||
Log.swarningln(FPSTR(L_OT), F("Disconnected"));
|
||||
|
||||
vars.states.otStatus = false;
|
||||
this->isInitialized = false;
|
||||
}
|
||||
|
||||
// If boiler is disconnected, no need try setting other OT stuff
|
||||
@@ -160,17 +167,27 @@ protected:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->isInitialized) {
|
||||
Log.sinfoln(FPSTR(L_OT), F("Initializing..."));
|
||||
this->isInitialized = true;
|
||||
this->initializedTime = millis();
|
||||
this->initializedMemberIdCode = settings.opentherm.memberIdCode;
|
||||
this->dhwFlowRateMultiplier = 1;
|
||||
this->pressureMultiplier = 1;
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
if (vars.parameters.heatingEnabled != heatingEnabled) {
|
||||
this->prevUpdateNonEssentialVars = 0;
|
||||
vars.parameters.heatingEnabled = heatingEnabled;
|
||||
Log.sinfoln(FPSTR(L_OT_HEATING), "%s", heatingEnabled ? F("Enabled") : F("Disabled"));
|
||||
}
|
||||
|
||||
vars.states.heating = ot->isCentralHeatingActive(localResponse);
|
||||
vars.states.dhw = settings.opentherm.dhwPresent ? ot->isHotWaterActive(localResponse) : false;
|
||||
vars.states.flame = ot->isFlameOn(localResponse);
|
||||
vars.states.fault = ot->isFault(localResponse);
|
||||
vars.states.diagnostic = ot->isDiagnostic(localResponse);
|
||||
vars.states.heating = CustomOpenTherm::isCentralHeatingActive(response);
|
||||
vars.states.dhw = settings.opentherm.dhwPresent ? CustomOpenTherm::isHotWaterActive(response) : false;
|
||||
vars.states.flame = CustomOpenTherm::isFlameOn(response);
|
||||
vars.states.fault = CustomOpenTherm::isFault(response);
|
||||
vars.states.diagnostic = CustomOpenTherm::isDiagnostic(response);
|
||||
|
||||
// These parameters will be updated every minute
|
||||
if (millis() - this->prevUpdateNonEssentialVars > 60000) {
|
||||
@@ -193,7 +210,7 @@ protected:
|
||||
|
||||
|
||||
// Get DHW min/max temp (if necessary)
|
||||
if (settings.opentherm.dhwPresent) {
|
||||
if (settings.opentherm.dhwPresent && settings.opentherm.getMinMaxTemp) {
|
||||
if (updateMinMaxDhwTemp()) {
|
||||
if (settings.dhw.minTemp < vars.parameters.dhwMinTemp) {
|
||||
settings.dhw.minTemp = vars.parameters.dhwMinTemp;
|
||||
@@ -208,46 +225,51 @@ protected:
|
||||
}
|
||||
|
||||
} else {
|
||||
vars.parameters.dhwMinTemp = convertTemp(DEFAULT_DHW_MIN_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
vars.parameters.dhwMaxTemp = convertTemp(DEFAULT_DHW_MAX_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
|
||||
Log.swarningln(FPSTR(L_OT_DHW), F("Failed get min/max temp"));
|
||||
}
|
||||
|
||||
if (settings.dhw.minTemp >= settings.dhw.maxTemp) {
|
||||
settings.dhw.minTemp = 30;
|
||||
settings.dhw.maxTemp = 60;
|
||||
settings.dhw.minTemp = vars.parameters.dhwMinTemp;
|
||||
settings.dhw.maxTemp = vars.parameters.dhwMaxTemp;
|
||||
fsSettings.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get heating min/max temp
|
||||
if (updateMinMaxHeatingTemp()) {
|
||||
if (settings.heating.minTemp < vars.parameters.heatingMinTemp) {
|
||||
settings.heating.minTemp = vars.parameters.heatingMinTemp;
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated min temp: %hhu"), settings.heating.minTemp);
|
||||
}
|
||||
if (settings.opentherm.getMinMaxTemp) {
|
||||
if (updateMinMaxHeatingTemp()) {
|
||||
if (settings.heating.minTemp < vars.parameters.heatingMinTemp) {
|
||||
settings.heating.minTemp = vars.parameters.heatingMinTemp;
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated min temp: %hhu"), settings.heating.minTemp);
|
||||
}
|
||||
|
||||
if (settings.heating.maxTemp > vars.parameters.heatingMaxTemp) {
|
||||
settings.heating.maxTemp = vars.parameters.heatingMaxTemp;
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated max temp: %hhu"), settings.heating.maxTemp);
|
||||
}
|
||||
if (settings.heating.maxTemp > vars.parameters.heatingMaxTemp) {
|
||||
settings.heating.maxTemp = vars.parameters.heatingMaxTemp;
|
||||
fsSettings.update();
|
||||
Log.snoticeln(FPSTR(L_OT_HEATING), F("Updated max temp: %hhu"), settings.heating.maxTemp);
|
||||
}
|
||||
|
||||
} else {
|
||||
vars.parameters.heatingMinTemp = convertTemp(DEFAULT_HEATING_MIN_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
vars.parameters.heatingMaxTemp = convertTemp(DEFAULT_HEATING_MAX_TEMP, UnitSystem::METRIC, settings.system.unitSystem);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed get min/max temp"));
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed get min/max temp"));
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.heating.minTemp >= settings.heating.maxTemp) {
|
||||
settings.heating.minTemp = 20;
|
||||
settings.heating.maxTemp = 90;
|
||||
settings.heating.minTemp = vars.parameters.heatingMinTemp;
|
||||
settings.heating.maxTemp = vars.parameters.heatingMaxTemp;
|
||||
fsSettings.update();
|
||||
}
|
||||
|
||||
// Force set max heating temp
|
||||
setMaxHeatingTemp(settings.heating.maxTemp);
|
||||
|
||||
// Get outdoor temp (if necessary)
|
||||
if (settings.sensors.outdoor.type == 0) {
|
||||
if (settings.sensors.outdoor.type == SensorType::BOILER) {
|
||||
updateOutsideTemp();
|
||||
}
|
||||
|
||||
@@ -263,7 +285,7 @@ protected:
|
||||
|
||||
|
||||
// Get current modulation level (if necessary)
|
||||
if ((settings.opentherm.dhwPresent && settings.dhw.enable) || settings.heating.enable || heatingEnabled) {
|
||||
if (vars.states.flame) {
|
||||
updateModulationLevel();
|
||||
|
||||
} else {
|
||||
@@ -283,11 +305,17 @@ protected:
|
||||
// Get current heating temp
|
||||
updateHeatingTemp();
|
||||
|
||||
// Get heating return temp
|
||||
updateHeatingReturnTemp();
|
||||
|
||||
// Get exhaust temp
|
||||
updateExhaustTemp();
|
||||
|
||||
|
||||
// Fault reset action
|
||||
if (vars.actions.resetFault) {
|
||||
if (vars.states.fault) {
|
||||
if (ot->sendBoilerReset()) {
|
||||
if (this->instance->sendBoilerReset()) {
|
||||
Log.sinfoln(FPSTR(L_OT), F("Boiler fault reset successfully"));
|
||||
|
||||
} else {
|
||||
@@ -301,7 +329,7 @@ protected:
|
||||
// Diag reset action
|
||||
if (vars.actions.resetDiagnostic) {
|
||||
if (vars.states.diagnostic) {
|
||||
if (ot->sendServiceReset()) {
|
||||
if (this->instance->sendServiceReset()) {
|
||||
Log.sinfoln(FPSTR(L_OT), F("Boiler diagnostic reset successfully"));
|
||||
|
||||
} else {
|
||||
@@ -315,15 +343,16 @@ protected:
|
||||
|
||||
// Update DHW temp
|
||||
byte newDhwTemp = settings.dhw.target;
|
||||
if (settings.opentherm.dhwPresent && settings.dhw.enable && (needSetDhwTemp() || newDhwTemp != currentDhwTemp)) {
|
||||
if (settings.opentherm.dhwPresent && settings.dhw.enable && (this->needSetDhwTemp() || newDhwTemp != currentDhwTemp)) {
|
||||
if (newDhwTemp < settings.dhw.minTemp || newDhwTemp > settings.dhw.maxTemp) {
|
||||
newDhwTemp = constrain(newDhwTemp, settings.dhw.minTemp, settings.dhw.maxTemp);
|
||||
}
|
||||
|
||||
Log.sinfoln(FPSTR(L_OT_DHW), F("Set temp = %u"), newDhwTemp);
|
||||
float convertedTemp = convertTemp(newDhwTemp, settings.system.unitSystem, settings.opentherm.unitSystem);
|
||||
Log.sinfoln(FPSTR(L_OT_DHW), F("Set temp: %u (converted: %.2f)"), newDhwTemp, convertedTemp);
|
||||
|
||||
// Set DHW temp
|
||||
if (ot->setDhwTemp(newDhwTemp)) {
|
||||
if (this->instance->setDhwTemp(convertedTemp)) {
|
||||
currentDhwTemp = newDhwTemp;
|
||||
this->dhwSetTempTime = millis();
|
||||
|
||||
@@ -333,7 +362,7 @@ protected:
|
||||
|
||||
// Set DHW temp to CH2
|
||||
if (settings.opentherm.dhwToCh2) {
|
||||
if (!ot->setHeatingCh2Temp(newDhwTemp)) {
|
||||
if (!this->instance->setHeatingCh2Temp(convertedTemp)) {
|
||||
Log.swarningln(FPSTR(L_OT_DHW), F("Failed set ch2 temp"));
|
||||
}
|
||||
}
|
||||
@@ -341,11 +370,12 @@ protected:
|
||||
|
||||
|
||||
// Update heating temp
|
||||
if (heatingEnabled && (needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) {
|
||||
Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint);
|
||||
if (heatingEnabled && (this->needSetHeatingTemp() || fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001)) {
|
||||
float convertedTemp = convertTemp(vars.parameters.heatingSetpoint, settings.system.unitSystem, settings.opentherm.unitSystem);
|
||||
Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp: %u (converted: %.2f)"), vars.parameters.heatingSetpoint, convertedTemp);
|
||||
|
||||
// Set heating temp
|
||||
if (ot->setHeatingCh1Temp(vars.parameters.heatingSetpoint)) {
|
||||
if (this->instance->setHeatingCh1Temp(convertedTemp) || this->setMaxHeatingTemp(convertedTemp)) {
|
||||
currentHeatingTemp = vars.parameters.heatingSetpoint;
|
||||
this->heatingSetTempTime = millis();
|
||||
|
||||
@@ -355,7 +385,7 @@ protected:
|
||||
|
||||
// Set heating temp to CH2
|
||||
if (settings.opentherm.heatingCh1ToCh2) {
|
||||
if (!ot->setHeatingCh2Temp(vars.parameters.heatingSetpoint)) {
|
||||
if (!this->instance->setHeatingCh2Temp(convertedTemp)) {
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set ch2 temp"));
|
||||
}
|
||||
}
|
||||
@@ -378,8 +408,40 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
// Not all boilers support these, only try once when the boiler becomes connected
|
||||
if (this->updateSlaveVersion()) {
|
||||
Log.straceln(FPSTR(L_OT), F("Slave version: %u, type: %u"), vars.parameters.slaveVersion, vars.parameters.slaveType);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Get slave version failed"));
|
||||
}
|
||||
|
||||
// 0x013F
|
||||
if (this->setMasterVersion(0x3F, 0x01)) {
|
||||
Log.straceln(FPSTR(L_OT), F("Master version: %u, type: %u"), vars.parameters.masterVersion, vars.parameters.masterType);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Set master version failed"));
|
||||
}
|
||||
|
||||
if (this->updateSlaveConfig()) {
|
||||
Log.straceln(FPSTR(L_OT), F("Slave member id: %u, flags: %u"), vars.parameters.slaveMemberId, vars.parameters.slaveFlags);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Get slave config failed"));
|
||||
}
|
||||
|
||||
if (this->setMasterConfig(settings.opentherm.memberIdCode & 0xFF, (settings.opentherm.memberIdCode & 0xFFFF) >> 8)) {
|
||||
Log.straceln(FPSTR(L_OT), F("Master member id: %u, flags: %u"), vars.parameters.masterMemberId, vars.parameters.masterFlags);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT), F("Set master config failed"));
|
||||
}
|
||||
}
|
||||
|
||||
bool isReady() {
|
||||
return millis() - this->startupTime > this->readyTime;
|
||||
return millis() - this->instanceCreatedTime > this->readyTime;
|
||||
}
|
||||
|
||||
bool needSetDhwTemp() {
|
||||
@@ -391,13 +453,13 @@ protected:
|
||||
}
|
||||
|
||||
bool updateSlaveConfig() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::SConfigSMemberIDcode,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -452,69 +514,69 @@ protected:
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::MConfigMMemberIDcode,
|
||||
request
|
||||
));
|
||||
|
||||
return ot->isValidResponse(response);
|
||||
return CustomOpenTherm::isValidResponse(response);
|
||||
}
|
||||
|
||||
bool setMaxModulationLevel(byte value) {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::MaxRelModLevelSetting,
|
||||
ot->toF88(value)
|
||||
CustomOpenTherm::toFloat(value)
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.parameters.maxModulation = ot->fromF88(response);
|
||||
vars.parameters.maxModulation = CustomOpenTherm::getFloat(response);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateSlaveOtVersion() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::OpenThermVersionSlave,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.parameters.slaveOtVersion = ot->getFloat(response);
|
||||
vars.parameters.slaveOtVersion = CustomOpenTherm::getFloat(response);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setMasterOtVersion(float version) {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::OpenThermVersionMaster,
|
||||
ot->toF88(version)
|
||||
CustomOpenTherm::toFloat(version)
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.parameters.masterOtVersion = ot->fromF88(response);
|
||||
vars.parameters.masterOtVersion = CustomOpenTherm::getFloat(response);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateSlaveVersion() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::SlaveVersion,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -525,13 +587,13 @@ protected:
|
||||
}
|
||||
|
||||
bool setMasterVersion(uint8_t version, uint8_t type) {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::MasterVersion,
|
||||
(unsigned int) version | (unsigned int) type << 8
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -542,13 +604,13 @@ protected:
|
||||
}
|
||||
|
||||
bool updateMinMaxDhwTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::TdhwSetUBTdhwSetLB,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -556,8 +618,8 @@ protected:
|
||||
byte maxTemp = (response & 0xFFFF) >> 8;
|
||||
|
||||
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
|
||||
vars.parameters.dhwMinTemp = minTemp;
|
||||
vars.parameters.dhwMaxTemp = maxTemp;
|
||||
vars.parameters.dhwMinTemp = convertTemp(minTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
|
||||
vars.parameters.dhwMaxTemp = convertTemp(maxTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -566,13 +628,13 @@ protected:
|
||||
}
|
||||
|
||||
bool updateMinMaxHeatingTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::MaxTSetUBMaxTSetLB,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -580,8 +642,8 @@ protected:
|
||||
byte maxTemp = (response & 0xFFFF) >> 8;
|
||||
|
||||
if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) {
|
||||
vars.parameters.heatingMinTemp = minTemp;
|
||||
vars.parameters.heatingMaxTemp = maxTemp;
|
||||
vars.parameters.heatingMinTemp = convertTemp(minTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
|
||||
vars.parameters.heatingMaxTemp = convertTemp(maxTemp, settings.opentherm.unitSystem, settings.system.unitSystem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -589,99 +651,164 @@ protected:
|
||||
}
|
||||
|
||||
bool setMaxHeatingTemp(byte value) {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermMessageType::WRITE_DATA,
|
||||
OpenThermMessageID::MaxTSet,
|
||||
ot->temperatureToData(value)
|
||||
CustomOpenTherm::temperatureToData(value)
|
||||
));
|
||||
|
||||
return ot->isValidResponse(response);
|
||||
return CustomOpenTherm::isValidResponse(response);
|
||||
}
|
||||
|
||||
bool updateOutsideTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::Toutside,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.outdoor = settings.sensors.outdoor.offset + convertTemp(
|
||||
CustomOpenTherm::getFloat(response),
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateExhaustTemp() {
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::Texhaust,
|
||||
0
|
||||
));
|
||||
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.outdoor = ot->getFloat(response) + settings.sensors.outdoor.offset;
|
||||
float value = (float) CustomOpenTherm::getInt(response);
|
||||
if (!isValidTemp(value, settings.opentherm.unitSystem, -40, 500)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.exhaust = convertTemp(
|
||||
value,
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateHeatingTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermMessageType::READ_DATA,
|
||||
OpenThermMessageID::Tboiler,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = ot->getFloat(response);
|
||||
float value = CustomOpenTherm::getFloat(response);
|
||||
if (value <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.heating = value;
|
||||
vars.temperatures.heating = convertTemp(
|
||||
value,
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateHeatingReturnTemp() {
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermMessageType::READ_DATA,
|
||||
OpenThermMessageID::Tret,
|
||||
0
|
||||
));
|
||||
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.heatingReturn = convertTemp(
|
||||
CustomOpenTherm::getFloat(response),
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool updateDhwTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermMessageType::READ_DATA,
|
||||
OpenThermMessageID::Tdhw,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = ot->getFloat(response);
|
||||
float value = CustomOpenTherm::getFloat(response);
|
||||
if (value <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vars.temperatures.dhw = value;
|
||||
vars.temperatures.dhw = convertTemp(
|
||||
value,
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateDhwFlowRate() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermMessageType::READ_DATA,
|
||||
OpenThermMessageID::DHWFlowRate,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = ot->getFloat(response);
|
||||
if (value > 16 && this->dhwFlowRateMultiplier != 10) {
|
||||
|
||||
float value = CustomOpenTherm::getFloat(response);
|
||||
if (this->dhwFlowRateMultiplier != 10 && value > convertVolume(16, UnitSystem::METRIC, settings.opentherm.unitSystem)) {
|
||||
this->dhwFlowRateMultiplier = 10;
|
||||
}
|
||||
vars.sensors.dhwFlowRate = this->dhwFlowRateMultiplier == 1 ? value : value / this->dhwFlowRateMultiplier;
|
||||
|
||||
vars.sensors.dhwFlowRate = convertVolume(
|
||||
value / this->dhwFlowRateMultiplier,
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updateFaultCode() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::ASFflags,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -690,42 +817,42 @@ protected:
|
||||
}
|
||||
|
||||
bool updateModulationLevel() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::RelModLevel,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float modulation = ot->fromF88(response);
|
||||
if (!vars.states.flame) {
|
||||
vars.sensors.modulation = 0;
|
||||
} else {
|
||||
vars.sensors.modulation = modulation;
|
||||
}
|
||||
vars.sensors.modulation = CustomOpenTherm::getFloat(response);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updatePressure() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
|
||||
OpenThermRequestType::READ_DATA,
|
||||
OpenThermMessageID::CHPressure,
|
||||
0
|
||||
));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
if (!CustomOpenTherm::isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = ot->getFloat(response);
|
||||
if (value > 5 && this->pressureMultiplier != 10) {
|
||||
float value = CustomOpenTherm::getFloat(response);
|
||||
if (this->pressureMultiplier != 10 && value > convertPressure(5, UnitSystem::METRIC, settings.opentherm.unitSystem)) {
|
||||
this->pressureMultiplier = 10;
|
||||
}
|
||||
vars.sensors.pressure = this->pressureMultiplier == 1 ? value : value / this->pressureMultiplier;
|
||||
|
||||
vars.sensors.pressure = convertPressure(
|
||||
value / this->pressureMultiplier,
|
||||
settings.opentherm.unitSystem,
|
||||
settings.system.unitSystem
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,9 +28,7 @@ public:
|
||||
}
|
||||
|
||||
~PortalTask() {
|
||||
if (this->bufferedWebServer != nullptr) {
|
||||
delete this->bufferedWebServer;
|
||||
}
|
||||
delete this->bufferedWebServer;
|
||||
|
||||
if (this->webServer != nullptr) {
|
||||
this->stopWebServer();
|
||||
@@ -64,7 +62,7 @@ protected:
|
||||
}*/
|
||||
|
||||
int getTaskPriority() {
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
@@ -436,6 +434,7 @@ protected:
|
||||
JsonDocument doc;
|
||||
varsToJson(vars, doc);
|
||||
|
||||
doc["system"]["unitSystem"] = static_cast<byte>(settings.system.unitSystem);
|
||||
doc["system"]["version"] = PROJECT_VERSION;
|
||||
doc["system"]["buildDate"] = __DATE__ " " __TIME__;
|
||||
doc["system"]["uptime"] = millis() / 1000ul;
|
||||
|
||||
@@ -69,7 +69,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
// Ограничиваем, если до этого не ограничило
|
||||
// Limits
|
||||
if (newTemp < settings.heating.minTemp || newTemp > settings.heating.maxTemp) {
|
||||
newTemp = constrain(newTemp, settings.heating.minTemp, settings.heating.maxTemp);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ protected:
|
||||
float newTemp = 0;
|
||||
|
||||
// if use equitherm
|
||||
if (settings.emergency.useEquitherm && settings.sensors.outdoor.type != 1) {
|
||||
if (settings.emergency.useEquitherm && settings.sensors.outdoor.type != SensorType::MANUAL) {
|
||||
float etResult = getEquithermTemp(settings.heating.minTemp, settings.heating.maxTemp);
|
||||
|
||||
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
|
||||
@@ -97,7 +97,7 @@ protected:
|
||||
newTemp += prevEtResult;
|
||||
}
|
||||
|
||||
} else if(settings.emergency.usePid && settings.sensors.indoor.type != 1) {
|
||||
} else if(settings.emergency.usePid && settings.sensors.indoor.type != SensorType::MANUAL) {
|
||||
if (vars.parameters.heatingEnabled) {
|
||||
float pidResult = getPidTemp(
|
||||
settings.heating.minTemp,
|
||||
@@ -159,7 +159,7 @@ protected:
|
||||
if (vars.parameters.heatingEnabled) {
|
||||
float pidResult = getPidTemp(
|
||||
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
|
||||
settings.equitherm.enable ? settings.pid.maxTemp : settings.pid.maxTemp
|
||||
settings.pid.maxTemp
|
||||
);
|
||||
|
||||
if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {
|
||||
@@ -182,7 +182,6 @@ protected:
|
||||
}
|
||||
|
||||
newTemp = round(newTemp);
|
||||
newTemp = constrain(newTemp, 0, 100);
|
||||
return newTemp;
|
||||
}
|
||||
|
||||
@@ -273,16 +272,36 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
etRegulator.Kt = 0;
|
||||
etRegulator.indoorTemp = 0;
|
||||
etRegulator.outdoorTemp = vars.temperatures.outdoor;
|
||||
etRegulator.outdoorTemp = outdoorTemp;
|
||||
|
||||
} else if (settings.pid.enable) {
|
||||
etRegulator.Kt = 0;
|
||||
etRegulator.indoorTemp = round(vars.temperatures.indoor);
|
||||
etRegulator.outdoorTemp = round(vars.temperatures.outdoor);
|
||||
etRegulator.indoorTemp = round(indoorTemp);
|
||||
etRegulator.outdoorTemp = round(outdoorTemp);
|
||||
|
||||
} else {
|
||||
if (settings.heating.turbo) {
|
||||
@@ -290,17 +309,21 @@ protected:
|
||||
} else {
|
||||
etRegulator.Kt = settings.equitherm.t_factor;
|
||||
}
|
||||
etRegulator.indoorTemp = vars.temperatures.indoor;
|
||||
etRegulator.outdoorTemp = vars.temperatures.outdoor;
|
||||
etRegulator.indoorTemp = indoorTemp;
|
||||
etRegulator.outdoorTemp = outdoorTemp;
|
||||
}
|
||||
|
||||
etRegulator.setLimits(minTemp, maxTemp);
|
||||
etRegulator.Kn = settings.equitherm.n_factor;
|
||||
// etRegulator.Kn = tuneEquithermN(etRegulator.Kn, vars.temperatures.indoor, settings.heating.target, 300, 1800, 0.01, 1);
|
||||
etRegulator.Kk = settings.equitherm.k_factor;
|
||||
etRegulator.targetTemp = vars.states.emergency ? settings.emergency.target : settings.heating.target;
|
||||
etRegulator.targetTemp = targetTemp;
|
||||
float result = etRegulator.getResult();
|
||||
|
||||
return etRegulator.getResult();
|
||||
if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||
result = c2f(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float getPidTemp(int minTemp, int maxTemp) {
|
||||
|
||||
@@ -3,13 +3,6 @@
|
||||
|
||||
#if USE_BLE
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
// BLE services and characterstics that we are interested in
|
||||
const uint16_t bleUuidServiceBattery = 0x180F;
|
||||
const uint16_t bleUuidServiceEnvironment = 0x181AU;
|
||||
const uint16_t bleUuidCharacteristicBatteryLevel = 0x2A19;
|
||||
const uint16_t bleUuidCharacteristicTemperature = 0x2A6E;
|
||||
const uint16_t bleUuidCharacteristicHumidity = 0x2A6F;
|
||||
#endif
|
||||
|
||||
class SensorsTask : public LeanTask {
|
||||
@@ -25,21 +18,10 @@ public:
|
||||
}
|
||||
|
||||
~SensorsTask() {
|
||||
if (this->outdoorSensor != nullptr) {
|
||||
delete this->outdoorSensor;
|
||||
}
|
||||
|
||||
if (this->oneWireOutdoorSensor != nullptr) {
|
||||
delete this->oneWireOutdoorSensor;
|
||||
}
|
||||
|
||||
if (this->indoorSensor != nullptr) {
|
||||
delete this->indoorSensor;
|
||||
}
|
||||
|
||||
if (this->oneWireIndoorSensor != nullptr) {
|
||||
delete this->oneWireIndoorSensor;
|
||||
}
|
||||
delete this->outdoorSensor;
|
||||
delete this->oneWireOutdoorSensor;
|
||||
delete this->indoorSensor;
|
||||
delete this->oneWireIndoorSensor;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -61,9 +43,8 @@ protected:
|
||||
|
||||
#if USE_BLE
|
||||
BLEClient* pBleClient = nullptr;
|
||||
BLERemoteService* pBleServiceBattery = nullptr;
|
||||
BLERemoteService* pBleServiceEnvironment = nullptr;
|
||||
bool initBleSensor = false;
|
||||
bool initBleNotify = false;
|
||||
#endif
|
||||
|
||||
const char* getTaskName() {
|
||||
@@ -79,76 +60,178 @@ protected:
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) {
|
||||
bool indoorTempUpdated = false;
|
||||
bool outdoorTempUpdated = false;
|
||||
|
||||
if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
|
||||
outdoorTemperatureSensor();
|
||||
outdoorTempUpdated = true;
|
||||
}
|
||||
|
||||
if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) {
|
||||
if (settings.sensors.indoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
|
||||
indoorTemperatureSensor();
|
||||
indoorTempUpdated = true;
|
||||
}
|
||||
#if USE_BLE
|
||||
else if (settings.sensors.indoor.type == SensorType::BLUETOOTH) {
|
||||
indoorTemperatureBluetoothSensor();
|
||||
indoorTempUpdated = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (outdoorTempUpdated) {
|
||||
float newTemp = settings.sensors.outdoor.offset;
|
||||
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
||||
newTemp += this->filteredOutdoorTemp;
|
||||
|
||||
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||
newTemp += c2f(this->filteredOutdoorTemp);
|
||||
}
|
||||
|
||||
if (fabs(vars.temperatures.outdoor - newTemp) > 0.099) {
|
||||
vars.temperatures.outdoor = newTemp;
|
||||
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor);
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
if (settings.sensors.indoor.type == 3 && strlen(settings.sensors.indoor.bleAddresss)) {
|
||||
bluetoothSensor();
|
||||
if (indoorTempUpdated) {
|
||||
float newTemp = settings.sensors.indoor.offset;
|
||||
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
||||
newTemp += this->filteredIndoorTemp;
|
||||
|
||||
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
||||
newTemp += c2f(this->filteredIndoorTemp);
|
||||
}
|
||||
|
||||
if (fabs(vars.temperatures.indoor - newTemp) > 0.099) {
|
||||
vars.temperatures.indoor = newTemp;
|
||||
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
void bluetoothSensor() {
|
||||
void indoorTemperatureBluetoothSensor() {
|
||||
static bool initBleNotify = false;
|
||||
if (!initBleSensor && millis() > 5000) {
|
||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Init BLE. Free heap %u bytes", ESP.getFreeHeap());
|
||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE"));
|
||||
BLEDevice::init("");
|
||||
|
||||
pBleClient = BLEDevice::createClient();
|
||||
pBleClient = BLEDevice::createClient();
|
||||
pBleClient->setConnectTimeout(5);
|
||||
|
||||
// Connect to the remote BLE Server.
|
||||
BLEAddress bleServerAddress(std::string(settings.sensors.indoor.bleAddresss));
|
||||
if (pBleClient->connect(bleServerAddress)) {
|
||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Connected to BLE device at %s", bleServerAddress.toString().c_str());
|
||||
// Obtain a reference to the services we are interested in
|
||||
pBleServiceBattery = pBleClient->getService(BLEUUID(bleUuidServiceBattery));
|
||||
if (pBleServiceBattery == nullptr) {
|
||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Failed to find battery service");
|
||||
}
|
||||
pBleServiceEnvironment = pBleClient->getService(BLEUUID(bleUuidServiceEnvironment));
|
||||
if (pBleServiceEnvironment == nullptr) {
|
||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Failed to find environmental service");
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), "Error connecting to BLE device at %s", bleServerAddress.toString().c_str());
|
||||
}
|
||||
|
||||
initBleSensor = true;
|
||||
}
|
||||
|
||||
if (pBleClient && pBleClient->isConnected()) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Connected. Free heap %u bytes", ESP.getFreeHeap());
|
||||
if (pBleServiceBattery) {
|
||||
uint8_t batteryLevel = *reinterpret_cast<const uint8_t *>(pBleServiceBattery->getValue(bleUuidCharacteristicBatteryLevel).data());
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Battery: %d", batteryLevel);
|
||||
}
|
||||
if (!initBleSensor || pBleClient->isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset init notify flag
|
||||
this->initBleNotify = false;
|
||||
|
||||
if (pBleServiceEnvironment) {
|
||||
float temperature = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicTemperature).data()) / 100.0f;
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Temperature: %.2f", temperature);
|
||||
float humidity = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicHumidity).data()) / 100.0f;
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Humidity: %.2f", humidity);
|
||||
// Connect to the remote BLE Server.
|
||||
BLEAddress bleServerAddress(settings.sensors.indoor.bleAddresss);
|
||||
if (!pBleClient->connect(bleServerAddress)) {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), "Failed connecting to device at %s", bleServerAddress.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
vars.temperatures.indoor = temperature + settings.sensors.indoor.offset;
|
||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Connected to device at %s", bleServerAddress.toString().c_str());
|
||||
|
||||
NimBLEUUID serviceUUID((uint16_t) 0x181AU);
|
||||
BLERemoteService* pRemoteService = pBleClient->getService(serviceUUID);
|
||||
if (!pRemoteService) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Failed to find service UUID: %s"), serviceUUID.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found service UUID: %s"), serviceUUID.toString().c_str());
|
||||
|
||||
// 0x2A6E - Notify temperature x0.01C (pvvx)
|
||||
if (!this->initBleNotify) {
|
||||
NimBLEUUID charUUID((uint16_t) 0x2A6E);
|
||||
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
|
||||
|
||||
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (length != 2) {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
|
||||
return;
|
||||
}
|
||||
|
||||
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01);
|
||||
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
||||
|
||||
if (this->emptyIndoorTemp) {
|
||||
this->filteredIndoorTemp = rawTemp;
|
||||
this->emptyIndoorTemp = false;
|
||||
|
||||
} else {
|
||||
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
||||
}
|
||||
|
||||
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
||||
});
|
||||
|
||||
if (this->initBleNotify) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), "Not connected");
|
||||
}
|
||||
|
||||
// 0x2A1F - Notify temperature x0.1C (atc1441/pvvx)
|
||||
if (!this->initBleNotify) {
|
||||
NimBLEUUID charUUID((uint16_t) 0x2A1F);
|
||||
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
|
||||
|
||||
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (length != 2) {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
|
||||
return;
|
||||
}
|
||||
|
||||
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1);
|
||||
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
||||
|
||||
if (this->emptyIndoorTemp) {
|
||||
this->filteredIndoorTemp = rawTemp;
|
||||
this->emptyIndoorTemp = false;
|
||||
|
||||
} else {
|
||||
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
||||
}
|
||||
|
||||
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
||||
});
|
||||
|
||||
if (this->initBleNotify) {
|
||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->initBleNotify) {
|
||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Not found supported characteristics"));
|
||||
pBleClient->disconnect();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void outdoorTemperatureSensor() {
|
||||
if (!this->initOutdoorSensor) {
|
||||
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Starting on gpio %hhu..."), settings.sensors.outdoor.pin);
|
||||
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Starting on gpio %hhu..."), settings.sensors.outdoor.gpio);
|
||||
|
||||
this->oneWireOutdoorSensor->begin(settings.sensors.outdoor.pin);
|
||||
this->oneWireOutdoorSensor->begin(settings.sensors.outdoor.gpio);
|
||||
this->outdoorSensor->begin();
|
||||
|
||||
Log.straceln(
|
||||
@@ -205,12 +288,6 @@ protected:
|
||||
}
|
||||
|
||||
this->filteredOutdoorTemp = floor(this->filteredOutdoorTemp * 100) / 100;
|
||||
|
||||
if (fabs(vars.temperatures.outdoor - this->filteredOutdoorTemp) > 0.099) {
|
||||
vars.temperatures.outdoor = this->filteredOutdoorTemp + settings.sensors.outdoor.offset;
|
||||
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), this->filteredOutdoorTemp);
|
||||
}
|
||||
|
||||
this->outdoorSensor->requestTemperatures();
|
||||
this->startOutdoorConversionTime = millis();
|
||||
}
|
||||
@@ -218,9 +295,9 @@ protected:
|
||||
|
||||
void indoorTemperatureSensor() {
|
||||
if (!this->initIndoorSensor) {
|
||||
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Starting on gpio %hhu..."), settings.sensors.indoor.pin);
|
||||
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Starting on gpio %hhu..."), settings.sensors.indoor.gpio);
|
||||
|
||||
this->oneWireIndoorSensor->begin(settings.sensors.indoor.pin);
|
||||
this->oneWireIndoorSensor->begin(settings.sensors.indoor.gpio);
|
||||
this->indoorSensor->begin();
|
||||
|
||||
Log.straceln(
|
||||
@@ -277,12 +354,6 @@ protected:
|
||||
}
|
||||
|
||||
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
||||
|
||||
if (fabs(vars.temperatures.indoor - this->filteredIndoorTemp) > 0.099) {
|
||||
vars.temperatures.indoor = this->filteredIndoorTemp + settings.sensors.indoor.offset;
|
||||
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), this->filteredIndoorTemp);
|
||||
}
|
||||
|
||||
this->indoorSensor->requestTemperatures();
|
||||
this->startIndoorConversionTime = millis();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
struct NetworkSettings {
|
||||
char hostname[25] = HOSTNAME_DEFAULT;
|
||||
char hostname[25] = DEFAULT_HOSTNAME;
|
||||
bool useDhcp = true;
|
||||
|
||||
struct {
|
||||
@@ -10,14 +10,14 @@ struct NetworkSettings {
|
||||
} staticConfig;
|
||||
|
||||
struct {
|
||||
char ssid[33] = AP_SSID_DEFAULT;
|
||||
char password[65] = AP_PASSWORD_DEFAULT;
|
||||
char ssid[33] = DEFAULT_AP_SSID;
|
||||
char password[65] = DEFAULT_AP_PASSWORD;
|
||||
byte channel = 6;
|
||||
} ap;
|
||||
|
||||
struct {
|
||||
char ssid[33] = STA_SSID_DEFAULT;
|
||||
char password[65] = STA_PASSWORD_DEFAULT;
|
||||
char ssid[33] = DEFAULT_STA_SSID;
|
||||
char password[65] = DEFAULT_STA_PASSWORD;
|
||||
byte channel = 0;
|
||||
} sta;
|
||||
} networkSettings;
|
||||
@@ -27,17 +27,19 @@ struct Settings {
|
||||
bool debug = DEBUG_BY_DEFAULT;
|
||||
bool useSerial = USE_SERIAL;
|
||||
bool useTelnet = USE_TELNET;
|
||||
UnitSystem unitSystem = UnitSystem::METRIC;
|
||||
} system;
|
||||
|
||||
struct {
|
||||
bool useAuth = false;
|
||||
char login[13] = PORTAL_LOGIN_DEFAULT;
|
||||
char password[33] = PORTAL_PASSWORD_DEFAULT;
|
||||
char login[13] = DEFAULT_PORTAL_LOGIN;
|
||||
char password[33] = DEFAULT_PORTAL_PASSWORD;
|
||||
} portal;
|
||||
|
||||
struct {
|
||||
byte inPin = OT_IN_PIN_DEFAULT;
|
||||
byte outPin = OT_OUT_PIN_DEFAULT;
|
||||
UnitSystem unitSystem = UnitSystem::METRIC;
|
||||
byte inGpio = DEFAULT_OT_IN_GPIO;
|
||||
byte outGpio = DEFAULT_OT_OUT_GPIO;
|
||||
unsigned int memberIdCode = 0;
|
||||
bool dhwPresent = true;
|
||||
bool summerWinterMode = false;
|
||||
@@ -46,14 +48,15 @@ struct Settings {
|
||||
bool dhwToCh2 = false;
|
||||
bool dhwBlocking = false;
|
||||
bool modulationSyncWithHeating = false;
|
||||
bool getMinMaxTemp = true;
|
||||
} opentherm;
|
||||
|
||||
struct {
|
||||
char server[81] = MQTT_SERVER_DEFAULT;
|
||||
unsigned short port = MQTT_PORT_DEFAULT;
|
||||
char user[33] = MQTT_USER_DEFAULT;
|
||||
char password[33] = MQTT_PASSWORD_DEFAULT;
|
||||
char prefix[33] = MQTT_PREFIX_DEFAULT;
|
||||
char server[81] = DEFAULT_MQTT_SERVER;
|
||||
unsigned short port = DEFAULT_MQTT_PORT;
|
||||
char user[33] = DEFAULT_MQTT_USER;
|
||||
char password[33] = DEFAULT_MQTT_PASSWORD;
|
||||
char prefix[33] = DEFAULT_MQTT_PREFIX;
|
||||
unsigned short interval = 5;
|
||||
} mqtt;
|
||||
|
||||
@@ -100,24 +103,22 @@ struct Settings {
|
||||
|
||||
struct {
|
||||
struct {
|
||||
// 0 - boiler, 1 - manual, 2 - ds18b20
|
||||
byte type = 0;
|
||||
byte pin = SENSOR_OUTDOOR_PIN_DEFAULT;
|
||||
SensorType type = SensorType::BOILER;
|
||||
byte gpio = DEFAULT_SENSOR_OUTDOOR_GPIO;
|
||||
float offset = 0.0f;
|
||||
} outdoor;
|
||||
|
||||
struct {
|
||||
// 1 - manual, 2 - ds18b20, 3 - ble
|
||||
byte type = 1;
|
||||
byte pin = SENSOR_INDOOR_PIN_DEFAULT;
|
||||
char bleAddresss[18] = "00:00:00:00:00:00";
|
||||
SensorType type = SensorType::MANUAL;
|
||||
byte gpio = DEFAULT_SENSOR_INDOOR_GPIO;
|
||||
uint8_t bleAddresss[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
float offset = 0.0f;
|
||||
} indoor;
|
||||
} sensors;
|
||||
|
||||
struct {
|
||||
bool use = false;
|
||||
byte pin = EXT_PUMP_PIN_DEFAULT;
|
||||
byte gpio = DEFAULT_EXT_PUMP_GPIO;
|
||||
unsigned short postCirculationTime = 600;
|
||||
unsigned int antiStuckInterval = 2592000;
|
||||
unsigned short antiStuckTime = 300;
|
||||
@@ -155,7 +156,9 @@ struct Variables {
|
||||
float indoor = 0.0f;
|
||||
float outdoor = 0.0f;
|
||||
float heating = 0.0f;
|
||||
float heatingReturn = 0.0f;
|
||||
float dhw = 0.0f;
|
||||
float exhaust = 0.0f;
|
||||
} temperatures;
|
||||
|
||||
struct {
|
||||
@@ -166,17 +169,17 @@ struct Variables {
|
||||
unsigned long extPumpLastEnableTime = 0;
|
||||
byte dhwMinTemp = DEFAULT_DHW_MIN_TEMP;
|
||||
byte dhwMaxTemp = DEFAULT_DHW_MAX_TEMP;
|
||||
byte maxModulation;
|
||||
uint8_t slaveMemberId;
|
||||
uint8_t slaveFlags;
|
||||
uint8_t slaveType;
|
||||
uint8_t slaveVersion;
|
||||
float slaveOtVersion;
|
||||
uint8_t masterMemberId;
|
||||
uint8_t masterFlags;
|
||||
uint8_t masterType;
|
||||
uint8_t masterVersion;
|
||||
float masterOtVersion;
|
||||
byte maxModulation = 0;
|
||||
uint8_t slaveMemberId = 0;
|
||||
uint8_t slaveFlags = 0;
|
||||
uint8_t slaveType = 0;
|
||||
uint8_t slaveVersion = 0;
|
||||
float slaveOtVersion = 0.0f;
|
||||
uint8_t masterMemberId = 0;
|
||||
uint8_t masterFlags = 0;
|
||||
uint8_t masterType = 0;
|
||||
uint8_t masterVersion = 0;
|
||||
float masterOtVersion = 0;
|
||||
} parameters;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
#define PROJECT_NAME "OpenTherm Gateway"
|
||||
#define PROJECT_VERSION "1.4.0-rc.14"
|
||||
#define PROJECT_VERSION "1.4.0-rc.20"
|
||||
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
||||
|
||||
#define EMERGENCY_TIME_TRESHOLD 120000
|
||||
#define MQTT_RECONNECT_INTERVAL 15000
|
||||
#define MQTT_KEEPALIVE 30
|
||||
|
||||
#define OPENTHERM_OFFLINE_TRESHOLD 10
|
||||
|
||||
#define EXT_SENSORS_INTERVAL 5000
|
||||
#define EXT_SENSORS_FILTER_K 0.15
|
||||
@@ -14,16 +11,12 @@
|
||||
#define CONFIG_URL "http://%s/"
|
||||
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
|
||||
|
||||
#define GPIO_IS_NOT_CONFIGURED 0xff
|
||||
#define DEFAULT_HEATING_MIN_TEMP 20
|
||||
#define DEFAULT_HEATING_MAX_TEMP 90
|
||||
#define DEFAULT_DHW_MIN_TEMP 30
|
||||
#define DEFAULT_DHW_MAX_TEMP 60
|
||||
|
||||
|
||||
#ifndef WM_DEBUG_MODE
|
||||
#define WM_DEBUG_MODE WM_DEBUG_NOTIFY
|
||||
#endif
|
||||
|
||||
#ifndef USE_SERIAL
|
||||
#define USE_SERIAL true
|
||||
#endif
|
||||
@@ -36,80 +29,98 @@
|
||||
#define USE_BLE false
|
||||
#endif
|
||||
|
||||
#ifndef HOSTNAME_DEFAULT
|
||||
#define HOSTNAME_DEFAULT "opentherm"
|
||||
#ifndef DEFAULT_HOSTNAME
|
||||
#define DEFAULT_HOSTNAME "opentherm"
|
||||
#endif
|
||||
|
||||
#ifndef AP_SSID_DEFAULT
|
||||
#define AP_SSID_DEFAULT "OpenTherm Gateway"
|
||||
#ifndef DEFAULT_AP_SSID
|
||||
#define DEFAULT_AP_SSID "OpenTherm Gateway"
|
||||
#endif
|
||||
|
||||
#ifndef AP_PASSWORD_DEFAULT
|
||||
#define AP_PASSWORD_DEFAULT "otgateway123456"
|
||||
#ifndef DEFAULT_AP_PASSWORD
|
||||
#define DEFAULT_AP_PASSWORD "otgateway123456"
|
||||
#endif
|
||||
|
||||
#ifndef STA_SSID_DEFAULT
|
||||
#define STA_SSID_DEFAULT ""
|
||||
#ifndef DEFAULT_STA_SSID
|
||||
#define DEFAULT_STA_SSID ""
|
||||
#endif
|
||||
|
||||
#ifndef STA_PASSWORD_DEFAULT
|
||||
#define STA_PASSWORD_DEFAULT ""
|
||||
#ifndef DEFAULT_STA_PASSWORD
|
||||
#define DEFAULT_STA_PASSWORD ""
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_BY_DEFAULT
|
||||
#define DEBUG_BY_DEFAULT false
|
||||
#endif
|
||||
|
||||
#ifndef PORTAL_LOGIN_DEFAULT
|
||||
#define PORTAL_LOGIN_DEFAULT ""
|
||||
#ifndef DEFAULT_PORTAL_LOGIN
|
||||
#define DEFAULT_PORTAL_LOGIN ""
|
||||
#endif
|
||||
|
||||
#ifndef PORTAL_PASSWORD_DEFAULT
|
||||
#define PORTAL_PASSWORD_DEFAULT ""
|
||||
#ifndef DEFAULT_PORTAL_PASSWORD
|
||||
#define DEFAULT_PORTAL_PASSWORD ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_SERVER_DEFAULT
|
||||
#define MQTT_SERVER_DEFAULT ""
|
||||
#ifndef DEFAULT_MQTT_SERVER
|
||||
#define DEFAULT_MQTT_SERVER ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_PORT_DEFAULT
|
||||
#define MQTT_PORT_DEFAULT 1883
|
||||
#ifndef DEFAULT_MQTT_PORT
|
||||
#define DEFAULT_MQTT_PORT 1883
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_USER_DEFAULT
|
||||
#define MQTT_USER_DEFAULT ""
|
||||
#ifndef DEFAULT_MQTT_USER
|
||||
#define DEFAULT_MQTT_USER ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_PASSWORD_DEFAULT
|
||||
#define MQTT_PASSWORD_DEFAULT ""
|
||||
#ifndef DEFAULT_MQTT_PASSWORD
|
||||
#define DEFAULT_MQTT_PASSWORD ""
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_PREFIX_DEFAULT
|
||||
#define MQTT_PREFIX_DEFAULT "opentherm"
|
||||
#ifndef DEFAULT_MQTT_PREFIX
|
||||
#define DEFAULT_MQTT_PREFIX "opentherm"
|
||||
#endif
|
||||
|
||||
#ifndef OT_IN_PIN_DEFAULT
|
||||
#define OT_IN_PIN_DEFAULT 0
|
||||
#ifndef DEFAULT_OT_IN_GPIO
|
||||
#define DEFAULT_OT_IN_GPIO GPIO_IS_NOT_CONFIGURED
|
||||
#endif
|
||||
|
||||
#ifndef OT_OUT_PIN_DEFAULT
|
||||
#define OT_OUT_PIN_DEFAULT 0
|
||||
#ifndef DEFAULT_OT_OUT_GPIO
|
||||
#define DEFAULT_OT_OUT_GPIO GPIO_IS_NOT_CONFIGURED
|
||||
#endif
|
||||
|
||||
#ifndef SENSOR_OUTDOOR_PIN_DEFAULT
|
||||
#define SENSOR_OUTDOOR_PIN_DEFAULT 0
|
||||
#ifndef DEFAULT_SENSOR_OUTDOOR_GPIO
|
||||
#define DEFAULT_SENSOR_OUTDOOR_GPIO GPIO_IS_NOT_CONFIGURED
|
||||
#endif
|
||||
|
||||
#ifndef SENSOR_INDOOR_PIN_DEFAULT
|
||||
#define SENSOR_INDOOR_PIN_DEFAULT 0
|
||||
#ifndef DEFAULT_SENSOR_INDOOR_GPIO
|
||||
#define DEFAULT_SENSOR_INDOOR_GPIO GPIO_IS_NOT_CONFIGURED
|
||||
#endif
|
||||
|
||||
#ifndef EXT_PUMP_PIN_DEFAULT
|
||||
#define EXT_PUMP_PIN_DEFAULT 0
|
||||
#ifndef DEFAULT_EXT_PUMP_GPIO
|
||||
#define DEFAULT_EXT_PUMP_GPIO GPIO_IS_NOT_CONFIGURED
|
||||
#endif
|
||||
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
#ifndef GPIO_IS_VALID_GPIO
|
||||
#define GPIO_IS_VALID_GPIO(gpioNum) (gpioNum >= 0 && gpioNum <= 16)
|
||||
#endif
|
||||
|
||||
#define GPIO_IS_VALID(gpioNum) (gpioNum != GPIO_IS_NOT_CONFIGURED && GPIO_IS_VALID_GPIO(gpioNum))
|
||||
|
||||
enum class SensorType : byte {
|
||||
BOILER,
|
||||
MANUAL,
|
||||
DS18B20,
|
||||
BLUETOOTH
|
||||
};
|
||||
|
||||
enum class UnitSystem : byte {
|
||||
METRIC,
|
||||
IMPERIAL
|
||||
};
|
||||
|
||||
char buffer[255];
|
||||
@@ -143,7 +143,7 @@ void setup() {
|
||||
tMqtt = new MqttTask(false, 500);
|
||||
Scheduler.start(tMqtt);
|
||||
|
||||
tOt = new OpenThermTask(false, 750);
|
||||
tOt = new OpenThermTask(true, 750);
|
||||
Scheduler.start(tOt);
|
||||
|
||||
tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL);
|
||||
|
||||
340
src/utils.h
340
src/utils.h
@@ -1,5 +1,74 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
inline float liter2gallon(float value) {
|
||||
return value / 4.546091879f;
|
||||
}
|
||||
|
||||
inline float gallon2liter(float value) {
|
||||
return value * 4.546091879f;
|
||||
}
|
||||
|
||||
float convertVolume(float value, const UnitSystem unitFrom, const UnitSystem unitTo) {
|
||||
if (unitFrom == UnitSystem::METRIC && unitTo == UnitSystem::IMPERIAL) {
|
||||
value = liter2gallon(value);
|
||||
|
||||
} else if (unitFrom == UnitSystem::IMPERIAL && unitTo == UnitSystem::METRIC) {
|
||||
value = gallon2liter(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline float bar2psi(float value) {
|
||||
return value * 14.5038f;
|
||||
}
|
||||
|
||||
inline float psi2bar(float value) {
|
||||
return value / 14.5038f;
|
||||
}
|
||||
|
||||
float convertPressure(float value, const UnitSystem unitFrom, const UnitSystem unitTo) {
|
||||
if (unitFrom == UnitSystem::METRIC && unitTo == UnitSystem::IMPERIAL) {
|
||||
value = bar2psi(value);
|
||||
|
||||
} else if (unitFrom == UnitSystem::IMPERIAL && unitTo == UnitSystem::METRIC) {
|
||||
value = psi2bar(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline float c2f(float value) {
|
||||
return (9.0f / 5.0f) * value + 32.0f;
|
||||
}
|
||||
|
||||
inline float f2c(float value) {
|
||||
return (value - 32.0f) * (5.0f / 9.0f);
|
||||
}
|
||||
|
||||
float convertTemp(float value, const UnitSystem unitFrom, const UnitSystem unitTo) {
|
||||
if (unitFrom == UnitSystem::METRIC && unitTo == UnitSystem::IMPERIAL) {
|
||||
value = c2f(value);
|
||||
|
||||
} else if (unitFrom == UnitSystem::IMPERIAL && unitTo == UnitSystem::METRIC) {
|
||||
value = f2c(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool isValidTemp(const float value, UnitSystem unit, const float min = 0.1f, const float max = 99.9f) {
|
||||
if (unit == UnitSystem::METRIC) {
|
||||
return value >= min && value <= max;
|
||||
|
||||
} else if (unit == UnitSystem::IMPERIAL) {
|
||||
return value >= c2f(min) && value <= c2f(max);
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double roundd(double value, uint8_t decimals = 2) {
|
||||
if (decimals == 0) {
|
||||
return (int)(value + 0.5);
|
||||
@@ -13,7 +82,7 @@ double roundd(double value, uint8_t decimals = 2) {
|
||||
return (int)(value * multiplier) / multiplier;
|
||||
}
|
||||
|
||||
size_t getTotalHeap() {
|
||||
inline size_t getTotalHeap() {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
return ESP.getHeapSize();
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
@@ -63,7 +132,7 @@ size_t getMaxFreeBlockHeap(bool getMinValue = false) {
|
||||
return getMinValue ? minValue : value;
|
||||
}
|
||||
|
||||
uint8_t getHeapFrag() {
|
||||
inline uint8_t getHeapFrag() {
|
||||
return 100 - getMaxFreeBlockHeap() * 100.0 / getFreeHeap();
|
||||
}
|
||||
|
||||
@@ -232,7 +301,7 @@ bool jsonToNetworkSettings(const JsonVariantConst src, NetworkSettings& dst) {
|
||||
}
|
||||
|
||||
|
||||
// ap
|
||||
// sta
|
||||
if (!src["sta"]["ssid"].isNull()) {
|
||||
String value = src["sta"]["ssid"].as<String>();
|
||||
|
||||
@@ -268,13 +337,15 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
dst["system"]["debug"] = src.system.debug;
|
||||
dst["system"]["useSerial"] = src.system.useSerial;
|
||||
dst["system"]["useTelnet"] = src.system.useTelnet;
|
||||
dst["system"]["unitSystem"] = static_cast<byte>(src.system.unitSystem);
|
||||
|
||||
dst["portal"]["useAuth"] = src.portal.useAuth;
|
||||
dst["portal"]["login"] = src.portal.login;
|
||||
dst["portal"]["password"] = src.portal.password;
|
||||
|
||||
dst["opentherm"]["inPin"] = src.opentherm.inPin;
|
||||
dst["opentherm"]["outPin"] = src.opentherm.outPin;
|
||||
dst["opentherm"]["unitSystem"] = static_cast<byte>(src.opentherm.unitSystem);
|
||||
dst["opentherm"]["inGpio"] = src.opentherm.inGpio;
|
||||
dst["opentherm"]["outGpio"] = src.opentherm.outGpio;
|
||||
dst["opentherm"]["memberIdCode"] = src.opentherm.memberIdCode;
|
||||
dst["opentherm"]["dhwPresent"] = src.opentherm.dhwPresent;
|
||||
dst["opentherm"]["summerWinterMode"] = src.opentherm.summerWinterMode;
|
||||
@@ -283,6 +354,7 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
dst["opentherm"]["dhwToCh2"] = src.opentherm.dhwToCh2;
|
||||
dst["opentherm"]["dhwBlocking"] = src.opentherm.dhwBlocking;
|
||||
dst["opentherm"]["modulationSyncWithHeating"] = src.opentherm.modulationSyncWithHeating;
|
||||
dst["opentherm"]["getMinMaxTemp"] = src.opentherm.getMinMaxTemp;
|
||||
|
||||
dst["mqtt"]["server"] = src.mqtt.server;
|
||||
dst["mqtt"]["port"] = src.mqtt.port;
|
||||
@@ -323,18 +395,30 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
dst["equitherm"]["k_factor"] = roundd(src.equitherm.k_factor, 3);
|
||||
dst["equitherm"]["t_factor"] = roundd(src.equitherm.t_factor, 3);
|
||||
|
||||
dst["sensors"]["outdoor"]["type"] = src.sensors.outdoor.type;
|
||||
dst["sensors"]["outdoor"]["pin"] = src.sensors.outdoor.pin;
|
||||
dst["sensors"]["outdoor"]["type"] = static_cast<byte>(src.sensors.outdoor.type);
|
||||
dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio;
|
||||
dst["sensors"]["outdoor"]["offset"] = roundd(src.sensors.outdoor.offset, 2);
|
||||
|
||||
dst["sensors"]["indoor"]["type"] = src.sensors.indoor.type;
|
||||
dst["sensors"]["indoor"]["pin"] = src.sensors.indoor.pin;
|
||||
dst["sensors"]["indoor"]["bleAddresss"] = src.sensors.indoor.bleAddresss;
|
||||
dst["sensors"]["indoor"]["type"] = static_cast<byte>(src.sensors.indoor.type);
|
||||
dst["sensors"]["indoor"]["gpio"] = src.sensors.indoor.gpio;
|
||||
|
||||
char bleAddress[18];
|
||||
sprintf(
|
||||
bleAddress,
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
src.sensors.indoor.bleAddresss[0],
|
||||
src.sensors.indoor.bleAddresss[1],
|
||||
src.sensors.indoor.bleAddresss[2],
|
||||
src.sensors.indoor.bleAddresss[3],
|
||||
src.sensors.indoor.bleAddresss[4],
|
||||
src.sensors.indoor.bleAddresss[5]
|
||||
);
|
||||
dst["sensors"]["indoor"]["bleAddresss"] = String(bleAddress);
|
||||
dst["sensors"]["indoor"]["offset"] = roundd(src.sensors.indoor.offset, 2);
|
||||
|
||||
if (!safe) {
|
||||
dst["externalPump"]["use"] = src.externalPump.use;
|
||||
dst["externalPump"]["pin"] = src.externalPump.pin;
|
||||
dst["externalPump"]["gpio"] = src.externalPump.gpio;
|
||||
dst["externalPump"]["postCirculationTime"] = roundd(src.externalPump.postCirculationTime / 60, 0);
|
||||
dst["externalPump"]["antiStuckInterval"] = roundd(src.externalPump.antiStuckInterval / 86400, 0);
|
||||
dst["externalPump"]["antiStuckTime"] = roundd(src.externalPump.antiStuckTime / 60, 0);
|
||||
@@ -365,6 +449,39 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["system"]["unitSystem"].isNull()) {
|
||||
byte value = src["system"]["unitSystem"].as<unsigned char>();
|
||||
UnitSystem prevUnitSystem = dst.system.unitSystem;
|
||||
|
||||
switch (value) {
|
||||
case static_cast<byte>(UnitSystem::METRIC):
|
||||
dst.system.unitSystem = UnitSystem::METRIC;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
case static_cast<byte>(UnitSystem::IMPERIAL):
|
||||
dst.system.unitSystem = UnitSystem::IMPERIAL;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// convert temps
|
||||
if (dst.system.unitSystem != prevUnitSystem) {
|
||||
dst.emergency.target = convertTemp(dst.emergency.target, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.heating.target = convertTemp(dst.heating.target, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.heating.minTemp = convertTemp(dst.heating.minTemp, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.heating.maxTemp = convertTemp(dst.heating.maxTemp, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.dhw.target = convertTemp(dst.dhw.target, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.dhw.minTemp = convertTemp(dst.dhw.minTemp, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.dhw.maxTemp = convertTemp(dst.dhw.maxTemp, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.pid.minTemp = convertTemp(dst.pid.minTemp, prevUnitSystem, dst.system.unitSystem);
|
||||
dst.pid.maxTemp = convertTemp(dst.pid.maxTemp, prevUnitSystem, dst.system.unitSystem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// portal
|
||||
if (src["portal"]["useAuth"].is<bool>()) {
|
||||
@@ -392,21 +509,56 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
|
||||
|
||||
// opentherm
|
||||
if (!src["opentherm"]["inPin"].isNull()) {
|
||||
unsigned char value = src["opentherm"]["inPin"].as<unsigned char>();
|
||||
if (!src["opentherm"]["unitSystem"].isNull()) {
|
||||
byte value = src["opentherm"]["unitSystem"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 50) {
|
||||
dst.opentherm.inPin = value;
|
||||
changed = true;
|
||||
switch (value) {
|
||||
case static_cast<byte>(UnitSystem::METRIC):
|
||||
dst.opentherm.unitSystem = UnitSystem::METRIC;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
case static_cast<byte>(UnitSystem::IMPERIAL):
|
||||
dst.opentherm.unitSystem = UnitSystem::IMPERIAL;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["opentherm"]["inGpio"].isNull()) {
|
||||
if (src["opentherm"]["inGpio"].is<JsonString>() && src["opentherm"]["inGpio"].as<JsonString>().size() == 0) {
|
||||
if (dst.opentherm.inGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||
dst.opentherm.inGpio = GPIO_IS_NOT_CONFIGURED;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
unsigned char value = src["opentherm"]["inGpio"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 254) {
|
||||
dst.opentherm.inGpio = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["opentherm"]["outPin"].isNull()) {
|
||||
unsigned char value = src["opentherm"]["outPin"].as<unsigned char>();
|
||||
if (!src["opentherm"]["outGpio"].isNull()) {
|
||||
if (src["opentherm"]["outGpio"].is<JsonString>() && src["opentherm"]["outGpio"].as<JsonString>().size() == 0) {
|
||||
if (dst.opentherm.outGpio != GPIO_IS_NOT_CONFIGURED) {
|
||||
dst.opentherm.outGpio = GPIO_IS_NOT_CONFIGURED;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
unsigned char value = src["opentherm"]["outGpio"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 50) {
|
||||
dst.opentherm.outPin = value;
|
||||
changed = true;
|
||||
if (value >= 0 && value <= 254) {
|
||||
dst.opentherm.outGpio = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,6 +624,11 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src["opentherm"]["getMinMaxTemp"].is<bool>()) {
|
||||
dst.opentherm.getMinMaxTemp = src["opentherm"]["getMinMaxTemp"].as<bool>();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
// mqtt
|
||||
if (!src["mqtt"]["server"].isNull()) {
|
||||
@@ -486,7 +643,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["mqtt"]["port"].isNull()) {
|
||||
unsigned short value = src["mqtt"]["port"].as<unsigned short>();
|
||||
|
||||
if (value >= 0 && value <= 65536) {
|
||||
if (value > 0 && value <= 65535) {
|
||||
dst.mqtt.port = value;
|
||||
changed = true;
|
||||
}
|
||||
@@ -539,14 +696,14 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["emergency"]["target"].isNull()) {
|
||||
double value = src["emergency"]["target"].as<double>();
|
||||
|
||||
if (value > 0 && value < 100) {
|
||||
if (isValidTemp(value, dst.system.unitSystem)) {
|
||||
dst.emergency.target = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (src["emergency"]["useEquitherm"].is<bool>()) {
|
||||
if (dst.sensors.outdoor.type != 1) {
|
||||
if (dst.sensors.outdoor.type != SensorType::MANUAL) {
|
||||
dst.emergency.useEquitherm = src["emergency"]["useEquitherm"].as<bool>();
|
||||
|
||||
} else {
|
||||
@@ -561,7 +718,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
}
|
||||
|
||||
if (src["emergency"]["usePid"].is<bool>()) {
|
||||
if (dst.sensors.indoor.type != 1) {
|
||||
if (dst.sensors.indoor.type != SensorType::MANUAL) {
|
||||
dst.emergency.usePid = src["emergency"]["usePid"].as<bool>();
|
||||
|
||||
} else {
|
||||
@@ -590,7 +747,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["heating"]["target"].isNull()) {
|
||||
double value = src["heating"]["target"].as<double>();
|
||||
|
||||
if (value > 0 && value < 100) {
|
||||
if (isValidTemp(value, dst.system.unitSystem)) {
|
||||
dst.heating.target = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
@@ -642,7 +799,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["dhw"]["target"].isNull()) {
|
||||
unsigned char value = src["dhw"]["target"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 100) {
|
||||
if (isValidTemp(value, dst.system.unitSystem)) {
|
||||
dst.dhw.target = value;
|
||||
changed = true;
|
||||
}
|
||||
@@ -712,7 +869,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["pid"]["maxTemp"].isNull()) {
|
||||
unsigned char value = src["pid"]["maxTemp"].as<unsigned char>();
|
||||
|
||||
if (value > 0 && value <= 100 && value > dst.pid.minTemp) {
|
||||
if (isValidTemp(value, dst.system.unitSystem) && value > dst.pid.minTemp) {
|
||||
dst.pid.maxTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
@@ -721,7 +878,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
if (!src["pid"]["minTemp"].isNull()) {
|
||||
unsigned char value = src["pid"]["minTemp"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value < 100 && value < dst.pid.maxTemp) {
|
||||
if (isValidTemp(value, dst.system.unitSystem) && value < dst.pid.maxTemp) {
|
||||
dst.pid.minTemp = value;
|
||||
changed = true;
|
||||
}
|
||||
@@ -764,25 +921,44 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
|
||||
// sensors
|
||||
if (!src["sensors"]["outdoor"]["type"].isNull()) {
|
||||
unsigned char value = src["sensors"]["outdoor"]["type"].as<unsigned char>();
|
||||
byte value = src["sensors"]["outdoor"]["type"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 2) {
|
||||
dst.sensors.outdoor.type = value;
|
||||
switch (value) {
|
||||
case static_cast<byte>(SensorType::BOILER):
|
||||
dst.sensors.outdoor.type = SensorType::BOILER;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
if (dst.sensors.outdoor.type == 1) {
|
||||
case static_cast<byte>(SensorType::MANUAL):
|
||||
dst.sensors.outdoor.type = SensorType::MANUAL;
|
||||
dst.emergency.useEquitherm = false;
|
||||
}
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
changed = true;
|
||||
case static_cast<byte>(SensorType::DS18B20):
|
||||
dst.sensors.outdoor.type = SensorType::DS18B20;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sensors"]["outdoor"]["pin"].isNull()) {
|
||||
unsigned char value = src["sensors"]["outdoor"]["pin"].as<unsigned char>();
|
||||
if (!src["sensors"]["outdoor"]["gpio"].isNull()) {
|
||||
if (src["sensors"]["outdoor"]["gpio"].is<JsonString>() && src["sensors"]["outdoor"]["gpio"].as<JsonString>().size() == 0) {
|
||||
if (dst.sensors.outdoor.gpio != GPIO_IS_NOT_CONFIGURED) {
|
||||
dst.sensors.outdoor.gpio = GPIO_IS_NOT_CONFIGURED;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
unsigned char value = src["sensors"]["outdoor"]["gpio"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 50) {
|
||||
dst.sensors.outdoor.pin = value;
|
||||
changed = true;
|
||||
if (value >= 0 && value <= 254) {
|
||||
dst.sensors.outdoor.gpio = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,34 +972,64 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
}
|
||||
|
||||
if (!src["sensors"]["indoor"]["type"].isNull()) {
|
||||
unsigned char value = src["sensors"]["indoor"]["type"].as<unsigned char>();
|
||||
byte value = src["sensors"]["indoor"]["type"].as<unsigned char>();
|
||||
|
||||
if (value >= 1 && value <= 3) {
|
||||
dst.sensors.indoor.type = value;
|
||||
|
||||
if (dst.sensors.indoor.type == 1) {
|
||||
dst.emergency.usePid = false;
|
||||
}
|
||||
switch (value) {
|
||||
case static_cast<byte>(SensorType::BOILER):
|
||||
dst.sensors.indoor.type = SensorType::BOILER;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
changed = true;
|
||||
case static_cast<byte>(SensorType::MANUAL):
|
||||
dst.sensors.indoor.type = SensorType::MANUAL;
|
||||
dst.emergency.useEquitherm = false;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
case static_cast<byte>(SensorType::DS18B20):
|
||||
dst.sensors.indoor.type = SensorType::DS18B20;
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
#if USE_BLE
|
||||
case static_cast<byte>(SensorType::BLUETOOTH):
|
||||
dst.sensors.indoor.type = SensorType::BLUETOOTH;
|
||||
changed = true;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src["sensors"]["indoor"]["pin"].isNull()) {
|
||||
unsigned char value = src["sensors"]["indoor"]["pin"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 50) {
|
||||
dst.sensors.indoor.pin = value;
|
||||
changed = true;
|
||||
if (!src["sensors"]["indoor"]["gpio"].isNull()) {
|
||||
if (src["sensors"]["indoor"]["gpio"].is<JsonString>() && src["sensors"]["indoor"]["gpio"].as<JsonString>().size() == 0) {
|
||||
if (dst.sensors.indoor.gpio != GPIO_IS_NOT_CONFIGURED) {
|
||||
dst.sensors.indoor.gpio = GPIO_IS_NOT_CONFIGURED;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
unsigned char value = src["sensors"]["indoor"]["gpio"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 254) {
|
||||
dst.sensors.indoor.gpio = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
if (!src["sensors"]["indoor"]["bleAddresss"].isNull()) {
|
||||
String value = src["sensors"]["indoor"]["bleAddresss"].as<String>();
|
||||
int tmp[6];
|
||||
if(sscanf(value.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) == 6) {
|
||||
for(uint8_t i = 0; i < 6; i++) {
|
||||
dst.sensors.indoor.bleAddresss[i] = (uint8_t) tmp[i];
|
||||
}
|
||||
|
||||
if (value.length() < sizeof(dst.sensors.indoor.bleAddresss)) {
|
||||
strcpy(dst.sensors.indoor.bleAddresss, value.c_str());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@@ -846,12 +1052,20 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!src["externalPump"]["pin"].isNull()) {
|
||||
unsigned char value = src["externalPump"]["pin"].as<unsigned char>();
|
||||
if (!src["externalPump"]["gpio"].isNull()) {
|
||||
if (src["externalPump"]["gpio"].is<JsonString>() && src["externalPump"]["gpio"].as<JsonString>().size() == 0) {
|
||||
if (dst.externalPump.gpio != GPIO_IS_NOT_CONFIGURED) {
|
||||
dst.externalPump.gpio = GPIO_IS_NOT_CONFIGURED;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
unsigned char value = src["externalPump"]["gpio"].as<unsigned char>();
|
||||
|
||||
if (value >= 0 && value <= 50) {
|
||||
dst.externalPump.pin = value;
|
||||
changed = true;
|
||||
if (value >= 0 && value <= 254) {
|
||||
dst.externalPump.gpio = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,7 +1127,9 @@ void varsToJson(const Variables& src, JsonVariant dst) {
|
||||
dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2);
|
||||
dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2);
|
||||
dst["temperatures"]["heating"] = roundd(src.temperatures.heating, 2);
|
||||
dst["temperatures"]["heatingReturn"] = roundd(src.temperatures.heatingReturn, 2);
|
||||
dst["temperatures"]["dhw"] = roundd(src.temperatures.dhw, 2);
|
||||
dst["temperatures"]["exhaust"] = roundd(src.temperatures.exhaust, 2);
|
||||
|
||||
dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled;
|
||||
dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp;
|
||||
@@ -946,7 +1162,7 @@ bool jsonToVars(const JsonVariantConst src, Variables& dst) {
|
||||
if (!src["temperatures"]["indoor"].isNull()) {
|
||||
double value = src["temperatures"]["indoor"].as<double>();
|
||||
|
||||
if (settings.sensors.indoor.type == 1 && value > -100 && value < 100) {
|
||||
if (settings.sensors.indoor.type == SensorType::MANUAL && isValidTemp(value, settings.system.unitSystem, -99.9f, 99.9f)) {
|
||||
dst.temperatures.indoor = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
@@ -955,7 +1171,7 @@ bool jsonToVars(const JsonVariantConst src, Variables& dst) {
|
||||
if (!src["temperatures"]["outdoor"].isNull()) {
|
||||
double value = src["temperatures"]["outdoor"].as<double>();
|
||||
|
||||
if (settings.sensors.outdoor.type == 1 && value > -100 && value < 100) {
|
||||
if (settings.sensors.outdoor.type == SensorType::MANUAL && isValidTemp(value, settings.system.unitSystem, -99.9f, 99.9f)) {
|
||||
dst.temperatures.outdoor = roundd(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -484,6 +484,7 @@ async function loadSettings() {
|
||||
setCheckboxValue('.system-debug', result.system.debug);
|
||||
setCheckboxValue('.system-use-serial', result.system.useSerial);
|
||||
setCheckboxValue('.system-use-telnet', result.system.useTelnet);
|
||||
setRadioValue('.system-unit-system', result.system.unitSystem);
|
||||
setBusy('#system-settings-busy', '#system-settings', false);
|
||||
|
||||
setCheckboxValue('.portal-use-auth', result.portal.useAuth);
|
||||
@@ -491,8 +492,9 @@ async function loadSettings() {
|
||||
setInputValue('.portal-password', result.portal.password);
|
||||
setBusy('#portal-settings-busy', '#portal-settings', false);
|
||||
|
||||
setInputValue('.opentherm-in-pin', result.opentherm.inPin);
|
||||
setInputValue('.opentherm-out-pin', result.opentherm.outPin);
|
||||
setRadioValue('.opentherm-unit-system', result.opentherm.unitSystem);
|
||||
setInputValue('.opentherm-in-gpio', result.opentherm.inGpio < 255 ? result.opentherm.inGpio : '');
|
||||
setInputValue('.opentherm-out-gpio', result.opentherm.outGpio < 255 ? result.opentherm.outGpio : '');
|
||||
setInputValue('.opentherm-member-id-code', result.opentherm.memberIdCode);
|
||||
setCheckboxValue('.opentherm-dhw-present', result.opentherm.dhwPresent);
|
||||
setCheckboxValue('.opentherm-sw-mode', result.opentherm.summerWinterMode);
|
||||
@@ -501,6 +503,7 @@ async function loadSettings() {
|
||||
setCheckboxValue('.opentherm-dhw-to-ch2', result.opentherm.dhwToCh2);
|
||||
setCheckboxValue('.opentherm-dhw-blocking', result.opentherm.dhwBlocking);
|
||||
setCheckboxValue('.opentherm-sync-modulation-with-heating', result.opentherm.modulationSyncWithHeating);
|
||||
setCheckboxValue('.opentherm-get-min-max-temp', result.opentherm.getMinMaxTemp);
|
||||
setBusy('#opentherm-settings-busy', '#opentherm-settings', false);
|
||||
|
||||
setInputValue('.mqtt-server', result.mqtt.server);
|
||||
@@ -512,18 +515,18 @@ async function loadSettings() {
|
||||
setBusy('#mqtt-settings-busy', '#mqtt-settings', false);
|
||||
|
||||
setRadioValue('.outdoor-sensor-type', result.sensors.outdoor.type);
|
||||
setInputValue('.outdoor-sensor-pin', result.sensors.outdoor.pin);
|
||||
setInputValue('.outdoor-sensor-gpio', result.sensors.outdoor.gpio < 255 ? result.sensors.outdoor.gpio : '');
|
||||
setInputValue('.outdoor-sensor-offset', result.sensors.outdoor.offset);
|
||||
setBusy('#outdoor-sensor-settings-busy', '#outdoor-sensor-settings', false);
|
||||
|
||||
setRadioValue('.indoor-sensor-type', result.sensors.indoor.type);
|
||||
setInputValue('.indoor-sensor-pin', result.sensors.indoor.pin);
|
||||
setInputValue('.indoor-sensor-gpio', result.sensors.indoor.gpio < 255 ? result.sensors.indoor.gpio : '');
|
||||
setInputValue('.indoor-sensor-offset', result.sensors.indoor.offset);
|
||||
setInputValue('.indoor-sensor-ble-addresss', result.sensors.indoor.bleAddresss);
|
||||
setBusy('#indoor-sensor-settings-busy', '#indoor-sensor-settings', false);
|
||||
|
||||
setCheckboxValue('.extpump-use', result.externalPump.use);
|
||||
setInputValue('.extpump-pin', result.externalPump.pin);
|
||||
setInputValue('.extpump-gpio', result.externalPump.gpio < 255 ? result.externalPump.gpio : '');
|
||||
setInputValue('.extpump-pc-time', result.externalPump.postCirculationTime);
|
||||
setInputValue('.extpump-as-interval', result.externalPump.antiStuckInterval);
|
||||
setInputValue('.extpump-as-time', result.externalPump.antiStuckTime);
|
||||
@@ -534,6 +537,27 @@ async function loadVars() {
|
||||
let response = await fetch('/api/vars');
|
||||
let result = await response.json();
|
||||
|
||||
let tempUnitStr, pressureUnitStr, volumeUnitStr;
|
||||
switch (result.system.unitSystem) {
|
||||
case 0:
|
||||
tempUnitStr = 'C';
|
||||
pressureUnitStr = 'bar';
|
||||
volumeUnitStr = 'L';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
tempUnitStr = 'F';
|
||||
pressureUnitStr = 'psi';
|
||||
volumeUnitStr = 'gal';
|
||||
break;
|
||||
|
||||
default:
|
||||
tempUnitStr = '?';
|
||||
pressureUnitStr = '?';
|
||||
volumeUnitStr = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
setState('.ot-connected', result.states.otStatus);
|
||||
setState('.ot-emergency', result.states.emergency);
|
||||
setState('.ot-heating', result.states.heating);
|
||||
@@ -552,10 +576,15 @@ async function loadVars() {
|
||||
setValue('.outdoor-temp', result.temperatures.outdoor);
|
||||
setValue('.heating-temp', result.temperatures.heating);
|
||||
setValue('.heating-setpoint-temp', result.parameters.heatingSetpoint);
|
||||
setValue('.heating-return-temp', result.temperatures.heatingReturn);
|
||||
setValue('.dhw-temp', result.temperatures.dhw);
|
||||
setValue('.exhaust-temp', result.temperatures.exhaust);
|
||||
|
||||
setBusy('.ot-busy', '.ot-table', false);
|
||||
|
||||
setValue('.temp-unit', tempUnitStr);
|
||||
setValue('.pressure-unit', pressureUnitStr);
|
||||
setValue('.volume-unit', volumeUnitStr);
|
||||
setValue('.version', result.system.version);
|
||||
setValue('.build-date', result.system.buildDate);
|
||||
setValue('.uptime', result.system.uptime);
|
||||
@@ -601,12 +630,14 @@ function setState(selector, value) {
|
||||
}
|
||||
|
||||
function setValue(selector, value) {
|
||||
let item = document.querySelector(selector);
|
||||
if (!item) {
|
||||
let items = document.querySelectorAll(selector);
|
||||
if (!items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.innerHTML = value;
|
||||
for (let item of items) {
|
||||
item.innerHTML = value;
|
||||
}
|
||||
}
|
||||
|
||||
function setCheckboxValue(selector, value) {
|
||||
|
||||
Reference in New Issue
Block a user