diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index 153c38db5..818c3954f 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -150,15 +150,75 @@ class MqttSettingsForm extends React.Component { 2 + + + + + diff --git a/interface/src/mqtt/types.ts b/interface/src/mqtt/types.ts index 7f9bf44c8..7db88dbfd 100644 --- a/interface/src/mqtt/types.ts +++ b/interface/src/mqtt/types.ts @@ -27,7 +27,12 @@ export interface MqttSettings { keep_alive: number; clean_session: boolean; max_topic_length: number; - publish_time: number; + publish_time_boiler: number; + publish_time_thermostat: number; + publish_time_solar: number; + publish_time_mixing: number; + publish_time_other: number; + publish_time_sensor: number; mqtt_format: number; mqtt_qos: number; system_heartbeat: boolean; diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index d75527c56..15b10340f 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -184,10 +184,15 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) { root["max_topic_length"] = settings.maxTopicLength; // added by proddy for EMS-ESP - root["system_heartbeat"] = settings.system_heartbeat; - root["publish_time"] = settings.publish_time; - root["mqtt_format"] = settings.mqtt_format; - root["mqtt_qos"] = settings.mqtt_qos; + root["system_heartbeat"] = settings.system_heartbeat; + root["publish_time_boiler"] = settings.publish_time_boiler; + root["publish_time_thermostat"] = settings.publish_time_thermostat; + root["publish_time_solar"] = settings.publish_time_solar; + root["publish_time_mixing"] = settings.publish_time_mixing; + root["publish_time_other"] = settings.publish_time_other; + root["publish_time_sensor"] = settings.publish_time_sensor; + root["mqtt_format"] = settings.mqtt_format; + root["mqtt_qos"] = settings.mqtt_qos; } StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & settings) { @@ -203,10 +208,15 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting newSettings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION; newSettings.maxTopicLength = root["max_topic_length"] | FACTORY_MQTT_MAX_TOPIC_LENGTH; - newSettings.system_heartbeat = root["system_heartbeat"] | EMSESP_DEFAULT_SYSTEM_HEARTBEAT; - newSettings.publish_time = root["publish_time"] | EMSESP_DEFAULT_PUBLISH_TIME; - newSettings.mqtt_format = root["mqtt_format"] | EMSESP_DEFAULT_MQTT_FORMAT; - newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS; + newSettings.system_heartbeat = root["system_heartbeat"] | EMSESP_DEFAULT_SYSTEM_HEARTBEAT; + newSettings.publish_time_boiler = root["publish_time_boiler"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.publish_time_thermostat = root["publish_time_thermostat"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.publish_time_solar = root["publish_time_solar"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.publish_time_mixing = root["publish_time_mixing"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME; + newSettings.mqtt_format = root["mqtt_format"] | EMSESP_DEFAULT_MQTT_FORMAT; + newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS; if (newSettings.system_heartbeat != settings.system_heartbeat) { emsesp::EMSESP::system_.set_heartbeat(newSettings.system_heartbeat); @@ -216,8 +226,23 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting emsesp::EMSESP::mqtt_.set_qos(newSettings.mqtt_qos); } - if (newSettings.publish_time != settings.publish_time) { - emsesp::EMSESP::mqtt_.set_publish_time(newSettings.publish_time); + if (newSettings.publish_time_boiler != settings.publish_time_boiler) { + emsesp::EMSESP::mqtt_.set_publish_time_boiler(newSettings.publish_time_boiler); + } + if (newSettings.publish_time_thermostat != settings.publish_time_thermostat) { + emsesp::EMSESP::mqtt_.set_publish_time_thermostat(newSettings.publish_time_thermostat); + } + if (newSettings.publish_time_solar != settings.publish_time_solar) { + emsesp::EMSESP::mqtt_.set_publish_time_solar(newSettings.publish_time_solar); + } + if (newSettings.publish_time_mixing != settings.publish_time_mixing) { + emsesp::EMSESP::mqtt_.set_publish_time_mixing(newSettings.publish_time_mixing); + } + if (newSettings.publish_time_other != settings.publish_time_other) { + emsesp::EMSESP::mqtt_.set_publish_time_other(newSettings.publish_time_other); + } + if (newSettings.publish_time_sensor != settings.publish_time_sensor) { + emsesp::EMSESP::mqtt_.set_publish_time_sensor(newSettings.publish_time_sensor); } emsesp::EMSESP::mqtt_.reset_publish_fails(); // reset fail counter back to 0 diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index aedc78054..573a13b6c 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -86,7 +86,12 @@ class MqttSettings { uint16_t maxTopicLength; // proddy EMS-ESP specific - uint16_t publish_time; // seconds + uint16_t publish_time_boiler; + uint16_t publish_time_thermostat; + uint16_t publish_time_solar; + uint16_t publish_time_mixing; + uint16_t publish_time_other; + uint16_t publish_time_sensor; uint8_t mqtt_format; // 1=single, 2=nested, 3=ha, 4=custom uint8_t mqtt_qos; bool system_heartbeat; diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 6b065bb93..5f75e7ed8 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -318,6 +318,10 @@ void Boiler::publish_values() { // called after a process command is called, to check values and see if we need to force an MQTT publish bool Boiler::updated_values() { + if (changed_) { + changed_ = false; + return true; + } return false; } @@ -468,40 +472,40 @@ void Boiler::check_active() { // 0x33 void Boiler::process_UBAParameterWW(std::shared_ptr telegram) { - telegram->read_value(wWActivated_, 1); // 0xFF means on - telegram->read_value(wWCircPump_, 6); // 0xFF means on - telegram->read_value(wWCircPumpMode_, 7); // 1=1x3min... 6=6x3min, 7=continuous - telegram->read_value(wWCircPumpType_, 10); // 0 = charge pump, 0xff = 3-way valve - telegram->read_value(wWSelTemp_, 2); - telegram->read_value(wWDisinfectTemp_, 8); - telegram->read_value(wWComfort_, 9); + changed_ |= telegram->read_value(wWActivated_, 1); // 0xFF means on + changed_ |= telegram->read_value(wWCircPump_, 6); // 0xFF means on + changed_ |= telegram->read_value(wWCircPumpMode_, 7); // 1=1x3min... 6=6x3min, 7=continuous + changed_ |= telegram->read_value(wWCircPumpType_, 10); // 0 = charge pump, 0xff = 3-way valve + changed_ |= telegram->read_value(wWSelTemp_, 2); + changed_ |= telegram->read_value(wWDisinfectTemp_, 8); + changed_ |= telegram->read_value(wWComfort_, 9); } // 0x18 void Boiler::process_UBAMonitorFast(std::shared_ptr telegram) { - telegram->read_value(selFlowTemp_, 0); - telegram->read_value(curFlowTemp_, 1); - telegram->read_value(selBurnPow_, 3); // burn power max setting - telegram->read_value(curBurnPow_, 4); + changed_ |= telegram->read_value(selFlowTemp_, 0); + changed_ |= telegram->read_value(curFlowTemp_, 1); + changed_ |= telegram->read_value(selBurnPow_, 3); // burn power max setting + changed_ |= telegram->read_value(curBurnPow_, 4); - telegram->read_bitvalue(burnGas_, 7, 0); - telegram->read_bitvalue(fanWork_, 7, 2); - telegram->read_bitvalue(ignWork_, 7, 3); - telegram->read_bitvalue(heatPmp_, 7, 5); - telegram->read_bitvalue(wWHeat_, 7, 6); - telegram->read_bitvalue(wWCirc_, 7, 7); + changed_ |= telegram->read_bitvalue(burnGas_, 7, 0); + changed_ |= telegram->read_bitvalue(fanWork_, 7, 2); + changed_ |= telegram->read_bitvalue(ignWork_, 7, 3); + changed_ |= telegram->read_bitvalue(heatPmp_, 7, 5); + changed_ |= telegram->read_bitvalue(wWHeat_, 7, 6); + changed_ |= telegram->read_bitvalue(wWCirc_, 7, 7); // warm water storage sensors (if present) // wwStorageTemp2 is also used by some brands as the boiler temperature - see https://github.com/proddy/EMS-ESP/issues/206 - telegram->read_value(wwStorageTemp1_, 9); // 0x8300 if not available - telegram->read_value(wwStorageTemp2_, 11); // 0x8000 if not available - this is boiler temp + changed_ |= telegram->read_value(wwStorageTemp1_, 9); // 0x8300 if not available + changed_ |= telegram->read_value(wwStorageTemp2_, 11); // 0x8000 if not available - this is boiler temp - telegram->read_value(retTemp_, 13); - telegram->read_value(flameCurr_, 15); - telegram->read_value(serviceCode_, 20); + changed_ |= telegram->read_value(retTemp_, 13); + changed_ |= telegram->read_value(flameCurr_, 15); + changed_ |= telegram->read_value(serviceCode_, 20); // system pressure. FF means missing - telegram->read_value(sysPress_, 17); // is *10 + changed_ |= telegram->read_value(sysPress_, 17); // is *10 // read the service code / installation status as appears on the display if ((telegram->message_length > 18) && (telegram->offset == 0)) { @@ -519,22 +523,22 @@ void Boiler::process_UBAMonitorFast(std::shared_ptr telegram) { * received only after requested (not broadcasted) */ void Boiler::process_UBATotalUptime(std::shared_ptr telegram) { - telegram->read_value(UBAuptime_, 0, 3); // force to 3 bytes + changed_ |= telegram->read_value(UBAuptime_, 0, 3); // force to 3 bytes } /* * UBAParameters - type 0x16 */ void Boiler::process_UBAParameters(std::shared_ptr telegram) { - telegram->read_value(heating_temp_, 1); - telegram->read_value(burnPowermax_, 2); - telegram->read_value(burnPowermin_, 3); - telegram->read_value(boilTemp_off_, 4); - telegram->read_value(boilTemp_on_, 5); - telegram->read_value(burnPeriod_, 6); - telegram->read_value(pumpDelay_, 8); - telegram->read_value(pump_mod_max_, 9); - telegram->read_value(pump_mod_min_, 10); + changed_ |= telegram->read_value(heating_temp_, 1); + changed_ |= telegram->read_value(burnPowermax_, 2); + changed_ |= telegram->read_value(burnPowermin_, 3); + changed_ |= telegram->read_value(boilTemp_off_, 4); + changed_ |= telegram->read_value(boilTemp_on_, 5); + changed_ |= telegram->read_value(burnPeriod_, 6); + changed_ |= telegram->read_value(pumpDelay_, 8); + changed_ |= telegram->read_value(pump_mod_max_, 9); + changed_ |= telegram->read_value(pump_mod_min_, 10); } /* @@ -542,19 +546,19 @@ void Boiler::process_UBAParameters(std::shared_ptr telegram) { * received every 10 seconds */ void Boiler::process_UBAMonitorWW(std::shared_ptr telegram) { - telegram->read_value(wWSetTmp_, 0); - telegram->read_value(wWCurTmp_, 1); - telegram->read_value(wWCurTmp2_, 3); - telegram->read_value(wWCurFlow_, 9); + changed_ |= telegram->read_value(wWSetTmp_, 0); + changed_ |= telegram->read_value(wWCurTmp_, 1); + changed_ |= telegram->read_value(wWCurTmp2_, 3); + changed_ |= telegram->read_value(wWCurFlow_, 9); - telegram->read_value(wWWorkM_, 10, 3); // force to 3 bytes - telegram->read_value(wWStarts_, 13, 3); // force to 3 bytes + changed_ |= telegram->read_value(wWWorkM_, 10, 3); // force to 3 bytes + changed_ |= telegram->read_value(wWStarts_, 13, 3); // force to 3 bytes - telegram->read_bitvalue(wWOneTime_, 5, 1); - telegram->read_bitvalue(wWDisinfecting_, 5, 2); - telegram->read_bitvalue(wWReadiness_, 5, 3); - telegram->read_bitvalue(wWRecharging_, 5, 4); - telegram->read_bitvalue(wWTemperatureOK_, 5, 5); + changed_ |= telegram->read_bitvalue(wWOneTime_, 5, 1); + changed_ |= telegram->read_bitvalue(wWDisinfecting_, 5, 2); + changed_ |= telegram->read_bitvalue(wWReadiness_, 5, 3); + changed_ |= telegram->read_bitvalue(wWRecharging_, 5, 4); + changed_ |= telegram->read_bitvalue(wWTemperatureOK_, 5, 5); } /* @@ -562,13 +566,13 @@ void Boiler::process_UBAMonitorWW(std::shared_ptr telegram) { * Still to figure out are: serviceCode, retTemp, sysPress */ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr telegram) { - telegram->read_value(selFlowTemp_, 6); - telegram->read_bitvalue(burnGas_, 11, 0); - telegram->read_bitvalue(wWHeat_, 11, 2); - telegram->read_value(curBurnPow_, 10); - telegram->read_value(selBurnPow_, 9); - telegram->read_value(curFlowTemp_, 7); - telegram->read_value(flameCurr_, 19); + changed_ |= telegram->read_value(selFlowTemp_, 6); + changed_ |= telegram->read_bitvalue(burnGas_, 11, 0); + changed_ |= telegram->read_bitvalue(wWHeat_, 11, 2); + changed_ |= telegram->read_value(curBurnPow_, 10); + changed_ |= telegram->read_value(selBurnPow_, 9); + changed_ |= telegram->read_value(curFlowTemp_, 7); + changed_ |= telegram->read_value(flameCurr_, 19); // read the service code / installation status as appears on the display if ((telegram->message_length > 4) && (telegram->offset == 0)) { @@ -589,79 +593,79 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr telegram * 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 17 19 20 21 22 23 24 */ void Boiler::process_UBAMonitorSlow(std::shared_ptr telegram) { - telegram->read_value(extTemp_, 0); - telegram->read_value(boilTemp_, 2); - telegram->read_value(exhaustTemp_, 4); - telegram->read_value(switchTemp_, 25); // only if there is a mixing module present - telegram->read_value(pumpMod_, 9); - telegram->read_value(burnStarts_, 10, 3); // force to 3 bytes - telegram->read_value(burnWorkMin_, 13, 3); // force to 3 bytes - telegram->read_value(heatWorkMin_, 19, 3); // force to 3 bytes + changed_ |= telegram->read_value(extTemp_, 0); + changed_ |= telegram->read_value(boilTemp_, 2); + changed_ |= telegram->read_value(exhaustTemp_, 4); + changed_ |= telegram->read_value(switchTemp_, 25); // only if there is a mixing module present + changed_ |= telegram->read_value(pumpMod_, 9); + changed_ |= telegram->read_value(burnStarts_, 10, 3); // force to 3 bytes + changed_ |= telegram->read_value(burnWorkMin_, 13, 3); // force to 3 bytes + changed_ |= telegram->read_value(heatWorkMin_, 19, 3); // force to 3 bytes } /* * UBAMonitorSlowPlus2 - type 0xE3 */ void Boiler::process_UBAMonitorSlowPlus2(std::shared_ptr telegram) { - telegram->read_value(pumpMod2_, 13); + changed_ |= telegram->read_value(pumpMod2_, 13); } /* * UBAMonitorSlowPlus - type 0xE5 - central heating monitor EMS+ */ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr telegram) { - telegram->read_bitvalue(fanWork_, 2, 2); - telegram->read_bitvalue(ignWork_, 2, 3); - telegram->read_bitvalue(heatPmp_, 2, 5); - telegram->read_bitvalue(wWCirc_, 2, 7); - telegram->read_value(burnStarts_, 10, 3); // force to 3 bytes - telegram->read_value(burnWorkMin_, 13, 3); // force to 3 bytes - telegram->read_value(heatWorkMin_, 19, 3); // force to 3 bytes - telegram->read_value(pumpMod_, 25); + changed_ |= telegram->read_bitvalue(fanWork_, 2, 2); + changed_ |= telegram->read_bitvalue(ignWork_, 2, 3); + changed_ |= telegram->read_bitvalue(heatPmp_, 2, 5); + changed_ |= telegram->read_bitvalue(wWCirc_, 2, 7); + changed_ |= telegram->read_value(burnStarts_, 10, 3); // force to 3 bytes + changed_ |= telegram->read_value(burnWorkMin_, 13, 3); // force to 3 bytes + changed_ |= telegram->read_value(heatWorkMin_, 19, 3); // force to 3 bytes + changed_ |= telegram->read_value(pumpMod_, 25); } // 0xE9 - DHW Status // e.g. 08 00 E9 00 37 01 F6 01 ED 00 00 00 00 41 3C 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 (CRC=77) #data=27 void Boiler::process_UBADHWStatus(std::shared_ptr telegram) { - telegram->read_value(wWSetTmp_, 0); - telegram->read_value(wWCurTmp_, 1); - telegram->read_value(wWCurTmp2_, 3); + changed_ |= telegram->read_value(wWSetTmp_, 0); + changed_ |= telegram->read_value(wWCurTmp_, 1); + changed_ |= telegram->read_value(wWCurTmp2_, 3); - telegram->read_value(wWWorkM_, 17, 3); // force to 3 bytes - telegram->read_value(wWStarts_, 14, 3); // force to 3 bytes + changed_ |= telegram->read_value(wWWorkM_, 17, 3); // force to 3 bytes + changed_ |= telegram->read_value(wWStarts_, 14, 3); // force to 3 bytes - telegram->read_bitvalue(wWOneTime_, 12, 2); - telegram->read_bitvalue(wWDisinfecting_, 12, 3); - telegram->read_bitvalue(wWReadiness_, 12, 4); - telegram->read_bitvalue(wWRecharging_, 13, 4); - telegram->read_bitvalue(wWTemperatureOK_, 13, 5); - telegram->read_bitvalue(wWCircPump_, 13, 2); + changed_ |= telegram->read_bitvalue(wWOneTime_, 12, 2); + changed_ |= telegram->read_bitvalue(wWDisinfecting_, 12, 3); + changed_ |= telegram->read_bitvalue(wWReadiness_, 12, 4); + changed_ |= telegram->read_bitvalue(wWRecharging_, 13, 4); + changed_ |= telegram->read_bitvalue(wWTemperatureOK_, 13, 5); + changed_ |= telegram->read_bitvalue(wWCircPump_, 13, 2); - telegram->read_value(wWActivated_, 20); - telegram->read_value(wWSelTemp_, 10); - telegram->read_value(wWDisinfectTemp_, 9); + changed_ |= telegram->read_value(wWActivated_, 20); + changed_ |= telegram->read_value(wWSelTemp_, 10); + changed_ |= telegram->read_value(wWDisinfectTemp_, 9); } // 0x2A - MC10Status // e.g. 88 00 2A 00 00 00 00 00 00 00 00 00 D2 00 00 80 00 00 01 08 80 00 02 47 00 // see https://github.com/proddy/EMS-ESP/issues/397 void Boiler::process_MC10Status(std::shared_ptr telegram) { - telegram->read_value(wwMixTemperature_, 14); - telegram->read_value(wwBufferBoilerTemperature_, 18); + changed_ |= telegram->read_value(wwMixTemperature_, 14); + changed_ |= telegram->read_value(wwBufferBoilerTemperature_, 18); } /* * UBAOutdoorTemp - type 0xD1 - external temperature EMS+ */ void Boiler::process_UBAOutdoorTemp(std::shared_ptr telegram) { - telegram->read_value(extTemp_, 0); + changed_ |= telegram->read_value(extTemp_, 0); } // UBASetPoint 0x1A void Boiler::process_UBASetPoints(std::shared_ptr telegram) { - telegram->read_value(setFlowTemp_, 0); // boiler set temp from thermostat - telegram->read_value(setBurnPow_, 1); // max output power in % - telegram->read_value(setWWPumpPow_, 2); // ww pump speed/power? + changed_ |= telegram->read_value(setFlowTemp_, 0); // boiler set temp from thermostat + changed_ |= telegram->read_value(setBurnPow_, 1); // max output power in % + changed_ |= telegram->read_value(setWWPumpPow_, 2); // ww pump speed/power? } #pragma GCC diagnostic push diff --git a/src/devices/boiler.h b/src/devices/boiler.h index e72ee6ca5..578c1789a 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -53,6 +53,7 @@ class Boiler : public EMSdevice { uint8_t last_boilerState = 0xFF; // remember last state of heating and warm water on/off uint8_t mqtt_format_; // single, nested or ha + bool changed_ = false;; static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33; static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D; diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp index 9b2a874a6..f0ead6797 100644 --- a/src/devices/mixing.cpp +++ b/src/devices/mixing.cpp @@ -82,6 +82,10 @@ void Mixing::device_info(JsonArray & root) { // check to see if values have been updated bool Mixing::updated_values() { + if (changed_) { + changed_ = false; + return true; + } return false; } @@ -165,10 +169,10 @@ void Mixing::publish_values() { void Mixing::process_MMPLUSStatusMessage_HC(std::shared_ptr telegram) { type_ = Type::HC; hc_ = telegram->type_id - 0x02D7 + 1; // determine which circuit this is - telegram->read_value(flowTemp_, 3); // is * 10 - telegram->read_value(flowSetTemp_, 5); - telegram->read_value(pumpMod_, 2); - telegram->read_value(status_, 1); // valve status + changed_ |= telegram->read_value(flowTemp_, 3); // is * 10 + changed_ |= telegram->read_value(flowSetTemp_, 5); + changed_ |= telegram->read_value(pumpMod_, 2); + changed_ |= telegram->read_value(status_, 1); // valve status } // Mixing module warm water loading/DHW - 0x0331, 0x0332 @@ -177,9 +181,9 @@ void Mixing::process_MMPLUSStatusMessage_HC(std::shared_ptr tele void Mixing::process_MMPLUSStatusMessage_WWC(std::shared_ptr telegram) { type_ = Type::WWC; hc_ = telegram->type_id - 0x0331 + 1; // determine which circuit this is. There are max 2. - telegram->read_value(flowTemp_, 0); // is * 10 - telegram->read_value(pumpMod_, 2); - telegram->read_value(status_, 11); // temp status + changed_ |= telegram->read_value(flowTemp_, 0); // is * 10 + changed_ |= telegram->read_value(pumpMod_, 2); + changed_ |= telegram->read_value(status_, 11); // temp status } // Mixing IMP - 0x010C @@ -189,17 +193,17 @@ void Mixing::process_IPMStatusMessage(std::shared_ptr telegram) type_ = Type::HC; hc_ = get_device_id() - 0x20 + 1; uint8_t ismixed = 0; - telegram->read_value(ismixed, 0); // check if circuit is active, 0-off, 1-unmixed, 2-mixed + changed_ |= telegram->read_value(ismixed, 0); // check if circuit is active, 0-off, 1-unmixed, 2-mixed if (ismixed == 0) { return; } if (ismixed == 2) { // we have a mixed circuit - telegram->read_value(flowTemp_, 3); // is * 10 - telegram->read_value(flowSetTemp_, 5); - telegram->read_value(status_, 2); // valve status + changed_ |= telegram->read_value(flowTemp_, 3); // is * 10 + changed_ |= telegram->read_value(flowSetTemp_, 5); + changed_ |= telegram->read_value(status_, 2); // valve status } uint8_t pump = 0xFF; - telegram->read_bitvalue(pump, 1, 0); // pump is also in unmixed circuits + changed_ |= telegram->read_bitvalue(pump, 1, 0); // pump is also in unmixed circuits if (pump != 0xFF) { pumpMod_ = 100 * pump; } @@ -215,9 +219,9 @@ void Mixing::process_MMStatusMessage(std::shared_ptr telegram) { // 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module // see https://github.com/proddy/EMS-ESP/issues/270 and https://github.com/proddy/EMS-ESP/issues/386#issuecomment-629610918 hc_ = get_device_id() - 0x20 + 1; - telegram->read_value(flowTemp_, 1); // is * 10 - telegram->read_value(pumpMod_, 3); - telegram->read_value(flowSetTemp_, 0); + changed_ |= telegram->read_value(flowTemp_, 1); // is * 10 + changed_ |= telegram->read_value(pumpMod_, 3); + changed_ |= telegram->read_value(flowSetTemp_, 0); } #pragma GCC diagnostic push diff --git a/src/devices/mixing.h b/src/devices/mixing.h index d1c6b09b9..b530ba6c2 100644 --- a/src/devices/mixing.h +++ b/src/devices/mixing.h @@ -66,6 +66,7 @@ class Mixing : public EMSdevice { uint8_t status_ = EMS_VALUE_UINT_NOTSET; uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET; Type type_ = Type::NONE; + bool changed_ = false; }; } // namespace emsesp diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index c6527e485..312b7f3cc 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -168,11 +168,18 @@ void Solar::publish_values() { doc["energyTotal"] = (float)energyTotal_ / 10; } - Mqtt::publish("sm_data", doc); + // if we have data, publish it + if (!doc.isNull()) { + Mqtt::publish("sm_data", doc); + } } // check to see if values have been updated bool Solar::updated_values() { + if (changed_) { + changed_ = false; + return true; + } return false; } @@ -182,11 +189,11 @@ void Solar::console_commands() { // SM10Monitor - type 0x97 void Solar::process_SM10Monitor(std::shared_ptr telegram) { - telegram->read_value(collectorTemp_, 2); // collector temp from SM10, is *10 - telegram->read_value(tankBottomTemp_, 5); // bottom temp from SM10, is *10 - telegram->read_value(solarPumpModulation_, 4); // modulation solar pump - telegram->read_bitvalue(solarPump_, 7, 1); - telegram->read_value(pumpWorkMin_, 8); + changed_ |= telegram->read_value(collectorTemp_, 2); // collector temp from SM10, is *10 + changed_ |= telegram->read_value(tankBottomTemp_, 5); // bottom temp from SM10, is *10 + changed_ |= telegram->read_value(solarPumpModulation_, 4); // modulation solar pump + changed_ |= telegram->read_bitvalue(solarPump_, 7, 1); + changed_ |= telegram->read_value(pumpWorkMin_, 8); } /* @@ -201,10 +208,10 @@ void Solar::process_SM10Monitor(std::shared_ptr telegram) { * bytes 20+21 = TS6 Temperature sensor external heat exchanger */ void Solar::process_SM100Monitor(std::shared_ptr telegram) { - telegram->read_value(collectorTemp_, 0); // is *10 - TS1: Temperature sensor for collector array 1 - telegram->read_value(tankBottomTemp_, 2); // is *10 - TS2: Temperature sensor 1 cylinder, bottom - telegram->read_value(tankBottomTemp2_, 16); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool - telegram->read_value(heatExchangerTemp_, 20); // is *10 - TS6: Heat exchanger temperature sensor + changed_ |= telegram->read_value(collectorTemp_, 0); // is *10 - TS1: Temperature sensor for collector array 1 + changed_ |= telegram->read_value(tankBottomTemp_, 2); // is *10 - TS2: Temperature sensor 1 cylinder, bottom + changed_ |= telegram->read_value(tankBottomTemp2_, 16); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool + changed_ |= telegram->read_value(heatExchangerTemp_, 20); // is *10 - TS6: Heat exchanger temperature sensor } #pragma GCC diagnostic push @@ -221,9 +228,9 @@ void Solar::process_SM100Monitor2(std::shared_ptr telegram) { // SM100Config - 0x0366 // e.g. B0 00 FF 00 02 66 01 62 00 13 40 14 void Solar::process_SM100Config(std::shared_ptr telegram) { - telegram->read_value(availabilityFlag_, 0); - telegram->read_value(configFlag_, 1); - telegram->read_value(userFlag_, 2); + changed_ |= telegram->read_value(availabilityFlag_, 0); + changed_ |= telegram->read_value(configFlag_, 1); + changed_ |= telegram->read_value(userFlag_, 2); } /* @@ -235,8 +242,8 @@ void Solar::process_SM100Config(std::shared_ptr telegram) { void Solar::process_SM100Status(std::shared_ptr telegram) { uint8_t solarpumpmod = solarPumpModulation_; uint8_t cylinderpumpmod = cylinderPumpModulation_; - telegram->read_value(cylinderPumpModulation_, 8); - telegram->read_value(solarPumpModulation_, 9); + changed_ |= telegram->read_value(cylinderPumpModulation_, 8); + changed_ |= telegram->read_value(solarPumpModulation_, 9); if (solarpumpmod == 0 && solarPumpModulation_ == 100) { // mask out boosts solarPumpModulation_ = 15; // set to minimum @@ -245,8 +252,8 @@ void Solar::process_SM100Status(std::shared_ptr telegram) { if (cylinderpumpmod == 0 && cylinderPumpModulation_ == 100) { // mask out boosts cylinderPumpModulation_ = 15; // set to minimum } - telegram->read_bitvalue(tankHeated_, 3, 1); // issue #422 - telegram->read_bitvalue(collectorShutdown_, 3, 0); // collector shutdown + changed_ |= telegram->read_bitvalue(tankHeated_, 3, 1); // issue #422 + changed_ |= telegram->read_bitvalue(collectorShutdown_, 3, 0); // collector shutdown } /* @@ -256,8 +263,8 @@ void Solar::process_SM100Status(std::shared_ptr telegram) { * byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3) */ void Solar::process_SM100Status2(std::shared_ptr telegram) { - telegram->read_bitvalue(valveStatus_, 4, 2); // on if bit 2 set - telegram->read_bitvalue(solarPump_, 10, 2); // on if bit 2 set + changed_ |= telegram->read_bitvalue(valveStatus_, 4, 2); // on if bit 2 set + changed_ |= telegram->read_bitvalue(solarPump_, 10, 2); // on if bit 2 set } /* @@ -265,9 +272,9 @@ void Solar::process_SM100Status2(std::shared_ptr telegram) { * e.g. 30 00 FF 00 02 8E 00 00 00 00 00 00 06 C5 00 00 76 35 */ void Solar::process_SM100Energy(std::shared_ptr telegram) { - telegram->read_value(energyLastHour_, 0); // last hour / 10 in Wh - telegram->read_value(energyToday_, 4); // todays in Wh - telegram->read_value(energyTotal_, 8); // total / 10 in kWh + changed_ |= telegram->read_value(energyLastHour_, 0); // last hour / 10 in Wh + changed_ |= telegram->read_value(energyToday_, 4); // todays in Wh + changed_ |= telegram->read_value(energyTotal_, 8); // total / 10 in kWh } /* @@ -275,26 +282,26 @@ void Solar::process_SM100Energy(std::shared_ptr telegram) { * e.g. B0 00 FF 00 00 03 32 00 00 00 00 13 00 D6 00 00 00 FB D0 F0 */ void Solar::process_ISM1StatusMessage(std::shared_ptr telegram) { - telegram->read_value(collectorTemp_, 4); // Collector Temperature - telegram->read_value(tankBottomTemp_, 6); // Temperature Bottom of Solar Boiler + changed_ |= telegram->read_value(collectorTemp_, 4); // Collector Temperature + changed_ |= telegram->read_value(tankBottomTemp_, 6); // Temperature Bottom of Solar Boiler uint16_t Wh = 0xFFFF; - telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10 + changed_ |= telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10 if (Wh != 0xFFFF) { energyLastHour_ = Wh * 10; // set to *10 } - telegram->read_bitvalue(solarPump_, 8, 0); // PS1 Solar pump on (1) or off (0) - telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes - telegram->read_bitvalue(collectorShutdown_, 9, 0); // collector shutdown on/off - telegram->read_bitvalue(tankHeated_, 9, 2); // tank full + changed_ |= telegram->read_bitvalue(solarPump_, 8, 0); // PS1 Solar pump on (1) or off (0) + changed_ |= telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes + changed_ |= telegram->read_bitvalue(collectorShutdown_, 9, 0); // collector shutdown on/off + changed_ |= telegram->read_bitvalue(tankHeated_, 9, 2); // tank full } /* * Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values */ void Solar::process_ISM1Set(std::shared_ptr telegram) { - telegram->read_value(setpoint_maxBottomTemp_, 6); + changed_ |= telegram->read_value(setpoint_maxBottomTemp_, 6); } } // namespace emsesp diff --git a/src/devices/solar.h b/src/devices/solar.h index 4c85cd1fb..a78927818 100644 --- a/src/devices/solar.h +++ b/src/devices/solar.h @@ -65,6 +65,7 @@ class Solar : public EMSdevice { uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET; uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET; uint8_t userFlag_ = EMS_VALUE_BOOL_NOTSET; + bool changed_ = false; void process_SM10Monitor(std::shared_ptr telegram); void process_SM100Monitor(std::shared_ptr telegram); diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 12f781e07..8ec94740b 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -242,23 +242,10 @@ bool Thermostat::updated_values() { if (EMSESP::actual_master_thermostat() != this->get_device_id()) { return false; } - - // quick hack to see if it changed. We simply just add up all the raw values - uint16_t new_value = 0; - static uint16_t current_value_ = 0; - for (const auto & hc : heating_circuits_) { - // don't publish if we haven't yet received some data - if (!Helpers::hasValue(hc->setpoint_roomTemp)) { - return false; - } - new_value += hc->setpoint_roomTemp + hc->curr_roomTemp + hc->mode; - } - - if (new_value != current_value_) { - current_value_ = new_value; + if (changed_) { + changed_ = false; return true; } - return false; } @@ -902,16 +889,16 @@ void Thermostat::show_values(uuid::console::Shell & shell) { void Thermostat::process_RC20Set(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->mode, 23); + changed_ |= telegram->read_value(hc->mode, 23); } // type 0xAE - data from the RC20 thermostat (0x17) void Thermostat::process_RC20Monitor_2(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_bitvalue(hc->mode_type, 0, 7); // day/night MSB 7th bit is day - telegram->read_value(hc->setpoint_roomTemp, 2, 1); // is * 2, force as single byte - telegram->read_value(hc->curr_roomTemp, 3); // is * 10 + changed_ |= telegram->read_bitvalue(hc->mode_type, 0, 7); // day/night MSB 7th bit is day + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 2, 1); // is * 2, force as single byte + changed_ |= telegram->read_value(hc->curr_roomTemp, 3); // is * 10 } // 0xAD - for reading the mode from the RC20/ES72 thermostat (0x17) @@ -919,21 +906,21 @@ void Thermostat::process_RC20Monitor_2(std::shared_ptr telegram) void Thermostat::process_RC20Set_2(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->mode, 3); + changed_ |= telegram->read_value(hc->mode, 3); } // 0xAF - for reading the roomtemperature from the RC20/ES72 thermostat (0x18, 0x19, ..) void Thermostat::process_RC20Remote(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->curr_roomTemp, 0); + changed_ |= telegram->read_value(hc->curr_roomTemp, 0); } // type 0xB1 - data from the RC10 thermostat (0x17) void Thermostat::process_RC10Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->setpoint_roomTemp, 1, 1); // is * 2, force as single byte - telegram->read_value(hc->curr_roomTemp, 2); // is * 10 + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 1, 1); // is * 2, force as single byte + changed_ |= telegram->read_value(hc->curr_roomTemp, 2); // is * 10 } #pragma GCC diagnostic push @@ -948,56 +935,56 @@ void Thermostat::process_RC10Set(std::shared_ptr telegram) { // type 0x0165, ff void Thermostat::process_JunkersSet(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->daytemp, 17); // is * 2 - telegram->read_value(hc->nighttemp, 16); // is * 2 - telegram->read_value(hc->nofrosttemp, 15); // is * 2 + changed_ |= telegram->read_value(hc->daytemp, 17); // is * 2 + changed_ |= telegram->read_value(hc->nighttemp, 16); // is * 2 + changed_ |= telegram->read_value(hc->nofrosttemp, 15); // is * 2 } // type 0x0179, ff void Thermostat::process_JunkersSet2(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->daytemp, 7); // is * 2 - telegram->read_value(hc->nighttemp, 6); // is * 2 - telegram->read_value(hc->nofrosttemp, 5); // is * 2 + changed_ |= telegram->read_value(hc->daytemp, 7); // is * 2 + changed_ |= telegram->read_value(hc->nighttemp, 6); // is * 2 + changed_ |= telegram->read_value(hc->nofrosttemp, 5); // is * 2 } // type 0xA3 - for external temp settings from the the RC* thermostats (e.g. RC35) void Thermostat::process_RCOutdoorTemp(std::shared_ptr telegram) { - telegram->read_value(dampedoutdoortemp_, 0); - telegram->read_value(tempsensor1_, 3); // sensor 1 - is * 10 - telegram->read_value(tempsensor2_, 5); // sensor 2 - is * 10 + changed_ |= telegram->read_value(dampedoutdoortemp_, 0); + changed_ |= telegram->read_value(tempsensor1_, 3); // sensor 1 - is * 10 + changed_ |= telegram->read_value(tempsensor2_, 5); // sensor 2 - is * 10 } // 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long void Thermostat::process_RC20Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->setpoint_roomTemp, 1, 1); // is * 2, force as single byte - telegram->read_value(hc->curr_roomTemp, 2); // is * 10 + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 1, 1); // is * 2, force as single byte + changed_ |= telegram->read_value(hc->curr_roomTemp, 2); // is * 10 } // type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long void Thermostat::process_EasyMonitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->curr_roomTemp, 8); // is * 100 - telegram->read_value(hc->setpoint_roomTemp, 10); // is * 100 + changed_ |= telegram->read_value(hc->curr_roomTemp, 8); // is * 100 + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 10); // is * 100 } // Settings Parameters - 0xA5 - RC30_1 void Thermostat::process_IBASettings(std::shared_ptr telegram) { // 22 - display line on RC35 - telegram->read_value(ibaMainDisplay_, + changed_ |= telegram->read_value(ibaMainDisplay_, 0); // display on Thermostat: 0 int. temp, 1 int. setpoint, 2 ext. temp., 3 burner temp., 4 ww temp, 5 functioning mode, 6 time, 7 data, 8 smoke temp - telegram->read_value(ibaLanguage_, 1); // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian - telegram->read_value(ibaCalIntTemperature_, 2); // offset int. temperature sensor, by * 0.1 Kelvin - telegram->read_value(ibaBuildingType_, 6); // building type: 0 = light, 1 = medium, 2 = heavy - telegram->read_value(ibaMinExtTemperature_, 5); // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1 - telegram->read_value(ibaClockOffset_, 12); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s + changed_ |= telegram->read_value(ibaLanguage_, 1); // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian + changed_ |= telegram->read_value(ibaCalIntTemperature_, 2); // offset int. temperature sensor, by * 0.1 Kelvin + changed_ |= telegram->read_value(ibaBuildingType_, 6); // building type: 0 = light, 1 = medium, 2 = heavy + changed_ |= telegram->read_value(ibaMinExtTemperature_, 5); // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1 + changed_ |= telegram->read_value(ibaClockOffset_, 12); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s } // Settings WW 0x37 - RC35 void Thermostat::process_RC35wwSettings(std::shared_ptr telegram) { - telegram->read_value(wwMode_, 2); // 0 off, 1-on, 2-auto + changed_ |= telegram->read_value(wwMode_, 2); // 0 off, 1-on, 2-auto } // type 0x6F - FR10/FR50/FR100 Junkers @@ -1009,21 +996,21 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr telegram std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->curr_roomTemp, 4); // value is * 10 - telegram->read_value(hc->setpoint_roomTemp, 2); // value is * 10 + changed_ |= telegram->read_value(hc->curr_roomTemp, 4); // value is * 10 + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 2); // value is * 10 - telegram->read_value(hc->mode_type, 0); // 1 = nofrost, 2 = eco, 3 = heat - telegram->read_value(hc->mode, 1); // 1 = manual, 2 = auto + changed_ |= telegram->read_value(hc->mode_type, 0); // 1 = nofrost, 2 = eco, 3 = heat + changed_ |= telegram->read_value(hc->mode, 1); // 1 = manual, 2 = auto } // type 0x02A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10 void Thermostat::process_RC300Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->curr_roomTemp, 0); // is * 10 + changed_ |= telegram->read_value(hc->curr_roomTemp, 0); // is * 10 - telegram->read_bitvalue(hc->mode_type, 10, 1); - telegram->read_bitvalue(hc->mode, 10, 0); // bit 1, mode (auto=1 or manual=0) + changed_ |= telegram->read_bitvalue(hc->mode_type, 10, 1); + changed_ |= telegram->read_bitvalue(hc->mode, 10, 0); // bit 1, mode (auto=1 or manual=0) // if manual, take the current setpoint temp at pos 6 // if auto, take the next setpoint temp at pos 7 @@ -1032,9 +1019,9 @@ void Thermostat::process_RC300Monitor(std::shared_ptr telegram) // pos 3 actual setpoint (optimized), i.e. changes with temporary change, summer/holiday-modes // pos 6 actual setpoint according to programmed changes eco/comfort // pos 7 next setpoint in the future, time to next setpoint in pos 8/9 - telegram->read_value(hc->setpoint_roomTemp, 3, 1); // is * 2, force as single byte - telegram->read_bitvalue(hc->summer_mode, 2, 4); - telegram->read_value(hc->targetflowtemp, 4); + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 3, 1); // is * 2, force as single byte + changed_ |= telegram->read_bitvalue(hc->summer_mode, 2, 4); + changed_ |= telegram->read_value(hc->targetflowtemp, 4); } // type 0x02B9 EMS+ for reading from RC300/RC310 thermostat @@ -1046,21 +1033,21 @@ void Thermostat::process_RC300Set(std::shared_ptr telegram) { // comfort is position 2 // I think auto is position 8? // actual setpoint taken from RC300Monitor (Michael 12.06.2020) - // telegram->read_value(hc->setpoint_roomTemp, 8, 1); // single byte conversion, value is * 2 - auto? - // telegram->read_value(hc->setpoint_roomTemp, 10, 1); // single byte conversion, value is * 2 - manual + // changed_ |= telegram->read_value(hc->setpoint_roomTemp, 8, 1); // single byte conversion, value is * 2 - auto? + // changed_ |= telegram->read_value(hc->setpoint_roomTemp, 10, 1); // single byte conversion, value is * 2 - manual // check why mode is both in the Monitor and Set for the RC300. It'll be read twice! - // telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF + // changed_ |= telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF - telegram->read_value(hc->daytemp, 2); // is * 2 - telegram->read_value(hc->nighttemp, 4); // is * 2 + changed_ |= telegram->read_value(hc->daytemp, 2); // is * 2 + changed_ |= telegram->read_value(hc->nighttemp, 4); // is * 2 } // types 0x31D and 0x31E void Thermostat::process_RC300WWmode(std::shared_ptr telegram) { // 0x31D for WW system 1, 0x31E for WW system 2 wwSystem_ = telegram->type_id - 0x31D + 1; - telegram->read_value(wwExtra_, 0); // 0=no, 1=yes + changed_ |= telegram->read_value(wwExtra_, 0); // 0=no, 1=yes // pos 1 = holiday mode // pos 2 = current status of DHW setpoint // pos 3 = current status of DHW circulation pump @@ -1070,15 +1057,15 @@ void Thermostat::process_RC300WWmode(std::shared_ptr telegram) { void Thermostat::process_RC30Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->setpoint_roomTemp, 1, 1); // is * 2, force as single byte - telegram->read_value(hc->curr_roomTemp, 2); + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 1, 1); // is * 2, force as single byte + changed_ |= telegram->read_value(hc->curr_roomTemp, 2); } // type 0xA7 - for reading the mode from the RC30 thermostat (0x10) void Thermostat::process_RC30Set(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->mode, 23); + changed_ |= telegram->read_value(hc->mode, 23); } // type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes @@ -1092,14 +1079,14 @@ void Thermostat::process_RC35Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->setpoint_roomTemp, 2, 1); // is * 2, force to single byte, is 0 in summermode - telegram->read_value(hc->curr_roomTemp, 3); // is * 10 - or 0x7D00 if thermostat is mounted on boiler + changed_ |= telegram->read_value(hc->setpoint_roomTemp, 2, 1); // is * 2, force to single byte, is 0 in summermode + changed_ |= telegram->read_value(hc->curr_roomTemp, 3); // is * 10 - or 0x7D00 if thermostat is mounted on boiler - telegram->read_bitvalue(hc->mode_type, 1, 1); - telegram->read_bitvalue(hc->summer_mode, 1, 0); - telegram->read_bitvalue(hc->holiday_mode, 0, 5); + changed_ |= telegram->read_bitvalue(hc->mode_type, 1, 1); + changed_ |= telegram->read_bitvalue(hc->summer_mode, 1, 0); + changed_ |= telegram->read_bitvalue(hc->holiday_mode, 0, 5); - telegram->read_value(hc->targetflowtemp, 14); + changed_ |= telegram->read_value(hc->targetflowtemp, 14); } // type 0x3D (HC1), 0x47 (HC2), 0x51 (HC3), 0x5B (HC4) - Working Mode Heating - for reading the mode from the RC35 thermostat (0x10) @@ -1111,16 +1098,16 @@ void Thermostat::process_RC35Set(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->mode, 7); // night, day, auto - telegram->read_value(hc->daytemp, 2); // is * 2 - telegram->read_value(hc->nighttemp, 1); // is * 2 - telegram->read_value(hc->holidaytemp, 3); // is * 2 - telegram->read_value(hc->heatingtype, 0); // 0- off, 1-radiator, 2-convector, 3-floor + changed_ |= telegram->read_value(hc->mode, 7); // night, day, auto + changed_ |= telegram->read_value(hc->daytemp, 2); // is * 2 + changed_ |= telegram->read_value(hc->nighttemp, 1); // is * 2 + changed_ |= telegram->read_value(hc->holidaytemp, 3); // is * 2 + changed_ |= telegram->read_value(hc->heatingtype, 0); // 0- off, 1-radiator, 2-convector, 3-floor - telegram->read_value(hc->summertemp, 22); // is * 1 - telegram->read_value(hc->nofrosttemp, 23); // is * 1 - telegram->read_value(hc->designtemp, 17); // is * 1 - telegram->read_value(hc->offsettemp, 6); // is * 2 + changed_ |= telegram->read_value(hc->summertemp, 22); // is * 1 + changed_ |= telegram->read_value(hc->nofrosttemp, 23); // is * 1 + changed_ |= telegram->read_value(hc->designtemp, 17); // is * 1 + changed_ |= telegram->read_value(hc->offsettemp, 6); // is * 2 } // process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long @@ -1138,6 +1125,7 @@ void Thermostat::process_RCTime(std::shared_ptr telegram) { if (datetime_.empty()) { datetime_.resize(25, '\0'); } + auto timeold = datetime_; // render time to HH:MM:SS DD/MM/YYYY // had to create separate buffers because of how printf works char buf1[6]; @@ -1156,6 +1144,9 @@ void Thermostat::process_RCTime(std::shared_ptr telegram) { Helpers::smallitoa(buf5, telegram->message_data[1]), // month Helpers::itoa(buf6, telegram->message_data[0] + 2000, 10) // year ); + if (timeold != datetime_) { + changed_ = true; + } } // add console commands diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 12da5457c..13ebb2659 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -121,6 +121,7 @@ class Thermostat : public EMSdevice { std::string datetime_; // date and time stamp uint8_t mqtt_format_; // single, nested or ha + bool changed_ = false; // Installation parameters uint8_t ibaMainDisplay_ = diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index b223e33ba..848041542 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -46,7 +46,7 @@ std::string EMSdevice::brand_to_string() const { break; case EMSdevice::Brand::NO_BRAND: default: - return read_flash_string(F("")); + return read_flash_string(F("---")); break; } diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 699156b82..522b1eeca 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -292,22 +292,37 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) { shell.println(); } -// publish all values from each EMS device to MQTT -// plus the heartbeat and sensor if activated -void EMSESP::publish_all_values() { +void EMSESP::publish_device_values(uint8_t device_type) { if (Mqtt::connected()) { - // Dallas sensors first - sensors_.publish_values(); - - // all the connected EMS devices we known about for (const auto & emsdevice : emsdevices) { - if (emsdevice) { + if (emsdevice && (emsdevice->device_type() == device_type)) { emsdevice->publish_values(); } } } } +void EMSESP::publish_other_values() { + if (Mqtt::connected()) { + for (const auto & emsdevice : emsdevices) { + if (emsdevice && (emsdevice->device_type() != EMSdevice::DeviceType::BOILER) + && (emsdevice->device_type() != EMSdevice::DeviceType::THERMOSTAT) + && (emsdevice->device_type() != EMSdevice::DeviceType::SOLAR) + && (emsdevice->device_type() != EMSdevice::DeviceType::MIXING)) { + emsdevice->publish_values(); + } + } + } +} + +void EMSESP::publish_sensor_values(const bool force) { + if (Mqtt::connected()) { + if (sensors_.updated_values() || force) { + sensors_.publish_values(); + } + } +} + // search for recognized device_ids : Me, All, otherwise print hex value std::string EMSESP::device_tostring(const uint8_t device_id) { if ((device_id & 0x7F) == rxservice_.ems_bus_id()) { @@ -508,7 +523,7 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { found = emsdevice->handle_telegram(telegram); // check to see if we need to follow up after the telegram has been processed if (found) { - if (emsdevice->updated_values() || telegram->type_id == publish_id_) { + if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->updated_values()) || telegram->type_id == publish_id_) { if (telegram->type_id == publish_id_) { publish_id_ = 0; } diff --git a/src/emsesp.h b/src/emsesp.h index b45760ba3..720fb3a41 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -59,7 +59,9 @@ class EMSESP { static void start(); static void loop(); - static void publish_all_values(); + static void publish_device_values(uint8_t device_type); + static void publish_other_values(); + static void publish_sensor_values(const bool force = false); #ifdef EMSESP_STANDALONE static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 396bc3783..69dd533c2 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -27,8 +27,13 @@ AsyncMqttClient * Mqtt::mqttClient_; // static parameters we make global std::string Mqtt::hostname_; uint8_t Mqtt::mqtt_qos_; -uint16_t Mqtt::publish_time_; uint8_t Mqtt::bus_id_; +uint32_t Mqtt::publish_time_boiler_; +uint32_t Mqtt::publish_time_thermostat_; +uint32_t Mqtt::publish_time_solar_; +uint32_t Mqtt::publish_time_mixing_; +uint32_t Mqtt::publish_time_other_; +uint32_t Mqtt::publish_time_sensor_; std::vector Mqtt::mqtt_subfunctions_; std::vector Mqtt::mqtt_cmdfunctions_; @@ -111,11 +116,30 @@ void Mqtt::loop() { uint32_t currentMillis = uuid::get_uptime(); // create publish messages for each of the EMS device values, adding to queue - if (publish_time_ && (currentMillis - last_publish_ > publish_time_)) { - last_publish_ = currentMillis; - EMSESP::publish_all_values(); + if (publish_time_boiler_ && (currentMillis - last_publish_boiler_ > publish_time_boiler_)) { + last_publish_boiler_ = currentMillis; + EMSESP::publish_device_values(EMSdevice::DeviceType::BOILER); + } + if (publish_time_thermostat_ && (currentMillis - last_publish_thermostat_ > publish_time_thermostat_)) { + last_publish_thermostat_ = currentMillis; + EMSESP::publish_device_values(EMSdevice::DeviceType::THERMOSTAT); + } + if (publish_time_solar_ && (currentMillis - last_publish_solar_ > publish_time_solar_)) { + last_publish_solar_ = currentMillis; + EMSESP::publish_device_values(EMSdevice::DeviceType::SOLAR); + } + if (publish_time_mixing_ && (currentMillis - last_publish_mixing_ > publish_time_mixing_)) { + last_publish_mixing_ = currentMillis; + EMSESP::publish_device_values(EMSdevice::DeviceType::MIXING); + } + if (publish_time_other_ && (currentMillis - last_publish_other_ > publish_time_other_)) { + last_publish_other_ = currentMillis; + EMSESP::publish_other_values(); + } + if (currentMillis - last_publish_sensor_ > publish_time_sensor_) { + last_publish_sensor_ = currentMillis; + EMSESP::publish_sensor_values(publish_time_sensor_ != 0); } - // publish top item from MQTT queue to stop flooding if ((uint32_t)(currentMillis - last_mqtt_poll_) > MQTT_PUBLISH_WAIT) { last_mqtt_poll_ = currentMillis; @@ -280,7 +304,7 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) { } if (!cmd_known) { - LOG_ERROR(F("MQTT: no matching cmd or invalid data: %s"), command); + LOG_ERROR(F("MQTT: no matching cmd or invalid data: %s"), message); } return; @@ -342,8 +366,13 @@ void Mqtt::start() { // fetch MQTT settings EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & mqttSettings) { - publish_time_ = mqttSettings.publish_time * 1000; // convert to milliseconds - mqtt_qos_ = mqttSettings.mqtt_qos; + publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000; // convert to milliseconds + publish_time_thermostat_ = mqttSettings.publish_time_thermostat * 1000; + publish_time_solar_ = mqttSettings.publish_time_solar * 1000; + publish_time_mixing_ = mqttSettings.publish_time_mixing * 1000; + publish_time_other_ = mqttSettings.publish_time_other * 1000; + publish_time_sensor_ = mqttSettings.publish_time_sensor * 1000; + mqtt_qos_ = mqttSettings.mqtt_qos; }); EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { bus_id_ = settings.ems_bus_id; }); @@ -390,8 +419,51 @@ void Mqtt::start() { mqtt_subfunctions_.reserve(10); } -void Mqtt::set_publish_time(uint16_t publish_time) { - publish_time_ = publish_time * 1000; // convert to milliseconds +void Mqtt::set_publish_time_boiler(uint16_t publish_time) { + publish_time_boiler_ = publish_time * 1000; // convert to milliseconds +} + +void Mqtt::set_publish_time_thermostat(uint16_t publish_time) { + publish_time_thermostat_ = publish_time * 1000; // convert to milliseconds +} + +void Mqtt::set_publish_time_solar(uint16_t publish_time) { + publish_time_solar_ = publish_time * 1000; // convert to milliseconds +} + +void Mqtt::set_publish_time_mixing(uint16_t publish_time) { + publish_time_mixing_ = publish_time * 1000; // convert to milliseconds +} + +void Mqtt::set_publish_time_other(uint16_t publish_time) { + publish_time_other_ = publish_time * 1000; // convert to milliseconds +} + +void Mqtt::set_publish_time_sensor(uint16_t publish_time) { + publish_time_sensor_ = publish_time * 1000; // convert to milliseconds +} + +bool Mqtt::get_publish_onchange(uint8_t device_type) { + if (device_type == EMSdevice::DeviceType::BOILER) { + if (!publish_time_boiler_) { + return true; + } + } else if (device_type == EMSdevice::DeviceType::THERMOSTAT) { + if (!publish_time_thermostat_) { + return true; + } + } else if (device_type == EMSdevice::DeviceType::SOLAR) { + if (!publish_time_solar_) { + return true; + } + } else if (device_type == EMSdevice::DeviceType::MIXING) { + if (!publish_time_mixing_) { + return true; + } + } else if (!publish_time_other_) { + return true; + } + return false; } void Mqtt::set_qos(uint8_t mqtt_qos) { @@ -482,13 +554,6 @@ void Mqtt::publish(const std::string & topic) { queue_publish_message(topic, "", false); } -// publish all queued messages to MQTT -void Mqtt::process_all_queue() { - while (!mqtt_messages_.empty()) { - process_queue(); - } -} - // take top from queue and perform the publish or subscribe action // assumes there is an MQTT connection void Mqtt::process_queue() { diff --git a/src/mqtt.h b/src/mqtt.h index fb8dac748..f7a53b33a 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -67,8 +67,14 @@ class Mqtt { void loop(); void start(); - void set_publish_time(uint16_t publish_time); + void set_publish_time_boiler(uint16_t publish_time); + void set_publish_time_thermostat(uint16_t publish_time); + void set_publish_time_solar(uint16_t publish_time); + void set_publish_time_mixing(uint16_t publish_time); + void set_publish_time_other(uint16_t publish_time); + void set_publish_time_sensor(uint16_t publish_time); void set_qos(uint8_t mqtt_qos); + bool get_publish_onchange(uint8_t device_type); enum Operation { PUBLISH, SUBSCRIBE }; @@ -167,7 +173,6 @@ class Mqtt { void on_publish(uint16_t packetId); void on_message(const char * topic, const char * payload, size_t len); void process_queue(); - void process_all_queue(); static uint16_t mqtt_publish_fails_; @@ -189,14 +194,25 @@ class Mqtt { static std::vector mqtt_subfunctions_; // list of mqtt subscribe callbacks for all devices static std::vector mqtt_cmdfunctions_; // list of commands - uint32_t last_mqtt_poll_ = 0; - uint32_t last_publish_ = 0; + uint32_t last_mqtt_poll_ = 0; + uint32_t last_publish_boiler_ = 0; + uint32_t last_publish_thermostat_ = 0; + uint32_t last_publish_solar_ = 0; + uint32_t last_publish_mixing_ = 0; + uint32_t last_publish_other_ = 0; + uint32_t last_publish_sensor_ = 0; // settings, copied over static std::string hostname_; static uint8_t mqtt_qos_; - static uint16_t publish_time_; + static uint32_t publish_time_; static uint8_t bus_id_; + static uint32_t publish_time_boiler_; + static uint32_t publish_time_thermostat_; + static uint32_t publish_time_solar_; + static uint32_t publish_time_mixing_; + static uint32_t publish_time_other_; + static uint32_t publish_time_sensor_; }; } // namespace emsesp diff --git a/src/sensors.cpp b/src/sensors.cpp index ecf8323c9..cbc2458fa 100644 --- a/src/sensors.cpp +++ b/src/sensors.cpp @@ -124,6 +124,15 @@ void Sensors::loop() { } else { bus_.depower(); if ((found_.size() >= devices_.size()) || (retrycnt_ > 5)) { + if (found_.size() == devices_.size()) { + for (uint8_t i = 0; i < devices_.size(); i++) { + if (found_[i].temperature_c != devices_[i].temperature_c) { + changed_ = true; + } + } + } else { + changed_ = true; + } devices_ = std::move(found_); retrycnt_ = 0; } else { @@ -237,6 +246,14 @@ std::string Sensors::Device::to_string() const { return str; } +// check to see if values have been updated +bool Sensors::updated_values() { + if (changed_) { + changed_ = false; + return true; + } + return false; +} // send all dallas sensor values as a JSON package to MQTT // assumes there are devices @@ -270,9 +287,10 @@ void Sensors::publish_values() { char s[7]; if (mqtt_format_ == MQTT_format::CUSTOM) { + // e.g. sensors = {28-EA41-9497-0E03-5F":23.30,"28-233D-9497-0C03-8B":24.0} doc[device.to_string()] = Helpers::render_value(s, device.temperature_c, 1); } else if ((mqtt_format_ == MQTT_format::NESTED) || (mqtt_format_ == MQTT_format::HA)) { - // e.g. {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"23.30"},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":"24.0"}} + // e.g. sensors = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"23.30"},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":"24.0"}} char sensorID[10]; // sensor{1-n} strlcpy(sensorID, "sensor", 10); strlcat(sensorID, Helpers::itoa(s, i), 10); diff --git a/src/sensors.h b/src/sensors.h index 4bd52f4c1..f1043c032 100644 --- a/src/sensors.h +++ b/src/sensors.h @@ -60,6 +60,7 @@ class Sensors { void loop(); void publish_values(); void reload(); + bool updated_values(); const std::vector devices() const; @@ -110,6 +111,7 @@ class Sensors { uint8_t mqtt_format_; uint8_t retrycnt_ = 0; uint8_t dallas_gpio_ = 0; + bool changed_ = false; }; } // namespace emsesp diff --git a/src/system.cpp b/src/system.cpp index 53056cd58..95c96c10a 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -35,6 +35,7 @@ int System::reset_counter_ = 0; bool System::upload_status_ = false; bool System::hide_led_ = false; uint8_t System::led_gpio_ = 0; +uint16_t System::analog_ = 0; // send on/off to a gpio pin // value: true = HIGH, false = LOW @@ -188,6 +189,7 @@ void System::loop() { #endif led_monitor(); // check status and report back using the LED system_check(); // check system health + measure_analog(); // send out heartbeat uint32_t currentMillis = uuid::get_uptime(); @@ -233,10 +235,33 @@ void System::send_heartbeat() { doc["mqttpublishfails"] = Mqtt::publish_fails(); doc["txfails"] = EMSESP::txservice_.telegram_fail_count(); doc["rxfails"] = EMSESP::rxservice_.telegram_error_count(); + doc["adc"] = analog_; //analogRead(A0); Mqtt::publish("heartbeat", doc, false); // send to MQTT with retain off. This will add to MQTT queue. } +// measure and moving average adc +void System::measure_analog() { + static uint32_t measure_last_ = 0; + static uint32_t sum_ = 0; + + if (!measure_last_ || (uint32_t)(uuid::get_uptime() - measure_last_) >= 1100) { + measure_last_ = uuid::get_uptime(); +#if defined(ESP8266) + uint16_t a = analogRead(A0); +#elif defined(ESP32) + uint16_t a = analogRead(36); +#endif + if (!analog_) { // init first time + analog_ = a; + sum_ = a * 256; + } else { // simple moving average filter + sum_ = sum_ * 255 / 256 + a; + analog_ = sum_ / 256; + } + } +} + // sets rate of led flash void System::set_led_speed(uint32_t speed) { led_flash_speed_ = speed; diff --git a/src/system.h b/src/system.h index 64e2e48ef..e12855426 100644 --- a/src/system.h +++ b/src/system.h @@ -74,23 +74,12 @@ class System { static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min) // internal LED -#ifndef EMSESP_NO_LED -#if defined(ESP8266) static constexpr uint8_t LED_ON = LOW; -#elif defined(ESP32) -#ifdef WEMOS_D1_32 - static constexpr uint8_t LED_ON = HIGH; -#else - static constexpr uint8_t LED_ON = LOW; -#endif -#endif -#else - static constexpr uint8_t LED_ON = 0; -#endif void led_monitor(); void set_led_speed(uint32_t speed); void system_check(); + void measure_analog(); static void show_system(uuid::console::Shell & shell); static void show_users(uuid::console::Shell & shell); @@ -103,6 +92,7 @@ class System { static int reset_counter_; uint32_t last_heartbeat_ = 0; static bool upload_status_; // true if we're in the middle of a OTA firmware upload + static uint16_t analog_; // settings bool system_heartbeat_; diff --git a/src/telegram.h b/src/telegram.h index 54e83676c..e8430cf7b 100644 --- a/src/telegram.h +++ b/src/telegram.h @@ -83,13 +83,17 @@ class Telegram { std::string to_string() const; // reads a bit value from a given telegram position - void read_bitvalue(uint8_t & value, const uint8_t index, const uint8_t bit) const { - uint8_t abs_index = (index - offset); - if (abs_index >= message_length - 1) { - return; // out of bounds + bool read_bitvalue(uint8_t & value, const uint8_t index, const uint8_t bit) const { + uint8_t abs_index = (index - this->offset); + if (abs_index >= this->message_length) { + return false; // out of bounds } - - value = (uint8_t)(((message_data[abs_index]) >> (bit)) & 0x01); + uint8_t val = value; + value = (uint8_t)(((this->message_data[abs_index]) >> (bit)) & 0x01); + if (val != value) { + return true; + } + return false; } // read a value from a telegram. We always store the value, regardless if its garbage @@ -99,18 +103,21 @@ class Telegram { // 2-compliment : https://www.rapidtables.com/convert/number/decimal-to-hex.html // https://en.wikipedia.org/wiki/Two%27s_complement // s is to override number of bytes read (e.g. use 3 to simulate a uint24_t) - void read_value(Value & value, const uint8_t index, uint8_t s = 0) const { + bool read_value(Value & value, const uint8_t index, uint8_t s = 0) const { uint8_t num_bytes = (!s) ? sizeof(Value) : s; - // check for out of bounds, if so don't modify the value - if ((index - this->offset + num_bytes - 1) >= this->message_length) { - return; + if ((index < this->offset) || ((index - this->offset + num_bytes - 1) >= this->message_length)) { + return false; } - + auto val = value; value = 0; for (uint8_t i = 0; i < num_bytes; i++) { - value = (value << 8) + message_data[index - this->offset + i]; // shift by byte + value = (value << 8) + this->message_data[index - this->offset + i]; // shift by byte } + if (val != value) { + return true; + } + return false; } private: