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 46226deba..8fbf50daf 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 = { @@ -288,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 { @@ -306,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)] ], @@ -332,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 { @@ -348,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)] ], @@ -387,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.toLowerCase() === n.toLowerCase())) { + 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)] ] }); @@ -412,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.toLowerCase() === n.toLowerCase())) { + 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..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) { @@ -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 { 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 diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 998fdea84..64816cefa 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 @@ -1549,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; @@ -1585,10 +1588,11 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t // TODO refactor to remove containsKey as it's costly and not advisable to use it // https://arduinojson.org/v7/api/jsonobject/containskey/#avoid + // 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) { @@ -1756,10 +1760,14 @@ 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) { + 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 // Should this also check for api output? 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'; 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 622b58b95..e9a942749 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -161,11 +161,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { } } - if (output.size()) { - return true; - } - - return EMSESP::return_not_found(output, "schedule", cmd); // not found + return true; } char command_s[COMMAND_MAX_LENGTH]; @@ -210,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