Many changes.

1. Migrate from microDS18B20 to DallasTemperature
2. Refactoring of sensors: added an external temperature sensor inside the house, added an "offset" parameter for sensors
3. Fixed PID
4. New parameters added:
- settings.heating.minTemp
- settings.heating.maxTemp
- settings.dhw.minTemp
- settings.dhw.maxTemp
- settings.pid.minTemp
- settings.pid.maxTemp
- settings.sensors.outdoor.type
- settings.sensors.outdoor.pin
- settings.sensors.outdoor.offset
- settings.sensors.indoor.type
- settings.sensors.indoor.pin
- settings.sensors.indoor.offset
5. Fixed and updated HomeAssistantHelper
7. Added check for validity of settings. After some updates, the settings may be reset to default, but this will prevent the settings from being distorted.
This commit is contained in:
Yurii
2023-09-21 05:18:05 +03:00
parent b0e01afecb
commit 229628fdc5
12 changed files with 921 additions and 174 deletions

View File

@@ -61,7 +61,7 @@ To save money, 2 levels are ordered as one board. After manufacturing, the board
- [TelnetStream](https://github.com/jandrassy/TelnetStream)
- [EEManager](https://github.com/GyverLibs/EEManager)
- [GyverPID](https://github.com/GyverLibs/GyverPID)
- [microDS18B20](https://github.com/GyverLibs/microDS18B20)
- [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library)
- [WiFiManager](https://github.com/tzapu/WiFiManager)

View File

@@ -21,7 +21,7 @@ lib_deps =
jandrassy/TelnetStream@^1.2.4
gyverlibs/EEManager@^2.0
gyverlibs/GyverPID@^3.3
gyverlibs/microDS18B20@^3.10
milesburton/DallasTemperature@^3.11.0
https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
upload_speed = 921600

View File

@@ -27,10 +27,10 @@ public:
_deviceConfigUrl = value;
}
bool publishSelectOutdoorTempSource(bool enabledByDefault = true) {
bool publishSelectOutdoorSensorType(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"outdoorTempSource\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}");
doc[F("command_template")] = F("{\"sensors\": {\"outdoor\": {\"type\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}}}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
@@ -40,17 +40,17 @@ public:
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_outdoorTempSource");
doc[F("object_id")] = _prefix + F("_outdoorTempSource");
doc[F("unique_id")] = _prefix + F("_outdoor_sensor_type");
doc[F("object_id")] = _prefix + F("_outdoor_sensor_type");
doc[F("entity_category")] = F("config");
doc[F("name")] = F("Outdoor temperature source");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{% if value_json.outdoorTempSource == 0 %}Boiler{% elif value_json.outdoorTempSource == 1 %}Manual{% elif value_json.outdoorTempSource == 2 %}External{% endif %}");
doc[F("value_template")] = F("{% if value_json.sensors.outdoor.type == 0 %}Boiler{% elif value_json.sensors.outdoor.type == 1 %}Manual{% elif value_json.sensors.outdoor.type == 2 %}External{% endif %}");
doc[F("options")][0] = F("Boiler");
doc[F("options")][1] = F("Manual");
doc[F("options")][2] = F("External");
client.beginPublish((F("homeassistant/select/") + _prefix + F("/outdoorTempSource/config")).c_str(), measureJson(doc), true);
client.beginPublish((F("homeassistant/select/") + _prefix + F("/outdoor_sensor_type/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
@@ -58,6 +58,105 @@ public:
return client.endPublish();
}
bool publishSelectIndoorSensorType(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"sensors\": {\"indoor\": {\"type\": {% if value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}}}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_indoor_sensor_type");
doc[F("object_id")] = _prefix + F("_indoor_sensor_type");
doc[F("entity_category")] = F("config");
doc[F("name")] = F("Indoor temperature source");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{% if value_json.sensors.indoor.type == 1 %}Manual{% elif value_json.sensors.indoor.type == 2 %}External{% endif %}");
doc[F("options")][0] = F("Manual");
doc[F("options")][1] = F("External");
client.beginPublish((F("homeassistant/select/") + _prefix + F("/indoor_sensor_type/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberOutdoorSensorOffset(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/settings");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_outdoor_sensor_offset");
doc[F("object_id")] = _prefix + F("_outdoor_sensor_offset");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Outdoor sensor offset");
doc[F("icon")] = F("mdi:altimeter");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.sensors.outdoor.offset|float(0)|round(2) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"sensors\": {\"outdoor\" : {\"offset\" : {{ value }}}}}");
doc[F("min")] = -10;
doc[F("max")] = 10;
doc[F("step")] = 0.1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/outdoor_sensor_offset/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberIndoorSensorOffset(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/settings");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.sensors.indoor.type != 1, 'online', 'offline') }}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_indoor_sensor_offset");
doc[F("object_id")] = _prefix + F("_indoor_sensor_offset");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Indoor sensor offset");
doc[F("icon")] = F("mdi:altimeter");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.sensors.indoor.offset|float(0)|round(2) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"sensors\": {\"indoor\" : {\"offset\" : {{ value }}}}}");
doc[F("min")] = -10;
doc[F("max")] = 10;
doc[F("step")] = 0.1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/indoor_sensor_offset/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSwitchDebug(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
@@ -148,6 +247,7 @@ public:
doc[F("min")] = 5;
doc[F("max")] = 50;
doc[F("step")] = 0.5;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/emergency_target/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -160,7 +260,7 @@ public:
bool publishSwitchEmergencyUseEquitherm(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/settings");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.outdoorTempSource != 1, 'online', 'offline') }}");
doc[F("availability")][F("value_template")] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
@@ -282,8 +382,9 @@ public:
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"heating\": {\"target\" : {{ value }}}}");
doc[F("min")] = minTemp;
doc[F("max")] = maxTemp;
doc[F("max")] = maxTemp <= minTemp ? maxTemp : maxTemp;
doc[F("step")] = 0.5;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_target/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -318,6 +419,7 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 5;
doc[F("step")] = 0.1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_hysteresis/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -358,6 +460,126 @@ public:
return client.endPublish();
}
bool publishSensorCurrentHeatingMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_heating_min_temp");
doc[F("object_id")] = _prefix + F("_current_heating_min_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current heating min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.heatingMinTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_heating_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorCurrentHeatingMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_heating_max_temp");
doc[F("object_id")] = _prefix + F("_current_heating_max_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current heating max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.heatingMaxTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_heating_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberHeatingMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_heating_min_temp");
doc[F("object_id")] = _prefix + F("_heating_min_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Heating min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.heating.minTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"heating\": {\"minTemp\" : {{ value }}}}");
doc[F("min")] = 0;
doc[F("max")] = 99;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberHeatingMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_heating_max_temp");
doc[F("object_id")] = _prefix + F("_heating_max_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Heating max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.heating.maxTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"heating\": {\"maxTemp\" : {{ value }}}}");
doc[F("min")] = 1;
doc[F("max")] = 100;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSwitchDHW(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
@@ -416,8 +638,9 @@ public:
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}");
doc[F("min")] = minTemp;
doc[F("max")] = maxTemp;
doc[F("max")] = maxTemp <= minTemp ? maxTemp : maxTemp;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_target/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -427,6 +650,126 @@ public:
return client.endPublish();
}
bool publishSensorCurrentDHWMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_dhw_min_temp");
doc[F("object_id")] = _prefix + F("_current_dhw_min_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current DHW min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.dhwMinTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSensorCurrentDHWMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_current_dhw_max_temp");
doc[F("object_id")] = _prefix + F("_current_dhw_max_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Current DHW max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.parameters.dhwMaxTemp|int(0) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/current_dhw_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberDHWMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_dhw_min_temp");
doc[F("object_id")] = _prefix + F("_dhw_min_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("DHW min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.dhw.minTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"dhw\": {\"minTemp\" : {{ value }}}}");
doc[F("min")] = 0;
doc[F("max")] = 99;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberDHWMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_dhw_max_temp");
doc[F("object_id")] = _prefix + F("_dhw_max_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("DHW max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.dhw.maxTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"dhw\": {\"maxTemp\" : {{ value }}}}");
doc[F("min")] = 1;
doc[F("max")] = 100;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSwitchPID(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
@@ -482,6 +825,7 @@ public:
doc[F("min")] = 0.001;
doc[F("max")] = 10;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_p_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -513,6 +857,7 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_i_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -544,6 +889,7 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_d_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -553,6 +899,70 @@ public:
return client.endPublish();
}
bool publishNumberPIDMinTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_pid_min_temp");
doc[F("object_id")] = _prefix + F("_pid_min_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("PID min temp");
doc[F("icon")] = F("mdi:thermometer-chevron-down");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.pid.minTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"pid\": {\"minTemp\" : {{ value }}}}");
doc[F("min")] = 0;
doc[F("max")] = 99;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_min_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberPIDMaxTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_pid_max_temp");
doc[F("object_id")] = _prefix + F("_pid_max_temp");
doc[F("entity_category")] = F("config");
doc[F("device_class")] = F("temperature");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("PID max temp");
doc[F("icon")] = F("mdi:thermometer-chevron-up");
doc[F("state_topic")] = _prefix + F("/settings");
doc[F("value_template")] = F("{{ value_json.pid.maxTemp|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/settings/set");
doc[F("command_template")] = F("{\"pid\": {\"maxTemp\" : {{ value }}}}");
doc[F("min")] = 1;
doc[F("max")] = 100;
doc[F("step")] = 1;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_max_temp/config")).c_str(), measureJson(doc), true);
serializeJson(doc, client);
return client.endPublish();
}
bool publishSwitchEquitherm(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
@@ -608,6 +1018,7 @@ public:
doc[F("min")] = 0.001;
doc[F("max")] = 5;
doc[F("step")] = 0.001;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_n_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -639,6 +1050,7 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_k_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -672,6 +1084,7 @@ public:
doc[F("min")] = 0;
doc[F("max")] = 10;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_t_factor/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -979,7 +1392,7 @@ public:
return client.endPublish();
}
bool publishSensorRssi(bool enabledByDefault = false) {
bool publishSensorRssi(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc["device"]["identifiers"][0] = _prefix;
doc["device"]["sw_version"] = _deviceVersion;
@@ -1076,7 +1489,6 @@ public:
bool publishNumberIndoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
//doc[F("availability")][F("topic")] = _prefix + F("/status");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
@@ -1097,8 +1509,10 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/state/set");
doc[F("command_template")] = F("{\"temperatures\": {\"indoor\":{{ value }}}}");
doc[F("min")] = -70;
doc[F("max")] = 50;
doc[F("min")] = -99;
doc[F("max")] = 99;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/indoor_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -1108,6 +1522,38 @@ public:
return client.endPublish();
}
bool publishSensorIndoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][0][F("topic")] = _prefix + F("/status");
doc[F("availability_mode")] = F("any");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
doc[F("device")][F("manufacturer")] = _deviceManufacturer;
doc[F("device")][F("model")] = _deviceModel;
doc[F("device")][F("name")] = _deviceName;
if (_deviceConfigUrl) {
doc[F("device")][F("configuration_url")] = _deviceConfigUrl;
}
doc[F("enabled_by_default")] = enabledByDefault;
doc[F("unique_id")] = _prefix + F("_indoor_temp");
doc[F("object_id")] = _prefix + F("_indoor_temp");
doc[F("entity_category")] = F("diagnostic");
doc[F("device_class")] = F("temperature");
doc[F("state_class")] = F("measurement");
doc[F("unit_of_measurement")] = F("°C");
doc[F("name")] = F("Indoor temperature");
doc[F("icon")] = F("mdi:home-thermometer");
doc[F("state_topic")] = _prefix + F("/state");
doc[F("value_template")] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}");
client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/indoor_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);
//bufferedClient.flush();
serializeJson(doc, client);
return client.endPublish();
}
bool publishNumberOutdoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("device")][F("identifiers")][0] = _prefix;
@@ -1130,8 +1576,10 @@ public:
doc[F("value_template")] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}");
doc[F("command_topic")] = _prefix + F("/state/set");
doc[F("command_template")] = F("{\"temperatures\": {\"outdoor\":{{ value }}}}");
doc[F("min")] = -70;
doc[F("max")] = 50;
doc[F("min")] = -99;
doc[F("max")] = 99;
doc[F("step")] = 0.01;
doc[F("mode")] = "box";
client.beginPublish((F("homeassistant/number/") + _prefix + F("/outdoor_temp/config")).c_str(), measureJson(doc), true);
//BufferingPrint bufferedClient(client, 32);
@@ -1144,8 +1592,6 @@ public:
bool publishSensorOutdoorTemp(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc;
doc[F("availability")][0][F("topic")] = _prefix + F("/status");
doc[F("availability")][1][F("topic")] = _prefix + F("/settings");
doc[F("availability")][1][F("value_template")] = F("{{ iif(value_json.outdoorTempSource == 2, 'online', 'offline') }}");
doc[F("availability_mode")] = F("any");
doc[F("device")][F("identifiers")][0] = _prefix;
doc[F("device")][F("sw_version")] = _deviceVersion;
@@ -1353,6 +1799,14 @@ public:
return client.publish((F("homeassistant/sensor/") + _prefix + F("/outdoor_temp/config")).c_str(), NULL, true);
}
bool deleteNumberIndoorTemp() {
return client.publish((F("homeassistant/number/") + _prefix + F("/indoor_temp/config")).c_str(), NULL, true);
}
bool deleteSensorIndoorTemp() {
return client.publish((F("homeassistant/sensor/") + _prefix + F("/indoor_temp/config")).c_str(), NULL, true);
}
private:
String _prefix = "opentherm";
String _deviceVersion = "1.0";

View File

@@ -13,7 +13,6 @@ protected:
void setup() {
pinMode(LED_STATUS_PIN, OUTPUT);
//pinMode(LED_OT_RX_PIN, OUTPUT);
}
void loop() {
@@ -49,12 +48,6 @@ protected:
}
}
if (!tSensors->isEnabled() && settings.outdoorTempSource == 2) {
tSensors->enable();
} else if (tSensors->isEnabled() && settings.outdoorTempSource != 2) {
tSensors->disable();
}
if (!tOt->isEnabled() && settings.opentherm.inPin > 0 && settings.opentherm.outPin > 0 && settings.opentherm.inPin != settings.opentherm.outPin) {
tOt->enable();
}

View File

@@ -19,7 +19,6 @@ protected:
void setup() {
DEBUG("[MQTT] Started");
client.setServer(settings.mqtt.server, settings.mqtt.port);
client.setCallback(__callback);
haHelper.setPrefix(settings.mqtt.prefix);
haHelper.setDeviceVersion(OT_GATEWAY_VERSION);
@@ -32,6 +31,7 @@ protected:
if (!client.connected() && millis() - lastReconnectAttempt >= MQTT_RECONNECT_INTERVAL) {
INFO_F("Mqtt not connected, state: %i, connecting to server %s...\n", client.state(), settings.mqtt.server);
client.setServer(settings.mqtt.server, settings.mqtt.port);
if (client.connect(settings.hostname, settings.mqtt.user, settings.mqtt.password)) {
INFO("Connected to MQTT server");
@@ -93,15 +93,6 @@ protected:
flag = true;
}
if (!doc["outdoorTempSource"].isNull() && doc["outdoorTempSource"].is<int>() && doc["outdoorTempSource"] >= 0 && doc["outdoorTempSource"] <= 2) {
settings.outdoorTempSource = doc["outdoorTempSource"];
flag = true;
}
if (!doc["mqtt"]["interval"].isNull() && doc["mqtt"]["interval"].is<int>() && doc["mqtt"]["interval"] >= 1000 && doc["mqtt"]["interval"] <= 120000) {
settings.mqtt.interval = doc["mqtt"]["interval"].as<unsigned int>();
flag = true;
}
// emergency
if (!doc["emergency"]["enable"].isNull() && doc["emergency"]["enable"].is<bool>()) {
@@ -109,9 +100,11 @@ protected:
flag = true;
}
if (!doc["emergency"]["target"].isNull() && (doc["emergency"]["target"].is<float>() || doc["emergency"]["target"].is<int>())) {
settings.emergency.target = round(doc["emergency"]["target"].as<float>() * 10) / 10;
flag = true;
if (!doc["emergency"]["target"].isNull() && doc["emergency"]["target"].is<float>()) {
if ( doc["emergency"]["target"].as<float>() > 0 && doc["emergency"]["target"].as<float>() < 100 ) {
settings.emergency.target = round(doc["emergency"]["target"].as<float>() * 10) / 10;
flag = true;
}
}
if (!doc["emergency"]["useEquitherm"].isNull() && doc["emergency"]["useEquitherm"].is<bool>()) {
@@ -119,6 +112,7 @@ protected:
flag = true;
}
// heating
if (!doc["heating"]["enable"].isNull() && doc["heating"]["enable"].is<bool>()) {
settings.heating.enable = doc["heating"]["enable"].as<bool>();
@@ -130,46 +124,106 @@ protected:
flag = true;
}
if (!doc["heating"]["target"].isNull() && (doc["heating"]["target"].is<float>() || doc["heating"]["target"].is<int>())) {
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
flag = true;
if (!doc["heating"]["target"].isNull() && doc["heating"]["target"].is<float>()) {
if ( doc["heating"]["target"].as<float>() > 0 && doc["heating"]["target"].as<float>() < 100 ) {
settings.heating.target = round(doc["heating"]["target"].as<float>() * 10) / 10;
flag = true;
}
}
if (!doc["heating"]["hysteresis"].isNull() && (doc["heating"]["hysteresis"].is<float>() || doc["heating"]["hysteresis"].is<int>())) {
settings.heating.hysteresis = round(doc["heating"]["hysteresis"].as<float>() * 10) / 10;
flag = true;
if (!doc["heating"]["hysteresis"].isNull() && doc["heating"]["hysteresis"].is<float>()) {
if ( doc["heating"]["hysteresis"].as<float>() > 0 && doc["heating"]["hysteresis"].as<float>() <= 5 ) {
settings.heating.hysteresis = round(doc["heating"]["hysteresis"].as<float>() * 10) / 10;
flag = true;
}
}
if (!doc["heating"]["maxTemp"].isNull() && doc["heating"]["maxTemp"].is<unsigned char>()) {
if ( doc["heating"]["maxTemp"].as<unsigned char>() > 0 && doc["heating"]["maxTemp"].as<unsigned char>() <= 100 && doc["heating"]["maxTemp"].as<unsigned char>() > settings.heating.minTemp ) {
settings.heating.maxTemp = doc["heating"]["maxTemp"].as<unsigned char>();
vars.parameters.heatingMaxTemp = settings.heating.maxTemp;
flag = true;
}
}
if (!doc["heating"]["minTemp"].isNull() && doc["heating"]["minTemp"].is<unsigned char>()) {
if ( doc["heating"]["minTemp"].as<unsigned char>() >= 0 && doc["heating"]["minTemp"].as<unsigned char>() < 100 && doc["heating"]["minTemp"].as<unsigned char>() < settings.heating.maxTemp ) {
settings.heating.minTemp = doc["heating"]["minTemp"].as<unsigned char>();
vars.parameters.heatingMinTemp = settings.heating.minTemp;
flag = true;
}
}
// dhw
if (!doc["dhw"]["enable"].isNull() && doc["dhw"]["enable"].is<bool>()) {
settings.dhw.enable = doc["dhw"]["enable"].as<bool>();
flag = true;
}
if (!doc["dhw"]["target"].isNull() && doc["dhw"]["target"].is<int>()) {
settings.dhw.target = doc["dhw"]["target"].as<int>();
flag = true;
if (!doc["dhw"]["target"].isNull() && doc["dhw"]["target"].is<unsigned char>()) {
if ( doc["dhw"]["target"].as<unsigned char>() >= 0 && doc["dhw"]["target"].as<unsigned char>() < 100 ) {
settings.dhw.target = doc["dhw"]["target"].as<unsigned char>();
flag = true;
}
}
if (!doc["dhw"]["maxTemp"].isNull() && doc["dhw"]["maxTemp"].is<unsigned char>()) {
if ( doc["dhw"]["maxTemp"].as<unsigned char>() > 0 && doc["dhw"]["maxTemp"].as<unsigned char>() <= 100 && doc["dhw"]["maxTemp"].as<unsigned char>() > settings.dhw.minTemp ) {
settings.dhw.maxTemp = doc["dhw"]["maxTemp"].as<unsigned char>();
vars.parameters.dhwMaxTemp = settings.dhw.maxTemp;
flag = true;
}
}
if (!doc["dhw"]["minTemp"].isNull() && doc["dhw"]["minTemp"].is<unsigned char>()) {
if ( doc["dhw"]["minTemp"].as<unsigned char>() >= 0 && doc["dhw"]["minTemp"].as<unsigned char>() < 100 && doc["dhw"]["minTemp"].as<unsigned char>() < settings.dhw.maxTemp ) {
settings.dhw.minTemp = doc["dhw"]["minTemp"].as<unsigned char>();
vars.parameters.dhwMinTemp = settings.dhw.minTemp;
flag = true;
}
}
// pid
if (!doc["pid"]["enable"].isNull() && doc["pid"]["enable"].is<bool>()) {
settings.pid.enable = doc["pid"]["enable"].as<bool>();
flag = true;
}
if (!doc["pid"]["p_factor"].isNull() && (doc["pid"]["p_factor"].is<float>() || doc["pid"]["p_factor"].is<int>())) {
settings.pid.p_factor = round(doc["pid"]["p_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["pid"]["p_factor"].isNull() && doc["pid"]["p_factor"].is<float>()) {
if ( doc["pid"]["p_factor"].as<float>() >= 0 && doc["pid"]["p_factor"].as<float>() <= 20 ) {
settings.pid.p_factor = round(doc["pid"]["p_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["pid"]["i_factor"].isNull() && (doc["pid"]["i_factor"].is<float>() || doc["pid"]["i_factor"].is<int>())) {
settings.pid.i_factor = round(doc["pid"]["i_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["pid"]["i_factor"].isNull() && doc["pid"]["i_factor"].is<float>()) {
if ( doc["pid"]["i_factor"].as<float>() >= 0 && doc["pid"]["i_factor"].as<float>() <= 20 ) {
settings.pid.i_factor = round(doc["pid"]["i_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["pid"]["d_factor"].isNull() && (doc["pid"]["d_factor"].is<float>() || doc["pid"]["d_factor"].is<int>())) {
settings.pid.d_factor = round(doc["pid"]["d_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["pid"]["d_factor"].isNull() && doc["pid"]["d_factor"].is<float>()) {
if ( doc["pid"]["d_factor"].as<float>() >= 0 && doc["pid"]["d_factor"].as<float>() <= 20 ) {
settings.pid.d_factor = round(doc["pid"]["d_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["pid"]["maxTemp"].isNull() && doc["pid"]["maxTemp"].is<unsigned char>()) {
if ( doc["pid"]["maxTemp"].as<unsigned char>() > 0 && doc["pid"]["maxTemp"].as<unsigned char>() <= 100 && doc["pid"]["maxTemp"].as<unsigned char>() > settings.pid.minTemp ) {
settings.pid.maxTemp = doc["pid"]["maxTemp"].as<unsigned char>();
flag = true;
}
}
if (!doc["pid"]["minTemp"].isNull() && doc["pid"]["minTemp"].is<unsigned char>()) {
if ( doc["pid"]["minTemp"].as<unsigned char>() >= 0 && doc["pid"]["minTemp"].as<unsigned char>() < 100 && doc["pid"]["minTemp"].as<unsigned char>() < settings.pid.maxTemp ) {
settings.pid.minTemp = doc["pid"]["minTemp"].as<unsigned char>();
flag = true;
}
}
// equitherm
@@ -178,21 +232,58 @@ protected:
flag = true;
}
if (!doc["equitherm"]["n_factor"].isNull() && (doc["equitherm"]["n_factor"].is<float>() || doc["equitherm"]["n_factor"].is<int>())) {
settings.equitherm.n_factor = round(doc["equitherm"]["n_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["equitherm"]["n_factor"].isNull() && doc["equitherm"]["n_factor"].is<float>()) {
if ( doc["equitherm"]["n_factor"].as<float>() >= 0 && doc["equitherm"]["n_factor"].as<float>() <= 20 ) {
settings.equitherm.n_factor = round(doc["equitherm"]["n_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["equitherm"]["k_factor"].isNull() && (doc["equitherm"]["k_factor"].is<float>() || doc["equitherm"]["k_factor"].is<int>())) {
settings.equitherm.k_factor = round(doc["equitherm"]["k_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["equitherm"]["k_factor"].isNull() && doc["equitherm"]["k_factor"].is<float>()) {
if ( doc["equitherm"]["k_factor"].as<float>() >= 0 && doc["equitherm"]["k_factor"].as<float>() <= 20 ) {
settings.equitherm.k_factor = round(doc["equitherm"]["k_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["equitherm"]["t_factor"].isNull() && (doc["equitherm"]["t_factor"].is<float>() || doc["equitherm"]["t_factor"].is<int>())) {
settings.equitherm.t_factor = round(doc["equitherm"]["t_factor"].as<float>() * 1000) / 1000;
flag = true;
if (!doc["equitherm"]["t_factor"].isNull() && doc["equitherm"]["t_factor"].is<float>()) {
if ( doc["equitherm"]["t_factor"].as<float>() >= 0 && doc["equitherm"]["t_factor"].as<float>() <= 20 ) {
settings.equitherm.t_factor = round(doc["equitherm"]["t_factor"].as<float>() * 1000) / 1000;
flag = true;
}
}
// sensors
if (!doc["sensors"]["outdoor"]["type"].isNull() && doc["sensors"]["outdoor"]["type"].is<unsigned char>()) {
if ( doc["sensors"]["outdoor"]["type"].as<unsigned char>() >= 0 && doc["sensors"]["outdoor"]["type"].as<unsigned char>() <= 2 ) {
settings.sensors.outdoor.type = doc["sensors"]["outdoor"]["type"].as<unsigned char>();
flag = true;
}
}
if (!doc["sensors"]["outdoor"]["offset"].isNull() && doc["sensors"]["outdoor"]["offset"].is<float>()) {
if ( doc["sensors"]["outdoor"]["offset"].as<float>() >= -10 && doc["sensors"]["outdoor"]["offset"].as<float>() <= 10 ) {
settings.sensors.outdoor.offset = round(doc["sensors"]["outdoor"]["offset"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (!doc["sensors"]["indoor"]["type"].isNull() && doc["sensors"]["indoor"]["type"].is<unsigned char>()) {
if ( doc["sensors"]["indoor"]["type"].as<unsigned char>() >= 1 && doc["sensors"]["indoor"]["type"].as<unsigned char>() <= 2 ) {
settings.sensors.indoor.type = doc["sensors"]["indoor"]["type"].as<unsigned char>();
flag = true;
}
}
if (!doc["sensors"]["indoor"]["offset"].isNull() && doc["sensors"]["indoor"]["offset"].is<float>()) {
if ( doc["sensors"]["indoor"]["offset"].as<float>() >= -10 && doc["sensors"]["indoor"]["offset"].as<float>() <= 10 ) {
settings.sensors.indoor.offset = round(doc["sensors"]["indoor"]["offset"].as<float>() * 1000) / 1000;
flag = true;
}
}
if (flag) {
eeSettings.update();
publish(true);
@@ -215,27 +306,33 @@ protected:
flag = true;
}
if (!doc["tuning"]["regulator"].isNull() && doc["tuning"]["regulator"].is<int>() && doc["tuning"]["regulator"] >= 0 && doc["tuning"]["regulator"] <= 1) {
vars.tuning.regulator = doc["tuning"]["regulator"];
flag = true;
if (!doc["tuning"]["regulator"].isNull() && doc["tuning"]["regulator"].is<unsigned char>()) {
if (doc["tuning"]["regulator"].as<unsigned char>() >= 0 && doc["tuning"]["regulator"].as<unsigned char>() <= 1) {
vars.tuning.regulator = doc["tuning"]["regulator"].as<unsigned char>();
flag = true;
}
}
if (!doc["temperatures"]["indoor"].isNull() && (doc["temperatures"]["indoor"].is<float>() || doc["temperatures"]["indoor"].is<int>())) {
vars.temperatures.indoor = round(doc["temperatures"]["indoor"].as<float>() * 100) / 100;
flag = true;
if (!doc["temperatures"]["indoor"].isNull() && doc["temperatures"]["indoor"].is<float>()) {
if ( settings.sensors.indoor.type == 1 && doc["temperatures"]["indoor"].as<float>() > -100 && doc["temperatures"]["indoor"].as<float>() < 100 ) {
vars.temperatures.indoor = round(doc["temperatures"]["indoor"].as<float>() * 100) / 100;
flag = true;
}
}
if (!doc["temperatures"]["outdoor"].isNull() && (doc["temperatures"]["outdoor"].is<float>() || doc["temperatures"]["outdoor"].is<int>()) && settings.outdoorTempSource == 1) {
vars.temperatures.outdoor = round(doc["temperatures"]["outdoor"].as<float>() * 100) / 100;
flag = true;
if (!doc["temperatures"]["outdoor"].isNull() && doc["temperatures"]["outdoor"].is<float>()) {
if ( settings.sensors.outdoor.type == 1 && doc["temperatures"]["outdoor"].as<float>() > -100 && doc["temperatures"]["outdoor"].as<float>() < 100 ) {
vars.temperatures.outdoor = round(doc["temperatures"]["outdoor"].as<float>() * 100) / 100;
flag = true;
}
}
if (!doc["restart"].isNull() && doc["restart"].is<bool>() && doc["restart"]) {
if (!doc["restart"].isNull() && doc["restart"].is<bool>() && doc["restart"].as<bool>()) {
DEBUG("Received restart message...");
eeSettings.updateNow();
Scheduler.delay(10000);
DEBUG("Restart...");
eeSettings.updateNow();
ESP.restart();
}
@@ -275,7 +372,10 @@ protected:
static void publishHaEntities() {
// main
haHelper.publishSelectOutdoorTempSource();
haHelper.publishSelectOutdoorSensorType();
haHelper.publishSelectIndoorSensorType();
haHelper.publishNumberOutdoorSensorOffset(false);
haHelper.publishNumberIndoorSensorOffset(false);
haHelper.publishSwitchDebug(false);
// emergency
@@ -289,16 +389,26 @@ protected:
//haHelper.publishNumberHeatingTarget(false);
haHelper.publishNumberHeatingHysteresis();
haHelper.publishSensorHeatingSetpoint(false);
haHelper.publishSensorCurrentHeatingMinTemp(false);
haHelper.publishSensorCurrentHeatingMaxTemp(false);
haHelper.publishNumberHeatingMinTemp(false);
haHelper.publishNumberHeatingMaxTemp(false);
// dhw
haHelper.publishSwitchDHW(false);
//haHelper.publishNumberDHWTarget(false);
haHelper.publishSensorCurrentDHWMinTemp(false);
haHelper.publishSensorCurrentDHWMaxTemp(false);
haHelper.publishNumberDHWMinTemp(false);
haHelper.publishNumberDHWMaxTemp(false);
// pid
haHelper.publishSwitchPID();
haHelper.publishNumberPIDFactorP();
haHelper.publishNumberPIDFactorI();
haHelper.publishNumberPIDFactorD();
haHelper.publishNumberPIDMinTemp(false);
haHelper.publishNumberPIDMaxTemp(false);
// equitherm
haHelper.publishSwitchEquitherm();
@@ -319,7 +429,7 @@ protected:
haHelper.publishBinSensorFault();
haHelper.publishBinSensorDiagnostic();
haHelper.publishSensorFaultCode();
haHelper.publishSensorRssi();
haHelper.publishSensorRssi(false);
// sensors
haHelper.publishSensorModulation(false);
@@ -333,18 +443,15 @@ protected:
}
static bool publishNonStaticHaEntities(bool force = false) {
static byte _heatingMinTemp;
static byte _heatingMaxTemp;
static byte _dhwMinTemp;
static byte _dhwMaxTemp;
static bool _editableOutdoorTemp;
static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp;
static bool _editableOutdoorTemp, _editableIndoorTemp;
bool published = false;
bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable;
byte heatingMinTemp = isStupidMode ? vars.parameters.heatingMinTemp : 10;
byte heatingMaxTemp = isStupidMode ? vars.parameters.heatingMaxTemp : 30;
bool editableOutdoorTemp = settings.outdoorTempSource == 1;
bool editableOutdoorTemp = settings.sensors.outdoor.type == 1;
bool editableIndoorTemp = settings.sensors.indoor.type == 1;
if (force || _heatingMinTemp != heatingMinTemp || _heatingMaxTemp != heatingMaxTemp) {
if (settings.heating.target < heatingMinTemp || settings.heating.target > heatingMaxTemp) {
@@ -384,6 +491,20 @@ protected:
published = true;
}
if (force || _editableIndoorTemp != editableIndoorTemp) {
_editableIndoorTemp = editableIndoorTemp;
if (editableIndoorTemp) {
haHelper.deleteSensorIndoorTemp();
haHelper.publishNumberIndoorTemp();
} else {
haHelper.deleteNumberIndoorTemp();
haHelper.publishSensorIndoorTemp();
}
published = true;
}
return published;
}
@@ -391,7 +512,6 @@ protected:
StaticJsonDocument<2048> doc;
doc["debug"] = settings.debug;
doc["outdoorTempSource"] = settings.outdoorTempSource;
doc["emergency"]["enable"] = settings.emergency.enable;
doc["emergency"]["target"] = settings.emergency.target;
@@ -401,20 +521,32 @@ protected:
doc["heating"]["turbo"] = settings.heating.turbo;
doc["heating"]["target"] = settings.heating.target;
doc["heating"]["hysteresis"] = settings.heating.hysteresis;
doc["heating"]["minTemp"] = settings.heating.minTemp;
doc["heating"]["maxTemp"] = settings.heating.maxTemp;
doc["dhw"]["enable"] = settings.dhw.enable;
doc["dhw"]["target"] = settings.dhw.target;
doc["dhw"]["minTemp"] = settings.dhw.minTemp;
doc["dhw"]["maxTemp"] = settings.dhw.maxTemp;
doc["pid"]["enable"] = settings.pid.enable;
doc["pid"]["p_factor"] = settings.pid.p_factor;
doc["pid"]["i_factor"] = settings.pid.i_factor;
doc["pid"]["d_factor"] = settings.pid.d_factor;
doc["pid"]["minTemp"] = settings.pid.minTemp;
doc["pid"]["maxTemp"] = settings.pid.maxTemp;
doc["equitherm"]["enable"] = settings.equitherm.enable;
doc["equitherm"]["n_factor"] = settings.equitherm.n_factor;
doc["equitherm"]["k_factor"] = settings.equitherm.k_factor;
doc["equitherm"]["t_factor"] = settings.equitherm.t_factor;
doc["sensors"]["outdoor"]["type"] = settings.sensors.outdoor.type;
doc["sensors"]["outdoor"]["offset"] = settings.sensors.outdoor.offset;
doc["sensors"]["indoor"]["type"] = settings.sensors.indoor.type;
doc["sensors"]["indoor"]["offset"] = settings.sensors.indoor.offset;
client.beginPublish(topic, measureJson(doc), false);
//BufferingPrint bufferedClient(client, 32);
//serializeJson(doc, bufferedClient);

View File

@@ -9,6 +9,11 @@ public:
protected:
void setup() {
vars.parameters.heatingMinTemp = settings.heating.minTemp;
vars.parameters.heatingMaxTemp = settings.heating.maxTemp;
vars.parameters.dhwMinTemp = settings.dhw.minTemp;
vars.parameters.dhwMaxTemp = settings.dhw.maxTemp;
ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin);
ot->begin(handleInterrupt, responseCallback);
@@ -63,7 +68,7 @@ protected:
updateMinMaxDhwTemp();
updateMinMaxHeatingTemp();
if (settings.outdoorTempSource == 0) {
if (settings.sensors.outdoor.type == 0) {
updateOutsideTemp();
}
if (vars.states.fault) {
@@ -83,6 +88,7 @@ protected:
if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) {
updateModulationLevel();
}
yield();
if ( settings.dhw.enable ) {
updateDHWTemp();
@@ -95,7 +101,6 @@ protected:
} else {
vars.temperatures.heating = 0;
}
yield();
//
@@ -306,8 +311,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 = minTemp < settings.dhw.minTemp ? settings.dhw.minTemp : minTemp;
vars.parameters.dhwMaxTemp = maxTemp > settings.dhw.maxTemp ? settings.dhw.maxTemp : maxTemp;
return true;
}
@@ -325,8 +330,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 = minTemp < settings.heating.minTemp ? settings.heating.minTemp : minTemp;
vars.parameters.heatingMaxTemp = maxTemp > settings.heating.maxTemp ? settings.heating.maxTemp : maxTemp;
return true;
}
@@ -340,7 +345,7 @@ protected:
return false;
}
vars.temperatures.outdoor = ot->getFloat(response);
vars.temperatures.outdoor = ot->getFloat(response) + settings.sensors.outdoor.offset;
return true;
}

View File

@@ -3,7 +3,7 @@
#include <PIDtuner.h>
Equitherm etRegulator;
GyverPID pidRegulator(0, 0, 0, 10000);
GyverPID pidRegulator(0, 0, 0);
PIDtuner pidTuner;
class RegulatorTask: public LeanTask {
@@ -73,7 +73,7 @@ protected:
float newTemp = 0;
// if use equitherm
if (settings.emergency.useEquitherm && settings.outdoorTempSource != 1) {
if (settings.emergency.useEquitherm && settings.sensors.outdoor.type != 1) {
float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp);
if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) {
@@ -123,21 +123,24 @@ protected:
}
// if use pid
if (settings.pid.enable) {
if (settings.pid.enable && vars.states.heating) {
float pidResult = getPidTemp(
settings.equitherm.enable ? -30 : vars.parameters.heatingMinTemp,
settings.equitherm.enable ? 30 : vars.parameters.heatingMaxTemp
settings.equitherm.enable ? (settings.pid.maxTemp * -1) : settings.pid.minTemp,
settings.equitherm.enable ? settings.pid.maxTemp : settings.pid.maxTemp
);
if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {
if (1 || fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) {
prevPidResult = pidResult;
newTemp += pidResult;
INFO_F("[REGULATOR][PID] New result: %u (%f) \n", (int)round(pidResult), pidResult);
INFO_F("[REGULATOR][PID] New result: %d (%f) \n", (int)round(pidResult), pidResult);
} else {
newTemp += prevPidResult;
}
} else if ( settings.pid.enable && !vars.states.heating && prevPidResult != 0 ) {
newTemp += prevPidResult;
}
// default temp, manual mode
@@ -145,7 +148,9 @@ protected:
newTemp = settings.heating.target;
}
return round(newTemp);
newTemp = round(newTemp);
newTemp = constrain(newTemp, 0, 100);
return newTemp;
}
byte getTuningModeTemp() {
@@ -269,7 +274,7 @@ protected:
pidRegulator.input = vars.temperatures.indoor;
pidRegulator.setpoint = settings.heating.target;
return pidRegulator.getResultTimer();
return pidRegulator.getResultNow();
}
float tuneEquithermN(float ratio, float currentTemp, float setTemp, unsigned int dirtyInterval = 60, unsigned int accurateInterval = 1800, float accurateStep = 0.01, float accurateStepAfter = 1) {

View File

@@ -1,45 +1,149 @@
#include <microDS18B20.h>
MicroDS18B20<DS18B20_PIN> outdoorSensor;
#include <OneWire.h>
#include <DallasTemperature.h>
class SensorsTask: public LeanTask {
public:
SensorsTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {}
protected:
OneWire* oneWireOutdoorSensor;
OneWire* oneWireIndoorSensor;
DallasTemperature* outdoorSensor;
DallasTemperature* indoorSensor;
bool initOutdoorSensor = false;
unsigned long startConversionTime = 0;
float filteredOutdoorTemp = 0;
bool emptyOutdoorTemp = true;
bool initIndoorSensor = false;
float filteredIndoorTemp = 0;
bool emptyIndoorTemp = true;
void setup() {}
void loop() {
// DS18B20 sensor
if (outdoorSensor.online()) {
if (outdoorSensor.readTemp()) {
float rawTemp = outdoorSensor.getTemp();
DEBUG_F("[SENSORS][DS18B20] Raw temp: %f \n", rawTemp);
if ( settings.sensors.outdoor.type == 2 ) {
outdoorTemperatureSensor();
}
if (emptyOutdoorTemp) {
filteredOutdoorTemp = rawTemp;
emptyOutdoorTemp = false;
} else {
filteredOutdoorTemp += (rawTemp - filteredOutdoorTemp) * OUTDOOR_SENSOR_FILTER_K;
}
filteredOutdoorTemp = floor(filteredOutdoorTemp * 100) / 100;
if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = filteredOutdoorTemp;
INFO_F("[SENSORS][DS18B20] New temp: %f \n", filteredOutdoorTemp);
}
} else {
ERROR("[SENSORS][DS18B20] Invalid data from sensor");
}
outdoorSensor.requestTemp();
} else {
ERROR("[SENSORS][DS18B20] Failed to connect to sensor");
if ( settings.sensors.indoor.type == 2 ) {
indoorTemperatureSensor();
}
}
void outdoorTemperatureSensor() {
if ( !initOutdoorSensor ) {
oneWireOutdoorSensor = new OneWire(settings.sensors.outdoor.pin);
outdoorSensor = new DallasTemperature(oneWireOutdoorSensor);
outdoorSensor->begin();
outdoorSensor->setResolution(12);
outdoorSensor->setWaitForConversion(false);
outdoorSensor->requestTemperatures();
startConversionTime = millis();
initOutdoorSensor = true;
}
unsigned long estimateConversionTime = millis() - startConversionTime;
if ( estimateConversionTime < outdoorSensor->millisToWaitForConversion() ) {
return;
}
bool completed = outdoorSensor->isConversionComplete();
if ( !completed && estimateConversionTime >= 1000 ) {
// fail, retry
outdoorSensor->requestTemperatures();
startConversionTime = millis();
ERROR("[SENSORS][OUTDOOR] Could not read temperature data (no response)");
}
if ( !completed ) {
return;
}
float rawTemp = outdoorSensor->getTempCByIndex(0);
if (rawTemp == DEVICE_DISCONNECTED_C) {
ERROR("[SENSORS][OUTDOOR] Could not read temperature data (not connected)");
} else {
DEBUG_F("[SENSORS][OUTDOOR] Raw temp: %f \n", rawTemp);
if (emptyOutdoorTemp) {
filteredOutdoorTemp = rawTemp;
emptyOutdoorTemp = false;
} else {
filteredOutdoorTemp += (rawTemp - filteredOutdoorTemp) * EXT_SENSORS_FILTER_K;
}
filteredOutdoorTemp = floor(filteredOutdoorTemp * 100) / 100;
if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) {
vars.temperatures.outdoor = filteredOutdoorTemp + settings.sensors.outdoor.offset;
INFO_F("[SENSORS][OUTDOOR] New temp: %f \n", filteredOutdoorTemp);
}
}
outdoorSensor->requestTemperatures();
startConversionTime = millis();
}
void indoorTemperatureSensor() {
if ( !initIndoorSensor ) {
oneWireIndoorSensor = new OneWire(settings.sensors.indoor.pin);
indoorSensor = new DallasTemperature(oneWireIndoorSensor);
indoorSensor->begin();
indoorSensor->setResolution(12);
indoorSensor->setWaitForConversion(false);
indoorSensor->requestTemperatures();
startConversionTime = millis();
initIndoorSensor = true;
}
unsigned long estimateConversionTime = millis() - startConversionTime;
if ( estimateConversionTime < indoorSensor->millisToWaitForConversion() ) {
return;
}
bool completed = indoorSensor->isConversionComplete();
if ( !completed && estimateConversionTime >= 1000 ) {
// fail, retry
indoorSensor->requestTemperatures();
startConversionTime = millis();
ERROR("[SENSORS][INDOOR] Could not read temperature data (no response)");
}
if ( !completed ) {
return;
}
float rawTemp = indoorSensor->getTempCByIndex(0);
if (rawTemp == DEVICE_DISCONNECTED_C) {
ERROR("[SENSORS][INDOOR] Could not read temperature data (not connected)");
} else {
DEBUG_F("[SENSORS][INDOOR] Raw temp: %f \n", rawTemp);
if (emptyIndoorTemp) {
filteredIndoorTemp = rawTemp;
emptyIndoorTemp = false;
} else {
filteredIndoorTemp += (rawTemp - filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
}
filteredIndoorTemp = floor(filteredIndoorTemp * 100) / 100;
if (fabs(vars.temperatures.indoor - filteredIndoorTemp) > 0.099) {
vars.temperatures.indoor = filteredIndoorTemp + settings.sensors.indoor.offset;
INFO_F("[SENSORS][INDOOR] New temp: %f \n", filteredIndoorTemp);
}
}
indoorSensor->requestTemperatures();
startConversionTime = millis();
}
};

View File

@@ -1,12 +1,10 @@
struct Settings {
bool debug = false;
// 0 - boiler, 1 - manual, 2 - ds18b20
byte outdoorTempSource = 0;
char hostname[80] = "opentherm";
struct {
byte inPin = 5;
byte outPin = 4;
byte inPin = 4;
byte outPin = 5;
unsigned int memberIdCode = 0;
} opentherm;
@@ -30,11 +28,15 @@ struct Settings {
bool turbo = false;
float target = 40.0f;
float hysteresis = 0.5f;
byte minTemp = 20.0f;
byte maxTemp = 90.0f;
} heating;
struct {
bool enable = true;
byte target = 40;
byte minTemp = 30.0f;
byte maxTemp = 60.0f;
} dhw;
struct {
@@ -42,6 +44,8 @@ struct Settings {
float p_factor = 3;
float i_factor = 0.2f;
float d_factor = 0;
byte minTemp = 0.0f;
byte maxTemp = 90.0f;
} pid;
struct {
@@ -51,6 +55,23 @@ struct Settings {
float t_factor = 2.0f;
} equitherm;
struct {
struct {
// 0 - boiler, 1 - manual, 2 - ds18b20
byte type = 0;
byte pin = 12;
float offset = 0.0f;
} outdoor;
struct {
// 1 - manual, 2 - ds18b20
byte type = 1;
byte pin = 14;
float offset = 0.0f;
} indoor;
} sensors;
char validationValue[8] = SETTINGS_VALID_VALUE;
} settings;
struct Variables {
@@ -84,11 +105,11 @@ struct Variables {
} temperatures;
struct {
byte heatingMinTemp = 20;
byte heatingMaxTemp = 90;
byte heatingMinTemp = 0;
byte heatingMaxTemp = 0;
byte heatingSetpoint = 0.0f;
byte dhwMinTemp = 30;
byte dhwMaxTemp = 60;
byte dhwMinTemp = 0;
byte dhwMaxTemp = 0;
uint8_t slaveMemberIdCode;
uint8_t slaveType;
uint8_t slaveVersion;

View File

@@ -3,14 +3,17 @@
// Wifimanager
WiFiManager wm;
WiFiManagerParameter* wmHostname;
WiFiManagerParameter* wmOtInPin;
WiFiManagerParameter* wmOtOutPin;
WiFiManagerParameter* wmOtMemberIdCode;
WiFiManagerParameter* wmMqttServer;
WiFiManagerParameter* wmMqttPort;
WiFiManagerParameter* wmMqttUser;
WiFiManagerParameter* wmMqttPassword;
WiFiManagerParameter* wmMqttPrefix;
WiFiManagerParameter* wmMqttPublishInterval;
WiFiManagerParameter* wmOtInPin;
WiFiManagerParameter* wmOtOutPin;
WiFiManagerParameter* wmOtMemberIdCode;
WiFiManagerParameter* wmOutdoorSensorPin;
WiFiManagerParameter* wmIndoorSensorPin;
class WifiManagerTask: public Task {
public:
@@ -24,18 +27,6 @@ protected:
wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80);
wm.addParameter(wmHostname);
sprintf(buffer, "%d", settings.opentherm.inPin);
wmOtInPin = new WiFiManagerParameter("ot_in_pin", "Opentherm pin IN", buffer, 1);
wm.addParameter(wmOtInPin);
sprintf(buffer, "%d", settings.opentherm.outPin);
wmOtOutPin = new WiFiManagerParameter("ot_out_pin", "Opentherm pin OUT", buffer, 1);
wm.addParameter(wmOtOutPin);
sprintf(buffer, "%d", settings.opentherm.memberIdCode);
wmOtMemberIdCode = new WiFiManagerParameter("ot_member_id_code", "Opentherm member id code", buffer, 5);
wm.addParameter(wmOtMemberIdCode);
wmMqttServer = new WiFiManagerParameter("mqtt_server", "MQTT server", settings.mqtt.server, 80);
wm.addParameter(wmMqttServer);
@@ -52,6 +43,30 @@ protected:
wmMqttPrefix = new WiFiManagerParameter("mqtt_prefix", "MQTT prefix", settings.mqtt.prefix, 32);
wm.addParameter(wmMqttPrefix);
sprintf(buffer, "%d", settings.mqtt.interval);
wmMqttPublishInterval = new WiFiManagerParameter("mqtt_publish_interval", "MQTT publish interval", buffer, 5);
wm.addParameter(wmMqttPublishInterval);
sprintf(buffer, "%d", settings.opentherm.inPin);
wmOtInPin = new WiFiManagerParameter("ot_in_pin", "Opentherm pin IN", buffer, 2);
wm.addParameter(wmOtInPin);
sprintf(buffer, "%d", settings.opentherm.outPin);
wmOtOutPin = new WiFiManagerParameter("ot_out_pin", "Opentherm pin OUT", buffer, 2);
wm.addParameter(wmOtOutPin);
sprintf(buffer, "%d", settings.opentherm.memberIdCode);
wmOtMemberIdCode = new WiFiManagerParameter("ot_member_id_code", "Opentherm member id", buffer, 5);
wm.addParameter(wmOtMemberIdCode);
sprintf(buffer, "%d", settings.sensors.outdoor.pin);
wmOutdoorSensorPin = new WiFiManagerParameter("outdoor_sensor_pin", "Outdoor sensor pin", buffer, 2);
wm.addParameter(wmOutdoorSensorPin);
sprintf(buffer, "%d", settings.sensors.indoor.pin);
wmIndoorSensorPin = new WiFiManagerParameter("indoor_sensor_pin", "Indoor sensor pin", buffer, 2);
wm.addParameter(wmIndoorSensorPin);
//wm.setCleanConnect(true);
wm.setRestorePersistent(false);
@@ -94,32 +109,43 @@ protected:
void static saveParamsCallback() {
strcpy(settings.hostname, wmHostname->getValue());
settings.opentherm.inPin = atoi(wmOtInPin->getValue());
settings.opentherm.outPin = atoi(wmOtOutPin->getValue());
settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue());
strcpy(settings.mqtt.server, wmMqttServer->getValue());
settings.mqtt.port = atoi(wmMqttPort->getValue());
strcpy(settings.mqtt.user, wmMqttUser->getValue());
strcpy(settings.mqtt.password, wmMqttPassword->getValue());
strcpy(settings.mqtt.prefix, wmMqttPrefix->getValue());
settings.mqtt.interval = atoi(wmMqttPublishInterval->getValue());
settings.opentherm.inPin = atoi(wmOtInPin->getValue());
settings.opentherm.outPin = atoi(wmOtOutPin->getValue());
settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue());
settings.sensors.outdoor.pin = atoi(wmOutdoorSensorPin->getValue());
settings.sensors.indoor.pin = atoi(wmIndoorSensorPin->getValue());
INFO_F(
"New settings:\r\n"
" Hostname: %s\r\n"
" OT in pin: %d"
" OT out pin: %d"
" OT member id code: %d"
" Mqtt server: %s:%d\r\n"
" Mqtt user: %s\r\n"
" Mqtt pass: %s\r\n",
" Mqtt pass: %s\r\n"
" Mqtt prefix: %s\r\n"
" Mqtt publish interval: %d\r\n"
" OT in pin: %d\r\n"
" OT out pin: %d\r\n"
" OT member id code: %d\r\n"
" Outdoor sensor pin: %d\r\n"
" Indoor sensor pin: %d\r\n",
settings.hostname,
settings.opentherm.inPin,
settings.opentherm.outPin,
settings.opentherm.memberIdCode,
settings.mqtt.server,
settings.mqtt.port,
settings.mqtt.user,
settings.mqtt.password
settings.mqtt.password,
settings.mqtt.prefix,
settings.mqtt.interval,
settings.opentherm.inPin,
settings.opentherm.outPin,
settings.opentherm.memberIdCode,
settings.sensors.outdoor.pin,
settings.sensors.indoor.pin
);
eeSettings.updateNow();
INFO(F("Settings saved"));

View File

@@ -1,4 +1,4 @@
#define OT_GATEWAY_VERSION "1.2.1"
#define OT_GATEWAY_VERSION "1.3.0"
#define AP_SSID "OpenTherm Gateway"
#define AP_PASSWORD "otgateway123456"
#define USE_TELNET
@@ -9,9 +9,8 @@
#define OPENTHERM_OFFLINE_TRESHOLD 10
#define DS18B20_PIN 2
#define DS18B20_INTERVAL 5000
#define OUTDOOR_SENSOR_FILTER_K 0.15
#define EXT_SENSORS_INTERVAL 5000
#define EXT_SENSORS_FILTER_K 0.15
#define DS_CHECK_CRC true
#define DS_CRC_USE_TABLE true
@@ -19,6 +18,7 @@
#define LED_OT_RX_PIN 15
#define CONFIG_URL "http://%s/"
#define SETTINGS_VALID_VALUE "stvalid" // only 8 chars!
#ifdef USE_TELNET

View File

@@ -29,7 +29,7 @@ MainTask* tMain;
void setup() {
#ifdef USE_TELNET
TelnetStream.begin();
delay(5000);
delay(1000);
#else
Serial.begin(115200);
Serial.println("\n\n");
@@ -40,6 +40,13 @@ void setup() {
if (eeSettingsResult == 0) {
INFO("Settings loaded");
if ( strcmp(SETTINGS_VALID_VALUE, settings.validationValue) != 0 ) {
INFO("Settings not valid, reset and restart...");
eeSettings.reset();
delay(1000);
ESP.restart();
}
} else if (eeSettingsResult == 1) {
INFO("Settings NOT loaded, first start");
@@ -56,7 +63,7 @@ void setup() {
tOt = new OpenThermTask(false);
Scheduler.start(tOt);
tSensors = new SensorsTask(false, DS18B20_INTERVAL);
tSensors = new SensorsTask(true, EXT_SENSORS_INTERVAL);
Scheduler.start(tSensors);
tRegulator = new RegulatorTask(true, 10000);