From 2f66fec748bf0ce34596236fb6bc83ce0afbe249 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 8 Jul 2024 08:06:27 +0200 Subject: [PATCH 1/9] add CMD to api/mqtt to show it in ioBroker --- src/emsdevice.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 3153f94b1..1b527f988 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -519,7 +519,6 @@ void EMSdevice::add_device_value(int8_t tag, // to b uint32_t max // max allowed value ) { // initialize the device value depending on it's type - // ignoring DeviceValueType::CMD if (type == DeviceValueType::STRING) { *(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later @@ -1761,10 +1760,10 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons } } - // commenting out as we don't want Commands in Console ('show values') - // else if (dv.type == DeviceValueType::CMD && output_target != EMSdevice::OUTPUT_TARGET::MQTT) { - // json[name] = ""; - // } + // we don't want Commands in Console ('show values') + else if (dv.type == DeviceValueType::CMD && output_target != EMSdevice::OUTPUT_TARGET::CONSOLE) { + json[name] = ""; + } // check for value outside min/max range and adapt the limits to avoid HA complains // Should this also check for api output? From d77459106582d449b93a9e7c70743c08b9183d68 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 8 Jul 2024 10:20:13 +0200 Subject: [PATCH 2/9] Scheduler name optional --- interface/src/project/SchedulerDialog.tsx | 2 +- interface/src/project/validators.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/project/SchedulerDialog.tsx b/interface/src/project/SchedulerDialog.tsx index c1eff1a4d..b168f7c81 100644 --- a/interface/src/project/SchedulerDialog.tsx +++ b/interface/src/project/SchedulerDialog.tsx @@ -344,7 +344,7 @@ const SchedulerDialog = ({ si.name === name) ) { callback('Name already in use'); @@ -303,10 +304,9 @@ export const schedulerItemValidation = ( ) => new Schema({ name: [ - { required: true, message: 'Name is required' }, { type: 'string', - pattern: /^[a-zA-Z0-9_\\.]{1,15}$/, + pattern: /^[a-zA-Z0-9_\\.]{0,15}$/, message: "Must be <15 characters: alpha numeric, '_' or '.'" }, ...[uniqueNameValidator(schedule, scheduleItem.o_name)] From 3d7378a1a8537d822f2d9d7592fc546183f5a80a Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 8 Jul 2024 18:02:14 +0200 Subject: [PATCH 3/9] cmd info shows right type (number/enum), no value --- src/emsdevice.cpp | 13 +++++++++---- src/web/WebSchedulerService.cpp | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 1b527f988..edef31030 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -1548,12 +1548,16 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t break; case DeviceValueType::CMD: - json[type] = F_(command); if (dv.options_size > 1) { + json[type] = F_(enum); JsonArray enum_ = json[F_(enum)].to(); for (uint8_t i = 0; i < dv.options_size; i++) { enum_.add(Helpers::translated_word(dv.options[i])); } + } else if (dv.uom != DeviceValueUOM::NONE) { + json[type] = F_(number); + } else { + json[type] = F_(command); } break; @@ -1581,10 +1585,11 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t json["writeable"] = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY); json["visible"] = !dv.has_state(DeviceValueState::DV_WEB_EXCLUDE); + // commented out, leads to issues if type is set to number // if there is no value, mention it - if (!json.containsKey(value)) { - json[value] = "not set"; - } + // if (!json.containsKey(value)) { + // json[value] = "not set"; + // } // if we're filtering on an attribute, go find it if (attribute_s) { diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index fb03487cb..45a2a0e6c 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -161,7 +161,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { } } - return (output.size() > 0); + return true; } char command_s[COMMAND_MAX_LENGTH]; From c3ce0c1e2d0147e9e2f33c3a80d8881b8eb566e4 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 8 Jul 2024 19:04:16 +0200 Subject: [PATCH 4/9] send CMD value for number as NAN --- src/emsdevice.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index edef31030..c25f32e1a 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -1767,7 +1767,11 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons // we don't want Commands in Console ('show values') else if (dv.type == DeviceValueType::CMD && output_target != EMSdevice::OUTPUT_TARGET::CONSOLE) { - json[name] = ""; + if (dv.uom == DeviceValueUOM::NONE) { + json[name] = ""; + } else { + json[name] = NAN; + } } // check for value outside min/max range and adapt the limits to avoid HA complains From 46f674f0dcc1c291080812d650af39272383f61b Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 15 Jul 2024 08:07:43 +0200 Subject: [PATCH 5/9] fix info/values command with circuit `api/device/circuit/values` --- src/command.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index f4e57ecc3..643fbb8ae 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -318,15 +318,17 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // determine flags based on id (which is the tag) uint8_t flag = CommandFlag::CMD_FLAG_DEFAULT; - int8_t tag = id; - if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) { - flag = CommandFlag::CMD_FLAG_HC; - } else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) { - flag = CommandFlag::CMD_FLAG_DHW; - } else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) { - flag = CommandFlag::CMD_FLAG_HS; - } else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) { - flag = CommandFlag::CMD_FLAG_AHS; + // info and values works with all tags, keep it default + if (std::string(cmd) != F_(values) && std::string(cmd) != F_(info)) { + if (id >= DeviceValueTAG::TAG_HC1 && id <= DeviceValueTAG::TAG_HC8) { + flag = CommandFlag::CMD_FLAG_HC; + } else if (id >= DeviceValueTAG::TAG_DHW1 && id <= DeviceValueTAG::TAG_DHW10) { + flag = CommandFlag::CMD_FLAG_DHW; + } else if (id >= DeviceValueTAG::TAG_HS1 && id <= DeviceValueTAG::TAG_HS16) { + flag = CommandFlag::CMD_FLAG_HS; + } else if (id >= DeviceValueTAG::TAG_AHS1 && id <= DeviceValueTAG::TAG_AHS1) { + flag = CommandFlag::CMD_FLAG_AHS; + } } // first see if there is a command registered and it's valid From 3d2912b998ef7a68163aee48f9271b61c979ccf9 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 15 Jul 2024 08:08:34 +0200 Subject: [PATCH 6/9] check system/values lower case --- src/system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index 047b9dbb1..374d28b9b 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -1302,7 +1302,7 @@ bool System::get_value_info(JsonObject root, const char * command) { } char cmd[COMMAND_MAX_LENGTH]; - strlcpy(cmd, command, sizeof(cmd)); + strlcpy(cmd, Helpers::toLower(command).c_str(), sizeof(cmd)); char * val = strstr(cmd, "/value"); if (val) { val[0] = '\0'; From 4c6d396d705ffb301a40ed913832863d95dd2f76 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 15 Jul 2024 08:10:21 +0200 Subject: [PATCH 7/9] analogsensor default name, validators --- interface/src/project/Sensors.tsx | 2 +- interface/src/project/validators.ts | 50 ++++++++++++++++++++++------- src/analogsensor.cpp | 4 +-- src/analogsensor.h | 3 ++ 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/interface/src/project/Sensors.tsx b/interface/src/project/Sensors.tsx index d600c3bfc..7f4a91116 100644 --- a/interface/src/project/Sensors.tsx +++ b/interface/src/project/Sensors.tsx @@ -465,7 +465,7 @@ const Sensors: FC = () => { onClose={onTemperatureDialogClose} onSave={onTemperatureDialogSave} selectedItem={selectedTemperatureSensor} - validator={temperatureSensorItemValidation()} + validator={temperatureSensorItemValidation(sensorData.ts)} /> )} {sensorData?.analog_enabled === true && ( diff --git a/interface/src/project/validators.ts b/interface/src/project/validators.ts index c854eb3d2..6dbc81889 100644 --- a/interface/src/project/validators.ts +++ b/interface/src/project/validators.ts @@ -7,7 +7,8 @@ import type { DeviceValue, EntityItem, ScheduleItem, - Settings + Settings, + TemperatureSensor } from './types'; export const GPIO_VALIDATOR = { @@ -289,7 +290,6 @@ export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) = if ( name !== '' && (o_name === undefined || o_name !== name) && - name !== '' && schedule.find((si) => si.name === name) ) { callback('Name already in use'); @@ -307,8 +307,8 @@ export const schedulerItemValidation = ( name: [ { type: 'string', - pattern: /^[a-zA-Z0-9_\\.]{0,15}$/, - message: "Must be <15 characters: alpha numeric, '_' or '.'" + pattern: /^[a-zA-Z0-9_\\.]{0,19}$/, + message: "Must be <20 characters: alpha numeric, '_' or '.'" }, ...[uniqueNameValidator(schedule, scheduleItem.o_name)] ], @@ -349,8 +349,8 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte { required: true, message: 'Name is required' }, { type: 'string', - pattern: /^[a-zA-Z0-9_\\.]{1,15}$/, - message: "Must be <15 characters: alpha numeric, '_' or '.'" + pattern: /^[a-zA-Z0-9_\\.]{1,19}$/, + message: "Must be <20 characters: alpha numeric, '_' or '.'" }, ...[uniqueCustomNameValidator(entity, entityItem.o_name)] ], @@ -388,14 +388,25 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte ] }); -export const temperatureSensorItemValidation = () => +export const uniqueTemperatureNameValidator = (sensors: TemperatureSensor[]) => ({ + validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { + if (n !== '' && sensors.find((ts) => ts.n === n)) { + callback('Name already in use'); + } else { + callback(); + } + } +}); + +export const temperatureSensorItemValidation = (sensors: TemperatureSensor[]) => new Schema({ n: [ { type: 'string', - pattern: /^[a-zA-Z0-9_\\.]{0,17}$/, - message: "Must be <18 characters: alpha numeric, '_' or '.'" - } + pattern: /^[a-zA-Z0-9_\\.]{0,19}$/, + message: "Must be <20 characters: alpha numeric, '_' or '.'" + }, + ...[uniqueTemperatureNameValidator(sensors)] ] }); @@ -413,13 +424,30 @@ export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({ } }); +export const uniqueAnalogNameValidator = (sensors: AnalogSensor[]) => ({ + validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { + if (n !== '' && sensors.find((as) => as.n === n)) { + callback('Name already in use'); + } else { + callback(); + } + } +}); + export const analogSensorItemValidation = ( sensors: AnalogSensor[], creating: boolean, platform: string ) => new Schema({ - n: [{ required: true, message: 'Name is required' }], + n: [ + { + type: 'string', + pattern: /^[a-zA-Z0-9_\\.]{0,19}$/, + message: "Must be <20 characters: alpha numeric, '_' or '.'" + }, + ...[uniqueAnalogNameValidator(sensors)] + ], g: [ { required: true, message: 'GPIO is required' }, platform === 'ESP32-S3' diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index ba050b9eb..469765075 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -759,8 +759,8 @@ AnalogSensor::Sensor::Sensor(const uint8_t gpio, const std::string & name, const // returns name of the analog sensor or creates one if its empty std::string AnalogSensor::Sensor::name() const { if (name_.empty()) { - char name[50]; - snprintf(name, sizeof(name), "Analog Sensor GPIO %02d", gpio_); + char name[20]; + snprintf(name, sizeof(name), "%s_%02d", FL_(AnalogTypeName)[type_], gpio_); return name; } return name_; diff --git a/src/analogsensor.h b/src/analogsensor.h index 43655606b..5636dc779 100644 --- a/src/analogsensor.h +++ b/src/analogsensor.h @@ -27,6 +27,9 @@ namespace emsesp { +// names, same order as AnalogType +MAKE_ENUM_FIXED(AnalogTypeName, "disabled", "dig_in", "counter", "adc", "timer", "rate", "pwm0", "pwm1", "pwm2") + class AnalogSensor { public: class Sensor { From 5b143cd22a24f9459c4db4015327cbc9154ee749 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 15 Jul 2024 08:56:36 +0200 Subject: [PATCH 8/9] change order in "cannot find 'device' in 'command'" --- src/analogsensor.cpp | 2 +- src/temperaturesensor.cpp | 2 +- src/web/WebCustomEntityService.cpp | 2 +- src/web/WebSchedulerService.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index 469765075..39902f7f1 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -711,7 +711,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int } } - return EMSESP::return_not_found(output, "analog sensor", cmd); // not found + return EMSESP::return_not_found(output, cmd, F_(analogsensor)); // not found } void AnalogSensor::addSensorJson(JsonObject output, const Sensor & sensor) { diff --git a/src/temperaturesensor.cpp b/src/temperaturesensor.cpp index 3e703e798..6919cc054 100644 --- a/src/temperaturesensor.cpp +++ b/src/temperaturesensor.cpp @@ -408,7 +408,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons } } - return EMSESP::return_not_found(output, "temperature sensor", cmd); // not found + return EMSESP::return_not_found(output, cmd, F_(temperaturesensor)); // not found } void TemperatureSensor::addSensorJson(JsonObject output, const Sensor & sensor) { diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 408b6279b..612f86b64 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -334,7 +334,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) } } - return EMSESP::return_not_found(output, "custom entity", cmd); // not found + return EMSESP::return_not_found(output, cmd, F_(custom)); // not found } // publish single value diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 80c143920..e9a942749 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -206,7 +206,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { return true; } - return EMSESP::return_not_found(output, "schedule", cmd); // not found + return EMSESP::return_not_found(output, cmd, F_(scheduler)); // not found } // publish single value From ce33ec4bd316860bd32a09031a0b7b5a4765c32c Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 15 Jul 2024 11:16:42 +0200 Subject: [PATCH 9/9] validate names lowerCase --- interface/src/project/validators.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/project/validators.ts b/interface/src/project/validators.ts index 6dbc81889..8fbf50daf 100644 --- a/interface/src/project/validators.ts +++ b/interface/src/project/validators.ts @@ -289,8 +289,8 @@ export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) = ) { if ( name !== '' && - (o_name === undefined || o_name !== name) && - schedule.find((si) => si.name === name) + (o_name === undefined || o_name.toLowerCase() !== name.toLowerCase()) && + schedule.find((si) => si.name.toLowerCase() === name.toLowerCase()) ) { callback('Name already in use'); } else { @@ -333,8 +333,8 @@ export const uniqueCustomNameValidator = ( callback: (error?: string) => void ) { if ( - (o_name === undefined || o_name !== name) && - entity.find((ei) => ei.name === name) + (o_name === undefined || o_name.toLowerCase() !== name.toLowerCase()) && + entity.find((ei) => ei.name.toLowerCase() === name.toLowerCase()) ) { callback('Name already in use'); } else { @@ -390,7 +390,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte export const uniqueTemperatureNameValidator = (sensors: TemperatureSensor[]) => ({ validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { - if (n !== '' && sensors.find((ts) => ts.n === n)) { + if (n !== '' && sensors.find((ts) => ts.n.toLowerCase() === n.toLowerCase())) { callback('Name already in use'); } else { callback(); @@ -426,7 +426,7 @@ export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({ export const uniqueAnalogNameValidator = (sensors: AnalogSensor[]) => ({ validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { - if (n !== '' && sensors.find((as) => as.n === n)) { + if (n !== '' && sensors.find((as) => as.n.toLowerCase() === n.toLowerCase())) { callback('Name already in use'); } else { callback();