Command values for analog/dallas sensors, writeable for analog-out, #758

This commit is contained in:
MichaelDvP
2022-11-21 18:08:16 +01:00
parent 702103aa66
commit 781fe03b5d
8 changed files with 112 additions and 43 deletions

View File

@@ -18,6 +18,7 @@
- Add MD5 check [#637](https://github.com/emsesp/EMS-ESP32/issues/637) - Add MD5 check [#637](https://github.com/emsesp/EMS-ESP32/issues/637)
- Add more bus-ids [#673](https://github.com/emsesp/EMS-ESP32/issues/673) - Add more bus-ids [#673](https://github.com/emsesp/EMS-ESP32/issues/673)
- Use HA connectivity device class for Status [#751](https://github.com/emsesp/EMS-ESP32/issues/751) - Use HA connectivity device class for Status [#751](https://github.com/emsesp/EMS-ESP32/issues/751)
- Add commands for analog sensors outputs
## Fixed ## Fixed
@@ -30,6 +31,8 @@
- render mqtt float json values with trailing zero - render mqtt float json values with trailing zero
- removed flash strings - removed flash strings
- reload page after restart button is pressed - reload page after restart button is pressed
- analog/dallas values command as list like ems-devices
- analog/dallas HA-entities based on id
## **BREAKING CHANGES:** ## **BREAKING CHANGES:**

View File

@@ -1141,7 +1141,7 @@ const DashboardData: FC = () => {
</Grid> </Grid>
</> </>
)} )}
{analog.t === AnalogType.DIGITAL_OUT && (analog.id === '25' || analog.id === '26') && ( {analog.t === AnalogType.DIGITAL_OUT && (analog.g === 25 || analog.g === 26) && (
<> <>
<Grid item xs={4}> <Grid item xs={4}>
<ValidatedTextField <ValidatedTextField
@@ -1157,7 +1157,7 @@ const DashboardData: FC = () => {
</Grid> </Grid>
</> </>
)} )}
{analog.t === AnalogType.DIGITAL_OUT && analog.id !== '25' && analog.id !== '26' && ( {analog.t === AnalogType.DIGITAL_OUT && analog.g !== 25 && analog.g !== 26 && (
<> <>
<Grid item xs={4}> <Grid item xs={4}>
<ValidatedTextField <ValidatedTextField

View File

@@ -39,6 +39,12 @@ void AnalogSensor::start() {
F_(info), F_(info),
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); }, [&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
FL_(info_cmd)); FL_(info_cmd));
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
F_(values),
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, 0, output); },
nullptr,
CommandFlag::HIDDEN); // this command is hidden
Command::add( Command::add(
EMSdevice::DeviceType::ANALOGSENSOR, EMSdevice::DeviceType::ANALOGSENSOR,
F_(setvalue), F_(setvalue),
@@ -111,6 +117,15 @@ void AnalogSensor::reload() {
sensors_.back().set_value(0); // reset value only for new sensors sensors_.back().set_value(0); // reset value only for new sensors
} }
} }
if (sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) {
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
sensor.name.c_str(),
[&](const char * value, const int8_t id) { return command_setvalue(value, sensor.gpio); },
sensor.type == AnalogType::COUNTER ? FL_(counter)
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
: FL_(pwm));
}
} }
return true; return true;
}); });
@@ -287,6 +302,9 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, float offset,
EMSESP::webCustomizationService.update( EMSESP::webCustomizationService.update(
[&](WebCustomization & settings) { [&](WebCustomization & settings) {
for (auto & AnalogCustomization : settings.analogCustomizations) { for (auto & AnalogCustomization : settings.analogCustomizations) {
if (AnalogCustomization.type == AnalogType::COUNTER || AnalogCustomization.type >= AnalogType::DIGITAL_OUT) {
Command::erase_command(EMSdevice::DeviceType::ANALOGSENSOR, AnalogCustomization.name.c_str());
}
if (AnalogCustomization.gpio == gpio) { if (AnalogCustomization.gpio == gpio) {
found_sensor = true; // found the record found_sensor = true; // found the record
// see if it's marked for deletion // see if it's marked for deletion
@@ -434,7 +452,7 @@ void AnalogSensor::publish_values(const bool force) {
config["val_tpl"] = str; config["val_tpl"] = str;
// snprintf(str, sizeof(str), "%s_analog_sensor_%s", Mqtt::basename().c_str(), sensor.name().c_str()); // snprintf(str, sizeof(str), "%s_analog_sensor_%s", Mqtt::basename().c_str(), sensor.name().c_str());
snprintf(str, sizeof(str), "analog_sensor_%s", sensor.name().c_str()); snprintf(str, sizeof(str), "analog_sensor_%d", sensor.gpio());
config["object_id"] = str; config["object_id"] = str;
snprintf(str, sizeof(str), "%s", sensor.name().c_str()); snprintf(str, sizeof(str), "%s", sensor.name().c_str());
@@ -467,6 +485,9 @@ void AnalogSensor::publish_values(const bool force) {
// called from emsesp.cpp, similar to the emsdevice->get_value_info // called from emsesp.cpp, similar to the emsdevice->get_value_info
// searches by name // searches by name
bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) const { bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) const {
if (sensors_.empty()) {
return false;
}
// make a copy of the string command for parsing // make a copy of the string command for parsing
char command_s[30]; char command_s[30];
strlcpy(command_s, cmd, sizeof(command_s)); strlcpy(command_s, cmd, sizeof(command_s));
@@ -480,15 +501,27 @@ bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const i
} }
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
if (strcmp(command_s, sensor.name().c_str()) == 0) { if (strcmp(command_s, sensor.name().c_str()) == 0 || Helpers::atoint(command_s) == sensor.gpio()) {
output["gpio"] = sensor.gpio(); output["gpio"] = sensor.gpio();
output["name"] = sensor.name(); output["name"] = sensor.name();
output["type"] = F_(number); output["type"] = F_(number);
output["analog"] = FL_(list_sensortype)[sensor.type()]; output["analog"] = FL_(list_sensortype)[sensor.type()];
output["uom"] = EMSdevice::uom_to_string(sensor.uom()); output["uom"] = EMSdevice::uom_to_string(sensor.uom());
output["offset"] = sensor.offset(); output["offset"] = sensor.offset();
output["factor"] = sensor.factor(); output["factor"] = sensor.factor();
output["value"] = sensor.value(); output["value"] = sensor.value();
output["writeable"] = sensor.type() == AnalogType::COUNTER || sensor.type() >= AnalogType::DIGITAL_OUT;
// min/max for writeable analogs
if (sensor.type() == AnalogType::COUNTER) {
output["min"] = 0;
output["max"] = 4000000;
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
output["min"] = 0;
output["max"] = sensor.gpio() == 25 || sensor.gpio() == 26 ? 255 : 1;
} else if (sensor.type() >= AnalogType::PWM_0) {
output["min"] = 0;
output["max"] = 100;
}
// if we're filtering on an attribute, go find it // if we're filtering on an attribute, go find it
if (attribute_s) { if (attribute_s) {
if (output.containsKey(attribute_s)) { if (output.containsKey(attribute_s)) {
@@ -518,7 +551,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
} }
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
if (id == -1) { // show number and id if (id == -1) { // show number and id for info command
JsonObject dataSensor = output.createNestedObject(sensor.name()); JsonObject dataSensor = output.createNestedObject(sensor.name());
dataSensor["gpio"] = sensor.gpio(); dataSensor["gpio"] = sensor.gpio();
dataSensor["type"] = F_(number); dataSensor["type"] = F_(number);
@@ -538,8 +571,12 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
dataSensor["frequency"] = sensor.factor(); dataSensor["frequency"] = sensor.factor();
} }
dataSensor["value"] = sensor.value(); dataSensor["value"] = sensor.value();
} else { } else if (id == 0) { // output values command
output[sensor.name()] = sensor.value(); output[sensor.name()] = sensor.value();
} else { // if someone wants gpio numbers
char gpio_str[9];
snprintf(gpio_str, sizeof(gpio_str), "gpio_%02d", sensor.gpio());
output[gpio_str] = sensor.value();
} }
} }

View File

@@ -43,8 +43,9 @@ class AnalogSensor {
} }
std::string name() const; std::string name() const;
void set_name(const std::string & name) {
name_ = name; void set_name(const std::string & name) {
name_ = name;
} }
uint8_t gpio() const { uint8_t gpio() const {

View File

@@ -116,7 +116,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
// handle dead endpoints like api/system or api/boiler // handle dead endpoints like api/system or api/boiler
// default to 'info' for SYSTEM, DALLASENSOR and ANALOGSENSOR, the other devices to 'values' for shortname version // default to 'info' for SYSTEM, DALLASENSOR and ANALOGSENSOR, the other devices to 'values' for shortname version
if (num_paths < (id_n > 0 ? 4 : 3)) { if (num_paths < (id_n > 0 ? 4 : 3)) {
if (device_type < EMSdevice::DeviceType::BOILER) { if (device_type == EMSdevice::DeviceType::SYSTEM) {
command_p = F_(info); command_p = F_(info);
} else { } else {
command_p = F_(values); command_p = F_(values);
@@ -193,20 +193,26 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
return nullptr; return nullptr;
} }
// convert cmd to lowercase and compare
char * lowerCmd = strdup(command);
for (char * p = lowerCmd; *p; p++) {
*p = tolower(*p);
}
// check prefix and valid number range, also check 'id' // check prefix and valid number range, also check 'id'
if (!strncmp(command, "hc", 2) && command[2] >= '1' && command[2] <= '8') { if (!strncmp(lowerCmd, "hc", 2) && command[2] >= '1' && command[2] <= '8') {
id = command[2] - '0'; id = command[2] - '0';
command += 3; command += 3;
} else if (!strncmp(command, "wwc", 3) && command[3] == '1' && command[4] == '0') { } else if (!strncmp(lowerCmd, "wwc", 3) && command[3] == '1' && command[4] == '0') {
id = 19; id = 19;
command += 5; command += 5;
} else if (!strncmp(command, "wwc", 3) && command[3] >= '1' && command[3] <= '9') { } else if (!strncmp(lowerCmd, "wwc", 3) && command[3] >= '1' && command[3] <= '9') {
id = command[3] - '0' + 8; id = command[3] - '0' + 8;
command += 4; command += 4;
} else if (!strncmp(command, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') { } else if (!strncmp(lowerCmd, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') {
id = command[3] - '0' + 10; id = command[3] - '0' + 10;
command += 4; command += 4;
} else if (!strncmp(command, "id", 2) && command[2] >= '1' && command[2] <= '9') { } else if (!strncmp(lowerCmd, "id", 2) && command[2] >= '1' && command[2] <= '9') {
id = command[2] - '0'; id = command[2] - '0';
command += 3; command += 3;
} }
@@ -214,6 +220,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
if (command[0] == '/' || command[0] == '.' || command[0] == '_') { if (command[0] == '/' || command[0] == '.' || command[0] == '_') {
command++; command++;
} }
free(lowerCmd);
// return null for empty command // return null for empty command
if (command[0] == '\0') { if (command[0] == '\0') {
return nullptr; return nullptr;
@@ -332,15 +339,8 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
return nullptr; return nullptr;
} }
// convert cmd to lowercase and compare
char lowerCmd[30];
strlcpy(lowerCmd, cmd, sizeof(lowerCmd));
for (char * p = lowerCmd; *p; p++) {
*p = tolower(*p);
}
for (auto & cf : cmdfunctions_) { for (auto & cf : cmdfunctions_) {
if (!strcmp(lowerCmd, cf.cmd_) && (cf.device_type_ == device_type)) { if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type)) {
return &cf; return &cf;
} }
} }
@@ -348,6 +348,20 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
return nullptr; // command not found return nullptr; // command not found
} }
void Command::erase_command(const uint8_t device_type, const char * cmd) {
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
return;
}
auto it = cmdfunctions_.begin();
for (auto & cf : cmdfunctions_) {
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type)) {
cmdfunctions_.erase(it);
return;
}
it++;
}
}
// list all commands for a specific device, output as json // list all commands for a specific device, output as json
bool Command::list(const uint8_t device_type, JsonObject & output) { bool Command::list(const uint8_t device_type, JsonObject & output) {
if (cmdfunctions_.empty()) { if (cmdfunctions_.empty()) {

View File

@@ -54,12 +54,12 @@ using cmd_json_function_p = std::function<bool(const char * data, const int8_t i
class Command { class Command {
public: public:
struct CmdFunction { struct CmdFunction {
uint8_t device_type_; // DeviceType:: uint8_t device_type_; // DeviceType::
uint8_t flags_; // mqtt flags for command subscriptions uint8_t flags_; // mqtt flags for command subscriptions
const char * cmd_; const char * cmd_;
const cmd_function_p cmdfunction_; cmd_function_p cmdfunction_;
const cmd_json_function_p cmdfunction_json_; cmd_json_function_p cmdfunction_json_;
const char * const * description_; const char * const * description_;
CmdFunction(const uint8_t device_type, CmdFunction(const uint8_t device_type,
const uint8_t flags, const uint8_t flags,
@@ -113,6 +113,7 @@ class Command {
static void show_all(uuid::console::Shell & shell); static void show_all(uuid::console::Shell & shell);
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd); static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
static void erase_command(const uint8_t device_type, const char * cmd);
static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose); static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose);
static void show_devices(uuid::console::Shell & shell); static void show_devices(uuid::console::Shell & shell);
static bool device_has_commands(const uint8_t device_type); static bool device_has_commands(const uint8_t device_type);

View File

@@ -51,6 +51,12 @@ void DallasSensor::start() {
F_(info), F_(info),
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); }, [&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
FL_(info_cmd)); FL_(info_cmd));
Command::add(
EMSdevice::DeviceType::DALLASSENSOR,
F_(values),
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, 0, output); },
nullptr,
CommandFlag::HIDDEN); // this command is hidden
Command::add( Command::add(
EMSdevice::DeviceType::DALLASSENSOR, EMSdevice::DeviceType::DALLASSENSOR,
F_(commands), F_(commands),
@@ -361,14 +367,18 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
char val[10]; char val[10];
if (id == -1) { // show number and id if (id == -1) { // show number and id, info command
JsonObject dataSensor = output.createNestedObject(sensor.name()); JsonObject dataSensor = output.createNestedObject(sensor.name());
dataSensor["id"] = sensor.id(); dataSensor["id"] = sensor.id();
dataSensor["uom"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
dataSensor["type"] = F_(number);
if (Helpers::hasValue(sensor.temperature_c)) { if (Helpers::hasValue(sensor.temperature_c)) {
dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0)); dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
} }
} else if (Helpers::hasValue(sensor.temperature_c)) { } else if (id == 0 && Helpers::hasValue(sensor.temperature_c)) { // values command
output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0)); output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
} else if (Helpers::hasValue(sensor.temperature_c)) {
output[sensor.id()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
} }
} }
@@ -377,6 +387,9 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
// called from emsesp.cpp, similar to the emsdevice->get_value_info // called from emsesp.cpp, similar to the emsdevice->get_value_info
bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) { bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) {
if (sensors_.empty()) {
return false;
}
// make a copy of the string command for parsing // make a copy of the string command for parsing
char command_s[30]; char command_s[30];
strlcpy(command_s, cmd, sizeof(command_s)); strlcpy(command_s, cmd, sizeof(command_s));
@@ -390,7 +403,7 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
} }
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
if (strcmp(command_s, sensor.name().c_str()) == 0) { if (strcmp(command_s, sensor.name().c_str()) == 0 || strcmp(command_s, sensor.id().c_str()) == 0) {
output["id"] = sensor.id(); output["id"] = sensor.id();
output["name"] = sensor.name(); output["name"] = sensor.name();
char val[10]; char val[10];
@@ -399,8 +412,6 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
} }
output["type"] = F_(number); output["type"] = F_(number);
output["min"] = serialized(Helpers::render_value(val, (int8_t)-55, 0, EMSESP::system_.fahrenheit() ? (uint8_t)2 : (uint8_t)0));
output["max"] = serialized(Helpers::render_value(val, (int8_t)125, 0, EMSESP::system_.fahrenheit() ? (uint8_t)2 : (uint8_t)0));
output["uom"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES); output["uom"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
output["writeable"] = false; output["writeable"] = false;
@@ -507,8 +518,8 @@ void DallasSensor::publish_values(const bool force) {
} }
config["val_tpl"] = str; config["val_tpl"] = str;
// snprintf(str, sizeof(str), "%s_temperature_sensor_%s", Mqtt::basename().c_str(), sensor.name().c_str()); // snprintf(str, sizeof(str), "%s_temperature_sensor_%s", Mqtt::basename().c_str(), sensor.id().c_str());
snprintf(str, sizeof(str), "temperature_sensor_%s", sensor.name().c_str()); snprintf(str, sizeof(str), "temperature_sensor_%s", sensor.id().c_str());
config["object_id"] = str; config["object_id"] = str;
snprintf(str, sizeof(str), "%s", sensor.name().c_str()); snprintf(str, sizeof(str), "%s", sensor.name().c_str());

View File

@@ -230,6 +230,8 @@ MAKE_PSTR_LIST(climate, "HA climate config creation")
MAKE_PSTR_LIST(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all") MAKE_PSTR_LIST(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
// sensors // sensors
MAKE_PSTR_LIST(counter, "counter")
MAKE_PSTR_LIST(digital_out, "digital_out")
MAKE_PSTR_LIST(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2") MAKE_PSTR_LIST(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
// watch // watch