mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
add mqtt subscribe settings, thermostat switchtime, boiler heatingsources
This commit is contained in:
@@ -173,6 +173,17 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||
<MenuItem value={3}>1/0</MenuItem>
|
||||
<MenuItem value={4}>"ON"/"OFF"</MenuItem>
|
||||
</SelectValidator>
|
||||
<SelectValidator name="subscribe_format"
|
||||
label="Subscribe Format"
|
||||
value={data.subscribes}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('subscribes')}
|
||||
margin="normal">
|
||||
<MenuItem value={0}>general device topic</MenuItem>
|
||||
<MenuItem value={1}>individual topics, main heating circuit</MenuItem>
|
||||
<MenuItem value={2}>individual topics, all heating circuits</MenuItem>
|
||||
</SelectValidator>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
|
||||
@@ -41,4 +41,5 @@ export interface MqttSettings {
|
||||
ha_enabled: boolean;
|
||||
ha_climate_format: number;
|
||||
nested_format: boolean;
|
||||
subscribes: number;
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
|
||||
root["ha_climate_format"] = settings.ha_climate_format;
|
||||
root["ha_enabled"] = settings.ha_enabled;
|
||||
root["nested_format"] = settings.nested_format;
|
||||
root["subscribes"] = settings.subscribes;
|
||||
}
|
||||
|
||||
StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & settings) {
|
||||
@@ -213,6 +214,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
|
||||
newSettings.ha_climate_format = root["ha_climate_format"] | EMSESP_DEFAULT_HA_CLIMATE_FORMAT;
|
||||
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
|
||||
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
|
||||
newSettings.subscribes = root["subscribes"] | 0;
|
||||
|
||||
if (newSettings.mqtt_qos != settings.mqtt_qos) {
|
||||
emsesp::EMSESP::mqtt_.set_qos(newSettings.mqtt_qos);
|
||||
@@ -228,6 +230,10 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.subscribes != settings.subscribes) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.ha_climate_format != settings.ha_climate_format) {
|
||||
emsesp::EMSESP::mqtt_.ha_climate_format(newSettings.ha_climate_format);
|
||||
changed = true;
|
||||
|
||||
@@ -104,6 +104,7 @@ class MqttSettings {
|
||||
uint8_t ha_climate_format;
|
||||
bool ha_enabled;
|
||||
bool nested_format;
|
||||
uint8_t subscribes;
|
||||
|
||||
static void read(MqttSettings & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, MqttSettings & settings);
|
||||
|
||||
@@ -85,7 +85,7 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
||||
}
|
||||
|
||||
// add a command to the list, which does not return json
|
||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb) {
|
||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||
return;
|
||||
@@ -95,7 +95,7 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cm
|
||||
|
||||
// see if we need to subscribe
|
||||
if (Mqtt::enabled()) {
|
||||
Mqtt::register_command(device_type, cmd, cb);
|
||||
Mqtt::register_command(device_type, cmd, cb, flag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class Command {
|
||||
|
||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json);
|
||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = 0);
|
||||
static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb);
|
||||
static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag = 0);
|
||||
static void add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb);
|
||||
static void show_all(uuid::console::Shell & shell);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
||||
|
||||
@@ -28,20 +28,20 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
LOG_DEBUG(F("Adding new Boiler with device ID 0x%02X"), device_id);
|
||||
|
||||
// cascaded heatingsources, add some values with new tags later
|
||||
// cascaded heatingsources, only some values per individual heatsource (hs)
|
||||
if (device_id != EMSdevice::EMS_DEVICE_ID_BOILER) {
|
||||
// uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source number
|
||||
// Runtime of each heatingsource in 6DC, ff
|
||||
// register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_CascadeMessage(t); });
|
||||
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0
|
||||
// Runtime of each heatingsource in 0x06DC, ff
|
||||
register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, MAKE_PF_CB(process_CascadeMessage));
|
||||
register_device_value(TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, nullptr, F("burnWorkMin"), F("total burner operating time"), DeviceValueUOM::MINUTES);
|
||||
// selBurnpower in D2 and E4
|
||||
// register_telegram_type(0xD2, F("CascadePowerMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_CascadePowerMessage(t); });
|
||||
// idividual Flowtemps and powervalues for each heatingsource in E4
|
||||
// register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorFastPlus(t); });
|
||||
|
||||
// register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES);
|
||||
// register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT);
|
||||
// register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES);
|
||||
// register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT);
|
||||
// register_telegram_type(0xD2, F("CascadePowerMessage"), false, MAKE_PF_CB(process_CascadePowerMessage));
|
||||
// individual Flowtemps and powervalues for each heatingsource in E4
|
||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
|
||||
register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT);
|
||||
return;
|
||||
}
|
||||
// register values for master boiler/cascade module
|
||||
@@ -172,12 +172,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, F("wWSetTemp"), F("set temperature"), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), F("wWType"), F("type"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), F("wWComfort"), F("comfort"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, F("wWFlowTempOffset"), F("flow offset temperature"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, F("wWFlowTempOffset"), F("flow temperature offset"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, F("wWMaxPower"), F("max power"), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, F("wWCircPump"), F("circulation pump available"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), F("wWChargeType"), F("charging type"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, F("wWDisinfectionTemp"), F("disinfection temperature"), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCircPumpMode_, DeviceValueType::ENUM, FL_(enum_freq), F("wWCircPumpMode"), F("circulation pump freq"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), F("wWCircMode"), F("circulation pump freq"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, F("wWCirc"), F("circulation active"));
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp"), F("current intern temperature"), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp2"), F("current extern temperature"), DeviceValueUOM::DEGREES);
|
||||
@@ -276,7 +276,7 @@ void Boiler::check_active(const bool force) {
|
||||
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on
|
||||
has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on
|
||||
has_update(telegram->read_value(wWCircPumpMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
|
||||
has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
|
||||
has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve
|
||||
has_update(telegram->read_value(wWSelTemp_, 2));
|
||||
has_update(telegram->read_value(wWDisinfectionTemp_, 8));
|
||||
@@ -505,7 +505,7 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr<const Telegram> telegram)
|
||||
void Boiler::process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on
|
||||
has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes
|
||||
has_update(telegram->read_value(wWCircPumpMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||
has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||
// has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9
|
||||
// has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9
|
||||
}
|
||||
@@ -600,19 +600,17 @@ void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(wWSetPumpPower_, 2)); // ww pump speed/power?
|
||||
}
|
||||
|
||||
// 0x6DC, ff for cascaded heatsources (hs)
|
||||
void Boiler::process_CascadeMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// uint8_t hsActivated;
|
||||
// has_update(telegram->read_value(hsActivated, 0));
|
||||
telegram->read_value(burnWorkMin_, 3); // this is in seconds
|
||||
burnWorkMin_ /= 60;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
// 0x6DC, ff for cascaded heatsources (hs)
|
||||
// not implemented yet, see #739
|
||||
void Boiler::process_CascadeMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t hs = telegram->dest - EMSdevice::EMS_DEVICE_ID_BOILER_1;
|
||||
// uint8_t hsActivated;
|
||||
// uint32_t hsRuntime;
|
||||
// has_update(telegram->read_value(hsActivated, 0));
|
||||
// has_update(telegram->read_value(hsRuntime, 3));
|
||||
}
|
||||
|
||||
// 0x35 - not yet implemented
|
||||
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class Boiler : public EMSdevice {
|
||||
uint8_t wWCircPump_; // Warm Water circulation pump available
|
||||
uint8_t wWChargeType_; // Warm Water charge type (pump or 3-way-valve)
|
||||
uint8_t wWDisinfectionTemp_; // Warm Water disinfection temperature to prevent infection
|
||||
uint8_t wWCircPumpMode_; // Warm Water circulation pump mode
|
||||
uint8_t wWCircMode_; // Warm Water circulation pump mode
|
||||
uint8_t wWCirc_; // Circulation on/off
|
||||
uint16_t wWCurTemp_; // Warm Water current temperature
|
||||
uint16_t wWCurTemp2_; // Warm Water current temperature storage
|
||||
|
||||
@@ -1394,6 +1394,7 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
|
||||
data[5] = (dt[6] - '0') * 10 + (dt[7] - '0'); // sec
|
||||
data[6] = (dt[20] - '0'); // day of week
|
||||
data[7] = (dt[22] - '0') + 2; // DST and flag
|
||||
LOG_INFO(F("Date and time: %02d.%02d.2%03d-%02d:%02d:%02d"),data[3], data[1], data[0], data[2], data[4], data[5]);
|
||||
}
|
||||
|
||||
LOG_INFO(F("Setting date and time"));
|
||||
@@ -1608,6 +1609,49 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sets a single switchtime in the thermostat program for RC35
|
||||
// format "01:0,1,15:30" Number, day, on, time
|
||||
bool Thermostat::set_switchtime(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
if (hc == nullptr) {
|
||||
LOG_WARNING(F("Setting switchtime: Heating Circuit %d not found or activated"), hc_num);
|
||||
return false;
|
||||
}
|
||||
if (strlen(value) != 12) {
|
||||
LOG_WARNING(F("Setting switchtime: Invalid data"));
|
||||
return false;
|
||||
}
|
||||
uint8_t no = (value[0] - '0') * 10 + (value[1] - '0');
|
||||
uint8_t day = value[3] - '0';
|
||||
uint8_t on = value[5] - '0';
|
||||
uint8_t time = 6 * ((value[7] - '0') * 10 + (value[8] - '0')) + (value[10] - '0');
|
||||
uint8_t data[2] = {0xE7, 0x90}; // unset switchtime
|
||||
|
||||
if (day != 7 && on != 7) {
|
||||
data[0] = (day << 5) + on;
|
||||
data[1] = time;
|
||||
}
|
||||
|
||||
if (no > 41 || day > 7 || (on > 1 && on != 7) || time > 0x90) {
|
||||
LOG_WARNING(F("Setting switchtime: Invalid data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1)) {
|
||||
write_command(timer_typeids[hc->hc_num() - 1], no * 2, (uint8_t *)&data, 2, timer_typeids[hc->hc_num() - 1]);
|
||||
} else {
|
||||
LOG_WARNING(F("Setting switchtime: thermostat not supported"));
|
||||
return false;
|
||||
}
|
||||
if (data[0] == 0xE7) {
|
||||
LOG_INFO(F("Setting switchtime no %d for heating circuit %d undefined"), no, hc->hc_num());
|
||||
} else {
|
||||
LOG_INFO(F("Setting switchtime no %d for heating circuit %d to day %d, %s, %02d:%d0"), no, hc->hc_num(), day, (on == 1) ? "on" : "off", time / 6, time % 6);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// sets the thermostat program for RC35 and RC20
|
||||
bool Thermostat::set_program(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
@@ -1986,33 +2030,33 @@ void Thermostat::add_commands() {
|
||||
}
|
||||
|
||||
// common to all thermostats
|
||||
register_mqtt_cmd(F("temp"), MAKE_CF_CB(set_temp));
|
||||
register_mqtt_cmd(F("mode"), MAKE_CF_CB(set_mode));
|
||||
register_mqtt_cmd(F("temp"), MAKE_CF_CB(set_temp), FLAG_HC);
|
||||
register_mqtt_cmd(F("mode"), MAKE_CF_CB(set_mode), FLAG_HC);
|
||||
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
|
||||
|
||||
switch (model()) {
|
||||
case EMS_DEVICE_FLAG_RC100:
|
||||
case EMS_DEVICE_FLAG_RC300:
|
||||
register_mqtt_cmd(F("manualtemp"), MAKE_CF_CB(set_manualtemp));
|
||||
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp));
|
||||
register_mqtt_cmd(F("comforttemp"), MAKE_CF_CB(set_comforttemp));
|
||||
register_mqtt_cmd(F("summermode"), MAKE_CF_CB(set_summermode));
|
||||
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp));
|
||||
register_mqtt_cmd(F("manualtemp"), MAKE_CF_CB(set_manualtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("comforttemp"), MAKE_CF_CB(set_comforttemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("summermode"), MAKE_CF_CB(set_summermode), FLAG_HC);
|
||||
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode));
|
||||
register_mqtt_cmd(F("wwsettemp"), MAKE_CF_CB(set_wwtemp));
|
||||
register_mqtt_cmd(F("wwsettemplow"), MAKE_CF_CB(set_wwtemplow));
|
||||
register_mqtt_cmd(F("wwonetime"), MAKE_CF_CB(set_wwonetime));
|
||||
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode));
|
||||
register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building));
|
||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp));
|
||||
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp));
|
||||
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp));
|
||||
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp));
|
||||
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp));
|
||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp));
|
||||
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence));
|
||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program));
|
||||
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode));
|
||||
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC);
|
||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC);
|
||||
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC);
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_RC20_2:
|
||||
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp));
|
||||
@@ -2025,35 +2069,36 @@ void Thermostat::add_commands() {
|
||||
register_mqtt_cmd(F("display"), MAKE_CF_CB(set_display));
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_RC35: // RC30 and RC35
|
||||
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp));
|
||||
register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp));
|
||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp));
|
||||
register_mqtt_cmd(F("remotetemp"), MAKE_CF_CB(set_remotetemp));
|
||||
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("remotetemp"), MAKE_CF_CB(set_remotetemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp));
|
||||
register_mqtt_cmd(F("calinttemp"), MAKE_CF_CB(set_calinttemp));
|
||||
register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building));
|
||||
register_mqtt_cmd(F("control"), MAKE_CF_CB(set_control));
|
||||
register_mqtt_cmd(F("pause"), MAKE_CF_CB(set_pause));
|
||||
register_mqtt_cmd(F("party"), MAKE_CF_CB(set_party));
|
||||
register_mqtt_cmd(F("holiday"), MAKE_CF_CB(set_holiday));
|
||||
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp));
|
||||
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp));
|
||||
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp));
|
||||
register_mqtt_cmd(F("holidaytemp"), MAKE_CF_CB(set_holidaytemp));
|
||||
register_mqtt_cmd(F("control"), MAKE_CF_CB(set_control), FLAG_HC);
|
||||
register_mqtt_cmd(F("pause"), MAKE_CF_CB(set_pause), FLAG_HC);
|
||||
register_mqtt_cmd(F("party"), MAKE_CF_CB(set_party), FLAG_HC);
|
||||
register_mqtt_cmd(F("holiday"), MAKE_CF_CB(set_holiday), FLAG_HC);
|
||||
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("holidaytemp"), MAKE_CF_CB(set_holidaytemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode));
|
||||
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode));
|
||||
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence));
|
||||
register_mqtt_cmd(F("flowtempoffset"), MAKE_CF_CB(set_flowtempoffset));
|
||||
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp));
|
||||
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp));
|
||||
register_mqtt_cmd(F("reducemode"), MAKE_CF_CB(set_reducemode));
|
||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program));
|
||||
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode));
|
||||
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC);
|
||||
register_mqtt_cmd(F("flowtempoffset"), MAKE_CF_CB(set_flowtempoffset), FLAG_HC);
|
||||
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("reducemode"), MAKE_CF_CB(set_reducemode), FLAG_HC);
|
||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC);
|
||||
register_mqtt_cmd(F("switchtime"), MAKE_CF_CB(set_switchtime), FLAG_HC);
|
||||
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC);
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_JUNKERS:
|
||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp));
|
||||
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp));
|
||||
register_mqtt_cmd(F("heattemp"), MAKE_CF_CB(set_heattemp));
|
||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC);
|
||||
register_mqtt_cmd(F("heattemp"), MAKE_CF_CB(set_heattemp), FLAG_HC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -315,6 +315,7 @@ class Thermostat : public EMSdevice {
|
||||
bool set_minflowtemp(const char * value, const int8_t id);
|
||||
bool set_maxflowtemp(const char * value, const int8_t id);
|
||||
bool set_reducemode(const char * value, const int8_t id);
|
||||
bool set_switchtime(const char * value, const int8_t id);
|
||||
bool set_program(const char * value, const int8_t id);
|
||||
bool set_controlmode(const char * value, const int8_t id);
|
||||
|
||||
|
||||
@@ -53,7 +53,23 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = {
|
||||
F_(tag_wwc1), // "wwc1"
|
||||
F_(tag_wwc2), // "Wwc2"
|
||||
F_(tag_wwc3), // "wwc3"
|
||||
F_(tag_wwc4) // "wwc4"
|
||||
F_(tag_wwc4), // "wwc4"
|
||||
F_(tag_hs1), // "hs1"
|
||||
F_(tag_hs2), // "hs2"
|
||||
F_(tag_hs3), // "hs3"
|
||||
F_(tag_hs4), // "hs4"
|
||||
F_(tag_hs5), // "hs5"
|
||||
F_(tag_hs6), // "hs6"
|
||||
F_(tag_hs7), // "hs7"
|
||||
F_(tag_hs8), // "hs8"
|
||||
F_(tag_hs9), // "hs9"
|
||||
F_(tag_hs10), // "hs10"
|
||||
F_(tag_hs11), // "hs11"
|
||||
F_(tag_hs12), // "hs12"
|
||||
F_(tag_hs13), // "hs13"
|
||||
F_(tag_hs14), // "hs14"
|
||||
F_(tag_hs15), // "hs15"
|
||||
F_(tag_hs16) // "hs16"
|
||||
|
||||
};
|
||||
|
||||
@@ -72,7 +88,23 @@ static const __FlashStringHelper * const DeviceValueTAG_mqtt[] PROGMEM = {
|
||||
F_(tag_wwc1), // "wwc1"
|
||||
F_(tag_wwc2), // "Wwc2"
|
||||
F_(tag_wwc3), // "wwc3"
|
||||
F_(tag_wwc4) // "wwc4"
|
||||
F_(tag_wwc4), // "wwc4"
|
||||
F_(tag_hs1), // "hs1"
|
||||
F_(tag_hs2), // "hs2"
|
||||
F_(tag_hs3), // "hs3"
|
||||
F_(tag_hs4), // "hs4"
|
||||
F_(tag_hs5), // "hs5"
|
||||
F_(tag_hs6), // "hs6"
|
||||
F_(tag_hs7), // "hs7"
|
||||
F_(tag_hs8), // "hs8"
|
||||
F_(tag_hs9), // "hs9"
|
||||
F_(tag_hs10), // "hs10"
|
||||
F_(tag_hs11), // "hs11"
|
||||
F_(tag_hs12), // "hs12"
|
||||
F_(tag_hs13), // "hs13"
|
||||
F_(tag_hs14), // "hs14"
|
||||
F_(tag_hs15), // "hs15"
|
||||
F_(tag_hs16) // "hs16"
|
||||
|
||||
};
|
||||
|
||||
@@ -373,8 +405,8 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_
|
||||
}
|
||||
|
||||
// add command to library
|
||||
void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f) {
|
||||
Command::add(device_type_, cmd, f);
|
||||
void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag) {
|
||||
Command::add(device_type_, cmd, f, flag);
|
||||
}
|
||||
|
||||
// register a call back function for a specific telegram type
|
||||
|
||||
@@ -83,6 +83,22 @@ MAKE_PSTR(tag_wwc1, "wwc1")
|
||||
MAKE_PSTR(tag_wwc2, "wwc2")
|
||||
MAKE_PSTR(tag_wwc3, "wwc3")
|
||||
MAKE_PSTR(tag_wwc4, "wwc4")
|
||||
MAKE_PSTR(tag_hs1, "hs1")
|
||||
MAKE_PSTR(tag_hs2, "hs2")
|
||||
MAKE_PSTR(tag_hs3, "hs3")
|
||||
MAKE_PSTR(tag_hs4, "hs4")
|
||||
MAKE_PSTR(tag_hs5, "hs5")
|
||||
MAKE_PSTR(tag_hs6, "hs6")
|
||||
MAKE_PSTR(tag_hs7, "hs7")
|
||||
MAKE_PSTR(tag_hs8, "hs8")
|
||||
MAKE_PSTR(tag_hs9, "hs9")
|
||||
MAKE_PSTR(tag_hs10, "hs10")
|
||||
MAKE_PSTR(tag_hs11, "hs11")
|
||||
MAKE_PSTR(tag_hs12, "hs12")
|
||||
MAKE_PSTR(tag_hs13, "hs13")
|
||||
MAKE_PSTR(tag_hs14, "hs14")
|
||||
MAKE_PSTR(tag_hs15, "hs15")
|
||||
MAKE_PSTR(tag_hs16, "hs16")
|
||||
|
||||
// MQTT topic names
|
||||
MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
|
||||
@@ -102,10 +118,33 @@ enum DeviceValueTAG : uint8_t {
|
||||
TAG_WWC1,
|
||||
TAG_WWC2,
|
||||
TAG_WWC3,
|
||||
TAG_WWC4
|
||||
TAG_WWC4,
|
||||
TAG_HS1,
|
||||
TAG_HS2,
|
||||
TAG_HS3,
|
||||
TAG_HS4,
|
||||
TAG_HS5,
|
||||
TAG_HS6,
|
||||
TAG_HS7,
|
||||
TAG_HS8,
|
||||
TAG_HS9,
|
||||
TAG_HS10,
|
||||
TAG_HS11,
|
||||
TAG_HS12,
|
||||
TAG_HS13,
|
||||
TAG_HS14,
|
||||
TAG_HS15,
|
||||
TAG_HS16
|
||||
|
||||
};
|
||||
|
||||
// mqtt flags for command subscriptions
|
||||
enum MqttSubFlag : uint8_t {
|
||||
FLAG_NONE = 0,
|
||||
FLAG_HC,
|
||||
FLAG_WWC
|
||||
};
|
||||
|
||||
class EMSdevice {
|
||||
public:
|
||||
virtual ~EMSdevice() = default; // destructor of base class must always be virtual because it's a polymorphic class
|
||||
@@ -242,7 +281,7 @@ class EMSdevice {
|
||||
void read_command(const uint16_t type_id);
|
||||
|
||||
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
|
||||
void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f);
|
||||
void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0);
|
||||
|
||||
void publish_mqtt_ha_sensor();
|
||||
|
||||
|
||||
49
src/mqtt.cpp
49
src/mqtt.cpp
@@ -40,6 +40,7 @@ uint8_t Mqtt::bool_format_;
|
||||
uint8_t Mqtt::ha_climate_format_;
|
||||
bool Mqtt::ha_enabled_;
|
||||
bool Mqtt::nested_format_;
|
||||
uint8_t Mqtt::subscribes_;
|
||||
|
||||
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
std::vector<Mqtt::MQTTSubFunction> Mqtt::mqtt_subfunctions_;
|
||||
@@ -83,12 +84,12 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
|
||||
}
|
||||
|
||||
// register in our libary with the callback function.
|
||||
// We store both the original topic and the fully-qualified one
|
||||
// We store the original topic without base
|
||||
mqtt_subfunctions_.emplace_back(device_type, std::move(topic), std::move(cb));
|
||||
}
|
||||
|
||||
// subscribe to the command topic if it doesn't exist yet
|
||||
void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb) {
|
||||
void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
|
||||
std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type); // thermostat, boiler, etc...
|
||||
|
||||
// see if we have already a handler for the device type (boiler, thermostat). If not add it
|
||||
@@ -106,12 +107,28 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper
|
||||
LOG_DEBUG(F("Registering MQTT cmd %s with topic %s"), uuid::read_flash_string(cmd).c_str(), EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
}
|
||||
|
||||
// only general device topics
|
||||
if (subscribes_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// register the individual commands too (e.g. ems-esp/boiler/wwonetime)
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/31
|
||||
if (device_type != EMSdevice::DeviceType::SYSTEM) {
|
||||
std::string topic(MQTT_TOPIC_MAX_SIZE, '\0');
|
||||
topic = cmd_topic + "/" + uuid::read_flash_string(cmd);
|
||||
Mqtt::subscribe(device_type, topic, nullptr);
|
||||
if (subscribes_ == 2 && flag == MqttSubFlag::FLAG_HC) {
|
||||
topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd);
|
||||
queue_subscribe_message(topic);
|
||||
topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd);
|
||||
queue_subscribe_message(topic);
|
||||
topic = cmd_topic + "/hc3/" + uuid::read_flash_string(cmd);
|
||||
queue_subscribe_message(topic);
|
||||
topic = cmd_topic + "/hc4/" + uuid::read_flash_string(cmd);
|
||||
queue_subscribe_message(topic);
|
||||
} else {
|
||||
topic = cmd_topic + "/" + uuid::read_flash_string(cmd);
|
||||
queue_subscribe_message(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +274,12 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
||||
char topic[100];
|
||||
strlcpy(topic, &fulltopic[1 + strlen(mqtt_base_.c_str())], 100);
|
||||
|
||||
// strip the topic substrings
|
||||
char * topic_end = strchr(topic,'/');
|
||||
if (topic_end != nullptr) {
|
||||
topic_end[0] = '\0';
|
||||
}
|
||||
|
||||
// convert payload to a null-terminated char string
|
||||
char message[len + 2];
|
||||
strlcpy(message, payload, len + 1);
|
||||
@@ -277,14 +300,22 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
||||
|
||||
// check if it's not json, then try and extract the command from the topic name
|
||||
if (message[0] != '{') {
|
||||
char * cmd_only = strrchr(topic, '/');
|
||||
// get topic with substrings again
|
||||
strlcpy(topic, &fulltopic[1 + strlen(mqtt_base_.c_str())], 100);
|
||||
char * cmd_only = strchr(topic, '/');
|
||||
if (cmd_only == NULL) {
|
||||
return; // invalid topic name
|
||||
}
|
||||
cmd_only++; // skip the /
|
||||
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s"), mf.device_type_, topic, cmd_only, message);
|
||||
if (!Command::call(mf.device_type_, cmd_only, message)) {
|
||||
LOG_ERROR(F("No matching cmd (%s) in topic %s, or invalid data"), cmd_only, topic);
|
||||
int8_t id = -1;
|
||||
// check for hcx/ prefix
|
||||
if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') {
|
||||
id = cmd_only[2] - '0';
|
||||
cmd_only += 4;
|
||||
}
|
||||
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s, id = %d"), mf.device_type_, topic, cmd_only, message, id);
|
||||
if (!Command::call(mf.device_type_, cmd_only, message, id)) {
|
||||
LOG_ERROR(F("No matching cmd (%s) in topic %s, id %d, or invalid data"), cmd_only, topic, id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -411,6 +442,7 @@ void Mqtt::load_settings() {
|
||||
dallas_format_ = mqttSettings.dallas_format;
|
||||
bool_format_ = mqttSettings.bool_format;
|
||||
nested_format_ = mqttSettings.nested_format;
|
||||
subscribes_ = mqttSettings.subscribes;
|
||||
|
||||
// convert to milliseconds
|
||||
publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000;
|
||||
@@ -573,7 +605,6 @@ void Mqtt::on_connect() {
|
||||
EMSESP::shower_.send_mqtt_stat(false); // Send shower_activated as false
|
||||
EMSESP::system_.send_heartbeat(); // send heatbeat
|
||||
|
||||
// } else {
|
||||
if (connectcount_ > 1) {
|
||||
// we doing a re-connect from a TCP break
|
||||
// only re-subscribe again to all MQTT topics
|
||||
|
||||
@@ -107,7 +107,7 @@ class Mqtt {
|
||||
static void publish_ha(const __FlashStringHelper * topic, const JsonObject & payload);
|
||||
|
||||
static void publish_mqtt_ha_sensor(uint8_t type, uint8_t tag, const __FlashStringHelper * name, const uint8_t device_type, const __FlashStringHelper * entity, const uint8_t uom = 0);
|
||||
static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb);
|
||||
static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t tag = 0);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
||||
static void show_mqtt(uuid::console::Shell & shell);
|
||||
@@ -275,6 +275,7 @@ class Mqtt {
|
||||
static uint8_t ha_climate_format_;
|
||||
static bool ha_enabled_;
|
||||
static bool nested_format_;
|
||||
static uint8_t subscribes_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
Reference in New Issue
Block a user