mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
Introduce parse_number() helper function (#2659)
This commit is contained in:
parent
219b225ac0
commit
15f9677d33
15 changed files with 70 additions and 36 deletions
|
@ -103,21 +103,21 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_TARGET_TEMPERATURE: {
|
case READ_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->target_temp_ = ftoc(this->target_temp_);
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SET_TARGET_TEMPERATURE: {
|
case SET_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->target_temp_ = ftoc(this->target_temp_);
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_CURRENT_TEMPERATURE: {
|
case READ_CURRENT_TEMPERATURE: {
|
||||||
this->current_temp_ = strtof(this->buf_, nullptr);
|
this->current_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->current_temp_ = ftoc(this->current_temp_);
|
this->current_temp_ = ftoc(this->current_temp_);
|
||||||
this->has_current_temp_ = true;
|
this->has_current_temp_ = true;
|
||||||
|
|
|
@ -74,7 +74,7 @@ void EZOSensor::loop() {
|
||||||
if (buf[0] != 1)
|
if (buf[0] != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float val = strtof((char *) &buf[1], nullptr);
|
float val = parse_number<float>((char *) &buf[1], sizeof(buf) - 1).value_or(0);
|
||||||
this->publish_state(val);
|
this->publish_state(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ static const char *const TAG = "homeassistant.sensor";
|
||||||
void HomeassistantSensor::setup() {
|
void HomeassistantSensor::setup() {
|
||||||
api::global_api_server->subscribe_home_assistant_state(
|
api::global_api_server->subscribe_home_assistant_state(
|
||||||
this->entity_id_, this->attribute_, [this](const std::string &state) {
|
this->entity_id_, this->attribute_, [this](const std::string &state) {
|
||||||
auto val = parse_float(state);
|
auto val = parse_number<float>(state);
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
|
|
|
@ -44,7 +44,7 @@ void HrxlMaxsonarWrComponent::check_buffer_() {
|
||||||
|
|
||||||
if (this->buffer_.length() == MAX_DATA_LENGTH_BYTES && this->buffer_[0] == 'R' &&
|
if (this->buffer_.length() == MAX_DATA_LENGTH_BYTES && this->buffer_[0] == 'R' &&
|
||||||
this->buffer_.back() == static_cast<char>(ASCII_CR)) {
|
this->buffer_.back() == static_cast<char>(ASCII_CR)) {
|
||||||
int millimeters = strtol(this->buffer_.substr(1, MAX_DATA_LENGTH_BYTES - 2).c_str(), nullptr, 10);
|
int millimeters = parse_number<int>(this->buffer_.substr(1, MAX_DATA_LENGTH_BYTES - 2)).value_or(0);
|
||||||
float meters = float(millimeters) / 1000.0;
|
float meters = float(millimeters) / 1000.0;
|
||||||
ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters);
|
ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters);
|
||||||
this->publish_state(meters);
|
this->publish_state(meters);
|
||||||
|
|
|
@ -137,7 +137,7 @@ void MQTTClimateComponent::setup() {
|
||||||
if (traits.get_supports_two_point_target_temperature()) {
|
if (traits.get_supports_two_point_target_temperature()) {
|
||||||
this->subscribe(this->get_target_temperature_low_command_topic(),
|
this->subscribe(this->get_target_temperature_low_command_topic(),
|
||||||
[this](const std::string &topic, const std::string &payload) {
|
[this](const std::string &topic, const std::string &payload) {
|
||||||
auto val = parse_float(payload);
|
auto val = parse_number<float>(payload);
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
||||||
return;
|
return;
|
||||||
|
@ -148,7 +148,7 @@ void MQTTClimateComponent::setup() {
|
||||||
});
|
});
|
||||||
this->subscribe(this->get_target_temperature_high_command_topic(),
|
this->subscribe(this->get_target_temperature_high_command_topic(),
|
||||||
[this](const std::string &topic, const std::string &payload) {
|
[this](const std::string &topic, const std::string &payload) {
|
||||||
auto val = parse_float(payload);
|
auto val = parse_number<float>(payload);
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
||||||
return;
|
return;
|
||||||
|
@ -160,7 +160,7 @@ void MQTTClimateComponent::setup() {
|
||||||
} else {
|
} else {
|
||||||
this->subscribe(this->get_target_temperature_command_topic(),
|
this->subscribe(this->get_target_temperature_command_topic(),
|
||||||
[this](const std::string &topic, const std::string &payload) {
|
[this](const std::string &topic, const std::string &payload) {
|
||||||
auto val = parse_float(payload);
|
auto val = parse_number<float>(payload);
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -24,7 +24,7 @@ void MQTTCoverComponent::setup() {
|
||||||
});
|
});
|
||||||
if (traits.get_supports_position()) {
|
if (traits.get_supports_position()) {
|
||||||
this->subscribe(this->get_position_command_topic(), [this](const std::string &topic, const std::string &payload) {
|
this->subscribe(this->get_position_command_topic(), [this](const std::string &topic, const std::string &payload) {
|
||||||
auto value = parse_float(payload);
|
auto value = parse_number<float>(payload);
|
||||||
if (!value.has_value()) {
|
if (!value.has_value()) {
|
||||||
ESP_LOGW(TAG, "Invalid position value: '%s'", payload.c_str());
|
ESP_LOGW(TAG, "Invalid position value: '%s'", payload.c_str());
|
||||||
return;
|
return;
|
||||||
|
@ -36,7 +36,7 @@ void MQTTCoverComponent::setup() {
|
||||||
}
|
}
|
||||||
if (traits.get_supports_tilt()) {
|
if (traits.get_supports_tilt()) {
|
||||||
this->subscribe(this->get_tilt_command_topic(), [this](const std::string &topic, const std::string &payload) {
|
this->subscribe(this->get_tilt_command_topic(), [this](const std::string &topic, const std::string &payload) {
|
||||||
auto value = parse_float(payload);
|
auto value = parse_number<float>(payload);
|
||||||
if (!value.has_value()) {
|
if (!value.has_value()) {
|
||||||
ESP_LOGW(TAG, "Invalid tilt value: '%s'", payload.c_str());
|
ESP_LOGW(TAG, "Invalid tilt value: '%s'", payload.c_str());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -71,7 +71,7 @@ void MQTTFanComponent::setup() {
|
||||||
if (this->state_->get_traits().supports_speed()) {
|
if (this->state_->get_traits().supports_speed()) {
|
||||||
this->subscribe(this->get_speed_level_command_topic(),
|
this->subscribe(this->get_speed_level_command_topic(),
|
||||||
[this](const std::string &topic, const std::string &payload) {
|
[this](const std::string &topic, const std::string &payload) {
|
||||||
optional<int> speed_level_opt = parse_int(payload);
|
optional<int> speed_level_opt = parse_number<int>(payload);
|
||||||
if (speed_level_opt.has_value()) {
|
if (speed_level_opt.has_value()) {
|
||||||
const int speed_level = speed_level_opt.value();
|
const int speed_level = speed_level_opt.value();
|
||||||
if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {
|
if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ MQTTNumberComponent::MQTTNumberComponent(Number *number) : MQTTComponent(), numb
|
||||||
|
|
||||||
void MQTTNumberComponent::setup() {
|
void MQTTNumberComponent::setup() {
|
||||||
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &state) {
|
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &state) {
|
||||||
auto val = parse_float(state);
|
auto val = parse_number<float>(state);
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -13,7 +13,7 @@ void MQTTSubscribeSensor::setup() {
|
||||||
mqtt::global_mqtt_client->subscribe(
|
mqtt::global_mqtt_client->subscribe(
|
||||||
this->topic_,
|
this->topic_,
|
||||||
[this](const std::string &topic, const std::string &payload) {
|
[this](const std::string &topic, const std::string &payload) {
|
||||||
auto val = parse_float(payload);
|
auto val = parse_number<float>(payload);
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
|
|
|
@ -656,7 +656,7 @@ void Pipsolar::loop() {
|
||||||
case 32:
|
case 32:
|
||||||
fc = tmp[i];
|
fc = tmp[i];
|
||||||
fc += tmp[i + 1];
|
fc += tmp[i + 1];
|
||||||
this->value_fault_code_ = strtol(fc.c_str(), nullptr, 10);
|
this->value_fault_code_ = parse_number<int>(fc).value_or(0);
|
||||||
break;
|
break;
|
||||||
case 34:
|
case 34:
|
||||||
this->value_warnung_low_pv_energy_ = enabled;
|
this->value_warnung_low_pv_energy_ = enabled;
|
||||||
|
|
|
@ -128,7 +128,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
|
||||||
if (message.compare(0, 5, "+CSQ:") == 0) {
|
if (message.compare(0, 5, "+CSQ:") == 0) {
|
||||||
size_t comma = message.find(',', 6);
|
size_t comma = message.find(',', 6);
|
||||||
if (comma != 6) {
|
if (comma != 6) {
|
||||||
this->rssi_ = strtol(message.substr(6, comma - 6).c_str(), nullptr, 10);
|
this->rssi_ = parse_number<int>(message.substr(6, comma - 6)).value_or(0);
|
||||||
ESP_LOGD(TAG, "RSSI: %d", this->rssi_);
|
ESP_LOGD(TAG, "RSSI: %d", this->rssi_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
|
||||||
while (end != start) {
|
while (end != start) {
|
||||||
item++;
|
item++;
|
||||||
if (item == 1) { // Slot Index
|
if (item == 1) { // Slot Index
|
||||||
this->parse_index_ = strtol(message.substr(start, end - start).c_str(), nullptr, 10);
|
this->parse_index_ = parse_number<uint8_t>(message.substr(start, end - start)).value_or(0);
|
||||||
}
|
}
|
||||||
// item 2 = STATUS, usually "REC UNERAD"
|
// item 2 = STATUS, usually "REC UNERAD"
|
||||||
if (item == 3) { // recipient
|
if (item == 3) { // recipient
|
||||||
|
|
|
@ -6,8 +6,8 @@ namespace teleinfo {
|
||||||
static const char *const TAG = "teleinfo_sensor";
|
static const char *const TAG = "teleinfo_sensor";
|
||||||
TeleInfoSensor::TeleInfoSensor(const char *tag) { this->tag = std::string(tag); }
|
TeleInfoSensor::TeleInfoSensor(const char *tag) { this->tag = std::string(tag); }
|
||||||
void TeleInfoSensor::publish_val(const std::string &val) {
|
void TeleInfoSensor::publish_val(const std::string &val) {
|
||||||
auto newval = parse_float(val);
|
auto newval = parse_number<float>(val).value_or(0.0f);
|
||||||
publish_state(*newval);
|
publish_state(newval);
|
||||||
}
|
}
|
||||||
void TeleInfoSensor::dump_config() { LOG_SENSOR(" ", "Teleinfo Sensor", this); }
|
void TeleInfoSensor::dump_config() { LOG_SENSOR(" ", "Teleinfo Sensor", this); }
|
||||||
} // namespace teleinfo
|
} // namespace teleinfo
|
||||||
|
|
|
@ -458,7 +458,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||||
}
|
}
|
||||||
if (request->hasParam("speed_level")) {
|
if (request->hasParam("speed_level")) {
|
||||||
String speed_level = request->getParam("speed_level")->value();
|
String speed_level = request->getParam("speed_level")->value();
|
||||||
auto val = parse_int(speed_level.c_str());
|
auto val = parse_number<int>(speed_level.c_str());
|
||||||
if (!val.has_value()) {
|
if (!val.has_value()) {
|
||||||
ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
|
ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -279,20 +279,6 @@ std::string to_string(long double val) {
|
||||||
sprintf(buf, "%Lf", val);
|
sprintf(buf, "%Lf", val);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
optional<float> parse_float(const std::string &str) {
|
|
||||||
char *end;
|
|
||||||
float value = ::strtof(str.c_str(), &end);
|
|
||||||
if (end == nullptr || end != str.end().base())
|
|
||||||
return {};
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
optional<int> parse_int(const std::string &str) {
|
|
||||||
char *end;
|
|
||||||
int value = ::strtol(str.c_str(), &end, 10);
|
|
||||||
if (end == nullptr || end != str.end().base())
|
|
||||||
return {};
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<int> parse_hex(const char chr) {
|
optional<int> parse_hex(const char chr) {
|
||||||
int out = chr;
|
int out = chr;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -51,8 +53,6 @@ std::string to_string(unsigned long long val); // NOLINT
|
||||||
std::string to_string(float val);
|
std::string to_string(float val);
|
||||||
std::string to_string(double val);
|
std::string to_string(double val);
|
||||||
std::string to_string(long double val);
|
std::string to_string(long double val);
|
||||||
optional<float> parse_float(const std::string &str);
|
|
||||||
optional<int> parse_int(const std::string &str);
|
|
||||||
optional<int> parse_hex(const std::string &str, size_t start, size_t length);
|
optional<int> parse_hex(const std::string &str, size_t start, size_t length);
|
||||||
optional<int> parse_hex(char chr);
|
optional<int> parse_hex(char chr);
|
||||||
/// Sanitize the hostname by removing characters that are not in the allowlist and truncating it to 63 chars.
|
/// Sanitize the hostname by removing characters that are not in the allowlist and truncating it to 63 chars.
|
||||||
|
@ -304,4 +304,52 @@ template<typename T> T *new_buffer(size_t length) {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// @name Parsing & formatting
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// Parse a unsigned decimal number.
|
||||||
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
||||||
|
optional<T> parse_number(const char *str, size_t len) {
|
||||||
|
char *end = nullptr;
|
||||||
|
unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
|
||||||
|
if (end == nullptr || end != str + len || value > std::numeric_limits<T>::max())
|
||||||
|
return {};
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
||||||
|
optional<T> parse_number(const std::string &str) {
|
||||||
|
return parse_number<T>(str.c_str(), str.length());
|
||||||
|
}
|
||||||
|
/// Parse a signed decimal number.
|
||||||
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
||||||
|
optional<T> parse_number(const char *str, size_t len) {
|
||||||
|
char *end = nullptr;
|
||||||
|
signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
|
||||||
|
if (end == nullptr || end != str + len || value < std::numeric_limits<T>::min() ||
|
||||||
|
value > std::numeric_limits<T>::max())
|
||||||
|
return {};
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
||||||
|
optional<T> parse_number(const std::string &str) {
|
||||||
|
return parse_number<T>(str.c_str(), str.length());
|
||||||
|
}
|
||||||
|
/// Parse a decimal floating-point number.
|
||||||
|
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
||||||
|
optional<T> parse_number(const char *str, size_t len) {
|
||||||
|
char *end = nullptr;
|
||||||
|
float value = ::strtof(str, &end);
|
||||||
|
if (end == nullptr || end != str + len || value == HUGE_VALF)
|
||||||
|
return {};
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
||||||
|
optional<T> parse_number(const std::string &str) {
|
||||||
|
return parse_number<T>(str.c_str(), str.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
Loading…
Reference in a new issue