Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ package-lock.json
sdkconfig.*
CMakeLists.txt
!sdkconfig.defaults
!.gitkeep
!.gitkeep
.DS_Store
326 changes: 172 additions & 154 deletions src/HaHelper.h

Large diffs are not rendered by default.

45 changes: 44 additions & 1 deletion src/MainTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ class MainTask : public Task {
this->emergency();
this->cascadeControl();
this->externalPump();
this->externalDev();
this->miscRunned = millis();

return true;
Expand Down Expand Up @@ -688,4 +689,46 @@ class MainTask : public Task {
Log.sinfoln(FPSTR(L_EXTPUMP), F("Enabled: anti stuck"));
}
}
};

void externalDev() {
static uint8_t configuredGpio = GPIO_IS_NOT_CONFIGURED;

if(!settings.externalDev.use) return;

// configure output
// if settings are different than the configured GPIO, update
if (settings.externalDev.gpio != configuredGpio) {
if (configuredGpio != GPIO_IS_NOT_CONFIGURED) {
digitalWrite(configuredGpio, LOW);
}

if (GPIO_IS_VALID(settings.externalDev.gpio)) {
configuredGpio = settings.externalDev.gpio;
pinMode(configuredGpio, OUTPUT);
digitalWrite(configuredGpio, LOW);

} else if (configuredGpio != GPIO_IS_NOT_CONFIGURED) {
configuredGpio = GPIO_IS_NOT_CONFIGURED;
}
}

if (configuredGpio == GPIO_IS_NOT_CONFIGURED) {
if (vars.externalDev.state) {
vars.externalDev.state = false;

Log.sinfoln(FPSTR(L_EXTDEV), F("Disabled: use = off"));
}

return;
}

// output configured update relay if required
if(settings.externalDev.state != vars.externalDev.state ) {
digitalWrite(configuredGpio, settings.externalDev.state? HIGH:LOW );
vars.externalDev.state = settings.externalDev.state;
}

}

};

5 changes: 5 additions & 0 deletions src/MqttTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@ class MqttTask : public Task {
this->haHelper->publishInputEquithermFactorK(false);
this->haHelper->publishInputEquithermFactorT(false);

// ext device
this->haHelper->publishSwitchExtDevice(String(settings.externalDev.caption), false);

// states
this->haHelper->publishStatusState();
this->haHelper->publishEmergencyState();
Expand All @@ -515,6 +518,8 @@ class MqttTask : public Task {
this->haHelper->publishFaultState();
this->haHelper->publishDiagState();
this->haHelper->publishExternalPumpState(false);
this->haHelper->publishExtDevState(String(settings.externalDev.caption), false);


// sensors
this->haHelper->publishFaultCode();
Expand Down
11 changes: 11 additions & 0 deletions src/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ struct Settings {
unsigned short antiStuckTime = 300;
} externalPump;

struct {
bool use = false;
byte gpio = DEFAULT_EXT_DEV_GPIO;
char caption[41] = DEFAULT_EXT_DEV_CAPTION;
bool state = false;
} externalDev;

struct {
struct {
bool enabled = false;
Expand Down Expand Up @@ -279,6 +286,10 @@ struct Variables {
unsigned long lastEnabledTime = 0;
} externalPump;

struct {
bool state = false;
} externalDev;

struct {
bool input = false;
bool output = false;
Expand Down
8 changes: 8 additions & 0 deletions src/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@
#define DEFAULT_EXT_PUMP_GPIO GPIO_IS_NOT_CONFIGURED
#endif

#ifndef DEFAULT_EXT_DEV_GPIO
#define DEFAULT_EXT_DEV_GPIO GPIO_IS_NOT_CONFIGURED
#endif

#ifndef DEFAULT_EXT_DEV_CAPTION
#define DEFAULT_EXT_DEV_CAPTION "Device"
#endif

#ifndef PROGMEM
#define PROGMEM
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const char L_REGULATOR_EQUITHERM[] PROGMEM = "REGULATOR.EQUITHE
const char L_CASCADE_INPUT[] PROGMEM = "CASCADE.INPUT";
const char L_CASCADE_OUTPUT[] PROGMEM = "CASCADE.OUTPUT";
const char L_EXTPUMP[] PROGMEM = "EXTPUMP";
const char L_EXTDEV[] PROGMEM = "EXTDEV";


const char S_ACTIONS[] PROGMEM = "actions";
Expand Down Expand Up @@ -82,6 +83,8 @@ const char S_ENV[] PROGMEM = "env";
const char S_EPC[] PROGMEM = "epc";
const char S_EQUITHERM[] PROGMEM = "equitherm";
const char S_EXTERNAL_PUMP[] PROGMEM = "externalPump";
const char S_EXTERNAL_DEV[] PROGMEM = "externalDev";
const char S_EXTERNAL_DEV_CAPTION[] PROGMEM = "caption";
const char S_FACTOR[] PROGMEM = "factor";
const char S_FAULT[] PROGMEM = "fault";
const char S_FREEZE_PROTECTION[] PROGMEM = "freezeProtection";
Expand Down
55 changes: 55 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,15 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
cascadeControlOutput[FPSTR(S_ON_LOSS_CONNECTION)] = src.cascadeControl.output.onLossConnection;
cascadeControlOutput[FPSTR(S_ON_ENABLED_HEATING)] = src.cascadeControl.output.onEnabledHeating;
}

if(!safe ) {
dst[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_USE)] = src.externalDev.use;
dst[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_GPIO)] = src.externalDev.gpio;
dst[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_EXTERNAL_DEV_CAPTION)] = src.externalDev.caption;
}

if(src.externalDev.use)
dst[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_STATE)] = src.externalDev.state;
}

inline void safeSettingsToJson(const Settings& src, JsonVariant dst) {
Expand Down Expand Up @@ -1531,6 +1540,42 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}

// external device
if (src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_USE)].is<bool>()) {
bool value = src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_USE)].as<bool>();

if (value != dst.externalDev.use) {
dst.externalDev.use = value;
changed = true;
}
}

if (!src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_GPIO)].isNull()) {
if (src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_GPIO)].is<JsonString>() &&
src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_GPIO)].as<JsonString>().size() == 0) {
if (dst.externalDev.gpio != GPIO_IS_NOT_CONFIGURED) {
dst.externalDev.gpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}

} else {
unsigned char value = src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_GPIO)].as<unsigned char>();

if (GPIO_IS_VALID(value) && value != dst.externalDev.gpio) {
dst.externalDev.gpio = value;
changed = true;
}
}
}

if (!src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_EXTERNAL_DEV_CAPTION)].isNull()) {
String value = src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_EXTERNAL_DEV_CAPTION)].as<String>();

if (value.length() < sizeof(dst.externalDev.caption) && !String(dst.externalDev.caption).equals(value)) {
strcpy(dst.externalDev.caption, value.c_str());
changed = true;
}
}

// cascade control
if (src[FPSTR(S_CASCADE_CONTROL)][FPSTR(S_INPUT)][FPSTR(S_ENABLED)].is<bool>()) {
Expand Down Expand Up @@ -1653,6 +1698,15 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}

if (dst.externalDev.use && src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_STATE)].is<bool>()) {
bool value = src[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_STATE)].as<bool>();

if (value != dst.externalDev.state) {
dst.externalDev.state = value;
changed = true;
}
}

// force check emergency target
{
float value = !src[FPSTR(S_EMERGENCY)][FPSTR(S_TARGET)].isNull() ? src[FPSTR(S_EMERGENCY)][FPSTR(S_TARGET)].as<float>() : dst.emergency.target;
Expand Down Expand Up @@ -2118,6 +2172,7 @@ void varsToJson(const Variables& src, JsonVariant dst) {
master[FPSTR(S_MQTT)][FPSTR(S_CONNECTED)] = src.mqtt.connected;
master[FPSTR(S_EMERGENCY)][FPSTR(S_STATE)] = src.emergency.state;
master[FPSTR(S_EXTERNAL_PUMP)][FPSTR(S_STATE)] = src.externalPump.state;
master[FPSTR(S_EXTERNAL_DEV)][FPSTR(S_STATE)] = src.externalDev.state;

auto mCascadeControl = master[FPSTR(S_CASCADE_CONTROL)].to<JsonObject>();
mCascadeControl[FPSTR(S_INPUT)] = src.cascadeControl.input;
Expand Down
9 changes: 9 additions & 0 deletions src_data/locales/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"mMqttConnected": "MQTT服务器连接状态",
"mEmergencyState": "应急模式",
"mExtPumpState": "外置循环泵",
"mExtDevState": "外置设备",
"mCascadeControlInput": "Cascade 控制 (input)",
"mCascadeControlOutput": "Cascade 控制 (output)",

Expand Down Expand Up @@ -289,6 +290,7 @@
"ot": "OpenTherm协议设置",
"mqtt": "MQTT 服务器设置",
"extPump": "外置循环泵设置",
"extDev": "外置设备设置",
"cascadeControl": "Cascade 级联控制设置"
},

Expand Down Expand Up @@ -459,6 +461,13 @@
"antiStuckTime": "防卡死运行时长<small>(分钟)</small>"
},

"extDev": {
"use": "使用外置设备",
"gpio": "GPIO 继电器",
"state": "状态",
"caption": "说明"
},

"cascadeControl": {
"input": {
"desc": "仅当另一台锅炉发生故障时启用本锅炉加热。另一台锅炉的控制器需在故障发生时切换GPIO输入状态以触发本功能。",
Expand Down
9 changes: 9 additions & 0 deletions src_data/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"mMqttConnected": "MQTT connection",
"mEmergencyState": "Emergency mode",
"mExtPumpState": "External pump",
"mExtDevState": "External device",
"mCascadeControlInput": "Cascade control (input)",
"mCascadeControlOutput": "Cascade control (output)",

Expand Down Expand Up @@ -289,6 +290,7 @@
"ot": "OpenTherm settings",
"mqtt": "MQTT settings",
"extPump": "External pump settings",
"extDev": "External device settings",
"cascadeControl": "Cascade control settings"
},

Expand Down Expand Up @@ -458,6 +460,13 @@
"antiStuckInterval": "Anti stuck interval <small>(days)</small>",
"antiStuckTime": "Anti stuck time <small>(min)</small>"
},

"extDev": {
"use": "Use external device",
"gpio": "Relay GPIO",
"state": "State",
"caption": "Caption"
},

"cascadeControl": {
"input": {
Expand Down
9 changes: 9 additions & 0 deletions src_data/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"mMqttConnected": "Connessione MQTT",
"mEmergencyState": "Modo Emergenza",
"mExtPumpState": "Pompa esterna",
"mExtDevState": "Dispositivo esterno",
"mCascadeControlInput": "Controllo a cascata (input)",
"mCascadeControlOutput": "Controllo a cascata (output)",

Expand Down Expand Up @@ -289,6 +290,7 @@
"ot": "Impostazioni OpenTherm",
"mqtt": "Impostazioni MQTT",
"extPump": "Impostazioni pompa esterna",
"extDev": "Impostazioni dispositivo esterno",
"cascadeControl": "Impostazioni controllo a cascata"
},

Expand Down Expand Up @@ -459,6 +461,13 @@
"antiStuckTime": "Tempo antiblocco <small>(min)</small>"
},

"extDev": {
"use": "Usa dispositivo esterno",
"gpio": "GPIO relè",
"state": "Stato",
"caption": "Didascalia"
},

"cascadeControl": {
"input": {
"desc": "Può essere attivata la caldaia se un'altra ha fallito. Il controllo dell'altra caldaia cambia lo stato dell'ingresso del GPIO in caso di errore.",
Expand Down
9 changes: 9 additions & 0 deletions src_data/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"mMqttConnected": "Подключение к MQTT",
"mEmergencyState": "Аварийный режим",
"mExtPumpState": "Внешний насос",
"mExtDevState": "Внешнее устройство",
"mCascadeControlInput": "Каскадное управление (вход)",
"mCascadeControlOutput": "Каскадное управление (выход)",

Expand Down Expand Up @@ -289,6 +290,7 @@
"ot": "Настройки OpenTherm",
"mqtt": "Настройки MQTT",
"extPump": "Настройки дополнительного насоса",
"extDev": "Настройки внешнего устройства",
"cascadeControl": "Настройки каскадного управления"
},

Expand Down Expand Up @@ -459,6 +461,13 @@
"antiStuckTime": "Время работы насоса <small>(в минутах)</small>"
},

"extDev": {
"use": "Использовать внешнее устройство",
"gpio": "GPIO реле",
"state": "Состояние",
"caption": "Назначение"
},

"cascadeControl": {
"input": {
"desc": "Может использоваться для включения отопления только при неисправности другого котла. Контроллер другого котла должен изменить состояние входа GPIO в случае неисправности.",
Expand Down
5 changes: 5 additions & 0 deletions src_data/pages/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ <h2 data-i18n>dashboard.name</h2>
<th scope="row" data-i18n>dashboard.states.mExtPumpState</th>
<td><i class="mExtPumpState"></i></td>
</tr>
<tr>
<th scope="row" data-i18n>dashboard.states.mExtDevState</th>
<td><i class="mExtDevState"></i></td>
</tr>
<tr>
<th scope="row" data-i18n>dashboard.states.mCascadeControlInput</th>
<td><i class="mCascadeControlInput"></i></td>
Expand Down Expand Up @@ -658,6 +662,7 @@ <h2 data-i18n>dashboard.name</h2>
result.master.emergency.state ? "red" : "green"
);
setState('.mExtPumpState', result.master.externalPump.state);
setState('.mExtDevState', result.master.externalDev.state);
setState('.mCascadeControlInput', result.master.cascadeControl.input);
setState('.mCascadeControlOutput', result.master.cascadeControl.output);

Expand Down
Loading