Introduce parse_number() helper function (#2659)

This commit is contained in:
Oxan van Leeuwen 2021-11-10 19:15:06 +01:00 committed by GitHub
parent 219b225ac0
commit 15f9677d33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 70 additions and 36 deletions

View file

@ -103,21 +103,21 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
break;
}
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_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
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_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
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_)
this->current_temp_ = ftoc(this->current_temp_);
this->has_current_temp_ = true;

View file

@ -74,7 +74,7 @@ void EZOSensor::loop() {
if (buf[0] != 1)
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);
}

View file

@ -10,7 +10,7 @@ static const char *const TAG = "homeassistant.sensor";
void HomeassistantSensor::setup() {
api::global_api_server->subscribe_home_assistant_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()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
this->publish_state(NAN);

View file

@ -44,7 +44,7 @@ void HrxlMaxsonarWrComponent::check_buffer_() {
if (this->buffer_.length() == MAX_DATA_LENGTH_BYTES && this->buffer_[0] == 'R' &&
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;
ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters);
this->publish_state(meters);

View file

@ -137,7 +137,7 @@ void MQTTClimateComponent::setup() {
if (traits.get_supports_two_point_target_temperature()) {
this->subscribe(this->get_target_temperature_low_command_topic(),
[this](const std::string &topic, const std::string &payload) {
auto val = parse_float(payload);
auto val = parse_number<float>(payload);
if (!val.has_value()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
return;
@ -148,7 +148,7 @@ void MQTTClimateComponent::setup() {
});
this->subscribe(this->get_target_temperature_high_command_topic(),
[this](const std::string &topic, const std::string &payload) {
auto val = parse_float(payload);
auto val = parse_number<float>(payload);
if (!val.has_value()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
return;
@ -160,7 +160,7 @@ void MQTTClimateComponent::setup() {
} else {
this->subscribe(this->get_target_temperature_command_topic(),
[this](const std::string &topic, const std::string &payload) {
auto val = parse_float(payload);
auto val = parse_number<float>(payload);
if (!val.has_value()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
return;

View file

@ -24,7 +24,7 @@ void MQTTCoverComponent::setup() {
});
if (traits.get_supports_position()) {
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()) {
ESP_LOGW(TAG, "Invalid position value: '%s'", payload.c_str());
return;
@ -36,7 +36,7 @@ void MQTTCoverComponent::setup() {
}
if (traits.get_supports_tilt()) {
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()) {
ESP_LOGW(TAG, "Invalid tilt value: '%s'", payload.c_str());
return;

View file

@ -71,7 +71,7 @@ void MQTTFanComponent::setup() {
if (this->state_->get_traits().supports_speed()) {
this->subscribe(this->get_speed_level_command_topic(),
[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()) {
const int speed_level = speed_level_opt.value();
if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {

View file

@ -17,7 +17,7 @@ MQTTNumberComponent::MQTTNumberComponent(Number *number) : MQTTComponent(), numb
void MQTTNumberComponent::setup() {
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()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str());
return;

View file

@ -13,7 +13,7 @@ void MQTTSubscribeSensor::setup() {
mqtt::global_mqtt_client->subscribe(
this->topic_,
[this](const std::string &topic, const std::string &payload) {
auto val = parse_float(payload);
auto val = parse_number<float>(payload);
if (!val.has_value()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
this->publish_state(NAN);

View file

@ -656,7 +656,7 @@ void Pipsolar::loop() {
case 32:
fc = tmp[i];
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;
case 34:
this->value_warnung_low_pv_energy_ = enabled;

View file

@ -128,7 +128,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
if (message.compare(0, 5, "+CSQ:") == 0) {
size_t comma = message.find(',', 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_);
}
}
@ -146,7 +146,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
while (end != start) {
item++;
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"
if (item == 3) { // recipient

View file

@ -6,8 +6,8 @@ namespace teleinfo {
static const char *const TAG = "teleinfo_sensor";
TeleInfoSensor::TeleInfoSensor(const char *tag) { this->tag = std::string(tag); }
void TeleInfoSensor::publish_val(const std::string &val) {
auto newval = parse_float(val);
publish_state(*newval);
auto newval = parse_number<float>(val).value_or(0.0f);
publish_state(newval);
}
void TeleInfoSensor::dump_config() { LOG_SENSOR(" ", "Teleinfo Sensor", this); }
} // namespace teleinfo

View file

@ -458,7 +458,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
}
if (request->hasParam("speed_level")) {
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()) {
ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str());
return;

View file

@ -279,20 +279,6 @@ std::string to_string(long double val) {
sprintf(buf, "%Lf", val);
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) {
int out = chr;

View file

@ -1,5 +1,7 @@
#pragma once
#include <cmath>
#include <string>
#include <functional>
#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(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(char chr);
/// 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;
}
// ---------------------------------------------------------------------------------------------------------------------
/// @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