diff --git a/interface/src/project/SettingsCustomization.tsx b/interface/src/project/SettingsCustomization.tsx index db41d3761..110535f34 100644 --- a/interface/src/project/SettingsCustomization.tsx +++ b/interface/src/project/SettingsCustomization.tsx @@ -65,7 +65,9 @@ const SettingsCustomization: FC = () => { const { data: devices } = useRequest(EMSESP.readDevices); - const { send: writeCustomEntities } = useRequest((data) => EMSESP.writeCustomEntities(data), { immediate: false }); + const { send: writeCustomizationEntities } = useRequest((data) => EMSESP.writeCustomizationEntities(data), { + immediate: false + }); const { send: readDeviceEntities, onSuccess: onSuccess } = useRequest((data) => EMSESP.readDeviceEntities(data), { initialData: [], @@ -312,7 +314,7 @@ const SettingsCustomization: FC = () => { return; } - await writeCustomEntities({ id: devices?.devices[selectedDevice].i, entity_ids: masked_entities }).catch( + await writeCustomizationEntities({ id: devices?.devices[selectedDevice].i, entity_ids: masked_entities }).catch( (error) => { if (error.message === 'Reboot required') { setRestartNeeded(true); diff --git a/interface/src/project/SettingsEntities.tsx b/interface/src/project/SettingsEntities.tsx index 526754df7..fd97ffe95 100644 --- a/interface/src/project/SettingsEntities.tsx +++ b/interface/src/project/SettingsEntities.tsx @@ -33,12 +33,12 @@ const SettingsEntities: FC = () => { data: entities, send: fetchEntities, error - } = useRequest(EMSESP.readEntities, { + } = useRequest(EMSESP.readCustomEntities, { initialData: [], force: true }); - const { send: writeEntities } = useRequest((data) => EMSESP.writeEntities(data), { immediate: false }); + const { send: writeEntities } = useRequest((data) => EMSESP.writeCustomEntities(data), { immediate: false }); function hasEntityChanged(ei: EntityItem) { return ( diff --git a/interface/src/project/api.ts b/interface/src/project/api.ts index 409f6def2..72f86d5af 100644 --- a/interface/src/project/api.ts +++ b/interface/src/project/api.ts @@ -62,7 +62,7 @@ export const readDeviceEntities = (id: number) => }); export const readDevices = () => alovaInstance.Get('/rest/devices'); export const resetCustomizations = () => alovaInstance.Post('/rest/resetCustomizations'); -export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customEntities', data); +export const writeCustomizationEntities = (data: any) => alovaInstance.Post('/rest/customizationEntities', data); // SettingsScheduler export const readSchedule = () => @@ -85,8 +85,8 @@ export const readSchedule = () => export const writeSchedule = (data: any) => alovaInstance.Post('/rest/schedule', data); // SettingsEntities -export const readEntities = () => - alovaInstance.Get('/rest/entities', { +export const readCustomEntities = () => + alovaInstance.Get('/rest/customentities', { name: 'entities', transformData(data: any) { return data.entities.map((ei: EntityItem) => ({ @@ -104,4 +104,4 @@ export const readEntities = () => })); } }); -export const writeEntities = (data: any) => alovaInstance.Post('/rest/entities', data); +export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customentities', data); diff --git a/mock-api/server.js b/mock-api/server.js index 5d3279d5d..a3ee73053 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -347,7 +347,7 @@ const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile'; const EMSESP_WRITE_VALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeDeviceValue'; const EMSESP_WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeTemperatureSensor'; const EMSESP_WRITE_ANALOG_ENDPOINT = REST_ENDPOINT_ROOT + 'writeAnalogSensor'; -const EMSESP_CUSTOM_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customEntities'; +const EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customizationEntities'; const EMSESP_RESET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'resetCustomizations'; const EMSESP_WRITE_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule'; const EMSESP_WRITE_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'entities'; @@ -2355,7 +2355,7 @@ function updateMask(entity, de, dd) { } } -rest_server.post(EMSESP_CUSTOM_ENTITIES_ENDPOINT, (req, res) => { +rest_server.post(EMSESP_CUSTOMIZATION_ENTITIES_ENDPOINT, (req, res) => { const id = req.body.id; console.log('customization id = ' + id); console.log(req.body.entity_ids); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 2cc94fd22..e5054c223 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -1074,7 +1074,7 @@ void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) { // 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 (not 0 the default) -void EMSdevice::setCustomEntity(const std::string & entity_id) { +void EMSdevice::setCustomizationEntity(const std::string & entity_id) { for (auto & dv : devicevalues_) { char entity_name[70]; if (dv.tag < DeviceValueTAG::TAG_HC1) { @@ -1126,7 +1126,7 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) { } // populate a string vector with entities that have masks set or have a custom name -void EMSdevice::getCustomEntities(std::vector & entity_ids) { +void EMSdevice::getCustomizationEntities(std::vector & entity_ids) { for (const auto & dv : devicevalues_) { char name[100]; name[0] = '\0'; diff --git a/src/emsdevice.h b/src/emsdevice.h index 00ab27f1c..569114ec8 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -191,8 +191,8 @@ class EMSdevice { void add_handlers_ignored(const uint16_t handler); void set_climate_minmax(uint8_t tag, int16_t min, uint16_t max); - void setCustomEntity(const std::string & entity_id); - void getCustomEntities(std::vector & entity_ids); + void setCustomizationEntity(const std::string & entity_id); + void getCustomizationEntities(std::vector & entity_ids); void register_telegram_type(const uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, const process_function_p cb); bool handle_telegram(std::shared_ptr telegram); diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 67a591d11..eca1f7633 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -32,13 +32,13 @@ ESP8266React EMSESP::esp8266React(&webServer, &dummyFS); WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); -WebEntityService EMSESP::webEntityService = WebEntityService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); +WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); #else ESP8266React EMSESP::esp8266React(&webServer, &LittleFS); WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); -WebEntityService EMSESP::webEntityService = WebEntityService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); +WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager()); #endif WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); @@ -481,7 +481,7 @@ void EMSESP::publish_all(bool force) { publish_device_values(EMSdevice::DeviceType::MIXER); publish_other_values(); // switch and heat pump, ... webSchedulerService.publish(); - webEntityService.publish(); + webCustomEntityService.publish(); publish_sensor_values(true); // includes temperature and analog sensors system_.send_heartbeat(); } @@ -514,7 +514,7 @@ void EMSESP::publish_all_loop() { case 5: publish_other_values(); // switch and heat pump webSchedulerService.publish(true); - webEntityService.publish(true); + webCustomEntityService.publish(true); break; case 6: publish_sensor_values(true, true); @@ -605,7 +605,7 @@ void EMSESP::publish_other_values() { // publish_device_values(EMSdevice::DeviceType::ALERT); // publish_device_values(EMSdevice::DeviceType::PUMP); // publish_device_values(EMSdevice::DeviceType::GENERIC); - webEntityService.publish(); + webCustomEntityService.publish(); } // publish both the temperature and analog sensor values @@ -657,7 +657,8 @@ void EMSESP::publish_response(std::shared_ptr telegram) { buffer = nullptr; } -// builds json with the detail of each value, for a specific EMS device type or the temperature sensor +// builds json with the detail of each value, +// for a specific EMS device type or the sensors, scheduler and custom entities bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype) { for (const auto & emsdevice : emsdevices) { if (emsdevice->device_type() == devicetype) { @@ -684,7 +685,7 @@ bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const in // own entities if (devicetype == DeviceType::CUSTOM) { - return EMSESP::webEntityService.get_value_info(root, cmd); + return EMSESP::webCustomEntityService.get_value_info(root, cmd); } char error[100]; @@ -895,7 +896,7 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { } // Check for custom entities reding this telegram - webEntityService.get_value(telegram); + webCustomEntityService.get_value(telegram); // check for common types, like the Version(0x02) if (telegram->type_id == EMSdevice::EMS_TYPE_VERSION) { @@ -1414,7 +1415,7 @@ void EMSESP::scheduled_fetch_values() { return; } } - webEntityService.fetch(); + webCustomEntityService.fetch(); no = 0; } } @@ -1497,7 +1498,7 @@ void EMSESP::start() { webCustomizationService.begin(); // load the customizations webSchedulerService.begin(); // load the scheduler events - webEntityService.begin(); // load the custom telegram reads + webCustomEntityService.begin(); // load the custom telegram reads // start telnet service if it's enabled // default idle is 10 minutes, default write timeout is 0 (automatic) diff --git a/src/emsesp.h b/src/emsesp.h index e83287fde..d31347574 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -46,7 +46,7 @@ #include "web/WebSchedulerService.h" #include "web/WebAPIService.h" #include "web/WebLogService.h" -#include "web/WebEntityService.h" +#include "web/WebCustomEntityService.h" #include "emsdevicevalue.h" #include "emsdevice.h" @@ -235,7 +235,7 @@ class EMSESP { static WebLogService webLogService; static WebCustomizationService webCustomizationService; static WebSchedulerService webSchedulerService; - static WebEntityService webEntityService; + static WebCustomEntityService webCustomEntityService; private: static std::string device_tostring(const uint8_t device_id); diff --git a/src/system.cpp b/src/system.cpp index a00c7e660..6476c0474 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -1031,7 +1031,7 @@ bool System::check_restore() { saveSettings(EMSESP_SCHEDULER_FILE, "Schedule", input); } else if (settings_type == "entities") { // it's a entity file, just replace it and there's no need to reboot - saveSettings(EMSESP_ENTITY_FILE, "Entities", input); + saveSettings(EMSESP_CUSTOMENTITY_FILE, "Entities", input); } else { LOG_ERROR("Unrecognized file uploaded"); } diff --git a/src/version.h b/src/version.h index 149046701..de05626c9 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.6.2-dev.0" +#define EMSESP_APP_VERSION "3.6.2-dev.1" diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index aa56f582b..67eadf422 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -216,7 +216,7 @@ void WebAPIService::getEntities(AsyncWebServerRequest * request) { root["type"] = "entities"; - System::extractSettings(EMSESP_ENTITY_FILE, "Entities", root); + System::extractSettings(EMSESP_CUSTOMENTITY_FILE, "Entities", root); response->setLength(); request->send(response); diff --git a/src/web/WebEntityService.cpp b/src/web/WebCustomEntityService.cpp similarity index 81% rename from src/web/WebEntityService.cpp rename to src/web/WebCustomEntityService.cpp index 1919c5eb3..72af0c429 100644 --- a/src/web/WebEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -22,13 +22,19 @@ namespace emsesp { using namespace std::placeholders; // for `_1` etc -WebEntityService::WebEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) - : _httpEndpoint(WebEntity::read, WebEntity::update, this, server, EMSESP_ENTITY_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED) - , _fsPersistence(WebEntity::read, WebEntity::update, this, fs, EMSESP_ENTITY_FILE, FS_BUFFER_SIZE) { +WebCustomEntityService::WebCustomEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) + : _httpEndpoint(WebCustomEntity::read, + WebCustomEntity::update, + this, + server, + EMSESP_CUSTOMENTITY_SERVICE_PATH, + securityManager, + AuthenticationPredicates::IS_AUTHENTICATED) + , _fsPersistence(WebCustomEntity::read, WebCustomEntity::update, this, fs, EMSESP_CUSTOMENTITY_FILE, FS_BUFFER_SIZE) { } // load the settings when the service starts -void WebEntityService::begin() { +void WebCustomEntityService::begin() { _fsPersistence.readFromFS(); EMSESP::logger().info("Starting Custom entity service"); Mqtt::subscribe(EMSdevice::DeviceType::CUSTOM, "custom/#", nullptr); // use empty function callback @@ -36,10 +42,10 @@ void WebEntityService::begin() { // this creates the entity file, saving it to the FS // and also calls when the Entity web page is refreshed -void WebEntity::read(WebEntity & webEntity, JsonObject & root) { +void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject & root) { JsonArray entity = root.createNestedArray("entities"); uint8_t counter = 0; - for (const EntityItem & entityItem : webEntity.entityItems) { + for (const CustomEntityItem & entityItem : webEntity.customEntityItems) { JsonObject ei = entity.createNestedObject(); ei["id"] = counter++; // id is only used to render the table and must be unique ei["device_id"] = entityItem.device_id; @@ -50,22 +56,38 @@ void WebEntity::read(WebEntity & webEntity, JsonObject & root) { ei["uom"] = entityItem.uom; ei["value_type"] = entityItem.value_type; ei["writeable"] = entityItem.writeable; - EMSESP::webEntityService.render_value(ei, entityItem, true, true); + EMSESP::webCustomEntityService.render_value(ei, entityItem, true, true); } } // call on initialization and also when the Entity web page is updated // this loads the data into the internal class -StateUpdateResult WebEntity::update(JsonObject & root, WebEntity & webEntity) { - for (EntityItem & entityItem : webEntity.entityItems) { +StateUpdateResult WebCustomEntity::update(JsonObject & root, WebCustomEntity & webCustomEntity) { +#ifdef EMSESP_STANDALONE + // invoke some fake data for testing + // clang-format off + /* prettier-ignore */ + const char * json = + "{\"entities\": [{\"id\":0,\"device_id\":8,\"type_id\":24,\"offset\":0,\"factor\":1,\"name\":\"boiler_flowtemp\",\"uom\":1,\"value_type\":1,\"writeable\":true}]}"; + // clang-format on + StaticJsonDocument<500> doc; + deserializeJson(doc, json); + root = doc.as(); + Serial.println(COLOR_BRIGHT_MAGENTA); + Serial.print(" Using fake custom entity file: "); + serializeJson(root, Serial); + Serial.println(COLOR_RESET); +#endif + + for (CustomEntityItem & entityItem : webCustomEntity.customEntityItems) { Command::erase_command(EMSdevice::DeviceType::CUSTOM, entityItem.name.c_str()); } - webEntity.entityItems.clear(); - EMSESP::webEntityService.ha_reset(); + webCustomEntity.customEntityItems.clear(); + EMSESP::webCustomEntityService.ha_reset(); if (root["entities"].is()) { for (const JsonObject ei : root["entities"].as()) { - auto entityItem = EntityItem(); + auto entityItem = CustomEntityItem(); 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"]; @@ -91,13 +113,13 @@ StateUpdateResult WebEntity::update(JsonObject & root, WebEntity & webEntity) { if (entityItem.factor == 0) { entityItem.factor = 1; } - webEntity.entityItems.push_back(entityItem); // add to list + webCustomEntity.customEntityItems.push_back(entityItem); // add to list if (entityItem.writeable) { Command::add( EMSdevice::DeviceType::CUSTOM, - webEntity.entityItems.back().name.c_str(), - [webEntity](const char * value, const int8_t id) { - return EMSESP::webEntityService.command_setvalue(value, webEntity.entityItems.back().name); + webCustomEntity.customEntityItems.back().name.c_str(), + [webCustomEntity](const char * value, const int8_t id) { + return EMSESP::webCustomEntityService.command_setvalue(value, webCustomEntity.customEntityItems.back().name); }, FL_(entity_cmd), CommandFlag::ADMIN_ONLY); @@ -108,9 +130,9 @@ StateUpdateResult WebEntity::update(JsonObject & root, WebEntity & webEntity) { } // set value by api command -bool WebEntityService::command_setvalue(const char * value, const std::string name) { - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); - for (EntityItem & entityItem : *entityItems) { +bool WebCustomEntityService::command_setvalue(const char * value, const std::string name) { + 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) { char telegram[84]; @@ -160,7 +182,7 @@ bool WebEntityService::command_setvalue(const char * value, const std::string na } // output of a single value -void WebEntityService::render_value(JsonObject & output, EntityItem entity, const bool useVal, const bool web) { +void WebCustomEntityService::render_value(JsonObject & output, CustomEntityItem entity, const bool useVal, const bool web) { char payload[12]; std::string name = useVal ? "value" : entity.name; switch (entity.value_type) { @@ -215,26 +237,33 @@ void WebEntityService::render_value(JsonObject & output, EntityItem entity, cons } // process json output for info/commands and value_info -bool WebEntityService::get_value_info(JsonObject & output, const char * cmd) { - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); - if (entityItems->size() == 0) { - return false; +bool WebCustomEntityService::get_value_info(JsonObject & output, const char * cmd) { + EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); + + // if no entries, return a message instead of an error + // https://github.com/emsesp/EMS-ESP32/issues/1297 + if (customEntityItems->size() == 0) { + output["message"] = "no entries"; + return true; } + if (Helpers::toLower(cmd) == "commands") { output["info"] = "list all values"; output["commands"] = "list all commands"; - for (const auto & entity : *entityItems) { + for (const auto & entity : *customEntityItems) { output[entity.name] = "custom entitiy"; } return true; } + if (strlen(cmd) == 0 || Helpers::toLower(cmd) == "values" || Helpers::toLower(cmd) == "info") { // list all names - for (const EntityItem & entity : *entityItems) { + for (const CustomEntityItem & entity : *customEntityItems) { render_value(output, entity); } return (output.size() != 0); } + char command_s[30]; strlcpy(command_s, cmd, sizeof(command_s)); char * attribute_s = nullptr; @@ -244,7 +273,8 @@ bool WebEntityService::get_value_info(JsonObject & output, const char * cmd) { *breakp = '\0'; attribute_s = breakp + 1; } - for (const auto & entity : *entityItems) { + + for (const auto & entity : *customEntityItems) { if (Helpers::toLower(entity.name) == Helpers::toLower(command_s)) { output["name"] = entity.name; if (entity.uom > 0) { @@ -276,16 +306,18 @@ bool WebEntityService::get_value_info(JsonObject & output, const char * cmd) { } } } + if (output.size()) { return true; } } + output["message"] = "unknown command"; return false; } // publish single value -void WebEntityService::publish_single(const EntityItem & entity) { +void WebCustomEntityService::publish_single(const CustomEntityItem & entity) { if (!Mqtt::enabled() || !Mqtt::publish_single()) { return; } @@ -302,19 +334,19 @@ void WebEntityService::publish_single(const EntityItem & entity) { } // publish to Mqtt -void WebEntityService::publish(const bool force) { +void WebCustomEntityService::publish(const bool force) { if (force) { ha_registered_ = false; } if (!Mqtt::enabled()) { return; } - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); - if (entityItems->size() == 0) { + EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); + if (customEntityItems->size() == 0) { return; } if (Mqtt::publish_single() && force) { - for (const EntityItem & entityItem : *entityItems) { + for (const CustomEntityItem & entityItem : *customEntityItems) { publish_single(entityItem); } } @@ -322,7 +354,7 @@ void WebEntityService::publish(const bool force) { DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE); JsonObject output = doc.to(); bool ha_created = ha_registered_; - for (const EntityItem & entityItem : *entityItems) { + for (const CustomEntityItem & entityItem : *customEntityItems) { render_value(output, entityItem); // create HA config if (Mqtt::ha_enabled() && !ha_registered_) { @@ -396,39 +428,39 @@ void WebEntityService::publish(const bool force) { } // count only entities with valid value or command to show in dashboard -uint8_t WebEntityService::count_entities() { - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); - if (entityItems->size() == 0) { +uint8_t WebCustomEntityService::count_entities() { + EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); + if (customEntityItems->size() == 0) { return 0; } DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE); JsonObject output = doc.to(); uint8_t count = 0; - for (const EntityItem & entity : *entityItems) { + for (const CustomEntityItem & entity : *customEntityItems) { render_value(output, entity); count += (output.containsKey(entity.name) || entity.writeable) ? 1 : 0; } return count; } -uint8_t WebEntityService::has_commands() { - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); +uint8_t WebCustomEntityService::has_commands() { + EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); uint8_t count = 0; - for (const EntityItem & entity : *entityItems) { + for (const CustomEntityItem & entity : *customEntityItems) { count += entity.writeable ? 1 : 0; } return count; } // send to dashboard, msgpack don't like serialized, use number -void WebEntityService::generate_value_web(JsonObject & output) { - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); +void WebCustomEntityService::generate_value_web(JsonObject & output) { + EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); output["label"] = (std::string) "Custom Entities"; JsonArray data = output.createNestedArray("data"); uint8_t index = 0; - for (const EntityItem & entity : *entityItems) { + for (const CustomEntityItem & entity : *customEntityItems) { JsonObject obj = data.createNestedObject(); // create the object, we know there is a value obj["id"] = "00" + entity.name; obj["u"] = entity.uom; @@ -493,10 +525,10 @@ void WebEntityService::generate_value_web(JsonObject & output) { } // fetch telegram, called from emsesp::fetch -void WebEntityService::fetch() { - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); +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 : *entityItems) { + for (auto & entity : *customEntityItems) { EMSESP::send_read_request(entity.type_id, entity.device_id, entity.offset, @@ -506,12 +538,12 @@ void WebEntityService::fetch() { } // called on process telegram, read from telegram -bool WebEntityService::get_value(std::shared_ptr telegram) { +bool WebCustomEntityService::get_value(std::shared_ptr telegram) { bool has_change = false; - EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; }); + EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; }); // read-length of BOOL, INT, UINT, SHORT, USHORT, ULONG, TIME const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3}; - for (auto & entity : *entityItems) { + 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); diff --git a/src/web/WebEntityService.h b/src/web/WebCustomEntityService.h similarity index 59% rename from src/web/WebEntityService.h rename to src/web/WebCustomEntityService.h index 7df9c4243..816b1bdb5 100644 --- a/src/web/WebEntityService.h +++ b/src/web/WebCustomEntityService.h @@ -17,15 +17,15 @@ */ #include "../telegram.h" -#ifndef WebEntityService_h -#define WebEntityService_h +#ifndef WebCustomEntityService_h +#define WebCustomEntityService_h -#define EMSESP_ENTITY_FILE "/config/emsespEntity.json" -#define EMSESP_ENTITY_SERVICE_PATH "/rest/entities" // GET and POST +#define EMSESP_CUSTOMENTITY_FILE "/config/emsespEntity.json" +#define EMSESP_CUSTOMENTITY_SERVICE_PATH "/rest/customentities" // GET and POST namespace emsesp { -class EntityItem { +class CustomEntityItem { public: uint8_t id; uint8_t device_id; @@ -40,26 +40,26 @@ class EntityItem { std::string data; }; -class WebEntity { +class WebCustomEntity { public: - std::list entityItems; + std::list customEntityItems; - static void read(WebEntity & webEntity, JsonObject & root); - static StateUpdateResult update(JsonObject & root, WebEntity & webEntity); + static void read(WebCustomEntity & webEntity, JsonObject & root); + static StateUpdateResult update(JsonObject & root, WebCustomEntity & webEntity); }; -class WebEntityService : public StatefulService { +class WebCustomEntityService : public StatefulService { public: - WebEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager); + WebCustomEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager); void begin(); - void publish_single(const EntityItem & entity); + void publish_single(const CustomEntityItem & entity); void publish(const bool force = false); bool command_setvalue(const char * value, const std::string name); bool get_value_info(JsonObject & output, const char * cmd); bool get_value(std::shared_ptr telegram); void fetch(); - void render_value(JsonObject & output, EntityItem entity, const bool useVal = false, const bool web = false); + void render_value(JsonObject & output, CustomEntityItem entity, const bool useVal = false, const bool web = false); uint8_t count_entities(); uint8_t has_commands(); void generate_value_web(JsonObject & output); @@ -69,11 +69,11 @@ class WebEntityService : public StatefulService { private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; - std::list * entityItems; // pointer to the list of entity items - bool ha_registered_ = false; + std::list * customEntityItems; // pointer to the list of entity items + bool ha_registered_ = false; }; } // namespace emsesp diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 2a1932778..6f1e9f711 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -31,8 +31,8 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f securityManager, AuthenticationPredicates::IS_AUTHENTICATED) , _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) - , _masked_entities_handler(CUSTOM_ENTITIES_PATH, - securityManager->wrapCallback(std::bind(&WebCustomizationService::custom_entities, this, _1, _2), + , _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH, + securityManager->wrapCallback(std::bind(&WebCustomizationService::customization_entities, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) { server->on(DEVICE_ENTITIES_PATH, HTTP_GET, @@ -54,10 +54,10 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f } // this creates the customization file, saving it to the FS -void WebCustomization::read(WebCustomization & settings, JsonObject & root) { +void WebCustomization::read(WebCustomization & customizations, JsonObject & root) { // Temperature Sensor customization JsonArray sensorsJson = root.createNestedArray("ts"); - for (const SensorCustomization & sensor : settings.sensorCustomizations) { + for (const SensorCustomization & sensor : customizations.sensorCustomizations) { JsonObject sensorJson = sensorsJson.createNestedObject(); sensorJson["id"] = sensor.id; // ID of chip sensorJson["name"] = sensor.name; // n @@ -66,7 +66,7 @@ void WebCustomization::read(WebCustomization & settings, JsonObject & root) { // Analog Sensor customization JsonArray analogJson = root.createNestedArray("as"); - for (const AnalogCustomization & sensor : settings.analogCustomizations) { + for (const AnalogCustomization & sensor : customizations.analogCustomizations) { JsonObject sensorJson = analogJson.createNestedObject(); sensorJson["gpio"] = sensor.gpio; // g sensorJson["name"] = sensor.name; // n @@ -78,7 +78,7 @@ void WebCustomization::read(WebCustomization & settings, JsonObject & root) { // Masked entities customization JsonArray masked_entitiesJson = root.createNestedArray("masked_entities"); - for (const EntityCustomization & entityCustomization : settings.entityCustomizations) { + for (const EntityCustomization & entityCustomization : customizations.entityCustomizations) { JsonObject entityJson = masked_entitiesJson.createNestedObject(); entityJson["product_id"] = entityCustomization.product_id; entityJson["device_id"] = entityCustomization.device_id; @@ -93,24 +93,22 @@ void WebCustomization::read(WebCustomization & settings, JsonObject & root) { // call on initialization and also when the page is saved via web UI // this loads the data into the internal class -StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & settings) { +StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & customizations) { #ifdef EMSESP_STANDALONE // invoke some fake data for testing - const char * json = "{\"ts\":[],\"as\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom " - "name for heating active\",\"08tapwateractive\"]}]}"; - + const char * json = "{\"ts\":[],\"as\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom " + "name for heating active\",\"08tapwateractive\"]}]}"; StaticJsonDocument<500> doc; deserializeJson(doc, json); root = doc.as(); - Serial.println(COLOR_BRIGHT_MAGENTA); - Serial.print("Using custom file: "); + Serial.print(" Using fake customization file: "); serializeJson(root, Serial); Serial.println(COLOR_RESET); #endif // Temperature Sensor customization - settings.sensorCustomizations.clear(); + customizations.sensorCustomizations.clear(); if (root["ts"].is()) { for (const JsonObject sensorJson : root["ts"].as()) { // create each of the sensor, overwriting any previous settings @@ -118,12 +116,12 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & sensor.id = sensorJson["id"].as(); sensor.name = sensorJson["name"].as(); sensor.offset = sensorJson["offset"]; - settings.sensorCustomizations.push_back(sensor); // add to list + customizations.sensorCustomizations.push_back(sensor); // add to list } } // Analog Sensor customization - settings.analogCustomizations.clear(); + customizations.analogCustomizations.clear(); if (root["as"].is()) { for (const JsonObject analogJson : root["as"].as()) { // create each of the sensor, overwriting any previous settings @@ -134,12 +132,12 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & sensor.factor = analogJson["factor"]; sensor.uom = analogJson["uom"]; sensor.type = analogJson["type"]; - settings.analogCustomizations.push_back(sensor); // add to list + customizations.analogCustomizations.push_back(sensor); // add to list } } // load array of entities id's with masks, building up the object class - settings.entityCustomizations.clear(); + customizations.entityCustomizations.clear(); if (root["masked_entities"].is()) { for (const JsonObject masked_entities : root["masked_entities"].as()) { auto new_entry = EntityCustomization(); @@ -152,7 +150,7 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & } } - settings.entityCustomizations.push_back(new_entry); // save the new object + customizations.entityCustomizations.push_back(new_entry); // save the new object } } @@ -236,7 +234,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request) { // takes a list of updated entities with new masks from the web UI // saves it in the customization service // and updates the entity list real-time -void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, JsonVariant & json) { +void WebCustomizationService::customization_entities(AsyncWebServerRequest * request, JsonVariant & json) { bool need_reboot = false; if (json.is()) { // find the device using the unique_id @@ -256,7 +254,7 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J entity_ids.push_back(id_s); need_reboot = true; } else { - emsdevice->setCustomEntity(id_s); + emsdevice->setCustomizationEntity(id_s); } // emsesp::EMSESP::logger().info(id.as()); } @@ -287,7 +285,7 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J } }); // get list of entities that have masks set or a custom fullname - emsdevice->getCustomEntities(entity_ids); + emsdevice->getCustomizationEntities(entity_ids); // Save the list to the customization file EMSESP::webCustomizationService.update( diff --git a/src/web/WebCustomizationService.h b/src/web/WebCustomizationService.h index 85435496c..ecb4b6a50 100644 --- a/src/web/WebCustomizationService.h +++ b/src/web/WebCustomizationService.h @@ -27,7 +27,7 @@ #define DEVICE_ENTITIES_PATH "/rest/deviceEntities" // POST -#define CUSTOM_ENTITIES_PATH "/rest/customEntities" +#define CUSTOMIZATION_ENTITIES_PATH "/rest/customizationEntities" #define RESET_CUSTOMIZATION_SERVICE_PATH "/rest/resetCustomizations" namespace emsesp { @@ -71,8 +71,8 @@ class WebCustomization { std::list sensorCustomizations; // for sensor names and offsets std::list analogCustomizations; // for analog sensors std::list entityCustomizations; // for a list of entities that have a special mask set - static void read(WebCustomization & settings, JsonObject & root); - static StateUpdateResult update(JsonObject & root, WebCustomization & settings); + static void read(WebCustomization & customizations, JsonObject & root); + static StateUpdateResult update(JsonObject & root, WebCustomization & customizations); }; class WebCustomizationService : public StatefulService { @@ -94,7 +94,7 @@ class WebCustomizationService : public StatefulService { void device_entities(AsyncWebServerRequest * request); // POST - void custom_entities(AsyncWebServerRequest * request, JsonVariant & json); + void customization_entities(AsyncWebServerRequest * request, JsonVariant & json); void reset_customization(AsyncWebServerRequest * request); // command AsyncCallbackJsonWebHandler _masked_entities_handler; diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 287d8e649..747e6a5cb 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -92,7 +92,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) { } // add any custom entities - if (EMSESP::webEntityService.count_entities()) { + if (EMSESP::webCustomEntityService.count_entities()) { JsonObject obj = devices.createNestedObject(); obj["id"] = 99; // the last unique id obj["tn"] = Helpers::translated_word(FL_(custom_device)); // translated device type name @@ -210,7 +210,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) { #ifndef EMSESP_STANDALONE if (id == 99) { JsonObject output = response->getRoot(); - EMSESP::webEntityService.generate_value_web(output); + EMSESP::webCustomEntityService.generate_value_web(output); response->setLength(); request->send(response); return; diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index b7b166a26..ee00954c6 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -57,12 +57,13 @@ StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webSche #ifdef EMSESP_STANDALONE // invoke some fake data for testing const char * json = - "{[{\"id\":1,\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1/mode\",\"value\": \"day\",\"name\": \"turn on central heating\"}]}"; + "{\"schedule\": [{\"id\":1,\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1mode\",\"value\": \"day\",\"name\": \"turn on " + "central heating\"}]}"; StaticJsonDocument<500> doc; deserializeJson(doc, json); root = doc.as(); Serial.println(COLOR_BRIGHT_MAGENTA); - Serial.print("Using custom file: "); + Serial.print(" Using fake scheduler file: "); serializeJson(root, Serial); Serial.println(COLOR_RESET); #endif