mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +05:00
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:
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
206
src/MqttTask.h
206
src/MqttTask.h
@@ -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,16 +100,19 @@ protected:
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["emergency"]["target"].isNull() && (doc["emergency"]["target"].is<float>() || doc["emergency"]["target"].is<int>())) {
|
||||
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>()) {
|
||||
settings.emergency.useEquitherm = doc["emergency"]["useEquitherm"].as<bool>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
|
||||
// heating
|
||||
if (!doc["heating"]["enable"].isNull() && doc["heating"]["enable"].is<bool>()) {
|
||||
settings.heating.enable = doc["heating"]["enable"].as<bool>();
|
||||
@@ -130,15 +124,36 @@ protected:
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["heating"]["target"].isNull() && (doc["heating"]["target"].is<float>() || doc["heating"]["target"].is<int>())) {
|
||||
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>())) {
|
||||
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>()) {
|
||||
@@ -146,10 +161,29 @@ protected:
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["dhw"]["target"].isNull() && doc["dhw"]["target"].is<int>()) {
|
||||
settings.dhw.target = doc["dhw"]["target"].as<int>();
|
||||
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>()) {
|
||||
@@ -157,20 +191,40 @@ protected:
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["pid"]["p_factor"].isNull() && (doc["pid"]["p_factor"].is<float>() || doc["pid"]["p_factor"].is<int>())) {
|
||||
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>())) {
|
||||
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>())) {
|
||||
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
|
||||
if (!doc["equitherm"]["enable"].isNull() && doc["equitherm"]["enable"].is<bool>()) {
|
||||
@@ -178,20 +232,57 @@ protected:
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!doc["equitherm"]["n_factor"].isNull() && (doc["equitherm"]["n_factor"].is<float>() || doc["equitherm"]["n_factor"].is<int>())) {
|
||||
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>())) {
|
||||
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>())) {
|
||||
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();
|
||||
@@ -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"];
|
||||
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>())) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
void setup() {}
|
||||
|
||||
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 ( 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) * OUTDOOR_SENSOR_FILTER_K;
|
||||
filteredOutdoorTemp += (rawTemp - filteredOutdoorTemp) * EXT_SENSORS_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);
|
||||
vars.temperatures.outdoor = filteredOutdoorTemp + settings.sensors.outdoor.offset;
|
||||
INFO_F("[SENSORS][OUTDOOR] New temp: %f \n", filteredOutdoorTemp);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ERROR("[SENSORS][DS18B20] Invalid data from sensor");
|
||||
outdoorSensor->requestTemperatures();
|
||||
startConversionTime = millis();
|
||||
}
|
||||
|
||||
outdoorSensor.requestTemp();
|
||||
} else {
|
||||
ERROR("[SENSORS][DS18B20] Failed to connect to sensor");
|
||||
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();
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user