13static const char *
const TAG =
"homeassistant.select";
18template<
typename C>
bool emit_utf8(
uint32_t code_point, C &on_char) {
19 if (code_point == 0 || code_point > 0x10FFFF)
21 if (code_point < 0x80) {
22 on_char(
static_cast<char>(code_point));
23 }
else if (code_point < 0x800) {
24 on_char(
static_cast<char>(0xC0 | (code_point >> 6)));
25 on_char(
static_cast<char>(0x80 | (code_point & 0x3F)));
26 }
else if (code_point < 0x10000) {
27 on_char(
static_cast<char>(0xE0 | (code_point >> 12)));
28 on_char(
static_cast<char>(0x80 | ((code_point >> 6) & 0x3F)));
29 on_char(
static_cast<char>(0x80 | (code_point & 0x3F)));
31 on_char(
static_cast<char>(0xF0 | (code_point >> 18)));
32 on_char(
static_cast<char>(0x80 | ((code_point >> 12) & 0x3F)));
33 on_char(
static_cast<char>(0x80 | ((code_point >> 6) & 0x3F)));
34 on_char(
static_cast<char>(0x80 | (code_point & 0x3F)));
45template<
typename C,
typename O>
bool parse_option_list(StringRef
src, C &&on_char, O &&on_option) {
46 const char *
pos =
src.c_str();
48 auto skip_spaces = [&]() {
65 const char quote = *
pos++;
69 const char c = *
pos++;
78 const char escape = *
pos++;
97 const size_t digits = escape ==
'x' ? 2 : (escape ==
'u' ? 4 : 8);
98 if (
static_cast<size_t>(
end -
pos) < digits)
101 if (!code_point.has_value() || !emit_utf8(*code_point, on_char))
114 const char separator = *
pos++;
115 if (separator ==
']')
117 if (separator !=
',')
128 this->options_buffer_ = std::make_unique<char[]>(this->options_buffer_size_);
129 this->option_list_ = std::make_unique<const char *[]>(this->max_options_);
145 size_t match_len = 0;
146 bool matching = active !=
nullptr;
147 optional<size_t> new_active;
148 bool valid = parse_option_list(
152 if (matching && active[match_len] == c) {
160 if (matching && active[match_len] ==
'\0' && !new_active.has_value())
164 matching = active !=
nullptr;
167 ESP_LOGW(TAG,
"'%s': Can't parse options %s", this->
entity_id_, options.
c_str());
170 if (
count > this->max_options_) {
171 ESP_LOGE(TAG,
"'%s': %zu options exceed max_options (%u)", this->
entity_id_, count, this->max_options_);
174 if (bytes > this->options_buffer_size_) {
175 ESP_LOGE(TAG,
"'%s': Options need %zu bytes, more than options_buffer_size (%u)", this->
entity_id_, bytes,
176 this->options_buffer_size_);
181 char *
buffer = this->options_buffer_.get();
183 size_t option_start = 0;
189 this->option_list_[index++] =
buffer + option_start;
190 option_start = offset;
196 if (new_active.has_value()) {
201 ESP_LOGD(TAG,
"'%s': Got %zu options", this->
entity_id_, count);
206 if (!index.has_value()) {
207 ESP_LOGW(TAG,
"'%s': State '%s' is not one of the options", this->
entity_id_, state.
c_str());
212 ESP_LOGD(TAG,
"'%s': Got state '%s'", this->
entity_id_, state.
c_str());
217 LOG_SELECT(
"",
"Homeassistant Select",
this);
221 " Options buffer size: %u",
222 this->
entity_id_, this->max_options_, this->options_buffer_size_);
229 ESP_LOGE(TAG,
"No clients connected to API server");
233#ifdef USE_API_WIZARD_LINKED_INPUTS
235 ESP_LOGW(TAG,
"'%s': No entity ID set yet", this->
get_name().c_str());
241 static constexpr auto SERVICE_INPUT_SELECT =
StringRef::from_lit(
"input_select.select_option");
244 static constexpr char INPUT_PREFIX[] =
"input_";
247 if (strncmp(this->
entity_id_, INPUT_PREFIX,
sizeof(INPUT_PREFIX) - 1) == 0) {
248 resp.
service = SERVICE_INPUT_SELECT;
254 auto &entity_id = resp.
data.emplace_back();
255 entity_id.key = ENTITY_ID_KEY;
258 auto &option = resp.
data.emplace_back();
259 option.key = OPTION_KEY;
const StringRef & get_name() const
void set_has_state(bool state)
StringRef is a reference to a string owned by something else.
constexpr const char * c_str() const
static constexpr StringRef from_lit(const CharT(&s)[N])
void send_homeassistant_action(const HomeassistantActionRequest &call)
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function< void(StringRef)> &&f)
FixedVector< HomeassistantServiceMap > data
float get_setup_priority() const override
void dump_config() override
void state_changed_(StringRef state)
void control(size_t index) override
void options_changed_(StringRef options)
const char * option_at(size_t index) const
Return the option value at the provided index offset (as const char* from flash).
optional< size_t > index_of(const char *option, size_t len) const
Find the (optional) index offset of the provided option value.
void publish_state(const std::string &state)
void set_options_static(const char *const *options, size_t count)
Codegen only: points at a table that outlives the select.
APIServer * global_api_server
constexpr float AFTER_CONNECTION
For components that should be initialized after a data connection (API/MQTT) is connected.
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.