diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/SettingsCustomization.tsx index 7cff1c4d8..fa276bba6 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/SettingsCustomization.tsx @@ -161,7 +161,7 @@ const SettingsCustomization: FC = () => { const saveCustomization = async () => { if (deviceEntities && selectedDevice) { const masked_entities = deviceEntities - .filter((de) => de.m) + // .filter((de) => de.m) .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.s); try { const response = await EMSESP.writeMaskedEntities({ diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index a2c2d9185..4b62d6f52 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -131,10 +131,10 @@ export interface DeviceValue { n: string; // name c: string; // command l: string[]; // list - h?: string; // help text - s?: string; // steps for up/down - m?: string; // min - x?: string; // max + h?: string; // help text, optional + s?: string; // steps for up/down, optional + m?: string; // min, optional + x?: string; // max, optional } export interface DeviceData { @@ -147,7 +147,7 @@ export interface DeviceEntity { n: string; // name s: string; // shortname m: number; // mask - w?: boolean; // writeable + w: boolean; // writeable } export interface MaskedEntities { diff --git a/mock-api/server.js b/mock-api/server.js index 5b2d5c7d6..6c232f54a 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -634,14 +634,14 @@ const emsesp_deviceentities_2 = [ { v: 5, n: 'selected flow temperature', s: 'selflowtemp', m: 0 }, { v: 0, n: 'burner selected max power', s: 'selburnpow', m: 0 }, { v: 0, n: 'heating pump modulation', s: 'heatingpumpmod', m: 0 }, - { n: 'heating pump 2 modulation', s: 'heatingpump2mod', m: 2 }, - { n: 'outside temperature', s: 'outdoortemp', m: 2 }, + { n: 'heating pump 2 modulation', s: 'heatingpump2mod', m: 0 }, + { n: 'outside temperature', s: 'outdoortemp', m: 0 }, { v: 53, n: 'current flow temperature', s: 'curflowtemp', m: 0 }, { v: 51.8, n: 'return temperature', s: 'rettemp', m: 0 }, - { n: 'mixing switch temperature', s: 'switchtemp', m: 2 }, + { n: 'mixing switch temperature', s: 'switchtemp', m: 0 }, { v: 1.3, n: 'system pressure', s: 'syspress', m: 0 }, { v: 54.6, n: 'actual boiler temperature', s: 'boiltemp', m: 0 }, - { n: 'exhaust temperature', s: 'exhausttemp', m: 2 }, + { n: 'exhaust temperature', s: 'exhausttemp', m: 0 }, { v: false, n: 'gas', s: 'burngas', m: 0 }, { v: false, n: 'gas stage 2', s: 'burngas2', m: 0 }, { v: 0, n: 'flame current', s: 'flamecurr', m: 0 }, @@ -923,22 +923,23 @@ rest_server.post(EMSESP_DEVICEENTITIES_ENDPOINT, (req, res) => { } }) -function myF(entity, de, dd) { +function updateMask(entity, de, dd) { const name = entity.slice(2) - const mask = parseInt(entity.slice(0, 2), 16) - const mask_hex = entity.slice(0, 2) + const new_mask = parseInt(entity.slice(0, 2), 16) objIndex = de.findIndex((obj) => obj.s == name) if (objIndex !== -1) { - de[objIndex].m = mask + de[objIndex].m = new_mask const fullname = de[objIndex].n objIndex = dd.data.findIndex((obj) => obj.n.slice(2) == fullname) if (objIndex !== -1) { - dd.data[objIndex].n = mask_hex + fullname - console.log('Updating: ') - console.log(dd.data[objIndex]) - } else { - console.log("can't locate record for " + fullname) + // see if the mask has changed + const old_mask = parseInt(dd.data[objIndex].n.slice(0, 2), 16) + if (old_mask !== new_mask) { + const mask_hex = entity.slice(0, 2) + console.log('Updating ' + dd.data[objIndex].n + ' -> ' + mask_hex + fullname) + dd.data[objIndex].n = mask_hex + fullname + } } } else { console.log("can't locate record for id " + id) @@ -949,11 +950,11 @@ rest_server.post(EMSESP_MASKED_ENTITIES_ENDPOINT, (req, res) => { const id = req.body.id for (const entity of req.body.entity_ids) { if (id === 1) { - myF(entity, emsesp_deviceentities_1, emsesp_devicedata_1) + updateMask(entity, emsesp_deviceentities_1, emsesp_devicedata_1) } else if (id === 2) { - myF(entity, emsesp_deviceentities_2, emsesp_devicedata_2) + updateMask(entity, emsesp_deviceentities_2, emsesp_devicedata_2) } else if (id === 4) { - myF(entity, emsesp_deviceentities_4, emsesp_devicedata_4) + updateMask(entity, emsesp_deviceentities_4, emsesp_devicedata_4) } } res.sendStatus(200) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 00c05b64d..35b749998 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -404,9 +404,9 @@ void EMSdevice::register_device_value(uint8_t tag, if ((entityCustomization.product_id == product_id()) && (entityCustomization.device_id == device_id())) { std::string entity = tag < DeviceValueTAG::TAG_HC1 ? read_flash_string(short_name) : tag_to_string(tag) + "/" + read_flash_string(short_name); for (std::string entity_id : entityCustomization.entity_ids) { - uint8_t flag = Helpers::hextoint(entity_id.substr(0, 2).c_str()); if (entity_id.substr(2) == entity) { - state = flag << 4; // set state high bits to flag, turn off active and ha flags + uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str()); + state = mask << 4; // set state high bits to flag, turn off active and ha flags break; } } @@ -759,7 +759,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_all(JsonArray & output) { - for (auto & dv : devicevalues_) { + for (const auto & dv : devicevalues_) { // also show commands and entities that have an empty full name JsonObject obj = output.createNestedObject(); @@ -847,30 +847,24 @@ void EMSdevice::generate_values_web_all(JsonArray & output) { } } -// reset all entities to being visible -// this is called before loading in the exclude entities list from the customization service -void EMSdevice::reset_entity_masks() { - for (auto & dv : devicevalues_) { - dv.state &= 0x0F; // clear high nibble - } -} - -// disable/exclude/mask_out a device entity based on the id -void EMSdevice::mask_entity(const std::string & entity_id) { - // first character contains mask flags - uint8_t flag = (Helpers::hextoint(entity_id.substr(0, 2).c_str()) << 4); +// set mask per device entity based on the id which is prefixed with the 2 char hex mask value +// returns true if the entity has a mask set +bool EMSdevice::mask_entity(const std::string & entity_id) { for (auto & dv : devicevalues_) { std::string entity = dv.tag < DeviceValueTAG::TAG_HC1 ? read_flash_string(dv.short_name) : tag_to_string(dv.tag) + "/" + read_flash_string(dv.short_name); if (entity == entity_id.substr(2)) { - // remove ha config on change of dv_readonly flag - if (Mqtt::ha_enabled() && ((dv.state ^ flag) & DeviceValueState::DV_READONLY)) { + uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str()); // first character contains mask flags + if (Mqtt::ha_enabled() && (((dv.state >> 4) ^ mask) & (DeviceValueState::DV_READONLY >> 4))) { + // remove ha config on change of dv_readonly flag dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED); Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true) } - dv.state = (dv.state & 0x0F) | flag; // set state high bits to flag - return; + dv.state = ((dv.state & 0x0F) | (mask << 4)); // set state high bits to flag + return mask; // true if entity mask is not the deafult 0 } } + + return false; } // builds json for a specific device value / entity diff --git a/src/emsdevice.h b/src/emsdevice.h index 99e780bd2..fdfbba7e2 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -183,8 +183,7 @@ class EMSdevice { char * show_telegram_handlers(char * result, const size_t len, const uint8_t handlers); void show_mqtt_handlers(uuid::console::Shell & shell) const; void list_device_entries(JsonObject & output) const; - void mask_entity(const std::string & entity_id); - void reset_entity_masks(); + bool mask_entity(const std::string & entity_id); using process_function_p = std::function)>; diff --git a/src/emsdevicevalue.cpp b/src/emsdevicevalue.cpp index a8a9bfd34..450f53aff 100644 --- a/src/emsdevicevalue.cpp +++ b/src/emsdevicevalue.cpp @@ -134,7 +134,7 @@ size_t DeviceValue::tag_count = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(_ // checks whether the device value has an actual value // returns true if its valid // state is stored in the dv object -bool DeviceValue::hasValue() { +bool DeviceValue::hasValue() const { bool has_value = false; switch (type) { case DeviceValueType::BOOL: diff --git a/src/emsdevicevalue.h b/src/emsdevicevalue.h index dee8f3570..5d6ac776e 100644 --- a/src/emsdevicevalue.h +++ b/src/emsdevicevalue.h @@ -168,20 +168,20 @@ class DeviceValue { , state(state) { } - bool hasValue(); + bool hasValue() const; bool get_min_max(int16_t & dv_set_min, int16_t & dv_set_max); // state flags - inline void add_state(uint8_t s) { + void add_state(uint8_t s) { state |= s; } - inline bool has_state(uint8_t s) const { + bool has_state(uint8_t s) const { return (state & s) == s; } - inline void remove_state(uint8_t s) { + void remove_state(uint8_t s) { state &= ~s; } - inline uint8_t get_state() const { + uint8_t get_state() const { return state; } diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 3733e3a93..abc0af963 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -919,6 +919,10 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model, int16_t dv_set_min, dv_set_max; (void)dv.get_min_max(dv_set_min, dv_set_max); + // determine if we're creating the command topics which we use special HA configs + // unless the entity has been marked as read-only and so it'll default to using the sensor/ type + bool has_cmd = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY); + publish_ha_sensor_config(dv.type, dv.tag, dv.full_name, @@ -926,7 +930,7 @@ void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model, dv.short_name, dv.uom, remove, - dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY), + has_cmd, dv.options, dv.options_size, dv_set_min, diff --git a/src/version.h b/src/version.h index bf2f94b81..b64112942 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.4.0b10" +#define EMSESP_APP_VERSION "3.4.0b11" diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 8c81d172c..7d2f579e7 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -172,11 +172,10 @@ void WebCustomizationService::devices(AsyncWebServerRequest * request) { obj["i"] = emsdevice->unique_id(); // a unique id /* - // shortname - we prefix the count to make it unique uint8_t device_index = EMSESP::device_index(emsdevice->device_type(), emsdevice->unique_id()); if (device_index) { char s[10]; - obj["s"] = emsdevice->device_type_name() + Helpers::smallitoa(s, device_index) + " (" + emsdevice->name() + ")"; + obj["s"] = emsdevice->device_type_name() + Helpers::smallitoa(s, device_index) + " (" + emsdevice->name() + ")"; // shortname - we prefix the count to make it unique } else { obj["s"] = emsdevice->device_type_name() + " (" + emsdevice->name() + ")"; } @@ -221,16 +220,16 @@ void WebCustomizationService::masked_entities(AsyncWebServerRequest * request, J if (emsdevice) { uint8_t unique_device_id = json["id"]; if (emsdevice->unique_id() == unique_device_id) { - // first reset all the entity ids - emsdevice->reset_entity_masks(); - // build a list of entities JsonArray entity_ids_json = json["entity_ids"]; std::vector entity_ids; - for (JsonVariant id : entity_ids_json) { + for (const JsonVariant id : entity_ids_json) { std::string entity_id = id.as(); - emsdevice->mask_entity(entity_id); // this will have immediate affect - entity_ids.push_back(entity_id); + // handle the mask change and add to the list of customized entities + // if the value is different from the default (mask == 0) + if (emsdevice->mask_entity(entity_id)) { + entity_ids.push_back(entity_id); + } } // Save the list to the customization file