mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Tuya rgb support (#2278)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
bdcffc7ba9
commit
7246f42a8e
9 changed files with 129 additions and 30 deletions
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||||
from esphome.components import light, output
|
from esphome.components import light, output
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BLUE,
|
CONF_BLUE,
|
||||||
|
CONF_COLOR_INTERLOCK,
|
||||||
CONF_COLOR_TEMPERATURE,
|
CONF_COLOR_TEMPERATURE,
|
||||||
CONF_GREEN,
|
CONF_GREEN,
|
||||||
CONF_RED,
|
CONF_RED,
|
||||||
|
@ -16,7 +17,6 @@ CODEOWNERS = ["@jesserockz"]
|
||||||
rgbct_ns = cg.esphome_ns.namespace("rgbct")
|
rgbct_ns = cg.esphome_ns.namespace("rgbct")
|
||||||
RGBCTLightOutput = rgbct_ns.class_("RGBCTLightOutput", light.LightOutput)
|
RGBCTLightOutput = rgbct_ns.class_("RGBCTLightOutput", light.LightOutput)
|
||||||
|
|
||||||
CONF_COLOR_INTERLOCK = "color_interlock"
|
|
||||||
CONF_WHITE_BRIGHTNESS = "white_brightness"
|
CONF_WHITE_BRIGHTNESS = "white_brightness"
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import light, output
|
from esphome.components import light, output
|
||||||
from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID, CONF_WHITE
|
from esphome.const import (
|
||||||
|
CONF_BLUE,
|
||||||
|
CONF_COLOR_INTERLOCK,
|
||||||
|
CONF_GREEN,
|
||||||
|
CONF_RED,
|
||||||
|
CONF_OUTPUT_ID,
|
||||||
|
CONF_WHITE,
|
||||||
|
)
|
||||||
|
|
||||||
rgbw_ns = cg.esphome_ns.namespace("rgbw")
|
rgbw_ns = cg.esphome_ns.namespace("rgbw")
|
||||||
RGBWLightOutput = rgbw_ns.class_("RGBWLightOutput", light.LightOutput)
|
RGBWLightOutput = rgbw_ns.class_("RGBWLightOutput", light.LightOutput)
|
||||||
CONF_COLOR_INTERLOCK = "color_interlock"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend(
|
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||||
from esphome.components import light, output
|
from esphome.components import light, output
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BLUE,
|
CONF_BLUE,
|
||||||
|
CONF_COLOR_INTERLOCK,
|
||||||
CONF_CONSTANT_BRIGHTNESS,
|
CONF_CONSTANT_BRIGHTNESS,
|
||||||
CONF_GREEN,
|
CONF_GREEN,
|
||||||
CONF_RED,
|
CONF_RED,
|
||||||
|
@ -16,7 +17,6 @@ from esphome.const import (
|
||||||
rgbww_ns = cg.esphome_ns.namespace("rgbww")
|
rgbww_ns = cg.esphome_ns.namespace("rgbww")
|
||||||
RGBWWLightOutput = rgbww_ns.class_("RGBWWLightOutput", light.LightOutput)
|
RGBWWLightOutput = rgbww_ns.class_("RGBWWLightOutput", light.LightOutput)
|
||||||
|
|
||||||
CONF_COLOR_INTERLOCK = "color_interlock"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
light.RGB_LIGHT_SCHEMA.extend(
|
light.RGB_LIGHT_SCHEMA.extend(
|
||||||
|
|
|
@ -10,6 +10,7 @@ from esphome.const import (
|
||||||
CONF_SWITCH_DATAPOINT,
|
CONF_SWITCH_DATAPOINT,
|
||||||
CONF_COLD_WHITE_COLOR_TEMPERATURE,
|
CONF_COLD_WHITE_COLOR_TEMPERATURE,
|
||||||
CONF_WARM_WHITE_COLOR_TEMPERATURE,
|
CONF_WARM_WHITE_COLOR_TEMPERATURE,
|
||||||
|
CONF_COLOR_INTERLOCK,
|
||||||
)
|
)
|
||||||
from .. import tuya_ns, CONF_TUYA_ID, Tuya
|
from .. import tuya_ns, CONF_TUYA_ID, Tuya
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ CONF_MIN_VALUE_DATAPOINT = "min_value_datapoint"
|
||||||
CONF_COLOR_TEMPERATURE_DATAPOINT = "color_temperature_datapoint"
|
CONF_COLOR_TEMPERATURE_DATAPOINT = "color_temperature_datapoint"
|
||||||
CONF_COLOR_TEMPERATURE_INVERT = "color_temperature_invert"
|
CONF_COLOR_TEMPERATURE_INVERT = "color_temperature_invert"
|
||||||
CONF_COLOR_TEMPERATURE_MAX_VALUE = "color_temperature_max_value"
|
CONF_COLOR_TEMPERATURE_MAX_VALUE = "color_temperature_max_value"
|
||||||
|
CONF_RGB_DATAPOINT = "rgb_datapoint"
|
||||||
|
|
||||||
TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component)
|
TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component)
|
||||||
|
|
||||||
|
@ -31,6 +33,8 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t,
|
||||||
cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t,
|
cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t,
|
||||||
|
cv.Optional(CONF_RGB_DATAPOINT): cv.uint8_t,
|
||||||
|
cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean,
|
||||||
cv.Inclusive(
|
cv.Inclusive(
|
||||||
CONF_COLOR_TEMPERATURE_DATAPOINT, "color_temperature"
|
CONF_COLOR_TEMPERATURE_DATAPOINT, "color_temperature"
|
||||||
): cv.uint8_t,
|
): cv.uint8_t,
|
||||||
|
@ -52,7 +56,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
): cv.positive_time_period_milliseconds,
|
): cv.positive_time_period_milliseconds,
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_at_least_one_key(CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT),
|
cv.has_at_least_one_key(
|
||||||
|
CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT, CONF_RGB_DATAPOINT
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,6 +73,8 @@ async def to_code(config):
|
||||||
cg.add(var.set_min_value_datapoint_id(config[CONF_MIN_VALUE_DATAPOINT]))
|
cg.add(var.set_min_value_datapoint_id(config[CONF_MIN_VALUE_DATAPOINT]))
|
||||||
if CONF_SWITCH_DATAPOINT in config:
|
if CONF_SWITCH_DATAPOINT in config:
|
||||||
cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT]))
|
cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT]))
|
||||||
|
if CONF_RGB_DATAPOINT in config:
|
||||||
|
cg.add(var.set_rgb_id(config[CONF_RGB_DATAPOINT]))
|
||||||
if CONF_COLOR_TEMPERATURE_DATAPOINT in config:
|
if CONF_COLOR_TEMPERATURE_DATAPOINT in config:
|
||||||
cg.add(var.set_color_temperature_id(config[CONF_COLOR_TEMPERATURE_DATAPOINT]))
|
cg.add(var.set_color_temperature_id(config[CONF_COLOR_TEMPERATURE_DATAPOINT]))
|
||||||
cg.add(var.set_color_temperature_invert(config[CONF_COLOR_TEMPERATURE_INVERT]))
|
cg.add(var.set_color_temperature_invert(config[CONF_COLOR_TEMPERATURE_INVERT]))
|
||||||
|
@ -87,5 +95,7 @@ async def to_code(config):
|
||||||
config[CONF_COLOR_TEMPERATURE_MAX_VALUE]
|
config[CONF_COLOR_TEMPERATURE_MAX_VALUE]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cg.add(var.set_color_interlock(config[CONF_COLOR_INTERLOCK]))
|
||||||
paren = await cg.get_variable(config[CONF_TUYA_ID])
|
paren = await cg.get_variable(config[CONF_TUYA_ID])
|
||||||
cg.add(var.set_tuya_parent(paren))
|
cg.add(var.set_tuya_parent(paren))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "tuya_light.h"
|
#include "tuya_light.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tuya {
|
namespace tuya {
|
||||||
|
@ -34,6 +35,18 @@ void TuyaLight::setup() {
|
||||||
call.perform();
|
call.perform();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (rgb_id_.has_value()) {
|
||||||
|
this->parent_->register_listener(*this->rgb_id_, [this](const TuyaDatapoint &datapoint) {
|
||||||
|
auto red = parse_hex(datapoint.value_string, 0, 2);
|
||||||
|
auto green = parse_hex(datapoint.value_string, 2, 2);
|
||||||
|
auto blue = parse_hex(datapoint.value_string, 4, 2);
|
||||||
|
if (red.has_value() && green.has_value() && blue.has_value()) {
|
||||||
|
auto call = this->state_->make_call();
|
||||||
|
call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255);
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
if (min_value_datapoint_id_.has_value()) {
|
if (min_value_datapoint_id_.has_value()) {
|
||||||
parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_);
|
parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_);
|
||||||
}
|
}
|
||||||
|
@ -45,14 +58,31 @@ void TuyaLight::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " Dimmer has datapoint ID %u", *this->dimmer_id_);
|
ESP_LOGCONFIG(TAG, " Dimmer has datapoint ID %u", *this->dimmer_id_);
|
||||||
if (this->switch_id_.has_value())
|
if (this->switch_id_.has_value())
|
||||||
ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_);
|
ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_);
|
||||||
|
if (this->rgb_id_.has_value())
|
||||||
|
ESP_LOGCONFIG(TAG, " RGB has datapoint ID %u", *this->rgb_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
light::LightTraits TuyaLight::get_traits() {
|
light::LightTraits TuyaLight::get_traits() {
|
||||||
auto traits = light::LightTraits();
|
auto traits = light::LightTraits();
|
||||||
if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) {
|
if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) {
|
||||||
traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
|
if (this->rgb_id_.has_value()) {
|
||||||
|
if (this->color_interlock_)
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE});
|
||||||
|
else
|
||||||
|
traits.set_supported_color_modes(
|
||||||
|
{light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE});
|
||||||
|
} else
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
|
||||||
traits.set_min_mireds(this->cold_white_temperature_);
|
traits.set_min_mireds(this->cold_white_temperature_);
|
||||||
traits.set_max_mireds(this->warm_white_temperature_);
|
traits.set_max_mireds(this->warm_white_temperature_);
|
||||||
|
} else if (this->rgb_id_.has_value()) {
|
||||||
|
if (this->dimmer_id_.has_value()) {
|
||||||
|
if (this->color_interlock_)
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE});
|
||||||
|
else
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
|
||||||
|
} else
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB});
|
||||||
} else if (this->dimmer_id_.has_value()) {
|
} else if (this->dimmer_id_.has_value()) {
|
||||||
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
|
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,38 +94,51 @@ light::LightTraits TuyaLight::get_traits() {
|
||||||
void TuyaLight::setup_state(light::LightState *state) { state_ = state; }
|
void TuyaLight::setup_state(light::LightState *state) { state_ = state; }
|
||||||
|
|
||||||
void TuyaLight::write_state(light::LightState *state) {
|
void TuyaLight::write_state(light::LightState *state) {
|
||||||
float brightness;
|
float red = 0.0f, green = 0.0f, blue = 0.0f;
|
||||||
state->current_values_as_brightness(&brightness);
|
float color_temperature = 0.0f, brightness = 0.0f;
|
||||||
|
|
||||||
if (brightness == 0.0f) {
|
if (this->rgb_id_.has_value()) {
|
||||||
// turning off, first try via switch (if exists), then dimmer
|
if (this->color_temperature_id_.has_value()) {
|
||||||
if (switch_id_.has_value()) {
|
state->current_values_as_rgbct(&red, &green, &blue, &color_temperature, &brightness);
|
||||||
parent_->set_boolean_datapoint_value(*this->switch_id_, false);
|
} else if (this->dimmer_id_.has_value()) {
|
||||||
} else if (dimmer_id_.has_value()) {
|
state->current_values_as_rgbw(&red, &green, &blue, &brightness);
|
||||||
parent_->set_integer_datapoint_value(*this->dimmer_id_, 0);
|
} else {
|
||||||
|
state->current_values_as_rgb(&red, &green, &blue);
|
||||||
}
|
}
|
||||||
return;
|
} else if (this->color_temperature_id_.has_value()) {
|
||||||
|
state->current_values_as_ct(&color_temperature, &brightness);
|
||||||
|
} else {
|
||||||
|
state->current_values_as_brightness(&brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->color_temperature_id_.has_value()) {
|
if (brightness > 0.0f || !color_interlock_) {
|
||||||
uint32_t color_temp_int =
|
if (this->color_temperature_id_.has_value()) {
|
||||||
static_cast<uint32_t>(this->color_temperature_max_value_ *
|
uint32_t color_temp_int = static_cast<uint32_t>(color_temperature * this->color_temperature_max_value_);
|
||||||
(state->current_values.get_color_temperature() - this->cold_white_temperature_) /
|
if (this->color_temperature_invert_) {
|
||||||
(this->warm_white_temperature_ - this->cold_white_temperature_));
|
color_temp_int = this->color_temperature_max_value_ - color_temp_int;
|
||||||
if (this->color_temperature_invert_) {
|
}
|
||||||
color_temp_int = this->color_temperature_max_value_ - color_temp_int;
|
parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->dimmer_id_.has_value()) {
|
||||||
|
auto brightness_int = static_cast<uint32_t>(brightness * this->max_value_);
|
||||||
|
brightness_int = std::max(brightness_int, this->min_value_);
|
||||||
|
|
||||||
|
parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int);
|
||||||
}
|
}
|
||||||
parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto brightness_int = static_cast<uint32_t>(brightness * this->max_value_);
|
if (brightness == 0.0f || !color_interlock_) {
|
||||||
brightness_int = std::max(brightness_int, this->min_value_);
|
if (this->rgb_id_.has_value()) {
|
||||||
|
char buffer[7];
|
||||||
if (this->dimmer_id_.has_value()) {
|
sprintf(buffer, "%02X%02X%02X", int(red * 255), int(green * 255), int(blue * 255));
|
||||||
parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int);
|
std::string value = buffer;
|
||||||
|
this->parent_->set_string_datapoint_value(*this->rgb_id_, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->switch_id_.has_value()) {
|
if (this->switch_id_.has_value()) {
|
||||||
parent_->set_boolean_datapoint_value(*this->switch_id_, true);
|
parent_->set_boolean_datapoint_value(*this->switch_id_, state->current_values.is_on());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ class TuyaLight : public Component, public light::LightOutput {
|
||||||
this->min_value_datapoint_id_ = min_value_datapoint_id;
|
this->min_value_datapoint_id_ = min_value_datapoint_id;
|
||||||
}
|
}
|
||||||
void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; }
|
void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; }
|
||||||
|
void set_rgb_id(uint8_t rgb_id) { this->rgb_id_ = rgb_id; }
|
||||||
void set_color_temperature_id(uint8_t color_temperature_id) { this->color_temperature_id_ = color_temperature_id; }
|
void set_color_temperature_id(uint8_t color_temperature_id) { this->color_temperature_id_ = color_temperature_id; }
|
||||||
void set_color_temperature_invert(bool color_temperature_invert) {
|
void set_color_temperature_invert(bool color_temperature_invert) {
|
||||||
this->color_temperature_invert_ = color_temperature_invert;
|
this->color_temperature_invert_ = color_temperature_invert;
|
||||||
|
@ -32,6 +33,8 @@ class TuyaLight : public Component, public light::LightOutput {
|
||||||
void set_warm_white_temperature(float warm_white_temperature) {
|
void set_warm_white_temperature(float warm_white_temperature) {
|
||||||
this->warm_white_temperature_ = warm_white_temperature;
|
this->warm_white_temperature_ = warm_white_temperature;
|
||||||
}
|
}
|
||||||
|
void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; }
|
||||||
|
|
||||||
light::LightTraits get_traits() override;
|
light::LightTraits get_traits() override;
|
||||||
void setup_state(light::LightState *state) override;
|
void setup_state(light::LightState *state) override;
|
||||||
void write_state(light::LightState *state) override;
|
void write_state(light::LightState *state) override;
|
||||||
|
@ -44,6 +47,7 @@ class TuyaLight : public Component, public light::LightOutput {
|
||||||
optional<uint8_t> dimmer_id_{};
|
optional<uint8_t> dimmer_id_{};
|
||||||
optional<uint8_t> min_value_datapoint_id_{};
|
optional<uint8_t> min_value_datapoint_id_{};
|
||||||
optional<uint8_t> switch_id_{};
|
optional<uint8_t> switch_id_{};
|
||||||
|
optional<uint8_t> rgb_id_{};
|
||||||
optional<uint8_t> color_temperature_id_{};
|
optional<uint8_t> color_temperature_id_{};
|
||||||
uint32_t min_value_ = 0;
|
uint32_t min_value_ = 0;
|
||||||
uint32_t max_value_ = 255;
|
uint32_t max_value_ = 255;
|
||||||
|
@ -51,6 +55,7 @@ class TuyaLight : public Component, public light::LightOutput {
|
||||||
float cold_white_temperature_;
|
float cold_white_temperature_;
|
||||||
float warm_white_temperature_;
|
float warm_white_temperature_;
|
||||||
bool color_temperature_invert_{false};
|
bool color_temperature_invert_{false};
|
||||||
|
bool color_interlock_{false};
|
||||||
light::LightState *state_{nullptr};
|
light::LightState *state_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ CONF_COLD_WHITE_COLOR_TEMPERATURE = "cold_white_color_temperature"
|
||||||
CONF_COLOR = "color"
|
CONF_COLOR = "color"
|
||||||
CONF_COLOR_BRIGHTNESS = "color_brightness"
|
CONF_COLOR_BRIGHTNESS = "color_brightness"
|
||||||
CONF_COLOR_CORRECT = "color_correct"
|
CONF_COLOR_CORRECT = "color_correct"
|
||||||
|
CONF_COLOR_INTERLOCK = "color_interlock"
|
||||||
CONF_COLOR_MODE = "color_mode"
|
CONF_COLOR_MODE = "color_mode"
|
||||||
CONF_COLOR_TEMPERATURE = "color_temperature"
|
CONF_COLOR_TEMPERATURE = "color_temperature"
|
||||||
CONF_COLORS = "colors"
|
CONF_COLORS = "colors"
|
||||||
|
|
|
@ -270,6 +270,39 @@ optional<int> parse_int(const std::string &str) {
|
||||||
return {};
|
return {};
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional<int> parse_hex(const char chr) {
|
||||||
|
int out = chr;
|
||||||
|
if (out >= '0' && out <= '9')
|
||||||
|
return (out - '0');
|
||||||
|
if (out >= 'A' && out <= 'F')
|
||||||
|
return (10 + (out - 'A'));
|
||||||
|
if (out >= 'a' && out <= 'f')
|
||||||
|
return (10 + (out - 'a'));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<int> parse_hex(const std::string &str, size_t start, size_t length) {
|
||||||
|
if (str.length() < start) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
size_t end = start + length;
|
||||||
|
if (str.length() < end) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
int out = 0;
|
||||||
|
for (size_t i = start; i < end; i++) {
|
||||||
|
char chr = str[i];
|
||||||
|
auto digit = parse_hex(chr);
|
||||||
|
if (!digit.has_value()) {
|
||||||
|
ESP_LOGW(TAG, "Can't convert '%s' to number, invalid character %c!", str.substr(start, length).c_str(), chr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
out = (out << 4) | *digit;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t fnv1_hash(const std::string &str) {
|
uint32_t fnv1_hash(const std::string &str) {
|
||||||
uint32_t hash = 2166136261UL;
|
uint32_t hash = 2166136261UL;
|
||||||
for (char c : str) {
|
for (char c : str) {
|
||||||
|
|
|
@ -43,7 +43,8 @@ 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<float> parse_float(const std::string &str);
|
||||||
optional<int> parse_int(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.
|
/// Sanitize the hostname by removing characters that are not in the allowlist and truncating it to 63 chars.
|
||||||
std::string sanitize_hostname(const std::string &hostname);
|
std::string sanitize_hostname(const std::string &hostname);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue