mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
1233 lines
55 KiB
C++
1233 lines
55 KiB
C++
/*
|
||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||
* Copyright 2020 Paul Derbyshire
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "solar.h"
|
||
|
||
namespace emsesp {
|
||
|
||
REGISTER_FACTORY(Solar, EMSdevice::DeviceType::SOLAR);
|
||
|
||
uuid::log::Logger Solar::logger_{F_(solar), uuid::log::Facility::CONSOLE};
|
||
|
||
Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||
// telegram handlers
|
||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
register_telegram_type(0x97, F("SM10Monitor"), false, MAKE_PF_CB(process_SM10Monitor));
|
||
register_telegram_type(0x96, F("SM10Config"), true, MAKE_PF_CB(process_SM10Config));
|
||
EMSESP::send_read_request(0x97, device_id);
|
||
}
|
||
|
||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
|
||
if (device_id == 0x2A) { // SM100 DHW
|
||
register_telegram_type(0x07D6, F("SM100wwTemperature"), false, MAKE_PF_CB(process_SM100wwTemperature));
|
||
register_telegram_type(0x07AA, F("SM100wwStatus"), false, MAKE_PF_CB(process_SM100wwStatus));
|
||
register_telegram_type(0x07AB, F("SM100wwCommand"), false, MAKE_PF_CB(process_SM100wwCommand));
|
||
register_telegram_type(0x07A5, F("SM100wwCirc"), true, MAKE_PF_CB(process_SM100wwCirc));
|
||
register_telegram_type(0x07A6, F("SM100wwParam"), true, MAKE_PF_CB(process_SM100wwParam));
|
||
register_telegram_type(0x07AE, F("SM100wwKeepWarm"), true, MAKE_PF_CB(process_SM100wwKeepWarm));
|
||
register_telegram_type(0x07E0, F("SM100wwStatus2"), true, MAKE_PF_CB(process_SM100wwStatus2));
|
||
} else {
|
||
// F9 is not a telegram type, it's a flag for configure
|
||
// register_telegram_type(0xF9, F("ParamCfg"), false, MAKE_PF_CB(process_SM100ParamCfg));
|
||
register_telegram_type(0x0358, F("SM100SystemConfig"), true, MAKE_PF_CB(process_SM100SystemConfig));
|
||
register_telegram_type(0x035A, F("SM100CircuitConfig"), true, MAKE_PF_CB(process_SM100CircuitConfig));
|
||
register_telegram_type(0x035D, F("SM100Circuit2Config"), true, MAKE_PF_CB(process_SM100Circuit2Config));
|
||
register_telegram_type(0x0362, F("SM100Monitor"), false, MAKE_PF_CB(process_SM100Monitor));
|
||
register_telegram_type(0x0363, F("SM100Monitor2"), false, MAKE_PF_CB(process_SM100Monitor2));
|
||
register_telegram_type(0x0366, F("SM100Config"), false, MAKE_PF_CB(process_SM100Config));
|
||
register_telegram_type(0x0364, F("SM100Status"), false, MAKE_PF_CB(process_SM100Status));
|
||
register_telegram_type(0x036A, F("SM100Status2"), false, MAKE_PF_CB(process_SM100Status2));
|
||
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, MAKE_PF_CB(process_SM100CollectorConfig));
|
||
register_telegram_type(0x038E, F("SM100Energy"), true, MAKE_PF_CB(process_SM100Energy));
|
||
register_telegram_type(0x0391, F("SM100Time"), true, MAKE_PF_CB(process_SM100Time));
|
||
register_telegram_type(0x035F, F("SM100Config1"), true, MAKE_PF_CB(process_SM100Config1));
|
||
register_telegram_type(0x035C, F("SM100HeatAssist"), true, MAKE_PF_CB(process_SM100HeatAssist));
|
||
register_telegram_type(0x0361, F("SM100Differential"), true, MAKE_PF_CB(process_SM100Differential));
|
||
}
|
||
}
|
||
|
||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||
if (device_id == 0x41) { // ISM DHW module
|
||
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_MonitorWW));
|
||
} else {
|
||
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, MAKE_PF_CB(process_ISM1StatusMessage));
|
||
register_telegram_type(0x0101, F("ISM1Set"), true, MAKE_PF_CB(process_ISM1Set));
|
||
register_telegram_type(0x0104, F("ISM2StatusMessage"), false, MAKE_PF_CB(process_ISM2StatusMessage));
|
||
}
|
||
}
|
||
|
||
// device values...
|
||
|
||
// special case ISM DHW module
|
||
if (device_id == 0x41) { // ISM DHW module
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwSelTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(wwSelTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_wwSelTemp));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_3_, DeviceValueType::USHORT, FL_(div10), FL_(wwCurTemp2), DeviceValueUOM::DEGREES);
|
||
return;
|
||
}
|
||
// special case for a SM100 DHW device_id with 0x2A where it's not actual a solar module
|
||
if (device_id == 0x2A) {
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp1), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_3_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp3), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_4_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp4), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_5_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp5), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwTemp_7_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp7), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwPump_, DeviceValueType::BOOL, nullptr, FL_(wwPump), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwMaxTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(wwMaxTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_wwMaxTemp));
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwSelTemp_, DeviceValueType::UINT, nullptr, FL_(wwSelTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwSelTemp));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwRedTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(wwRedTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_wwRedTemp));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwDailyTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(wwDailyTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_wwDailyTemp));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwDisinfectionTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(wwDisinfectionTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_wwDisinfectionTemp));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwCirc_, DeviceValueType::BOOL, nullptr, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCirc));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwCircMode_,
|
||
DeviceValueType::ENUM,
|
||
FL_(enum_wwCircMode),
|
||
FL_(wwCircMode),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_wwCircMode));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwKeepWarm_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(wwKeepWarm),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_wwKeepWarm));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwStatus2_, DeviceValueType::ENUM, FL_(enum_wwStatus2), FL_(wwStatus2), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwPumpMod_, DeviceValueType::UINT, nullptr, FL_(wwPumpMod), DeviceValueUOM::PERCENT);
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwFlow_, DeviceValueType::UINT, FL_(div10), FL_(wwFlow), DeviceValueUOM::LMIN);
|
||
return;
|
||
}
|
||
|
||
// common solar values for all modules (except dhw)
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(collectorTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylBottomTemp_, DeviceValueType::SHORT, FL_(div10), FL_(cylBottomTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, FL_(pumpWorkTime), DeviceValueUOM::MINUTES);
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &cylMaxTemp_, DeviceValueType::UINT, nullptr, FL_(cylMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_cylMaxTemp));
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylHeated_, DeviceValueType::BOOL, nullptr, FL_(cylHeated), DeviceValueUOM::NONE);
|
||
|
||
// values per device flag
|
||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &solarPumpMod_, DeviceValueType::UINT, nullptr, FL_(solarPumpMod), DeviceValueUOM::PERCENT);
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &solarPumpMinMod_, DeviceValueType::UINT, nullptr, FL_(pumpMinMod), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_PumpMinMod));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPumpTurnonDiff_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(solarPumpTurnonDiff),
|
||
DeviceValueUOM::DEGREES_R,
|
||
MAKE_CF_CB(set_TurnonDiff));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPumpTurnoffDiff_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(solarPumpTurnoffDiff),
|
||
DeviceValueUOM::DEGREES_R,
|
||
MAKE_CF_CB(set_TurnoffDiff));
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &solarPower_, DeviceValueType::SHORT, nullptr, FL_(solarPower), DeviceValueUOM::W);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &maxFlow_, DeviceValueType::UINT, FL_(div10), FL_(maxFlow), DeviceValueUOM::LMIN, MAKE_CF_CB(set_SM10MaxFlow));
|
||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||
&wwMinTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(wwMinTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_wwMinTemp));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarIsEnabled_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(solarIsEnabled),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_solarEnabled));
|
||
|
||
/* unknown values for testing and logging. Used by MichaelDvP
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &setting3_, DeviceValueType::UINT, nullptr, FL_(setting3), DeviceValueUOM::NONE, MAKE_CF_CB(set_CollectorMaxTemp));
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &setting4_, DeviceValueType::UINT, nullptr, FL_(setting4), DeviceValueUOM::NONE, MAKE_CF_CB(set_CollectorMinTemp));
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &data11_, DeviceValueType::UINT, nullptr, FL_(data11), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &data12_, DeviceValueType::UINT, nullptr, FL_(data12), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &data1_, DeviceValueType::UINT, nullptr, FL_(data1), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &data0_, DeviceValueType::UINT, nullptr, FL_(data0), DeviceValueUOM::NONE);
|
||
*/
|
||
}
|
||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylMiddleTemp_, DeviceValueType::SHORT, FL_(div10), FL_(cylMiddleTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &retHeatAssist_, DeviceValueType::SHORT, FL_(div10), FL_(retHeatAssist), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &m1Valve_, DeviceValueType::BOOL, nullptr, FL_(m1Valve), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||
}
|
||
|
||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &solarPumpMod_, DeviceValueType::UINT, nullptr, FL_(solarPumpMod), DeviceValueUOM::PERCENT);
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPumpMinMod_,
|
||
DeviceValueType::UINT,
|
||
FL_(mul5),
|
||
FL_(pumpMinMod),
|
||
DeviceValueUOM::PERCENT,
|
||
MAKE_CF_CB(set_PumpMinMod));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPumpTurnonDiff_,
|
||
DeviceValueType::UINT,
|
||
FL_(div10),
|
||
FL_(solarPumpTurnonDiff),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_TurnonDiff));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPumpTurnoffDiff_,
|
||
DeviceValueType::UINT,
|
||
FL_(div10),
|
||
FL_(solarPumpTurnoffDiff),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_TurnoffDiff));
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &collector2Temp_, DeviceValueType::SHORT, FL_(div10), FL_(collector2Temp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylMiddleTemp_, DeviceValueType::SHORT, FL_(div10), FL_(cylMiddleTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &retHeatAssist_, DeviceValueType::SHORT, FL_(div10), FL_(retHeatAssist), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &m1Valve_, DeviceValueType::BOOL, nullptr, FL_(m1Valve), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &m1Power_, DeviceValueType::UINT, nullptr, FL_(m1Power), DeviceValueUOM::PERCENT);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &solarPump2_, DeviceValueType::BOOL, nullptr, FL_(solarPump2), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &solarPump2Mod_, DeviceValueType::UINT, nullptr, FL_(solarPump2Mod), DeviceValueUOM::PERCENT);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylBottomTemp2_, DeviceValueType::SHORT, FL_(div10), FL_(cyl2BottomTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), FL_(heatExchangerTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylPumpMod_, DeviceValueType::UINT, nullptr, FL_(cylPumpMod), DeviceValueUOM::PERCENT);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &cylHeated_, DeviceValueType::BOOL, nullptr, FL_(cylHeated), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&collectorMaxTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(collectorMaxTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_CollectorMaxTemp));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&collectorMinTemp_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(collectorMinTemp),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_CollectorMinTemp));
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, FL_(energyToday), DeviceValueUOM::WH);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), FL_(energyTotal), DeviceValueUOM::KWH);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &pump2WorkTime_, DeviceValueType::TIME, nullptr, FL_(pump2WorkTime), DeviceValueUOM::MINUTES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &m1WorkTime_, DeviceValueType::TIME, nullptr, FL_(m1WorkTime), DeviceValueUOM::MINUTES);
|
||
// register_device_value(DeviceValueTAG::TAG_NONE, &cyl2MaxTemp_, DeviceValueType::UINT, nullptr, FL_(cyl2MaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_cyl2MaxTemp));
|
||
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&heatTransferSystem_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(heatTransferSystem),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_heatTransferSystem));
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &externalCyl_, DeviceValueType::BOOL, nullptr, FL_(externalCyl), DeviceValueUOM::NONE, MAKE_CF_CB(set_externalCyl));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&thermalDisinfect_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(thermalDisinfect),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_thermalDisinfect));
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &heatMetering_, DeviceValueType::BOOL, nullptr, FL_(heatMetering), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatMetering));
|
||
register_device_value(
|
||
DeviceValueTAG::TAG_NONE, &solarIsEnabled_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE, MAKE_CF_CB(set_solarEnabled));
|
||
|
||
// telegram 0x035A
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPumpMode_,
|
||
DeviceValueType::ENUM,
|
||
FL_(enum_solarmode),
|
||
FL_(solarPumpMode),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_solarMode));
|
||
register_device_value( // pump kick for vacuum collector, 00=off
|
||
DeviceValueTAG::TAG_NONE,
|
||
&solarPumpKick_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(solarPumpKick),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_solarPumpKick));
|
||
register_device_value( // system does not use antifreeze, 00=off
|
||
DeviceValueTAG::TAG_NONE,
|
||
&plainWaterMode_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(plainWaterMode),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_plainWaterMode));
|
||
register_device_value( // double Match Flow, 00=off
|
||
DeviceValueTAG::TAG_NONE,
|
||
&doubleMatchFlow_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(doubleMatchFlow),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_doubleMatchFlow));
|
||
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPump2MinMod_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(pump2MinMod),
|
||
DeviceValueUOM::PERCENT,
|
||
MAKE_CF_CB(set_Pump2MinMod));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPump2TurnonDiff_,
|
||
DeviceValueType::UINT,
|
||
FL_(div10),
|
||
FL_(solarPump2TurnonDiff),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_TurnonDiff2));
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&solarPump2TurnoffDiff_,
|
||
DeviceValueType::UINT,
|
||
FL_(div10),
|
||
FL_(solarPump2TurnoffDiff),
|
||
DeviceValueUOM::DEGREES,
|
||
MAKE_CF_CB(set_TurnoffDiff2));
|
||
register_device_value( // pump kick for vacuum collector, 00=off
|
||
DeviceValueTAG::TAG_NONE,
|
||
&solarPump2Kick_,
|
||
DeviceValueType::BOOL,
|
||
nullptr,
|
||
FL_(solarPump2Kick),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_solarPump2Kick));
|
||
|
||
// telegram 0x380
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&climateZone_,
|
||
DeviceValueType::UINT,
|
||
nullptr,
|
||
FL_(climateZone),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_climateZone)); // climate zone identifier
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&collector1Area_,
|
||
DeviceValueType::USHORT,
|
||
FL_(div10),
|
||
FL_(collector1Area),
|
||
DeviceValueUOM::SQM,
|
||
MAKE_CF_CB(set_collector1Area)); // Area of collector field 1
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&collector1Type_,
|
||
DeviceValueType::ENUM,
|
||
FL_(enum_collectortype),
|
||
FL_(collector1Type),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_collector1Type)); // Type of collector field 1, 01=flat, 02=vacuum
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&collector2Area_,
|
||
DeviceValueType::USHORT,
|
||
FL_(div10),
|
||
FL_(collector2Area),
|
||
DeviceValueUOM::SQM,
|
||
MAKE_CF_CB(set_collector2Area)); // Area of collector field 2
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&collector2Type_,
|
||
DeviceValueType::ENUM,
|
||
FL_(enum_collectortype),
|
||
FL_(collector2Type),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_collector2Type)); // Type of collector field 2, 01=flat, 02=vacuum
|
||
register_device_value(DeviceValueTAG::TAG_NONE,
|
||
&cylPriority_,
|
||
DeviceValueType::ENUM,
|
||
FL_(enum_cylprio),
|
||
FL_(cylPriority),
|
||
DeviceValueUOM::NONE,
|
||
MAKE_CF_CB(set_cylPriority));
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &heatCntFlowTemp_, DeviceValueType::USHORT, FL_(div10), FL_(heatCntFlowTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &heatCntRetTemp_, DeviceValueType::USHORT, FL_(div10), FL_(heatCntRetTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &heatCnt_, DeviceValueType::UINT, nullptr, FL_(heatCnt), DeviceValueUOM::NONE);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &swapFlowTemp_, DeviceValueType::USHORT, FL_(div10), FL_(swapFlowTemp), DeviceValueUOM::DEGREES);
|
||
register_device_value(DeviceValueTAG::TAG_NONE, &swapRetTemp_, DeviceValueType::USHORT, FL_(div10), FL_(swapRetTemp), DeviceValueUOM::DEGREES);
|
||
}
|
||
}
|
||
|
||
// SM10Monitor - type 0x96
|
||
// Solar(0x30) -> All(0x00), (0x96), data: FF 18 19 0A 02 5A 27 0A 05 2D 1E 0F 64 28 0A
|
||
void Solar::process_SM10Config(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, solarIsEnabled_, 0); // FF on
|
||
has_update(telegram, setting3_, 3);
|
||
has_update(telegram, setting4_, 4);
|
||
/*
|
||
uint8_t colmax = collectorMaxTemp_ / 10;
|
||
telegram->read_value(colmax, 3);
|
||
has_update(collectorMaxTemp_, colmax * 10);
|
||
uint8_t colmin = collectorMinTemp_ / 10;
|
||
telegram->read_value(colmin, 4);
|
||
has_update(collectorMinTemp_, colmin * 10);
|
||
*/
|
||
has_update(telegram, solarPumpMinMod_, 2);
|
||
has_update(telegram, solarPumpTurnonDiff_, 7);
|
||
has_update(telegram, solarPumpTurnoffDiff_, 8);
|
||
has_update(telegram, cylMaxTemp_, 5);
|
||
has_update(telegram, wwMinTemp_, 6);
|
||
}
|
||
|
||
// SM10Monitor - type 0x97
|
||
// Solar(0x30) -> All(0x00), SM10Monitor(0x97), data: 00 00 00 22 00 00 D2 01 00 F6 2A 00 00
|
||
void Solar::process_SM10Monitor(std::shared_ptr<const Telegram> telegram) {
|
||
uint8_t solarpumpmod = solarPumpMod_;
|
||
|
||
has_update(telegram, data0_, 0);
|
||
has_update(telegram, data1_, 1);
|
||
has_update(telegram, data11_, 11);
|
||
has_update(telegram, data12_, 12);
|
||
|
||
has_bitupdate(telegram, collectorShutdown_, 0, 3); // collectorMaxTemp reached
|
||
has_bitupdate(telegram, cylHeated_, 0, 2); // cylMaxTemp reached
|
||
has_update(telegram, collectorTemp_, 2); // collector temp from SM10, is *10
|
||
telegram->read_value(solarpumpmod, 4); // modulation solar pump
|
||
has_update(telegram, cylBottomTemp_, 5); // cyl bottom temp from SM10, is *10
|
||
has_bitupdate(telegram, solarPump_, 7, 1); // pump onoff
|
||
has_update(telegram, pumpWorkTime_, 8, 3);
|
||
|
||
// mask out pump-boosts
|
||
if (solarpumpmod == 100 && solarPumpMod_ == 0 && solarPumpMinMod_ > 0) {
|
||
solarpumpmod = solarPumpMinMod_; // set to minimum
|
||
}
|
||
has_update(solarPumpMod_, solarpumpmod);
|
||
|
||
if (!Helpers::hasValue(maxFlow_)) {
|
||
EMSESP::webSettingsService.read([&](WebSettings & settings) { maxFlow_ = settings.solar_maxflow; });
|
||
has_update(&maxFlow_);
|
||
}
|
||
|
||
// solar publishes every minute, do not count reads by other devices
|
||
if (telegram->dest == 0) {
|
||
// water 4.184 J/gK, glycol ~2.6-2.8 J/gK, no aceotrope
|
||
// solarPower_ = (collectorTemp_ - cylBottomTemp_) * solarPumpModulation_ * maxFlow_ * 10 / 1434; // water
|
||
solarPower_ = (collectorTemp_ - cylBottomTemp_) * solarpumpmod * maxFlow_ * 10 / 1665; //40% glycol@40°C
|
||
if (energy.size() >= 60) {
|
||
energy.pop_front();
|
||
}
|
||
energy.push_back(solarPower_);
|
||
int32_t sum = 0;
|
||
for (auto e : energy) {
|
||
sum += e;
|
||
}
|
||
energyLastHour_ = sum > 0 ? sum / 6 : 0; // counts in 0.1 Wh
|
||
has_update(&solarPower_);
|
||
has_update(&energyLastHour_);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* process_SM100SystemConfig - type 0x0358 EMS+ - for MS/SM100 and MS/SM200
|
||
* e.g. B0 0B FF 00 02 58 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 00 FF 01 00 00
|
||
* SM100SystemConfig(0x358), data: FF 00 FF 00 FF 00 00 00 00 00 00 FF 00 00 FF 00 00 00 00 FF 00 FF 01 01 00
|
||
* SM100SystemConfig(0x358), data: 00 00 00 00 00 00 00 (offset 25)
|
||
*/
|
||
void Solar::process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, heatTransferSystem_, 5, 1);
|
||
has_update(telegram, externalCyl_, 9, 1);
|
||
has_update(telegram, thermalDisinfect_, 10, 1);
|
||
has_update(telegram, heatMetering_, 14, 1);
|
||
has_update(telegram, solarIsEnabled_, 19, 1);
|
||
}
|
||
|
||
/*
|
||
* process_SM100SolarCircuitConfig - type 0x035A EMS+ - for MS/SM100 and MS/SM200
|
||
* e.g. B0 0B FF 00 02 5A 64 05 00 58 14 01 01 32 64 00 00 00 5A 0C
|
||
*/
|
||
void Solar::process_SM100CircuitConfig(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, collectorMaxTemp_, 0);
|
||
has_update(telegram, cylMaxTemp_, 3);
|
||
has_update(telegram, collectorMinTemp_, 4);
|
||
has_update(telegram, solarPumpMode_, 5);
|
||
has_update(telegram, solarPumpMinMod_, 6); // is / 5
|
||
has_update(telegram, solarPumpTurnoffDiff_, 7); // is * 10
|
||
has_update(telegram, solarPumpTurnonDiff_, 8); // is * 10
|
||
has_update(telegram, solarPumpKick_, 9);
|
||
has_update(telegram, plainWaterMode_, 10);
|
||
has_update(telegram, doubleMatchFlow_, 11);
|
||
}
|
||
|
||
/*
|
||
* process_SM100Solar2CircuitConfig - type 0x035D EMS+ - for MS/SM100 and MS/SM200
|
||
*/
|
||
void Solar::process_SM100Circuit2Config(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, solarPump2Kick_, 0);
|
||
//has_update(telegram, solar2PumpTurnoffDiff_, 3); // is * 10
|
||
has_update(telegram, solarPump2TurnonDiff_, 4); // is * 10
|
||
/*
|
||
has_update(telegram, collector2MaxTemp_, 0);
|
||
has_update(telegram, cylMaxTemp2_, 3);
|
||
has_update(telegram, collector2MinTemp_, 4);
|
||
has_update(telegram, solar2PumpMode_, 5);
|
||
has_update(telegram, solar2PumpMinMod_, 6);
|
||
has_update(telegram, plainWaterMode2_, 10);
|
||
has_update(telegram, doubleMatchFlow2_, 11);
|
||
*/
|
||
}
|
||
|
||
// type 0x35C Heat assistance
|
||
void Solar::process_SM100HeatAssist(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, solarHeatAssist_, 0); // is *10
|
||
}
|
||
|
||
// type 0x361 differential control
|
||
void Solar::process_SM100Differential(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, diffControl_, 0); // is *10
|
||
}
|
||
|
||
/* process_SM100ParamCfg - type 0xF9 EMS 1.0
|
||
* This telegram is used to inquire the min, default, max, and current values of a value that is usually read and written with another telegram ID
|
||
* The CS200 uses this method extensively to find out which values may be set in the SM100
|
||
* e.g. B0 10 F9 00 FF 02 5A 03 17 00 00 00 14 00 00 00 3C 00 00 00 5A 00 00 00 59 29 - requested with 90 B0 F9 00 11 FF 02 5A 03 AF
|
||
* byte 0 = 0xFF
|
||
* byte 1-2 = telegram ID used to write this value
|
||
* byte 3 = offset in telegram used to write this value
|
||
* byte 4 = unknown
|
||
* bytes 5..8 = minimum value
|
||
* bytes 9..12 = default value
|
||
* bytes 13..16 = maximum value
|
||
* bytes 17..20 = current value
|
||
*
|
||
* e.g. B0 0B F9 00 00 02 5A 00 00 6E
|
||
*/
|
||
void Solar::process_SM100ParamCfg(std::shared_ptr<const Telegram> telegram) {
|
||
uint16_t t_id = EMS_VALUE_USHORT_NOTSET;
|
||
uint8_t of = EMS_VALUE_UINT_NOTSET;
|
||
int32_t min = EMS_VALUE_USHORT_NOTSET;
|
||
int32_t def = EMS_VALUE_USHORT_NOTSET;
|
||
int32_t max = EMS_VALUE_USHORT_NOTSET;
|
||
int32_t cur = EMS_VALUE_USHORT_NOTSET;
|
||
telegram->read_value(t_id, 1);
|
||
telegram->read_value(of, 3);
|
||
telegram->read_value(min, 5);
|
||
telegram->read_value(def, 9);
|
||
telegram->read_value(max, 13);
|
||
telegram->read_value(cur, 17);
|
||
|
||
// LOG_DEBUG(F("SM100ParamCfg param=0x%04X, offset=%d, min=%d, default=%d, max=%d, current=%d"), t_id, of, min, def, max, cur));
|
||
}
|
||
|
||
/*
|
||
* SM100Monitor - type 0x0362 EMS+ - for MS/SM100 and MS/SM200
|
||
* e.g. B0 0B FF 00 02 62 00 77 01 D4 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 00 F9 80 00 80 9E - for heat exchanger temp
|
||
* e.g, 30 00 FF 00 02 62 01 AC
|
||
* 30 00 FF 18 02 62 80 00
|
||
* 30 00 FF 00 02 62 01 A1 - for cyl bottom temps
|
||
* bytes 0+1 = TS1 Temperature sensor for collector
|
||
* bytes 2+3 = TS2 Temperature sensor 1 cylinder, bottom
|
||
* bytes 16+17 = TS5 Temperature sensor 2 cylinder, bottom, or swimming pool
|
||
* bytes 20+21 = TS6 Temperature sensor external heat exchanger
|
||
*/
|
||
void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, collectorTemp_, 0); // is *10 - TS1: Temperature sensor for collector array 1
|
||
has_update(telegram, cylBottomTemp_, 2); // is *10 - TS2: Temperature sensor 1 cylinder, bottom
|
||
has_update(telegram, cylBottomTemp2_, 16); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool
|
||
has_update(telegram, heatExchangerTemp_, 20); // is *10 - TS6: Heat exchanger temperature sensor
|
||
|
||
has_update(telegram, collector2Temp_, 6); // is *10 - TS7: Temperature sensor for collector array 2
|
||
has_update(telegram, cylMiddleTemp_, 8); // is *10 - TS14: cylinder middle temperature
|
||
has_update(telegram, retHeatAssist_, 10); // is *10 - TS15: return temperature heating assistance
|
||
}
|
||
|
||
// SM100wwTemperature - 0x07D6
|
||
// Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90
|
||
void Solar::process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwTemp_1_, 0); // is *10
|
||
has_update(telegram, wwTemp_3_, 4); // is *10
|
||
has_update(telegram, wwTemp_4_, 6); // is *10
|
||
has_update(telegram, wwTemp_5_, 8); // is *10
|
||
has_update(telegram, wwTemp_7_, 12); // is *10
|
||
}
|
||
|
||
// SM100wwStatus - 0x07AA
|
||
// Solar Module(0x2A) -> (0x00), (0x7AA), data: 64 00 04 00 03 00 28 01 0F
|
||
void Solar::process_SM100wwStatus(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwPump_, 0);
|
||
}
|
||
|
||
// SM100wwParam - 0x07A6, Solar Module(0x2A) -> (0x00)
|
||
// data: FF 05 0F 5F 00 01 3C 3C 3C 3C 28 12 46 01 3C 1E 03 07 3C 00 0F 00 05
|
||
void Solar::process_SM100wwParam(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwMaxTemp_, 8);
|
||
has_update(telegram, wwSelTemp_, 9);
|
||
has_update(telegram, wwRedTemp_, 10);
|
||
has_update(telegram, wwDailyTemp_, 6);
|
||
has_update(telegram, wwDisinfectionTemp_, 12);
|
||
// (daily heating time thermostat 2F5, offset 9, offset 8 on/off)
|
||
}
|
||
|
||
// SM100wwCirc - 0x07A5
|
||
// Solar Module(0x2A) -> (0x00), (0x7A5), data:
|
||
void Solar::process_SM100wwCirc(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwCirc_, 0);
|
||
has_update(telegram, wwCircMode_, 3);
|
||
}
|
||
|
||
// SM100wwKeepWarm - 0x7AE, keepWarm
|
||
// Thermostat(0x10) -> Solar(0x2A), ?(0x7AE), data: FF
|
||
void Solar::process_SM100wwKeepWarm(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwKeepWarm_, 0);
|
||
}
|
||
|
||
/*
|
||
// SM100ww? - 0x7E0, some kind of status
|
||
// data: 00 00 46 00 00 01 06 0E 06 0E 00 00 00 00 00 03 03 03 03
|
||
// publishes single values offset 1/2(16bit), offset 5, offset 6, offset 7, offset 8, offset 9,
|
||
// status2 = 03:"no heat", 06:"heat request", 08:"disinfecting", 09:"hold"
|
||
*/
|
||
void Solar::process_SM100wwStatus2(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwFlow_, 7);
|
||
has_update(telegram, wwStatus2_, 8);
|
||
has_update(telegram, wwPumpMod_, 9);
|
||
}
|
||
|
||
// SM100Monitor2 - 0x0363 Heatcounter
|
||
// e.g. B0 00 FF 00 02 63 80 00 80 00 00 00 80 00 80 00 80 00 00 80 00 5A
|
||
// Solar(0x30) -> All(0x00), SM100Monitor2(0x363), data: 01 E1 01 6B 00 00 01 5D 02 8E 80 00 0F 80 00
|
||
void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram->read_value(heatCntFlowTemp_, 0)); // is *10
|
||
has_update(telegram->read_value(heatCntRetTemp_, 2)); // is *10
|
||
has_update(telegram->read_value(heatCnt_, 12));
|
||
has_update(telegram->read_value(swapRetTemp_, 6)); // is *10
|
||
has_update(telegram->read_value(swapFlowTemp_, 8)); // is *10
|
||
}
|
||
|
||
#pragma GCC diagnostic push
|
||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||
|
||
// SM100wwCommand - 0x07AB
|
||
// Thermostat(0x10) -> Solar Module(0x2A), (0x7AB), data: 01 00 01
|
||
void Solar::process_SM100wwCommand(std::shared_ptr<const Telegram> telegram) {
|
||
// not implemented yet
|
||
}
|
||
|
||
#pragma GCC diagnostic pop
|
||
|
||
// SM100Config - 0x0366
|
||
// e.g. B0 00 FF 00 02 66 01 62 00 13 40 14
|
||
void Solar::process_SM100Config(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, availabilityFlag_, 0);
|
||
has_update(telegram, configFlag_, 1);
|
||
has_update(telegram, userFlag_, 2);
|
||
}
|
||
|
||
// SM100Config1 - 0x035F
|
||
// e.g. Solar(0x30) -> Me(0x0B), ?(0x35F), data: 00 00 41 01 1E 0A 0C 19 00 3C 19
|
||
void Solar::process_SM100Config1(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram->read_value(cylPriority_, 3));
|
||
}
|
||
|
||
/*
|
||
* SM100Status - type 0x0364 EMS+ for pump modulations - for MS/SM100 and MS/SM200
|
||
- PS1: Solar circuit pump for collector array 1
|
||
- PS5: Cylinder primary pump when using an external heat exchanger
|
||
* e.g. 30 00 FF 09 02 64 64 = 100%
|
||
* Solar(0x30) -> All(0x00), (0x364), data: 00 64 05 24 00 00 FF 00 00 05 00 14 3C 64 00 00 00 00
|
||
*/
|
||
void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
|
||
uint8_t solarpumpmod = solarPumpMod_;
|
||
uint8_t cylinderpumpmod = cylPumpMod_;
|
||
telegram->read_value(cylinderpumpmod, 8);
|
||
telegram->read_value(solarpumpmod, 9);
|
||
|
||
// mask out boosts
|
||
if (solarpumpmod == 100 && solarPumpMod_ == 0 && solarPumpMinMod_ > 0) {
|
||
solarpumpmod = solarPumpMinMod_ * 5;
|
||
}
|
||
has_update(solarPumpMod_, solarpumpmod);
|
||
if (cylinderpumpmod == 100 && cylPumpMod_ == 0 && solarPumpMinMod_ > 0) {
|
||
cylinderpumpmod = solarPumpMinMod_ * 5;
|
||
}
|
||
has_update(cylPumpMod_, cylinderpumpmod);
|
||
|
||
has_bitupdate(telegram, cylHeated_, 3, 1); // issue #422
|
||
has_bitupdate(telegram, collectorShutdown_, 3, 0); // collector shutdown
|
||
has_update(telegram, m1Power_, 13);
|
||
|
||
solarpumpmod = solarPump2Mod_;
|
||
telegram->read_value(solarpumpmod, 4);
|
||
// mask out boost
|
||
if (solarpumpmod == 100 && solarPump2Mod_ == 0 && solarPumpMinMod_ > 0) {
|
||
solarpumpmod = solarPumpMinMod_ * 5; // set to minimum
|
||
}
|
||
has_update(solarPump2Mod_, solarpumpmod);
|
||
}
|
||
|
||
/*
|
||
* SM100Status2 - type 0x036A EMS+ for pump on/off at offset 0x0A - for SM100 and SM200
|
||
* e.g. B0 00 FF 00 02 6A 03 03 03 03 01 03 03 03 03 03 01 03
|
||
* byte 4 = VS2 3-way valve for cylinder 2 : test=01, on=04 and off=03
|
||
* 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<const Telegram> telegram) {
|
||
has_bitupdate(telegram, valveStatus_, 4, 2); // on if bit 2 set
|
||
has_bitupdate(telegram, solarPump_, 10, 2); // on if bit 2 set
|
||
has_bitupdate(telegram, solarPump2_, 1, 2); // on if bit 2 set
|
||
has_bitupdate(telegram, m1Valve_, 7, 2); // values 8/4 seen
|
||
}
|
||
|
||
/*
|
||
* SM100CollectorConfig - type 0x0380 EMS+ - for SM100 and SM200
|
||
* e.g. B0 0B FF 00 02 80 50 64 00 00 29 01 00 00 01
|
||
* SM100CollectorConfig(0x380), data: 5A 3B 00 00 41 02 00 2D 02 (with 2 collectors)
|
||
*/
|
||
void Solar::process_SM100CollectorConfig(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, climateZone_, 0);
|
||
has_update(telegram, collector1Area_, 3);
|
||
// has_enumupdate(telegram, collector1Type_, 5, 1);
|
||
// has_update(telegram, collector2Area_, 6);
|
||
// do not show collector 2 if area is zero
|
||
telegram->read_value(collector2Area_, 6);
|
||
telegram->read_enumvalue(collector2Type_, 8, 1);
|
||
if (collector2Area_ == 0) {
|
||
collector2Area_ = EMS_VALUE_USHORT_NOTSET;
|
||
collector2Type_ = EMS_VALUE_UINT_NOTSET;
|
||
}
|
||
// has_enumupdate(telegram, collector2Type_, 8, 1);
|
||
}
|
||
|
||
/*
|
||
* SM100Energy - type 0x038E EMS+ for energy readings
|
||
* e.g. 30 00 FF 00 02 8E 00 00 00 00 00 00 06 C5 00 00 76 35
|
||
* SM100Energy(0x38E), data: 00 00 01 79 00 00 22 3D 00 00 09 31 (with 2 collectors)
|
||
*/
|
||
void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, energyLastHour_, 0); // last hour / 10 in Wh
|
||
has_update(telegram, energyToday_, 4); // todays in Wh
|
||
has_update(telegram, energyTotal_, 8); // total / 10 in kWh
|
||
}
|
||
|
||
/*
|
||
* SM100Time - type 0x0391 EMS+ for pump working time
|
||
* SM100Time(0x391), data: 00 00 2A 13 00 00 00 00 00 00 70 13 00 00 00 00 00 00 24 7E 00 00 00 00 00
|
||
* SM100Time(0x391), data: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 4A 00 (offset 24)
|
||
*/
|
||
void Solar::process_SM100Time(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, pumpWorkTime_, 1, 3);
|
||
// has_update(telegram, pumpXWorkTime_, 9, 3);
|
||
has_update(telegram, pump2WorkTime_, 17, 3);
|
||
has_update(telegram, m1WorkTime_, 45, 3);
|
||
}
|
||
|
||
/*
|
||
* Junkers ISM1 Solar Module - type 0x0103 EMS+ for energy readings
|
||
* 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<const Telegram> telegram) {
|
||
has_update(telegram, collectorTemp_, 4); // Collector Temperature
|
||
has_update(telegram, cylBottomTemp_, 6); // Temperature Bottom of Solar Boiler cyl
|
||
uint16_t Wh = energyLastHour_ / 10;
|
||
telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
|
||
if (energyLastHour_ != Wh * 10) {
|
||
energyLastHour_ = Wh * 10;
|
||
has_update(&energyLastHour_);
|
||
}
|
||
|
||
has_bitupdate(telegram, solarPump_, 8, 0); // PS1 Solar pump on (1) or off (0)
|
||
has_update(telegram, pumpWorkTime_, 10, 3); // force to 3 bytes
|
||
has_bitupdate(telegram, collectorShutdown_, 9, 0); // collector shutdown on/off
|
||
has_bitupdate(telegram, cylHeated_, 9, 2); // cyl full
|
||
}
|
||
|
||
/*
|
||
* Junkers ISM12 Solar Module - type 0x0104 EMS+ for heat assist
|
||
* ?(0x103), data: 00 00 00 00 00 7A 01 15 00 00 05 37 F0
|
||
* ?(0x104), data: 01 A9 01 22 27 0F 27 0F 27 0F 27 0F 27 0F 27 0F
|
||
* ?(0x104), data: 01 01 00 00 00 00 00 27 0F 27 0F (offset 16)
|
||
*/
|
||
void Solar::process_ISM2StatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, cylMiddleTemp_, 0); // Temperature Middle of Solar Boiler cyl
|
||
has_update(telegram, retHeatAssist_, 2); // return temperature from heating T4
|
||
has_bitupdate(telegram, m1Valve_, 17, 0); // return valve DUW1 (also 16,0)
|
||
}
|
||
|
||
/*
|
||
* Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values
|
||
*/
|
||
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, cylMaxTemp_, 6);
|
||
}
|
||
|
||
/*
|
||
* Junkers ISM1 Solar DHW Module - type 0x34 ww
|
||
*/
|
||
void Solar::process_MonitorWW(std::shared_ptr<const Telegram> telegram) {
|
||
has_update(telegram, wwSelTemp_, 0);
|
||
has_update(telegram, wwTemp_1_, 1);
|
||
has_update(telegram, wwTemp_3_, 3);
|
||
}
|
||
|
||
|
||
/*
|
||
* Settings
|
||
*/
|
||
|
||
// collector shutdown temperature
|
||
bool Solar::set_CollectorMaxTemp(const char * value, const int8_t id) {
|
||
int temperature;
|
||
if (!Helpers::value2temperature(value, temperature)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 3, (uint8_t)temperature, 0x96);
|
||
} else {
|
||
write_command(0x35A, 0, (uint8_t)temperature, 0x35A);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// collector shutdown temperature
|
||
bool Solar::set_CollectorMinTemp(const char * value, const int8_t id) {
|
||
int temperature;
|
||
if (!Helpers::value2temperature(value, temperature)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 4, (uint8_t)temperature, 0x96);
|
||
} else {
|
||
write_command(0x35A, 4, (uint8_t)temperature, 0x35A);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// cylinder max temperature
|
||
bool Solar::set_cylMaxTemp(const char * value, const int8_t id) {
|
||
int temperature;
|
||
if (!Helpers::value2temperature(value, temperature)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 5, (uint8_t)temperature, 0x96);
|
||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||
write_command(0x101, 6, (uint8_t)temperature, 0x101);
|
||
} else {
|
||
write_command(0x35A, 3, (uint8_t)temperature, 0x35A);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// solar pump minimum modulation
|
||
bool Solar::set_PumpMinMod(const char * value, const int8_t id) {
|
||
int modulation;
|
||
if (!Helpers::value2number(value, modulation)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 2, (uint8_t)modulation, 0x96);
|
||
} else {
|
||
write_command(0x35A, 6, (uint8_t)(modulation + 2) / 5, 0x35A);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// solar pump 2 minimum modulation
|
||
bool Solar::set_Pump2MinMod(const char * value, const int8_t id) {
|
||
int modulation;
|
||
if (!Helpers::value2number(value, modulation)) {
|
||
return false;
|
||
}
|
||
// write_command(0x35D, x, (uint8_t)(modulation), 0x35D);
|
||
return true;
|
||
}
|
||
|
||
// warm water minimum temperature
|
||
bool Solar::set_wwMinTemp(const char * value, const int8_t id) {
|
||
int temperature;
|
||
if (!Helpers::value2temperature(value, temperature)) {
|
||
return false;
|
||
}
|
||
write_command(0x96, 6, (uint8_t)temperature, 0x96);
|
||
return true;
|
||
}
|
||
|
||
// turn on difference for solar pump
|
||
bool Solar::set_TurnoffDiff(const char * value, const int8_t id) {
|
||
float temperature;
|
||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 8, (uint8_t)temperature, 0x96);
|
||
} else {
|
||
write_command(0x35A, 7, (uint8_t)(temperature * 10), 0x35A);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// turn off difference for solar pump
|
||
bool Solar::set_TurnonDiff(const char * value, const int8_t id) {
|
||
float temperature;
|
||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 7, (uint8_t)temperature, 0x96);
|
||
} else {
|
||
write_command(0x35A, 8, (uint8_t)(temperature * 10), 0x35A);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// turn on difference for solar pump 2
|
||
bool Solar::set_TurnoffDiff2(const char * value, const int8_t id) {
|
||
float temperature;
|
||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||
return false;
|
||
}
|
||
write_command(0x361, 3, (uint8_t)(temperature * 10), 0x361);
|
||
return true;
|
||
}
|
||
|
||
// turn off difference for solar pump 2
|
||
bool Solar::set_TurnonDiff2(const char * value, const int8_t id) {
|
||
float temperature;
|
||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||
return false;
|
||
}
|
||
write_command(0x361, 4, (uint8_t)(temperature * 10), 0x361);
|
||
return true;
|
||
}
|
||
|
||
// external value to calculate energy
|
||
bool Solar::set_SM10MaxFlow(const char * value, const int8_t id) {
|
||
float flow;
|
||
if (!Helpers::value2float(value, flow)) {
|
||
return false;
|
||
}
|
||
maxFlow_ = (flow * 10);
|
||
EMSESP::webSettingsService.update(
|
||
[&](WebSettings & settings) {
|
||
settings.solar_maxflow = maxFlow_;
|
||
return StateUpdateResult::CHANGED;
|
||
},
|
||
"local");
|
||
return true;
|
||
}
|
||
|
||
// switch heat transfer system on/off
|
||
bool Solar::set_heatTransferSystem(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x358, 5, v ? 0x01 : 0x00, 0x358);
|
||
return true;
|
||
}
|
||
|
||
// switch external cylinder on/off
|
||
bool Solar::set_externalCyl(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x358, 9, v ? 0x01 : 0x00, 0x358);
|
||
return true;
|
||
}
|
||
|
||
// switch thermal disinfection on/off
|
||
bool Solar::set_thermalDisinfect(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x358, 10, v ? 0x01 : 0x00, 0x358);
|
||
return true;
|
||
}
|
||
|
||
// switch heat metering on/off
|
||
bool Solar::set_heatMetering(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x358, 14, v ? 0x01 : 0x00, 0x358);
|
||
return true;
|
||
}
|
||
|
||
// switch solar system on/off
|
||
bool Solar::set_solarEnabled(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||
write_command(0x96, 0, v ? 0xFF : 0x00, 0x96);
|
||
} else {
|
||
write_command(0x358, 19, v ? 0x01 : 0x00, 0x358);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// pump mode: constant, pwm or analog
|
||
bool Solar::set_solarMode(const char * value, const int8_t id) {
|
||
uint8_t num;
|
||
if (!Helpers::value2enum(value, num, FL_(enum_solarmode))) {
|
||
return false;
|
||
}
|
||
write_command(0x35A, 5, num, 0x35A);
|
||
return true;
|
||
}
|
||
|
||
/*/ pump mode: constant, pwm or analog
|
||
bool Solar::set_solarMode2(const char * value, const int8_t id) {
|
||
uint8_t num;
|
||
if (!Helpers::value2enum(value, num, FL_(enum_solarmode))) {
|
||
return false;
|
||
}
|
||
write_command(0x35D, x, num, 0x35D);
|
||
return true;
|
||
}
|
||
*/
|
||
|
||
// switch pumpkick on/off
|
||
bool Solar::set_solarPumpKick(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x35A, 9, v ? 0x01 : 0x00, 0x35A);
|
||
return true;
|
||
}
|
||
|
||
// switch pump2kick on/off
|
||
bool Solar::set_solarPump2Kick(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x35D, 0, v ? 0x01 : 0x00, 0x35D);
|
||
return true;
|
||
}
|
||
|
||
// switch plain water mode on/off
|
||
bool Solar::set_plainWaterMode(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x35A, 10, v ? 0x01 : 0x00, 0x35A);
|
||
return true;
|
||
}
|
||
|
||
// switch double match flow on/off
|
||
bool Solar::set_doubleMatchFlow(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x35A, 11, v ? 0x01 : 0x00, 0x35A);
|
||
return true;
|
||
}
|
||
|
||
// set climate zone number
|
||
bool Solar::set_climateZone(const char * value, const int8_t id) {
|
||
int v = 0;
|
||
if (!Helpers::value2number(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x380, 0, v, 0x380);
|
||
return true;
|
||
}
|
||
|
||
// collector area in squaremeters
|
||
bool Solar::set_collector1Area(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2float(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x380, 3, (uint16_t)(v * 10), 0x380);
|
||
return true;
|
||
}
|
||
|
||
// collector area in squaremeters
|
||
bool Solar::set_collector2Area(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2float(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x380, 6, (uint16_t)(v * 10), 0x380);
|
||
return true;
|
||
}
|
||
|
||
// collector type flat/vacuum
|
||
bool Solar::set_collector1Type(const char * value, const int8_t id) {
|
||
uint8_t num;
|
||
if (!Helpers::value2enum(value, num, FL_(enum_collectortype))) {
|
||
return false;
|
||
}
|
||
write_command(0x380, 5, num + 1, 0x380);
|
||
return true;
|
||
}
|
||
|
||
// collector type flat/vacuum
|
||
bool Solar::set_collector2Type(const char * value, const int8_t id) {
|
||
uint8_t num;
|
||
if (!Helpers::value2enum(value, num, FL_(enum_collectortype))) {
|
||
return false;
|
||
}
|
||
write_command(0x380, 8, num + 1, 0x380);
|
||
return true;
|
||
}
|
||
|
||
// priority of cylinders if there are 2
|
||
bool Solar::set_cylPriority(const char * value, const int8_t id) {
|
||
uint8_t n;
|
||
if (!Helpers::value2enum(value, n, FL_(enum_cylprio))) {
|
||
return false;
|
||
}
|
||
write_command(0x35F, 3, n, 0x35F);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_heatAssist(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x35C, 0, (uint8_t)(v * 10), 0x35C);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_diffControl(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x361, 4, (uint8_t)(v * 10), 0x361);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwSelTemp(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||
write_command(0x35, 3, (uint8_t)v, 0x34);
|
||
} else { // SM100
|
||
write_command(0x7A6, 9, (uint8_t)v, 0x7A6);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwMaxTemp(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x7A6, 8, (uint8_t)v, 0x7A6);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwRedTemp(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x7A6, 10, (uint8_t)v, 0x7A6);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwDailyTemp(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x7A6, 6, (uint8_t)v, 0x7A6);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwDisinfectionTemp(const char * value, const int8_t id) {
|
||
float v = 0;
|
||
if (!Helpers::value2temperature(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x7A6, 12, (uint8_t)v, 0x7A6);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwCirc(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x7A5, 0, v ? 0xFF : 0x00, 0x7A5);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwCircMode(const char * value, const int8_t id) {
|
||
uint8_t n;
|
||
if (!Helpers::value2enum(value, n, FL_(enum_wwCircMode))) {
|
||
return false;
|
||
}
|
||
write_command(0x7A5, 3, n, 0x7A5);
|
||
return true;
|
||
}
|
||
|
||
bool Solar::set_wwKeepWarm(const char * value, const int8_t id) {
|
||
bool v = false;
|
||
if (!Helpers::value2bool(value, v)) {
|
||
return false;
|
||
}
|
||
write_command(0x7AE, 0, v ? 0xFF : 0x00, 0x7AE);
|
||
return true;
|
||
}
|
||
|
||
} // namespace emsesp
|