Merge pull request #650 from MichaelDvP/dev

custom min/max
This commit is contained in:
Proddy
2022-09-28 21:20:08 +02:00
committed by GitHub
16 changed files with 692 additions and 352 deletions

View File

@@ -8,6 +8,9 @@
- Add support for Lolin C3 mini [#620](https://github.com/emsesp/EMS-ESP32/pull/620)
- Add Greenstar 30Ri boiler
- Add program memory info
- Add min/max setting to customizations
- Adapt min/max if ems-value is not in this range
- Add heatpump settings for inputs and limits
## Fixed

File diff suppressed because it is too large Load Diff

View File

@@ -8,10 +8,10 @@
"@emotion/styled": "^11.10.4",
"@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.10.6",
"@mui/material": "^5.10.6",
"@mui/material": "^5.10.7",
"@table-library/react-table-library": "4.0.18",
"@types/lodash": "^4.14.185",
"@types/node": "^18.7.20",
"@types/node": "^18.7.23",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
@@ -30,8 +30,8 @@
"react-router-dom": "^6.4.1",
"react-scripts": "5.0.1",
"sockette": "^2.0.6",
"typesafe-i18n": "^5.13.0",
"typescript": "^4.8.3"
"typesafe-i18n": "^5.13.1",
"typescript": "^4.8.4"
},
"scripts": {
"start": "react-app-rewired start",

View File

@@ -179,7 +179,7 @@ const de: Translation = {
PLATFORM: 'Platform (Platform / SDK)',
UPTIME: 'System Betriebszeit',
CPU_FREQ: 'CPU Frequenz',
HEAP: 'RAM Speicher (Frei / Max Belegt)',
HEAP: 'RAM freier Speicher (Gesamt / max. Block)',
PSRAM: 'PSRAM (Größe / Frei)',
FLASH: 'Flash Speicher (Größe / Geschwindigkeit)',
APPSIZE: 'Programm (Genutzt / Frei)',
@@ -263,7 +263,7 @@ const de: Translation = {
GUEST: 'Gast',
NEW: 'Neuer',
RENAME: 'Ändere',
ENTITY: 'Einheit'
ENTITY: 'Entität'
};
export default de;

View File

@@ -137,7 +137,7 @@ const SettingsCustomization: FC = () => {
}, [LL]);
const setInitialMask = (data: DeviceEntity[]) => {
setDeviceEntities(data.map((de) => ({ ...de, o_m: de.m, o_cn: de.cn })));
setDeviceEntities(data.map((de) => ({ ...de, o_m: de.m, o_cn: de.cn, o_mi: de.mi, o_ma: de.ma })));
};
const fetchDeviceEntities = async (unique_id: number) => {
@@ -249,8 +249,14 @@ const SettingsCustomization: FC = () => {
const saveCustomization = async () => {
if (devices && deviceEntities && selectedDevice !== -1) {
const masked_entities = deviceEntities
.filter((de) => de.m !== de.o_m || de.cn !== de.o_cn)
.map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.id + (new_de.cn ? '|' + new_de.cn : ''));
.filter((de) => de.m !== de.o_m || de.cn !== de.o_cn || de.ma !== de.o_ma || de.mi !== de.o_mi)
.map((new_de) =>
new_de.m.toString(16).padStart(2, '0') +
new_de.id +
((new_de.cn || new_de.mi || new_de.ma) ? '|' : '') +
(new_de.cn ? new_de.cn : '') +
(new_de.mi ? '>' + new_de.mi : '') +
(new_de.ma ? '<' + new_de.ma : ''));
// check size in bytes to match buffer in CPP, which is 4096
const bytes = new TextEncoder().encode(JSON.stringify(masked_entities)).length;
@@ -331,7 +337,7 @@ const SettingsCustomization: FC = () => {
setDeviceEntities((prevState) => {
const newState = prevState.map((obj) => {
if (obj.id === deviceEntity.id) {
return { ...obj, cn: deviceEntity.cn };
return { ...obj, cn: deviceEntity.cn, mi: deviceEntity.mi, ma: deviceEntity.ma };
}
return obj;
});
@@ -627,6 +633,28 @@ const SettingsCustomization: FC = () => {
onChange={updateValue(setDeviceEntity)}
/>
</Grid>
{typeof de.v === 'number' && de.w && (
<>
<Grid item>
<TextField
name="mi"
label="min"
value={deviceEntity.mi}
sx={{ width: '8ch' }}
onChange={updateValue(setDeviceEntity)}
/>
</Grid>
<Grid item>
<TextField
name="ma"
label="max"
value={deviceEntity.ma}
sx={{ width: '8ch' }}
onChange={updateValue(setDeviceEntity)}
/>
</Grid>
</>
)}
</Grid>
</DialogContent>
<DialogActions>

View File

@@ -142,6 +142,10 @@ export interface DeviceEntity {
o_m?: number; // original mask before edits
o_cn?: string; // original cn before edits
w: boolean; // writeable
mi?: string; // min value
ma?: string; // max value
o_mi?: string;
o_ma?: string;
}
export interface CustomEntities {

View File

@@ -178,6 +178,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_telegram_type(0x48D, F("HpPower"), true, MAKE_PF_CB(process_HpPower));
register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor));
register_telegram_type(0x48A, F("HpPool"), true, MAKE_PF_CB(process_HpPool));
register_telegram_type(0x4A2, F("HpInput"), false, MAKE_PF_CB(process_HpInput));
register_telegram_type(0x486, F("HpInConfig"), false, MAKE_PF_CB(process_HpInConfig));
register_telegram_type(0x492, F("HpHeaterConfig"), false, MAKE_PF_CB(process_HpHeaterConfig));
}
/*
@@ -497,6 +500,59 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(poolSetTemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_pool_temp));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[0].state, DeviceValueType::BOOL, FL_(hpInput1), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[0].option,
DeviceValueType::STRING,
FL_(tpl_input),
FL_(hpIn1Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn1Logic));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[1].state, DeviceValueType::BOOL, FL_(hpInput2), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[1].option,
DeviceValueType::STRING,
FL_(tpl_input),
FL_(hpIn2Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn2Logic));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[2].state, DeviceValueType::BOOL, FL_(hpInput3), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[2].option,
DeviceValueType::STRING,
FL_(tpl_input),
FL_(hpIn3Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn3Logic));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[3].state, DeviceValueType::BOOL, FL_(hpInput4), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[3].option,
DeviceValueType::STRING,
FL_(tpl_input4),
FL_(hpIn4Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn4Logic));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&maxHeatComp_,
DeviceValueType::ENUM,
FL_(enum_maxHeat),
FL_(maxHeatComp),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_maxHeatComp));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&maxHeatHeat_,
DeviceValueType::ENUM,
FL_(enum_maxHeat),
FL_(maxHeatHeat),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_maxHeatHeat));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&maxHeatDhw_,
DeviceValueType::ENUM,
FL_(enum_maxHeat),
FL_(maxHeatDhw),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_maxHeatDhw));
}
// dhw - DEVICE_DATA_ww topic
@@ -1093,6 +1149,44 @@ void Boiler::process_HpPool(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, poolSetTemp_, 1);
}
// Heatpump inputs - type 0x4A2
// Boiler(0x08) -> All(0x00), ?(0x04A2), data: 02 01 01 00 01 00
void Boiler::process_HpInput(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpInput[0].state, 2);
has_update(telegram, hpInput[1].state, 3);
has_update(telegram, hpInput[2].state, 4);
has_update(telegram, hpInput[3].state, 5);
}
// Heatpump inputs settings- type 0x486
// Boiler(0x08) -> All(0x00), ?(0x0486), data: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
// Boiler(0x08) -> All(0x00), ?(0x0486), data: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 00 00 00 00 00 (offset 25)
// Boiler(0x08) -> All(0x00), ?(0x0486), data: 00 00 (offset 51)
void Boiler::process_HpInConfig(std::shared_ptr<const Telegram> telegram) {
char option[12];
for (uint8_t i = 0; i < 2; i++) {
for (uint8_t j = 0; j < 11; j++) {
option[j] = hpInput[i].option[j] - '0';
telegram->read_value(option[j], j * 4 + i);
option[j] = option[j] ? '1' : '0';
}
option[11] = '\0'; // terminate string
has_update(hpInput[i].option, option, 12);
}
for (uint8_t j = 0; j < 9; j++) {
option[j] = hpInput[3].option[j] - '0';
telegram->read_value(option[j], 42 + j);
option[j] = option[j] ? '1' : '0';
}
option[9] = '\0'; // terminate string
has_update(hpInput[3].option, option, 12);
}
void Boiler::process_HpHeaterConfig(std::shared_ptr<const Telegram> telegram) {
has_update(maxHeatComp_, 2);
has_update(maxHeatHeat_, 3);
has_update(maxHeatDhw_, 4);
}
// 0x2A - MC110Status
// 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
@@ -1335,6 +1429,9 @@ void Boiler::process_amCommandMessage(std::shared_ptr<const Telegram> telegram)
// pos 6: boiler blocking 0-off, 1-on
}
// 0x0550 AM200 broadcasted message, all 27 bytes unkown
// Rx: 60 00 FF 00 04 50 00 FF 00 FF FF 00 0D 00 01 00 00 00 00 01 03 01 00 03 00 2D 19 C8 02 94 00 4A
// Rx: 60 00 FF 19 04 50 00 FF FF 39
void Boiler::process_amExtraMessage(std::shared_ptr<const Telegram> telegram) {
}
@@ -2241,4 +2338,48 @@ bool Boiler::set_emergency_ops(const char * value, const int8_t id) {
return true;
}
bool Boiler::set_HpInLogic(const char * value, const int8_t id) {
if (id == 0 || id > 4) {
return false;
}
bool v;
if (Helpers::value2bool(value, v)) {
write_command(0x486, id == 4 ? 42 : id, v ? 1 : 0, 0x486);
return true;
}
if (strlen(value) == 11 && id != 4) {
uint8_t v[11];
for (uint8_t i = 0; i < 11; i++) {
v[i] = value[i] - '0';
if (v[i] > 1) {
return false;
}
write_command(0x486, i * 3 + id - 1, v[i]);
}
return true;
}
// input 4
if (strlen(value) == 8 && id == 4) {
uint8_t v[11];
for (uint8_t i = 0; i < 8; i++) {
v[i] = value[i] - '0';
if (v[i] > 1) {
return false;
}
write_command(0x486, 42 + i, v[i]);
}
return true;
}
return false;
}
bool Boiler::set_maxHeat(const char * value, const int8_t id) {
uint8_t v;
if (!Helpers::value2enum(value, v, FL_(enum_maxHeat))) {
return false;
}
write_command(0x492, id, v, 0x492);
return true;
}
} // namespace emsesp

View File

@@ -198,6 +198,17 @@ class Boiler : public EMSdevice {
// Pool unit
int8_t poolSetTemp_;
// Inputs
struct {
uint8_t state;
char option[12]; // logic, block_comp, block_dhw, block_heat, block_cool, overheat_protect, evu_blocktime1,2,3, block_heater, Solar
} hpInput[4];
// Heater limits
uint8_t maxHeatComp_;
uint8_t maxHeatHeat_;
uint8_t maxHeatDhw_;
// Alternative Heatsource AM200
int16_t cylTopTemp_; // TB1
int16_t cylCenterTemp_; // TB2
@@ -269,6 +280,9 @@ class Boiler : public EMSdevice {
void process_HpPower(std::shared_ptr<const Telegram> telegram);
void process_HpOutdoor(std::shared_ptr<const Telegram> telegram);
void process_HpPool(std::shared_ptr<const Telegram> telegram);
void process_HpInput(std::shared_ptr<const Telegram> telegram);
void process_HpInConfig(std::shared_ptr<const Telegram> telegram);
void process_HpHeaterConfig(std::shared_ptr<const Telegram> telegram);
void process_HybridHp(std::shared_ptr<const Telegram> telegram);
void process_amTempMessage(std::shared_ptr<const Telegram> telegram);
void process_amStatusMessage(std::shared_ptr<const Telegram> telegram);
@@ -331,6 +345,29 @@ class Boiler : public EMSdevice {
bool set_blockTerm(const char * value, const int8_t id); // pos 17: Config of block terminal: NO(00), NC(01)
bool set_blockHyst(const char * value, const int8_t id); // pos 14?: Hyst. for bolier block (K)
bool set_releaseWait(const char * value, const int8_t id); // pos 15: Boiler release wait time (min)
bool set_HpInLogic(const char * value, const int8_t id);
bool set_HpIn1Logic(const char * value, const int8_t id) {
return set_HpInLogic(value, 1);
}
bool set_HpIn2Logic(const char * value, const int8_t id) {
return set_HpInLogic(value, 2);
}
bool set_HpIn3Logic(const char * value, const int8_t id) {
return set_HpInLogic(value, 3);
}
bool set_HpIn4Logic(const char * value, const int8_t id) {
return set_HpInLogic(value, 4);
}
bool set_maxHeat(const char * value, const int8_t id);
bool set_maxHeatComp(const char * value, const int8_t id) {
return set_maxHeat(value, 2);
}
bool set_maxHeatHeat(const char * value, const int8_t id) {
return set_maxHeat(value, 3);
}
bool set_maxHeatDhw(const char * value, const int8_t id) {
return set_maxHeat(value, 4);
}
/*
bool set_hybridStrategy(const char * value, const int8_t id);

View File

@@ -686,6 +686,7 @@ void Thermostat::process_JunkersSet(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hc->control, 1); // remote: 0-off, 1-FB10, 2-FB100
has_enumupdate(telegram, hc->program, 13, 1); // 1-6: 1 = A, 2 = B,...
has_enumupdate(telegram, hc->mode, 14, 1); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto
has_update(telegram, hc->roomsensor, 9); // 1-intern, 2-extern, 3-autoselect the lower value
}
// type 0x0179, ff
@@ -1702,6 +1703,24 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
return false;
}
// Set sensor for Junkers, 1-external (from remote), 2-internal, 3-minimum value from int/ext
bool Thermostat::set_roomsensor(const char * value, const int8_t id) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
return false;
}
uint8_t ctrl = 0;
if (model() == EMS_DEVICE_FLAG_JUNKERS && !has_flags(EMS_DEVICE_FLAG_JUNKERS_OLD)) {
if (Helpers::value2enum(value, ctrl, FL_(enum_roomsensor))) {
write_command(set_typeids[hc->hc()], 9, ctrl);
return true;
}
}
return false;
}
// sets the thermostat ww working mode, where mode is a string, ems and ems+
bool Thermostat::set_wwmode(const char * value, const int8_t id) {
uint8_t set = 0xFF;
@@ -3715,7 +3734,9 @@ void Thermostat::register_device_values() {
DeviceValueType::INT,
FL_(ibaMinExtTemperature),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_minexttemp));
MAKE_CF_CB(set_minexttemp),
-30,
0);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&tempsensor1_,
DeviceValueType::SHORT,
@@ -3781,12 +3802,8 @@ void Thermostat::register_device_values() {
MAKE_CF_CB(set_wwDisinfectHour),
0,
23);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
&wwMaxTemp_,
DeviceValueType::UINT,
FL_(wwMaxTemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_wwMaxTemp));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA_WW, &wwMaxTemp_, DeviceValueType::UINT, FL_(wwMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwMaxTemp), 60, 80);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
&wwOneTimeKey_,
DeviceValueType::BOOL,
@@ -3930,7 +3947,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
if (has_flags(EMS_DEVICE_FLAG_NO_WRITE)) {
register_device_value(tag, &hc->selTemp, DeviceValueType::SHORT, seltemp_divider, FL_(selRoomTemp), DeviceValueUOM::DEGREES);
} else {
register_device_value(tag, &hc->selTemp, DeviceValueType::SHORT, seltemp_divider, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 0, 29);
register_device_value(tag, &hc->selTemp, DeviceValueType::SHORT, seltemp_divider, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 0, 30);
}
register_device_value(tag, &hc->roomTemp, DeviceValueType::SHORT, roomtemp_divider, FL_(roomTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(climate), DeviceValueUOM::NONE);
@@ -4121,14 +4138,16 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
tag, &hc->daytemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
register_device_value(
tag, &hc->nighttemp, DeviceValueType::UINT, DeviceValueNumOp::DV_NUMOP_DIV2, FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp));
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp), 30, 90);
register_device_value(tag,
&hc->offsettemp,
DeviceValueType::INT,
DeviceValueNumOp::DV_NUMOP_DIV2,
FL_(offsettemp),
DeviceValueUOM::DEGREES_R,
MAKE_CF_CB(set_offsettemp));
MAKE_CF_CB(set_offsettemp),
-5,
5);
register_device_value(tag,
&hc->holidaytemp,
DeviceValueType::UINT,
@@ -4140,13 +4159,13 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp), 9, 25);
register_device_value(tag, &hc->summermode, DeviceValueType::ENUM, FL_(enum_summer), FL_(summermode), DeviceValueUOM::NONE);
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, FL_(holidaymode), DeviceValueUOM::NONE);
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp), -20, 10);
register_device_value(
tag, &hc->nofrostmode, DeviceValueType::ENUM, FL_(enum_nofrostmode), FL_(nofrostmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_nofrostmode));
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, FL_(roominfluence), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_roominfluence));
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, FL_(flowtempoffset), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_flowtempoffset));
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, FL_(roominfluence), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_roominfluence), 0, 10);
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp), 5, 70);
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp), 30, 90);
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, FL_(flowtempoffset), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_flowtempoffset), 0, 20);
register_device_value(
tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatingtype));
register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(reducemode), DeviceValueUOM::NONE, MAKE_CF_CB(set_reducemode));
@@ -4156,8 +4175,8 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->holiday, DeviceValueType::STRING, FL_(tpl_holidays), FL_(holidays), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday));
register_device_value(tag, &hc->vacation, DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations), DeviceValueUOM::NONE, MAKE_CF_CB(set_vacation));
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode2), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, &hc->pause, DeviceValueType::UINT, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause));
register_device_value(tag, &hc->party, DeviceValueType::UINT, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party));
register_device_value(tag, &hc->pause, DeviceValueType::UINT, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause), 0, 99);
register_device_value(tag, &hc->party, DeviceValueType::UINT, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party), 0, 99);
register_device_value(tag,
&hc->tempautotemp,
DeviceValueType::UINT,
@@ -4165,9 +4184,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
FL_(tempautotemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_tempautotemp));
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp));
register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp));
register_device_value(tag, &hc->vacreducetemp, DeviceValueType::INT, FL_(vacreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_vacreducetemp));
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp), -30, 10);
register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp), -20, 10);
register_device_value(tag, &hc->vacreducetemp, DeviceValueType::INT, FL_(vacreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_vacreducetemp), -20, 10);
register_device_value(
tag, &hc->vacreducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(vacreducemode), DeviceValueUOM::NONE, MAKE_CF_CB(set_vacreducemode));
register_device_value(tag,
@@ -4201,6 +4220,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode4), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, &hc->remotetemp, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(remotetemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->roomsensor, DeviceValueType::ENUM, FL_(enum_roomsensor), FL_(roomsensor), DeviceValueUOM::NONE, MAKE_CF_CB(set_roomsensor));
break;
default:
break;

View File

@@ -84,10 +84,11 @@ class Thermostat : public EMSdevice {
uint8_t climate;
uint8_t switchonoptimization;
uint8_t statusbyte; // from RC300monitor
// RC 10
uint8_t reducehours; // night reduce duration
uint16_t reduceminutes; // remaining minutes to night->day
// FW100 temperature
uint8_t roomsensor; // 1-intern, 2-extern, 3-autoselect the lower value
uint8_t hc_num() const {
return hc_num_;
@@ -489,6 +490,7 @@ class Thermostat : public EMSdevice {
bool set_electricFactor(const char * value, const int8_t id);
bool set_delayBoiler(const char * value, const int8_t id);
bool set_tempDiffBoiler(const char * value, const int8_t id);
bool set_roomsensor(const char * value, const int8_t id);
};
} // namespace emsesp

View File

@@ -472,6 +472,7 @@ void EMSdevice::add_device_value(uint8_t tag,
// add the device entity
devicevalues_.emplace_back(
device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, custom_fullname, uom, has_cmd, min, max, state);
devicevalues_.back().set_custom_minmax();
// add a new command if it has a function attached
if (!has_cmd) {
@@ -873,7 +874,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
// as generate_values_web() but stripped down to only show all entities and their state
// this is used only for WebCustomizationService::device_entities()
void EMSdevice::generate_values_web_customization(JsonArray & output) {
for (const auto & dv : devicevalues_) {
for (auto & dv : devicevalues_) {
// also show commands and entities that have an empty full name
JsonObject obj = output.createNestedObject();
@@ -958,8 +959,9 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
}
// add the custom name, is optional
if (!dv.custom_fullname.empty()) {
obj["cn"] = dv.custom_fullname;
std::string custom_fullname = dv.get_custom_fullname();
if (!custom_fullname.empty()) {
obj["cn"] = custom_fullname;
}
} else {
// it's a command
@@ -968,6 +970,16 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
obj["m"] = dv.state >> 4; // send back the mask state. We're only interested in the high nibble
obj["w"] = dv.has_cmd; // if writable
// set the custom min and max values if there are any
int16_t dv_set_min;
uint16_t dv_set_max;
if (dv.get_custom_min(dv_set_min)) {
obj["mi"] = dv_set_min;
}
if (dv.get_custom_max(dv_set_max)) {
obj["ma"] = dv_set_max;
}
}
}
@@ -1009,7 +1021,7 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
} else {
dv.custom_fullname = "";
}
dv.set_custom_minmax();
return;
}
}
@@ -1405,6 +1417,24 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
// else if (dv.type == DeviceValueType::CMD && output_target != EMSdevice::OUTPUT_TARGET::MQTT) {
// json[name] = "";
// }
// check for value outside min/max range and adapt the limits to avoid HA complains
// Should this also check for api output?
if ((output_target == OUTPUT_TARGET::MQTT) && (dv.min != 0 || dv.max != 0)) {
if (json[name].is<float>() || json[name].is<int>()) {
int v = json[name];
if (fahrenheit) {
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
}
if (v < dv.min) {
dv.min = v;
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
} else if (v > dv.max) {
dv.max = v;
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
}
}
}
}
}
}

View File

@@ -326,13 +326,66 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, int16_t & dv_set_max) {
return false; // nothing changed, not supported
}
// returns the translated fullname or the custom fullname (if provided)
// always returns a std::string
std::string DeviceValue::get_fullname() const {
if (custom_fullname.empty()) {
return Helpers::translated_word(fullname);
// extract custom min from custom_fullname
bool DeviceValue::get_custom_min(int16_t & val) {
auto min_pos = custom_fullname.find('>');
bool has_min = (min_pos != std::string::npos);
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
if (has_min) {
int v = Helpers::atoint(custom_fullname.substr(min_pos + 1).c_str());
if (fahrenheit) {
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
}
if (v > max) {
return false;
}
val = v;
}
return has_min;
}
// extract custom max from custom_fullname
bool DeviceValue::get_custom_max(uint16_t & val) {
auto max_pos = custom_fullname.find('<');
bool has_max = (max_pos != std::string::npos);
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
if (has_max) {
int v = Helpers::atoint(custom_fullname.substr(max_pos + 1).c_str());
if (fahrenheit) {
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
}
if (v < 0 || v < min) {
return false;
}
val = v;
}
return has_max;
}
// sets min max to stored custom values (if set)
void DeviceValue::set_custom_minmax() {
get_custom_min(min);
get_custom_max(max);
}
std::string DeviceValue::get_custom_fullname() const {
auto min_pos = custom_fullname.find('>');
auto max_pos = custom_fullname.find('<');
auto minmax_pos = min_pos < max_pos ? min_pos : max_pos;
if (minmax_pos != std::string::npos) {
return custom_fullname.substr(0, minmax_pos);
}
return custom_fullname;
}
// returns the translated fullname or the custom fullname (if provided)
// always returns a std::string
std::string DeviceValue::get_fullname() const {
std::string customname = get_custom_fullname();
if (customname.empty()) {
return Helpers::translated_word(fullname);
}
return customname;
}
} // namespace emsesp

View File

@@ -179,6 +179,10 @@ class DeviceValue {
bool hasValue() const;
bool get_min_max(int16_t & dv_set_min, int16_t & dv_set_max);
void set_custom_minmax();
bool get_custom_min(int16_t & val);
bool get_custom_max(uint16_t & val);
std::string get_custom_fullname() const;
std::string get_fullname() const;
// dv state flags

View File

@@ -177,6 +177,13 @@ MAKE_PSTR_LIST(progc, F("prog c"))
MAKE_PSTR_LIST(progd, F("prog d"))
MAKE_PSTR_LIST(proge, F("prog e"))
MAKE_PSTR_LIST(progf, F("prog f"))
MAKE_PSTR_LIST(rc35, F("RC35"))
MAKE_PSTR_LIST(0kW, F("0 kW"))
MAKE_PSTR_LIST(2kW, F("2 kW"))
MAKE_PSTR_LIST(3kW, F("3 kW"))
MAKE_PSTR_LIST(4kW, F("4 kW"))
MAKE_PSTR_LIST(6kW, F("6 kW"))
MAKE_PSTR_LIST(9kW, F("9 kW"))
// templates - this are not translated and will be saved under optons_single
MAKE_PSTR_LIST(tpl_datetime, F("Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >"))
@@ -184,6 +191,8 @@ MAKE_PSTR_LIST(tpl_switchtime, F("Format: <nn> [ not_set | day hh:mm on|off ]"))
MAKE_PSTR_LIST(tpl_switchtime1, F("Format: <nn> [ not_set | day hh:mm Tn ]"))
MAKE_PSTR_LIST(tpl_holidays, F("Format: < dd.mm.yyyy-dd.mm.yyyy >"))
MAKE_PSTR_LIST(tpl_date, F("Format: < dd.mm.yyyy >"))
MAKE_PSTR_LIST(tpl_input, F("Format: <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]"))
MAKE_PSTR_LIST(tpl_input4, F("Format: <inv>[<comp><aux><cool><heat><dhw><pv>]"))
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
// These don't need translating, it will mess up HA and the API
@@ -290,6 +299,7 @@ MAKE_PSTR_ENUM(enum_comfort, FL_(hot), FL_(eco), FL_(intelligent))
MAKE_PSTR_ENUM(enum_comfort1, FL_(high_comfort), FL_(eco))
MAKE_PSTR_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer))
MAKE_PSTR_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error))
MAKE_PSTR_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
// thermostat lists
MAKE_PSTR_ENUM(enum_ibaMainDisplay,
@@ -339,6 +349,7 @@ MAKE_PSTR_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoin
MAKE_PSTR_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
MAKE_PSTR_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
MAKE_PSTR_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
MAKE_PSTR_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
MAKE_PSTR_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))

View File

@@ -74,6 +74,9 @@ MAKE_PSTR_LIST(co2_cost_mix, F("co2 cost mix"), F("Kostenmix"), F("Kostenmix"),
MAKE_PSTR_LIST(analog, F("analog"), F("analog"), F("analoog"), F("analog"))
MAKE_PSTR_LIST(normal, F("normal"), F("normal"), F("normaal"), F("normal"))
MAKE_PSTR_LIST(blocking, F("blocking"), F("Blockierung"), F("Blokkering"), F("Blockering"))
MAKE_PSTR_LIST(extern, F("extern"), F("extern"), F("extern"), F("extern"))
MAKE_PSTR_LIST(intern, F("intern"), F("intern"), F("intern"), F("intern"))
MAKE_PSTR_LIST(lower, F("lower"), F("niedirger"), F("lager"), F("lägre"))
// boiler
MAKE_PSTR_LIST(time, F("time"), F("Zeit"), F("Tijd"), F("Tid"))
@@ -457,6 +460,9 @@ MAKE_PSTR_LIST(hpIn1Opt, F("hpin1opt"), F("input 1 options"), F("Eingang 1 Einst
MAKE_PSTR_LIST(hpIn2Opt, F("hpin2opt"), F("input 2 options"), F("Eingang 2 Einstellung"), F("Instelling input 2"), F("Inställningar Ingång 2"))
MAKE_PSTR_LIST(hpIn3Opt, F("hpin3opt"), F("input 3 options"), F("Eingang 3 Einstellung"), F("Instelling input 3"), F("Inställningar Ingång 3"))
MAKE_PSTR_LIST(hpIn4Opt, F("hpin4opt"), F("input 4 options"), F("Eingang 4 Einstellung"), F("Instelling input 4"), F("Inställningar Ingång 4"))
MAKE_PSTR_LIST(maxHeatComp, F("maxheatcomp"), F("heat limit compressor"), F("Heizgrenze Kompressor"), F("heat limit compressor"), F("heat limit compressor"))
MAKE_PSTR_LIST(maxHeatHeat, F("maxheatheat"), F("heat limit heating"), F("Heizgrenze Heizen"), F("heat limit heating"), F("heat limit heating"))
MAKE_PSTR_LIST(maxHeatDhw, F("maxheatdhw"), F("heat limit dhw"), F("Heizgrenze Warmwasser"), F("heat limit dhw"), F("heat limit dhw"))
// hybrid heatpump
MAKE_PSTR_LIST(hybridStrategy, F("hybridstrategy"), F("hybrid control strategy"), F("Hybrid Strategie"), F("Hybride strategie"), F("Hybrid kontrollstrategi"))
@@ -589,7 +595,7 @@ MAKE_PSTR_LIST(wwSelTempSingle,
MAKE_PSTR_LIST(wwCylMiddleTemp,
F("wwcylmiddletemp"),
F("cylinder middle temperature (TS3)"),
F("Speichertemperature Mitte"),
F("Speichertemperatur Mitte"),
F("Buffer temperatuur midden"),
F("Cylinder Temperatur Mitten (TS3)"))
@@ -814,6 +820,7 @@ MAKE_PSTR_LIST(hpoperatingmode,
MAKE_PSTR_LIST(hpoperatingstate, F("hpoperatingstate"), F("heatpump operating state"), F("WP Arbeitsweise"), F("Huidige modus warmtepomp"), F("Värmepump Driftstatus"))
MAKE_PSTR_LIST(controlmode, F("controlmode"), F("control mode"), F("Kontrollmodus"), F("Comtrolemodus"), F("Kontrolläge"))
MAKE_PSTR_LIST(control, F("control"), F("control device"), F("Fernsteuerung"), F("Afstandsbedieding"), F("Kontrollenhet"))
MAKE_PSTR_LIST(roomsensor, F("roomsensor"), F("room sensor"), F("Raumsensor"), F("Ruimtesensor"), F("Rumssensor"))
MAKE_PSTR_LIST(program, F("program"), F("program"), F("Programm"), F("Programma"), F("Program"))
MAKE_PSTR_LIST(pause, F("pause"), F("pause time"), F("Pausenzeit"), F("Pausetijd"), F("Paustid"))
MAKE_PSTR_LIST(party, F("party"), F("party time"), F("Partyzeit"), F("Partytijd"), F("Partytid"))

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.5.0b3"
#define EMSESP_APP_VERSION "3.5.0b4"