6 Commits

9 changed files with 104 additions and 33 deletions

View File

@@ -2,7 +2,7 @@
![logo](/assets/logo.svg) ![logo](/assets/logo.svg)
<br> <br>
[![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg)](https://github.com/Laxilef/OTGateway/releases) [![GitHub version](https://img.shields.io/github/release/Laxilef/OTGateway.svg?include_prereleases)](https://github.com/Laxilef/OTGateway/releases)
[![GitHub download](https://img.shields.io/github/downloads/Laxilef/OTGateway/total.svg)](https://github.com/Laxilef/OTGateway/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/Laxilef/OTGateway/total.svg)](https://github.com/Laxilef/OTGateway/releases/latest)
[![License](https://img.shields.io/github/license/Laxilef/OTGateway.svg)](LICENSE.txt) [![License](https://img.shields.io/github/license/Laxilef/OTGateway.svg)](LICENSE.txt)
[![Telegram](https://img.shields.io/badge/Telegram-Channel-33A8E3)](https://t.me/otgateway) [![Telegram](https://img.shields.io/badge/Telegram-Channel-33A8E3)](https://t.me/otgateway)

View File

@@ -163,24 +163,13 @@ public:
} }
// converters // converters
float fromF88(unsigned long response) { template <class T>
const byte valueLB = response & 0xFF; static unsigned int toFloat(const T val) {
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) {
return (unsigned int)(val * 256); return (unsigned int)(val * 256);
} }
int16_t fromS16(unsigned long response) { static short getInt(const unsigned long response) {
const byte valueLB = response & 0xFF; return response & 0xffff;
const byte valueHB = (response >> 8) & 0xFF;
int16_t value = valueHB;
return ((value << 8) + valueLB);
} }
protected: protected:

View File

@@ -51,7 +51,7 @@ monitor_speed = 115200
monitor_filters = direct monitor_filters = direct
board_build.flash_mode = dio board_build.flash_mode = dio
board_build.filesystem = littlefs board_build.filesystem = littlefs
version = 1.4.0-rc.18 version = 1.4.0-rc.19
; Defaults ; Defaults
[esp8266_defaults] [esp8266_defaults]

View File

@@ -1136,6 +1136,29 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("heating_temp")).c_str(), doc);
} }
bool publishSensorHeatingReturnTemp(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("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");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
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(bool enabledByDefault = true) { bool publishSensorDhwTemp(bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;
doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status")); doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = this->getDeviceTopic(F("status"));
@@ -1159,6 +1182,29 @@ public:
return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("dhw_temp")).c_str(), doc); return this->publish(this->getTopic(FPSTR(HA_ENTITY_SENSOR), F("dhw_temp")).c_str(), doc);
} }
bool publishSensorExhaustTemp(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");
doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = F("°C");
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(byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) { bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) {
JsonDocument doc; JsonDocument doc;

View File

@@ -370,6 +370,8 @@ protected:
// temperatures // temperatures
this->haHelper->publishNumberIndoorTemp(); this->haHelper->publishNumberIndoorTemp();
this->haHelper->publishSensorHeatingTemp(); this->haHelper->publishSensorHeatingTemp();
this->haHelper->publishSensorHeatingReturnTemp(false);
this->haHelper->publishSensorExhaustTemp(false);
// buttons // buttons
this->haHelper->publishButtonRestart(false); this->haHelper->publishButtonRestart(false);

View File

@@ -253,9 +253,6 @@ protected:
fsSettings.update(); fsSettings.update();
} }
// Force set max heating temp
setMaxHeatingTemp(settings.heating.maxTemp);
// Get outdoor temp (if necessary) // Get outdoor temp (if necessary)
if (settings.sensors.outdoor.type == SensorType::BOILER) { if (settings.sensors.outdoor.type == SensorType::BOILER) {
updateOutsideTemp(); updateOutsideTemp();
@@ -273,7 +270,7 @@ protected:
// Get current modulation level (if necessary) // Get current modulation level (if necessary)
if ((settings.opentherm.dhwPresent && settings.dhw.enable) || settings.heating.enable || heatingEnabled) { if (vars.states.flame) {
updateModulationLevel(); updateModulationLevel();
} else { } else {
@@ -293,6 +290,12 @@ protected:
// Get current heating temp // Get current heating temp
updateHeatingTemp(); updateHeatingTemp();
// Get heating return temp
updateHeatingReturnTemp();
// Get exhaust temp
updateExhaustTemp();
// Fault reset action // Fault reset action
if (vars.actions.resetFault) { if (vars.actions.resetFault) {
@@ -355,7 +358,7 @@ protected:
Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint); Log.sinfoln(FPSTR(L_OT_HEATING), F("Set temp = %u"), vars.parameters.heatingSetpoint);
// Set heating temp // Set heating temp
if (this->instance->setHeatingCh1Temp(vars.parameters.heatingSetpoint)) { if (this->instance->setHeatingCh1Temp(vars.parameters.heatingSetpoint) || this->setMaxHeatingTemp(vars.parameters.heatingSetpoint)) {
currentHeatingTemp = vars.parameters.heatingSetpoint; currentHeatingTemp = vars.parameters.heatingSetpoint;
this->heatingSetTempTime = millis(); this->heatingSetTempTime = millis();
@@ -507,14 +510,14 @@ protected:
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::WRITE_DATA, OpenThermRequestType::WRITE_DATA,
OpenThermMessageID::MaxRelModLevelSetting, OpenThermMessageID::MaxRelModLevelSetting,
this->instance->toF88(value) CustomOpenTherm::toFloat(value)
)); ));
if (!CustomOpenTherm::isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
vars.parameters.maxModulation = this->instance->fromF88(response); vars.parameters.maxModulation = CustomOpenTherm::getFloat(response);
return true; return true;
} }
@@ -537,14 +540,14 @@ protected:
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::WRITE_DATA, OpenThermRequestType::WRITE_DATA,
OpenThermMessageID::OpenThermVersionMaster, OpenThermMessageID::OpenThermVersionMaster,
this->instance->toF88(version) CustomOpenTherm::toFloat(version)
)); ));
if (!CustomOpenTherm::isValidResponse(response)) { if (!CustomOpenTherm::isValidResponse(response)) {
return false; return false;
} }
vars.parameters.masterOtVersion = this->instance->fromF88(response); vars.parameters.masterOtVersion = CustomOpenTherm::getFloat(response);
return true; return true;
} }
@@ -655,6 +658,23 @@ protected:
return true; return true;
} }
bool updateExhaustTemp() {
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermRequestType::READ_DATA,
OpenThermMessageID::Texhaust,
0
));
if (!CustomOpenTherm::isValidResponse(response)) {
return false;
}
short value = CustomOpenTherm::getInt(response);
vars.temperatures.exhaust = (value >= -40 && value <= 500) ? (float)value : 0.0f;
return true;
}
bool updateHeatingTemp() { bool updateHeatingTemp() {
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
OpenThermMessageType::READ_DATA, OpenThermMessageType::READ_DATA,
@@ -675,6 +695,21 @@ protected:
return true; 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 = CustomOpenTherm::getFloat(response);
return true;
}
bool updateDhwTemp() { bool updateDhwTemp() {
unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest( unsigned long response = this->instance->sendRequest(CustomOpenTherm::buildRequest(
@@ -742,12 +777,7 @@ protected:
return false; return false;
} }
float modulation = this->instance->fromF88(response); vars.sensors.modulation = CustomOpenTherm::getFloat(response);
if (!vars.states.flame) {
vars.sensors.modulation = 0;
} else {
vars.sensors.modulation = modulation;
}
return true; return true;
} }

View File

@@ -153,7 +153,9 @@ struct Variables {
float indoor = 0.0f; float indoor = 0.0f;
float outdoor = 0.0f; float outdoor = 0.0f;
float heating = 0.0f; float heating = 0.0f;
float heatingReturn = 0.0f;
float dhw = 0.0f; float dhw = 0.0f;
float exhaust = 0.0f;
} temperatures; } temperatures;
struct { struct {

View File

@@ -1,5 +1,5 @@
#define PROJECT_NAME "OpenTherm Gateway" #define PROJECT_NAME "OpenTherm Gateway"
#define PROJECT_VERSION "1.4.0-rc.18" #define PROJECT_VERSION "1.4.0-rc.19"
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway" #define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
#define EMERGENCY_TIME_TRESHOLD 120000 #define EMERGENCY_TIME_TRESHOLD 120000

View File

@@ -998,7 +998,9 @@ void varsToJson(const Variables& src, JsonVariant dst) {
dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2); dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2);
dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2); dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2);
dst["temperatures"]["heating"] = roundd(src.temperatures.heating, 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"]["dhw"] = roundd(src.temperatures.dhw, 2);
dst["temperatures"]["exhaust"] = roundd(src.temperatures.exhaust, 2);
dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled; dst["parameters"]["heatingEnabled"] = src.parameters.heatingEnabled;
dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp; dst["parameters"]["heatingMinTemp"] = src.parameters.heatingMinTemp;