diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index f53d4b1ca..b6b3d9d48 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -17,6 +17,7 @@ - boiler Bosch C1200W, id 12, [#1536](https://github.com/emsesp/EMS-ESP32/issues/1536) - mixer MM100 telegram 0x2CC [#1554](https://github.com/emsesp/EMS-ESP32/issues/1554) - boiler hpSetDiffPressure [#1563](https://github.com/emsesp/EMS-ESP32/issues/1563) +- custom variables [#1423](https://github.com/emsesp/EMS-ESP32/issues/1423) ## Fixed diff --git a/interface/src/project/SettingsCustomEntities.tsx b/interface/src/project/SettingsCustomEntities.tsx index ee176d32c..dfda883f0 100644 --- a/interface/src/project/SettingsCustomEntities.tsx +++ b/interface/src/project/SettingsCustomEntities.tsx @@ -1,5 +1,6 @@ import AddIcon from '@mui/icons-material/Add'; import CancelIcon from '@mui/icons-material/Cancel'; +import RefreshIcon from '@mui/icons-material/Refresh'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import WarningIcon from '@mui/icons-material/Warning'; import { Button, Typography, Box } from '@mui/material'; @@ -44,6 +45,7 @@ const SettingsCustomEntities: FC = () => { function hasEntityChanged(ei: EntityItem) { return ( ei.id !== ei.o_id || + ei.ram !== ei.o_ram || (ei?.name || '') !== (ei?.o_name || '') || ei.device_id !== ei.o_device_id || ei.type_id !== ei.o_type_id || @@ -52,7 +54,8 @@ const SettingsCustomEntities: FC = () => { ei.factor !== ei.o_factor || ei.value_type !== ei.o_value_type || ei.writeable !== ei.o_writeable || - ei.deleted !== ei.o_deleted + ei.deleted !== ei.o_deleted || + (ei.value || '') !== (ei.o_value || '') ); } @@ -119,6 +122,7 @@ const SettingsCustomEntities: FC = () => { .filter((ei) => !ei.deleted) .map((condensed_ei) => ({ id: condensed_ei.id, + ram: condensed_ei.ram, name: condensed_ei.name, device_id: condensed_ei.device_id, type_id: condensed_ei.type_id, @@ -126,7 +130,8 @@ const SettingsCustomEntities: FC = () => { factor: condensed_ei.factor, uom: condensed_ei.uom, writeable: condensed_ei.writeable, - value_type: condensed_ei.value_type + value_type: condensed_ei.value_type, + value: condensed_ei.value })) }) .then(() => { @@ -174,14 +179,16 @@ const SettingsCustomEntities: FC = () => { setSelectedEntityItem({ id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100), name: '', - device_id: '', - type_id: '', + ram: 0, + device_id: '0', + type_id: '0', offset: 0, factor: 1, uom: 0, value_type: 0, writeable: false, - deleted: false + deleted: false, + value: '' }); setDialogOpen(true); }; @@ -213,7 +220,7 @@ const SettingsCustomEntities: FC = () => { {LL.ID_OF(LL.DEVICE())} {LL.ID_OF(LL.TYPE(1))} {LL.OFFSET()} - {LL.VALUE(1) + ' ' + LL.TYPE(1)} + {LL.TYPE(1)} {LL.VALUE(1)} @@ -224,10 +231,10 @@ const SettingsCustomEntities: FC = () => { {ei.name}  {ei.writeable && } - {showHex(ei.device_id as number, 2)} - {showHex(ei.type_id as number, 3)} - {ei.offset} - {DeviceValueTypeNames[ei.value_type]} + {ei.ram === 1 ? '' : showHex(ei.device_id as number, 2)} + {ei.ram === 1 ? '' : showHex(ei.type_id as number, 3)} + {ei.ram === 1 ? '' : ei.offset} + {ei.ram === 1 ? 'RAM' : DeviceValueTypeNames[ei.value_type]} {formatValue(ei.value, ei.uom)} ))} @@ -278,7 +285,10 @@ const SettingsCustomEntities: FC = () => { - + diff --git a/interface/src/project/SettingsCustomEntitiesDialog.tsx b/interface/src/project/SettingsCustomEntitiesDialog.tsx index f0516f048..691f23b25 100644 --- a/interface/src/project/SettingsCustomEntitiesDialog.tsx +++ b/interface/src/project/SettingsCustomEntitiesDialog.tsx @@ -100,7 +100,7 @@ const SettingsCustomEntitiesDialog = ({ - + - - } - label={LL.WRITEABLE()} - /> - - - 0x }} - /> - - - 0x }} - /> - - - - - BOOL - INT - UINT - SHORT - USHORT - ULONG - TIME - RAW + EMS-{LL.VALUE(1)} + RAM-{LL.VALUE(1)} - - {editItem.value_type !== DeviceValueType.BOOL && editItem.value_type !== DeviceValueType.STRING && ( - <> - - - - - - {DeviceValueUOM_s.map((val, i) => ( - - {val} - - ))} - - - - )} - {editItem.value_type === DeviceValueType.STRING && ( + {editItem.ram === 1 && ( )} + {editItem.ram === 0 && ( + <> + + } + label={LL.WRITEABLE()} + /> + + + 0x }} + /> + + + 0x }} + /> + + + + + + + BOOL + INT + UINT + SHORT + USHORT + ULONG + TIME + RAW + + + + {editItem.value_type !== DeviceValueType.BOOL && editItem.value_type !== DeviceValueType.STRING && ( + <> + + + + + + {DeviceValueUOM_s.map((val, i) => ( + + {val} + + ))} + + + + )} + {editItem.value_type === DeviceValueType.STRING && editItem.device_id !== '0' && ( + + + + )} + + )} diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index 20d8a6518..41e8f6f56 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -325,6 +325,7 @@ export enum ScheduleFlag { export interface EntityItem { id: number; // unique number + ram: number; name: string; device_id: number | string; type_id: number | string; @@ -336,6 +337,7 @@ export interface EntityItem { writeable: boolean; deleted?: boolean; o_id?: number; + o_ram?: number; o_name?: string; o_device_id?: number | string; o_type_id?: number | string; @@ -345,6 +347,7 @@ export interface EntityItem { o_value_type?: number; o_deleted?: boolean; o_writeable?: boolean; + o_value?: any; } export interface Entities { @@ -397,6 +400,6 @@ export const DeviceValueTypeNames = [ 'ULONG', 'TIME', 'ENUM', - 'STRING', + 'RAW', 'CMD' ]; diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index bb5919615..74195e884 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -488,7 +488,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const DeviceValueNumOp::DV_NUMOP_MUL50, FL_(hpSetDiffPress), DeviceValueUOM::MBAR, - MAKE_CF_CB(set_hpDiffPress)); + MAKE_CF_CB(set_hpDiffPress), + 150, + 750); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpCompOn_, DeviceValueType::BOOL, FL_(hpCompOn), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpActivity_, DeviceValueType::ENUM, FL_(enum_hpactivity), FL_(hpActivity), DeviceValueUOM::NONE); // register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpHeatingOn_, DeviceValueType::BOOL, FL_(hpHeatingOn), DeviceValueUOM::NONE); diff --git a/src/version.h b/src/version.h index e6a41a3b6..9988523a9 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.6.5-dev.9" +#define EMSESP_APP_VERSION "3.6.5-dev.10" diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 87623df46..ea0b5ae55 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -48,6 +48,7 @@ void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) { for (const CustomEntityItem & entityItem : webEntity.customEntityItems) { JsonObject ei = entity.add(); ei["id"] = counter++; // id is only used to render the table and must be unique + ei["ram"] = entityItem.ram; ei["device_id"] = entityItem.device_id; ei["type_id"] = entityItem.type_id; ei["offset"] = entityItem.offset; @@ -88,6 +89,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web if (root["entities"].is()) { for (const JsonObject ei : root["entities"].as()) { auto entityItem = CustomEntityItem(); + entityItem.ram = ei["ram"] | 0; entityItem.device_id = ei["device_id"]; // send as numeric, will be converted to string in web entityItem.type_id = ei["type_id"]; entityItem.offset = ei["offset"]; @@ -96,6 +98,14 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web entityItem.uom = ei["uom"]; entityItem.value_type = ei["value_type"]; entityItem.writeable = ei["writeable"]; + entityItem.data = ei["value"].as(); + if (entityItem.ram == 1) { + entityItem.device_id = 0; + entityItem.type_id = 0; + entityItem.uom = 0; + entityItem.value_type = DeviceValueType::STRING; + entityItem.writeable = true; + } if (entityItem.value_type == DeviceValueType::BOOL) { entityItem.value = EMS_VALUE_DEFAULT_BOOL; @@ -107,7 +117,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web entityItem.value = EMS_VALUE_DEFAULT_SHORT; } else if (entityItem.value_type == DeviceValueType::USHORT) { entityItem.value = EMS_VALUE_DEFAULT_USHORT; - } else { // if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) { + } else if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) { entityItem.value = EMS_VALUE_DEFAULT_ULONG; } if (entityItem.factor == 0) { @@ -134,7 +144,9 @@ bool WebCustomEntityService::command_setvalue(const char * value, const std::str EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); for (CustomEntityItem & entityItem : *customEntityItems) { if (Helpers::toLower(entityItem.name) == Helpers::toLower(name)) { - if (entityItem.value_type == DeviceValueType::STRING) { + if (entityItem.ram == 1) { + entityItem.data = value; + } else if (entityItem.value_type == DeviceValueType::STRING) { char telegram[84]; strlcpy(telegram, value, sizeof(telegram)); uint8_t data[EMS_MAX_TELEGRAM_LENGTH]; @@ -274,7 +286,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) for (const CustomEntityItem & entity : *customEntityItems) { render_value(output, entity); } - return (output.size() != 0); + return true; } char command_s[30]; @@ -297,13 +309,15 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) output["readable"] = true; output["writeable"] = entity.writeable; output["visible"] = true; - output["device_id"] = Helpers::hextoa(entity.device_id); - output["type_id"] = Helpers::hextoa(entity.type_id); - output["offset"] = entity.offset; - if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) { - output["factor"] = entity.factor; - } else if (entity.value_type == DeviceValueType::STRING) { - output["bytes"] = (uint8_t)entity.factor; + if (entity.ram == 0) { + output["device_id"] = Helpers::hextoa(entity.device_id); + output["type_id"] = Helpers::hextoa(entity.type_id); + output["offset"] = entity.offset; + if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) { + output["factor"] = entity.factor; + } else if (entity.value_type == DeviceValueType::STRING) { + output["bytes"] = (uint8_t)entity.factor; + } } render_value(output, entity, true); if (attribute_s) { @@ -547,10 +561,21 @@ void WebCustomEntityService::fetch() { EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3}; for (auto & entity : *customEntityItems) { - EMSESP::send_read_request(entity.type_id, - entity.device_id, - entity.offset, - entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]); + if (entity.device_id > 0 && entity.type_id > 0) { // ths excludes also RAM type + bool needFetch = true; + for (const auto & emsdevice : EMSESP::emsdevices) { + if (entity.value_type != DeviceValueType::STRING && emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id)) { + needFetch = false; + break; + } + } + if (needFetch) { + EMSESP::send_read_request(entity.type_id, + entity.device_id, + entity.offset, + entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]); + } + } } // EMSESP::logger().debug("fetch custom entities"); } @@ -563,8 +588,8 @@ bool WebCustomEntityService::get_value(std::shared_ptr telegram) const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3}; for (auto & entity : *customEntityItems) { if (entity.value_type == DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id - && telegram->offset == entity.offset) { - auto data = Helpers::data_to_hex(telegram->message_data, telegram->message_length); + && telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + (uint8_t)entity.factor)) { + auto data = Helpers::data_to_hex(telegram->message_data, (uint8_t)entity.factor); if (entity.data != data) { entity.data = data; if (Mqtt::publish_single()) { @@ -573,9 +598,8 @@ bool WebCustomEntityService::get_value(std::shared_ptr telegram) has_change = true; } } - } - if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id - && telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) { + } else if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id + && telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) { uint32_t value = 0; for (uint8_t i = 0; i < len[entity.value_type]; i++) { value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset]; diff --git a/src/web/WebCustomEntityService.h b/src/web/WebCustomEntityService.h index ccb98afd8..228cd95c6 100644 --- a/src/web/WebCustomEntityService.h +++ b/src/web/WebCustomEntityService.h @@ -38,6 +38,7 @@ class CustomEntityItem { bool writeable; uint32_t value; std::string data; + uint8_t ram; }; class WebCustomEntity {