This commit is contained in:
MichaelDvP
2023-09-19 14:31:25 +02:00
43 changed files with 320 additions and 248 deletions

View File

@@ -300,7 +300,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
// see if there is a command registered
auto cf = find_command(device_type, device_id, cmd);
// check if its a call to and end-point to a device
// check if its a call to an end-point of a device
// this is used to fetch the attributes of the device entity, or call a command directly
bool single_command = (!value || !strlen(value));
if (single_command) {
@@ -546,8 +546,9 @@ bool Command::device_has_commands(const uint8_t device_type) {
}
if (device_type == EMSdevice::DeviceType::CUSTOM) {
return true; // we always have info
// return (EMSESP::webEntityService.count_entities() != 0);
// if there are no custom entities, don't error but return a message
return true;
// return (EMSESP::webCustomEntityService.count_entities() != 0);
}
if (device_type == EMSdevice::DeviceType::TEMPERATURESENSOR) {
@@ -612,7 +613,7 @@ void Command::show_all(uuid::console::Shell & shell) {
show(shell, EMSdevice::DeviceType::SYSTEM, true);
// show Custom
// if (EMSESP::webEntityService.has_commands()) {
// if (EMSESP::webCustomEntityService.has_commands()) {
shell.print(COLOR_BOLD_ON);
shell.print(COLOR_YELLOW);
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::CUSTOM));

View File

@@ -1074,7 +1074,7 @@ void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint32_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<std::string> & entity_ids) {
void EMSdevice::getCustomizationEntities(std::vector<std::string> & entity_ids) {
for (const auto & dv : devicevalues_) {
char name[100];
name[0] = '\0';

View File

@@ -207,8 +207,8 @@ class EMSdevice {
void add_handlers_ignored(const uint16_t handler);
void set_climate_minmax(uint8_t tag, int16_t min, uint32_t max);
void setCustomEntity(const std::string & entity_id);
void getCustomEntities(std::vector<std::string> & entity_ids);
void setCustomizationEntity(const std::string & entity_id);
void getCustomizationEntities(std::vector<std::string> & 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<const Telegram> telegram);

View File

@@ -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());
@@ -482,7 +482,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();
}
@@ -515,7 +515,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);
@@ -606,7 +606,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
@@ -658,7 +658,8 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> 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) {
@@ -685,7 +686,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];
@@ -896,7 +897,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> 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) {
@@ -1415,7 +1416,7 @@ void EMSESP::scheduled_fetch_values() {
return;
}
}
webEntityService.fetch();
webCustomEntityService.fetch();
no = 0;
}
}
@@ -1499,7 +1500,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)

View File

@@ -47,7 +47,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"
@@ -237,7 +237,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);

View File

@@ -1037,7 +1037,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");
}

View File

@@ -280,6 +280,21 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
ok = true;
}
if (command == "custom_entities") {
shell.printfln("custom entities...");
run_test("general");
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
request.method(HTTP_GET);
request.url("/api/custom");
request.url("/api/custom/boiler_flowtemp");
request.url("/api/custom/boiler_flowtemp2");
EMSESP::webAPIService.webAPIService_get(&request);
#endif
ok = true;
}
if (command == "coldshot") {
shell.printfln("Testing coldshot...");
run_test("general");
@@ -734,7 +749,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (emsdevice->unique_id() == 1) { // thermostat
std::string a = "00hc1/seltemp|new name>5<52";
emsdevice->setCustomEntity(a);
emsdevice->setCustomizationEntity(a);
break;
}
}
@@ -760,7 +775,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice->unique_id() == 1) { // boiler
std::string a = "07wwseltemp";
emsdevice->setCustomEntity(a);
emsdevice->setCustomizationEntity(a);
break;
}
}

View File

@@ -54,7 +54,9 @@ namespace emsesp {
// #define EMSESP_DEBUG_DEFAULT "custom"
// #define EMSESP_DEBUG_DEFAULT "entity_dump"
// #define EMSESP_DEBUG_DEFAULT "memory"
#define EMSESP_DEBUG_DEFAULT "coldshot"
// #define EMSESP_DEBUG_DEFAULT "coldshot"
#define EMSESP_DEBUG_DEFAULT "custom_entities"
class Test {
public:

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.2-dev.0b"
#define EMSESP_APP_VERSION "3.6.2-dev.1a"

View File

@@ -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);

View File

@@ -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<JsonObject>();
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<JsonArray>()) {
for (const JsonObject ei : root["entities"].as<JsonArray>()) {
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,27 +237,30 @@ 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; });
bool WebCustomEntityService::get_value_info(JsonObject & output, const char * cmd) {
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
if (Helpers::toLower(cmd) == F_(commands)) {
output[F_(info)] = Helpers::translated_word(FL_(info_cmd));
output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd));
for (const auto & entity : *entityItems) {
for (const auto & entity : *customEntityItems) {
output[entity.name] = "custom entitiy";
}
return true;
}
if (entityItems->size() == 0) {
// 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 (strlen(cmd) == 0 || Helpers::toLower(cmd) == F_(values) || Helpers::toLower(cmd) == F_(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;
@@ -245,7 +270,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) {
@@ -279,16 +305,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;
}
@@ -305,19 +333,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);
}
}
@@ -325,7 +353,7 @@ void WebEntityService::publish(const bool force) {
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
JsonObject output = doc.to<JsonObject>();
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_) {
@@ -399,39 +427,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<JsonObject>();
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;
@@ -496,10 +524,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,
@@ -509,12 +537,12 @@ void WebEntityService::fetch() {
}
// called on process telegram, read from telegram
bool WebEntityService::get_value(std::shared_ptr<const Telegram> telegram) {
bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> 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);

View File

@@ -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<EntityItem> entityItems;
std::list<CustomEntityItem> 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<WebEntity> {
class WebCustomEntityService : public StatefulService<WebCustomEntity> {
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<const Telegram> 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<WebEntity> {
private:
HttpEndpoint<WebEntity> _httpEndpoint;
FSPersistence<WebEntity> _fsPersistence;
HttpEndpoint<WebCustomEntity> _httpEndpoint;
FSPersistence<WebCustomEntity> _fsPersistence;
std::list<EntityItem> * entityItems; // pointer to the list of entity items
bool ha_registered_ = false;
std::list<CustomEntityItem> * customEntityItems; // pointer to the list of entity items
bool ha_registered_ = false;
};
} // namespace emsesp

View File

@@ -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<JsonObject>();
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<JsonArray>()) {
for (const JsonObject sensorJson : root["ts"].as<JsonArray>()) {
// create each of the sensor, overwriting any previous settings
@@ -118,12 +116,12 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
sensor.id = sensorJson["id"].as<std::string>();
sensor.name = sensorJson["name"].as<std::string>();
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<JsonArray>()) {
for (const JsonObject analogJson : root["as"].as<JsonArray>()) {
// 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<JsonArray>()) {
for (const JsonObject masked_entities : root["masked_entities"].as<JsonArray>()) {
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<JsonObject>()) {
// 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<const char *>());
}
@@ -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(

View File

@@ -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<SensorCustomization> sensorCustomizations; // for sensor names and offsets
std::list<AnalogCustomization> analogCustomizations; // for analog sensors
std::list<EntityCustomization> 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<WebCustomization> {
@@ -94,7 +94,7 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
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;

View File

@@ -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;

View File

@@ -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<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA);
Serial.print("Using custom file: ");
Serial.print(" Using fake scheduler file: ");
serializeJson(root, Serial);
Serial.println(COLOR_RESET);
#endif