diff --git a/code/espurna/button.cpp b/code/espurna/button.cpp index 6ba233c2..bc716d65 100644 --- a/code/espurna/button.cpp +++ b/code/espurna/button.cpp @@ -773,7 +773,7 @@ namespace terminal { void button(::terminal::CommandContext&& ctx) { if (ctx.argv.size() == 2) { size_t id; - if (!tryParseId(ctx.argv[1].c_str(), buttonCount, id)) { + if (!tryParseId(ctx.argv[1], buttonCount, id)) { terminalError(ctx, F("Invalid button ID")); return; } @@ -1010,35 +1010,36 @@ namespace { #if DEBUG_SUPPORT || MQTT_SUPPORT -String _buttonEventString(ButtonEvent event) { - const __FlashStringHelper* ptr = nullptr; +const char* _buttonEventString(ButtonEvent event) { + const char* out = "none"; + switch (event) { case ButtonEvent::Pressed: - ptr = F("pressed"); + out = PSTR("pressed"); break; case ButtonEvent::Released: - ptr = F("released"); + out = PSTR("released"); break; case ButtonEvent::Click: - ptr = F("click"); + out = PSTR("click"); break; case ButtonEvent::DoubleClick: - ptr = F("double-click"); + out = PSTR("double-click"); break; case ButtonEvent::LongClick: - ptr = F("long-click"); + out = PSTR("long-click"); break; case ButtonEvent::LongLongClick: - ptr = F("looong-click"); + out = PSTR("looong-click"); break; case ButtonEvent::TripleClick: - ptr = F("triple-click"); + out = PSTR("triple-click"); break; case ButtonEvent::None: - ptr = F("none"); break; } - return String(ptr); + + return out; } #endif @@ -1048,7 +1049,7 @@ String _buttonEventString(ButtonEvent event) { void buttonEvent(size_t id, ButtonEvent event) { DEBUG_MSG_P(PSTR("[BUTTON] Button #%u event %d (%s)\n"), - id, static_cast(event), _buttonEventString(event).c_str() + id, static_cast(event), _buttonEventString(event) ); if (event == ButtonEvent::None) { @@ -1064,7 +1065,7 @@ void buttonEvent(size_t id, ButtonEvent event) { #if MQTT_SUPPORT if ((action != ButtonAction::None) || _buttons_mqtt_send_all[id]) { - mqttSend(MQTT_TOPIC_BUTTON, id, _buttonEventString(event).c_str(), false, _buttons_mqtt_retain[id]); + mqttSend(MQTT_TOPIC_BUTTON, id, String(_buttonEventString(event)).c_str(), false, _buttons_mqtt_retain[id]); } #endif diff --git a/code/espurna/compat.h b/code/espurna/compat.h index 7b9d81fc..6a7ad58c 100644 --- a/code/espurna/compat.h +++ b/code/espurna/compat.h @@ -104,7 +104,7 @@ using std::isnan; #endif // ----------------------------------------------------------------------------- -// std::make_unique & std::clamp backports for C++11, since we still use it +// various backports for C++11, since we still use it with gcc v4.8 // ----------------------------------------------------------------------------- #if __cplusplus <= 201103L @@ -141,6 +141,11 @@ constexpr auto cend(const T& value) -> decltype(std::end(value)) { return std::end(value); } +template +constexpr std::reverse_iterator make_reverse_iterator(T iterator) { + return std::reverse_iterator(iterator); +} + } // namespace std #endif diff --git a/code/espurna/light.cpp b/code/espurna/light.cpp index 77962cbd..76ca0082 100644 --- a/code/espurna/light.cpp +++ b/code/espurna/light.cpp @@ -970,26 +970,27 @@ char _lightTag(size_t channels, size_t index) { } const char* _lightDesc(size_t channels, size_t index) { - const __FlashStringHelper* ptr { F("UNKNOWN") }; + const char* out = PSTR("UNKNOWN"); + switch (_lightTag(channels, index)) { case 'W': - ptr = F("WARM WHITE"); + out = PSTR("WARM WHITE"); break; case 'C': - ptr = F("COLD WHITE"); + out = PSTR("COLD WHITE"); break; case 'R': - ptr = F("RED"); + out = PSTR("RED"); break; case 'G': - ptr = F("GREEN"); + out = PSTR("GREEN"); break; case 'B': - ptr = F("BLUE"); + out = PSTR("BLUE"); break; } - return reinterpret_cast(ptr); + return out; } } // namespace @@ -1000,15 +1001,15 @@ const char* _lightDesc(size_t channels, size_t index) { namespace { -void _lightFromHexPayload(const char* payload, size_t len) { - const bool JustRgb { (len == 6) }; - const bool WithBrightness { (len == 8) }; +void _lightFromHexPayload(espurna::StringView payload) { + const bool JustRgb { (payload.length() == 6) }; + const bool WithBrightness { (payload.length() == 8) }; if (!JustRgb && !WithBrightness) { return; } uint8_t values[4] {0, 0, 0, 0}; - if (hexDecode(payload, len, values, sizeof(values))) { + if (hexDecode(payload.begin(), payload.length(), values, sizeof(values))) { _light_mapping.red(values[0]); _light_mapping.green(values[1]); _light_mapping.blue(values[2]); @@ -1018,11 +1019,11 @@ void _lightFromHexPayload(const char* payload, size_t len) { } } -void _lightFromCommaSeparatedPayload(const char* payload, size_t len) { +void _lightFromCommaSeparatedPayload(espurna::StringView payload) { constexpr size_t BufferSize { 16 }; - if (len < BufferSize) { + if (payload.length() < BufferSize) { char buffer[BufferSize] = {0}; - std::copy(payload, payload + len, buffer); + std::copy(payload.begin(), payload.end(), buffer); auto it = _light_channels.begin(); char* tok = std::strtok(buffer, ","); @@ -1048,28 +1049,27 @@ void _lightFromCommaSeparatedPayload(const char* payload, size_t len) { } } -void _lightFromRgbPayload(const char* rgb) { +void _lightFromRgbPayload(espurna::StringView payload) { if (!_light_has_color) { return; } - if (!rgb || (*rgb == '\0')) { + if (!payload.length() || (payload[0] == '\0')) { return; } - const size_t PayloadLen { strlen(rgb) }; - // HEX value is always prefixed, like CSS // - #AABBCC // Extra byte is interpreted like RGB + brightness // - #AABBCCDD - if (rgb[0] == '#') { - _lightFromHexPayload(rgb + 1, PayloadLen - 1); + if (payload[0] == '#') { + _lightFromHexPayload( + espurna::StringView(payload.begin() + 1, payload.end())); return; } // Otherwise, assume comma-separated decimal values - _lightFromCommaSeparatedPayload(rgb, PayloadLen); + _lightFromCommaSeparatedPayload(payload); } // HSV string is expected to be "H,S,V", where: @@ -1077,17 +1077,16 @@ void _lightFromRgbPayload(const char* rgb) { // - S [0...100] // - V [0...100] -void _lightFromHsvPayload(const char* hsv) { - if (!_light_has_color || !hsv || (*hsv == '\0')) { +void _lightFromHsvPayload(espurna::StringView payload) { + if (!_light_has_color || !payload.length() || (payload[0] == '\0')) { return; } - const size_t PayloadLen { strlen(hsv) }; constexpr size_t BufferSize { 16 }; - if (PayloadLen < BufferSize) { + if (payload.length() < BufferSize) { char buffer[BufferSize] = {0}; - std::copy(hsv, hsv + PayloadLen, buffer); + std::copy(payload.begin(), payload.end(), buffer); long values[3] {0, 0, 0}; char* tok = std::strtok(buffer, ","); @@ -1374,11 +1373,11 @@ String _lightGroupPayload() { // Basic value adjustments. Expression can be: // +offset, -offset or the new value -long _lightAdjustValue(long value, const String& operation) { +long _lightAdjustValue(long value, espurna::StringView operation) { if (operation.length()) { char* endp { nullptr }; - auto updated = std::strtol(operation.c_str(), &endp, 10); - if ((endp == operation.c_str()) || (*endp != '\0')) { + auto updated = std::strtol(operation.begin(), &endp, 10); + if ((endp == operation.begin()) || (*endp != '\0')) { return value; } @@ -1394,44 +1393,28 @@ long _lightAdjustValue(long value, const String& operation) { return value; } -void _lightAdjustBrightness(const String& payload) { +void _lightAdjustBrightness(espurna::StringView payload) { lightBrightness(_lightAdjustValue(_light_brightness, payload)); } -void _lightAdjustBrightness(const char* payload) { - _lightAdjustBrightness(String(payload)); -} - -void _lightAdjustChannel(LightChannel& channel, const String& payload) { +void _lightAdjustChannel(LightChannel& channel, espurna::StringView payload) { channel = _lightAdjustValue(channel.inputValue, payload); } -void _lightAdjustChannel(size_t id, const String& payload) { +void _lightAdjustChannel(size_t id, espurna::StringView payload) { if (id < _light_channels.size()) { _lightAdjustChannel(_light_channels[id], payload); } } -void _lightAdjustChannel(size_t id, const char* payload) { - _lightAdjustChannel(id, String(payload)); -} - -void _lightAdjustKelvin(const String& payload) { +void _lightAdjustKelvin(espurna::StringView payload) { _fromKelvin(_lightAdjustValue(_toKelvin(_light_mireds), payload)); } -void _lightAdjustKelvin(const char* payload) { - _lightAdjustKelvin(String(payload)); -} - -void _lightAdjustMireds(const String& payload) { +void _lightAdjustMireds(espurna::StringView payload) { _fromMireds(_lightAdjustValue(_light_mireds, payload)); } -void _lightAdjustMireds(const char* payload) { - _lightAdjustMireds(String(payload)); -} - } // namespace // ----------------------------------------------------------------------------- @@ -2054,8 +2037,8 @@ bool _lightParsePayload(espurna::StringView payload) { return true; } -bool _lightTryParseChannel(const char* p, size_t& id) { - return tryParseId(p, lightChannels, id); +bool _lightTryParseChannel(espurna::StringView value, size_t& id) { + return tryParseId(value, lightChannels, id); } } // namespace @@ -2190,12 +2173,10 @@ void _lightMqttCallback(unsigned int type, const char* topic, char* payload) { // Channel if (t.startsWith(MQTT_TOPIC_CHANNEL)) { size_t id; - if (!_lightTryParseChannel(t.c_str() + strlen(MQTT_TOPIC_CHANNEL) + 1, id)) { - return; + if (_lightTryParseChannel(mqttMagnitudeTail(t, MQTT_TOPIC_CHANNEL), id)) { + _lightAdjustChannel(id, payload); + _lightUpdateFromMqtt(); } - - _lightAdjustChannel(id, payload); - _lightUpdateFromMqtt(); return; } @@ -2258,9 +2239,10 @@ namespace { template bool _lightApiTryHandle(ApiRequest& request, T&& callback) { - auto id_param = request.wildcard(0); + const auto param = request.wildcard(0); + size_t id; - if (!_lightTryParseChannel(id_param.c_str(), id)) { + if (!_lightTryParseChannel(param, id)) { return false; } @@ -2268,7 +2250,7 @@ bool _lightApiTryHandle(ApiRequest& request, T&& callback) { } bool _lightApiRgbSetter(ApiRequest& request) { - lightColor(request.param(F("value")), true); + lightParseRgb(request.param(F("value"))); lightUpdate(); return true; } @@ -2297,7 +2279,7 @@ void _lightApiSetup() { return true; }, [](ApiRequest& request) { - lightColor(request.param(F("value")), false); + lightParseHsv(request.param(F("value"))); lightUpdate(); return true; } @@ -2458,31 +2440,42 @@ void _lightWebSocketOnConnected(JsonObject& root) { } void _lightWebSocketOnAction(uint32_t client_id, const char* action, JsonObject& data) { + STRING_VIEW_INLINE(Color, "color"); + if (_light_has_color) { - if (STRING_VIEW("color") == action) { - if (data.containsKey(F("rgb"))) { - _lightFromRgbPayload(data[F("rgb")].as()); + if (Color == action) { + STRING_VIEW_INLINE(Rgb, "rgb"); + STRING_VIEW_INLINE(Hsv, "hsv"); + + if (data.containsKey(Rgb)) { + _lightFromRgbPayload(data[Rgb].as()); lightUpdate(); - } else if (data.containsKey("hsv")) { - _lightFromHsvPayload(data[F("hsv")].as()); + } else if (data.containsKey(Hsv)) { + _lightFromHsvPayload(data[Hsv].as()); lightUpdate(); } } } - if (STRING_VIEW("mireds") == action) { - if (data.containsKey(F("mireds"))) { - _fromMireds(data[F("mireds")].as()); + STRING_VIEW_INLINE(Mireds, "mireds"); + STRING_VIEW_INLINE(Brightness, "brightness"); + STRING_VIEW_INLINE(Id, "id"); + STRING_VIEW_INLINE(Channel, "channel"); + STRING_VIEW_INLINE(Value, "value"); + + if (Mireds == action) { + if (data.containsKey(Mireds)) { + _fromMireds(data[Mireds].as()); lightUpdate(); } - } else if (STRING_VIEW("channel") == action) { - if (data.containsKey(F("id")) && data.containsKey(F("value"))) { - lightChannel(data[F("id")].as(), data[F("value")].as()); + } else if (Channel == action) { + if (data.containsKey(Id) && data.containsKey(Value)) { + lightChannel(data[Id].as(), data[Value].as()); lightUpdate(); } - } else if (STRING_VIEW("brightness") == action) { - if (data.containsKey(F("value"))) { - lightBrightness(data[F("value")].as()); + } else if (Brightness == action) { + if (data.containsKey(Value)) { + lightBrightness(data[Value].as()); lightUpdate(); } } @@ -2557,7 +2550,7 @@ static void _lightCommandNotify(::terminal::CommandContext&& ctx) { } size_t channel; - if (!_lightTryParseChannel(ctx.argv[1].c_str(), channel)) { + if (!_lightTryParseChannel(ctx.argv[1], channel)) { terminalError(ctx, F("Invalid channel ID")); return; } @@ -2649,7 +2642,8 @@ static void _lightCommandChannel(::terminal::CommandContext&& ctx) { auto description = [&](size_t channel) { ctx.output.printf_P(PSTR("#%zu (%s) input:%ld value:%ld target:%ld current:%s\n"), - channel, _lightDesc(Channels, channel), + channel, + _lightDesc(Channels, channel), _light_channels[channel].inputValue, _light_channels[channel].value, _light_channels[channel].target, @@ -2658,7 +2652,7 @@ static void _lightCommandChannel(::terminal::CommandContext&& ctx) { if (ctx.argv.size() > 2) { size_t id; - if (!_lightTryParseChannel(ctx.argv[1].c_str(), id)) { + if (!_lightTryParseChannel(ctx.argv[1], id)) { terminalError(ctx, F("Invalid channel ID")); return; } @@ -2679,7 +2673,7 @@ alignas(4) static constexpr char LightCommandRgb[] PROGMEM = "RGB"; static void _lightCommandRgb(::terminal::CommandContext&& ctx) { if (ctx.argv.size() > 1) { - _lightFromRgbPayload(ctx.argv[1].c_str()); + _lightFromRgbPayload(ctx.argv[1]); lightUpdate(); } @@ -2692,7 +2686,7 @@ alignas(4) static constexpr char LightCommandHsv[] PROGMEM = "HSV"; static void _lightCommandHsv(::terminal::CommandContext&& ctx) { if (ctx.argv.size() > 1) { - _lightFromHsvPayload(ctx.argv[1].c_str()); + _lightFromHsvPayload(ctx.argv[1]); lightUpdate(); } @@ -3081,25 +3075,12 @@ bool lightState() { return _light_state; } -void lightColor(const char* color, bool rgb) { - DEBUG_MSG_P(PSTR("[LIGHT] %s: %s\n"), rgb ? "RGB" : "HSV", color); - if (rgb) { - _lightFromRgbPayload(color); - } else { - _lightFromHsvPayload(color); - } +void lightParseHsv(espurna::StringView value) { + _lightFromHsvPayload(value); } -void lightColor(const String& color, bool rgb) { - lightColor(color.c_str(), rgb); -} - -void lightColor(const char* color) { - lightColor(color, true); -} - -void lightColor(const String& color) { - lightColor(color.c_str()); +void lightParseRgb(espurna::StringView value) { + _lightFromRgbPayload(value); } String lightRgbPayload() { diff --git a/code/espurna/light.h b/code/espurna/light.h index 7a754a27..3ec4ac04 100644 --- a/code/espurna/light.h +++ b/code/espurna/light.h @@ -202,11 +202,8 @@ using LightSequenceCallbacks = std::forward_list; void lightSequence(LightSequenceCallbacks); void lightUpdateSequence(LightTransition); -void lightColor(const char* color, bool rgb); -void lightColor(const String& color, bool rgb); - -void lightColor(const char* color); -void lightColor(const String& color); +void lightParseHsv(espurna::StringView); +void lightParseRgb(espurna::StringView); String lightRgbPayload(); String lightHsvPayload(); diff --git a/code/espurna/mqtt.cpp b/code/espurna/mqtt.cpp index b16c8f84..edc91ba6 100644 --- a/code/espurna/mqtt.cpp +++ b/code/espurna/mqtt.cpp @@ -1274,6 +1274,11 @@ String mqttMagnitude(const char* topic) { return output; } +// Retrieve lefthand side of the extracted magnitude value +espurna::StringView mqttMagnitudeTail(espurna::StringView magnitude, espurna::StringView topic) { + return espurna::StringView(magnitude.begin() + topic.length(), magnitude.end()); +} + /** Returns a full MQTT topic from the magnitude diff --git a/code/espurna/mqtt.h b/code/espurna/mqtt.h index a17a4eca..06fc5f0f 100644 --- a/code/espurna/mqtt.h +++ b/code/espurna/mqtt.h @@ -70,6 +70,7 @@ String mqttTopic(const String& magnitude, unsigned int index, bool is_set); String mqttTopic(const char* magnitude, unsigned int index, bool is_set); String mqttMagnitude(const char* topic); +espurna::StringView mqttMagnitudeTail(espurna::StringView magnitude, espurna::StringView topic); uint16_t mqttSendRaw(const char * topic, const char * message, bool retain, int qos); uint16_t mqttSendRaw(const char * topic, const char * message, bool retain); diff --git a/code/espurna/relay.cpp b/code/espurna/relay.cpp index 275ecfb4..5eb82684 100644 --- a/code/espurna/relay.cpp +++ b/code/espurna/relay.cpp @@ -217,41 +217,33 @@ constexpr RelayMqttTopicMode mqttTopicMode(size_t index) { ); } -const __FlashStringHelper* payloadOn() { - return F(RELAY_MQTT_ON); -} +alignas(4) static constexpr char PayloadOn[] PROGMEM = RELAY_MQTT_ON; +alignas(4) static constexpr char PayloadOff[] PROGMEM = RELAY_MQTT_OFF; +alignas(4) static constexpr char PayloadToggle[] PROGMEM = RELAY_MQTT_TOGGLE; -const __FlashStringHelper* payloadOff() { - return F(RELAY_MQTT_OFF); -} - -const __FlashStringHelper* payloadToggle() { - return F(RELAY_MQTT_TOGGLE); -} - -constexpr const char* mqttTopicSub(size_t index) { +constexpr espurna::StringView mqttTopicSub(size_t index) { return ( - (index == 0) ? (RELAY1_MQTT_TOPIC_SUB) : - (index == 1) ? (RELAY2_MQTT_TOPIC_SUB) : - (index == 2) ? (RELAY3_MQTT_TOPIC_SUB) : - (index == 3) ? (RELAY4_MQTT_TOPIC_SUB) : - (index == 4) ? (RELAY5_MQTT_TOPIC_SUB) : - (index == 5) ? (RELAY6_MQTT_TOPIC_SUB) : - (index == 6) ? (RELAY7_MQTT_TOPIC_SUB) : - (index == 7) ? (RELAY8_MQTT_TOPIC_SUB) : "" + (index == 0) ? STRING_VIEW(RELAY1_MQTT_TOPIC_SUB) : + (index == 1) ? STRING_VIEW(RELAY2_MQTT_TOPIC_SUB) : + (index == 2) ? STRING_VIEW(RELAY3_MQTT_TOPIC_SUB) : + (index == 3) ? STRING_VIEW(RELAY4_MQTT_TOPIC_SUB) : + (index == 4) ? STRING_VIEW(RELAY5_MQTT_TOPIC_SUB) : + (index == 5) ? STRING_VIEW(RELAY6_MQTT_TOPIC_SUB) : + (index == 6) ? STRING_VIEW(RELAY7_MQTT_TOPIC_SUB) : + (index == 7) ? STRING_VIEW(RELAY8_MQTT_TOPIC_SUB) : "" ); } -constexpr const char* mqttTopicPub(size_t index) { +constexpr espurna::StringView mqttTopicPub(size_t index) { return ( - (index == 0) ? (RELAY1_MQTT_TOPIC_PUB) : - (index == 1) ? (RELAY2_MQTT_TOPIC_PUB) : - (index == 2) ? (RELAY3_MQTT_TOPIC_PUB) : - (index == 3) ? (RELAY4_MQTT_TOPIC_PUB) : - (index == 4) ? (RELAY5_MQTT_TOPIC_PUB) : - (index == 5) ? (RELAY6_MQTT_TOPIC_PUB) : - (index == 6) ? (RELAY7_MQTT_TOPIC_PUB) : - (index == 7) ? (RELAY8_MQTT_TOPIC_PUB) : "" + (index == 0) ? STRING_VIEW(RELAY1_MQTT_TOPIC_PUB) : + (index == 1) ? STRING_VIEW(RELAY2_MQTT_TOPIC_PUB) : + (index == 2) ? STRING_VIEW(RELAY3_MQTT_TOPIC_PUB) : + (index == 3) ? STRING_VIEW(RELAY4_MQTT_TOPIC_PUB) : + (index == 4) ? STRING_VIEW(RELAY5_MQTT_TOPIC_PUB) : + (index == 5) ? STRING_VIEW(RELAY6_MQTT_TOPIC_PUB) : + (index == 6) ? STRING_VIEW(RELAY7_MQTT_TOPIC_PUB) : + (index == 7) ? STRING_VIEW(RELAY8_MQTT_TOPIC_PUB) : "" ); } @@ -872,17 +864,17 @@ RelaySync syncMode() { [[gnu::unused]] String payloadOn() { - return getSetting(keys::PayloadOn, build::payloadOn()); + return getSetting(keys::PayloadOn, StringView(build::PayloadOn)); } [[gnu::unused]] String payloadOff() { - return getSetting(keys::PayloadOff, build::payloadOff()); + return getSetting(keys::PayloadOff, StringView(build::PayloadOff)); } [[gnu::unused]] String payloadToggle() { - return getSetting(keys::PayloadToggle, build::payloadToggle()); + return getSetting(keys::PayloadToggle, StringView(build::PayloadToggle)); } #if MQTT_SUPPORT @@ -1466,24 +1458,27 @@ Stream* StmProvider::_port = nullptr; // UTILITY // ----------------------------------------------------------------------------- -bool _relayTryParseId(const char* p, size_t& id) { - return tryParseId(p, relayCount, id); +bool _relayTryParseId(espurna::StringView value, size_t& id) { + return tryParseId(value, relayCount, id); } [[gnu::unused]] -bool _relayTryParseIdFromPath(const String& endpoint, size_t& id) { - int next_slash { endpoint.lastIndexOf('/') }; - if (next_slash < 0) { +bool _relayTryParseIdFromPath(espurna::StringView endpoint, size_t& id) { + const auto begin = std::make_reverse_iterator(endpoint.end()); + const auto end = std::make_reverse_iterator(endpoint.begin()); + + auto next_slash = std::find(begin, end, '/'); + if (next_slash == end) { return false; } - const char* p { endpoint.c_str() + next_slash + 1 }; - if (*p == '\0') { + espurna::StringView tail { next_slash.base() + 1, endpoint.end() }; + if ((*tail.begin()) == '\0') { DEBUG_MSG_P(PSTR("[RELAY] relayID was not specified\n")); return false; } - return _relayTryParseId(p, id); + return _relayTryParseId(tail, id); } void _relayHandleStatus(size_t id, PayloadStatus status) { @@ -2201,9 +2196,10 @@ namespace { template bool _relayApiTryHandle(ApiRequest& request, T&& callback) { - auto id_param = request.wildcard(0); + const auto param = request.wildcard(0); + size_t id; - if (!_relayTryParseId(id_param.c_str(), id)) { + if (!_relayTryParseId(param, id)) { return false; } @@ -2514,7 +2510,7 @@ void relayMQTTCallback(unsigned int type, const char* topic, char* payload) { auto is_pulse = t.startsWith(MQTT_TOPIC_PULSE); if (is_relay || is_pulse) { size_t id; - if (!_relayTryParseIdFromPath(t.c_str(), id)) { + if (!_relayTryParseIdFromPath(t, id)) { return; } @@ -2591,7 +2587,7 @@ static void _relayCommand(::terminal::CommandContext&& ctx) { } size_t id; - if (!_relayTryParseId(ctx.argv[1].c_str(), id)) { + if (!_relayTryParseId(ctx.argv[1], id)) { terminalError(ctx, F("Invalid relayID")); return; } @@ -2622,7 +2618,7 @@ static void _relayCommandPulse(::terminal::CommandContext&& ctx) { } size_t id; - if (!_relayTryParseId(ctx.argv[1].c_str(), id)) { + if (!_relayTryParseId(ctx.argv[1], id)) { terminalError(ctx, F("Invalid relayID")); return; } diff --git a/code/espurna/rfbridge.cpp b/code/espurna/rfbridge.cpp index 06fdb7fa..8649bc9d 100644 --- a/code/espurna/rfbridge.cpp +++ b/code/espurna/rfbridge.cpp @@ -524,13 +524,13 @@ RfbRelayMatch _rfbMatch(const char* code) { return; } - const char* id_ptr = key.c_str() + prefix.length(); - if (*id_ptr == '\0') { + espurna::StringView id_view(key.begin() + prefix.length(), key.end()); + if (!id_view.length() || (*id_view.begin()) == '\0') { return; } size_t id; - if (!tryParseId(id_ptr, relayCount, id)) { + if (!tryParseId(id_view, relayCount, id)) { return; } @@ -1185,7 +1185,7 @@ static void _rfbCommandLearn(::terminal::CommandContext&& ctx) { } size_t id; - if (!tryParseId(ctx.argv[1].c_str(), relayCount, id)) { + if (!tryParseId(ctx.argv[1], relayCount, id)) { terminalError(ctx, F("Invalid relay ID")); return; } @@ -1202,7 +1202,7 @@ static void _rfbCommandForget(::terminal::CommandContext&& ctx) { } size_t id; - if (!tryParseId(ctx.argv[1].c_str(), relayCount, id)) { + if (!tryParseId(ctx.argv[1], relayCount, id)) { terminalError(ctx, F("Invalid relay ID")); return; } diff --git a/code/espurna/rpnrules.cpp b/code/espurna/rpnrules.cpp index 1ad1eaf7..9bba5bb4 100644 --- a/code/espurna/rpnrules.cpp +++ b/code/espurna/rpnrules.cpp @@ -763,8 +763,17 @@ void init(rpn_context& context) { return 0; }); - rpn_operator_set(context, "black", 0, [](rpn_context& ctxt) -> rpn_error { - ::lightColor(0ul); + rpn_operator_set(context, "brightness", 0, [](rpn_context& ctxt) -> rpn_error { + rpn_value value { static_cast(::lightBrightness()) }; + rpn_stack_push(ctxt, value); + return 0; + }); + + rpn_operator_set(context, "set_brightness", 1, [](rpn_context& ctxt) -> rpn_error { + rpn_value value; + rpn_stack_pop(ctxt, value); + + ::lightBrightness(value.toInt()); return 0; }); diff --git a/code/espurna/scheduler.cpp b/code/espurna/scheduler.cpp index 5f0baa7e..d52ff9e6 100644 --- a/code/espurna/scheduler.cpp +++ b/code/espurna/scheduler.cpp @@ -532,8 +532,10 @@ bool set(ApiRequest&, JsonObject& root) { namespace schedule { bool get(ApiRequest& req, JsonObject& root) { + const auto param = req.wildcard(0); + size_t id; - if (tryParseId(req.wildcard(0).c_str(), build::max, id)) { + if (tryParseId(param, build::max, id)) { print(root, settings::schedule(id)); return true; } @@ -542,8 +544,10 @@ bool get(ApiRequest& req, JsonObject& root) { } bool set(ApiRequest& req, JsonObject& root) { + const auto param = req.wildcard(0); + size_t id; - if (tryParseId(req.wildcard(0).c_str(), build::max, id)) { + if (tryParseId(param, build::max, id)) { return api::set(root, id); } diff --git a/code/espurna/types.h b/code/espurna/types.h index 420813b7..e59760dd 100644 --- a/code/espurna/types.h +++ b/code/espurna/types.h @@ -209,7 +209,7 @@ private: }; struct StringView { - StringView() = delete; + StringView() noexcept = default; ~StringView() = default; constexpr StringView(const StringView&) noexcept = default; @@ -251,7 +251,6 @@ struct StringView { _len(0) {} - StringView(const String&&) = delete; StringView(const String& string) noexcept : StringView(string.c_str(), string.length()) {} @@ -281,6 +280,10 @@ struct StringView { return _ptr; } + constexpr const char& operator[](size_t offset) const { + return *(_ptr + offset); + } + constexpr size_t length() const { return _len; } @@ -340,4 +343,8 @@ inline String operator+=(String& lhs, StringView rhs) { ::espurna::StringView{__pstr__};\ }) +#define STRING_VIEW_INLINE(NAME, X)\ + alignas(4) static constexpr char __pstr__ ## NAME ## __ [] PROGMEM = (X);\ + constexpr auto NAME = ::espurna::StringView(__pstr__ ## NAME ## __) + } // namespace espurna diff --git a/code/espurna/utils.cpp b/code/espurna/utils.cpp index 52ef3bf6..bae8c66e 100644 --- a/code/espurna/utils.cpp +++ b/code/espurna/utils.cpp @@ -13,12 +13,12 @@ Copyright (C) 2017-2019 by Xose Pérez #include #include -bool tryParseId(const char* p, TryParseIdFunc limit, size_t& out) { +bool tryParseId(espurna::StringView value, TryParseIdFunc limit, size_t& out) { static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), ""); char* endp { nullptr }; - out = strtoul(p, &endp, 10); - if ((endp == p) || (*endp != '\0') || (out >= limit())) { + out = strtoul(value.begin(), &endp, 10); // TODO from_chars + if ((endp == value.begin()) || (*endp != '\0') || (out >= limit())) { return false; } diff --git a/code/espurna/utils.h b/code/espurna/utils.h index 5ad10782..1be39683 100644 --- a/code/espurna/utils.h +++ b/code/espurna/utils.h @@ -51,6 +51,6 @@ uint8_t* hexDecode(const char* in_begin, const char* in_end, uint8_t* out_begin, size_t hexDecode(const char* in, size_t in_size, uint8_t* out, size_t out_size); using TryParseIdFunc = size_t(*)(); -bool tryParseId(const char* ptr, TryParseIdFunc limit, size_t& out); +bool tryParseId(espurna::StringView, TryParseIdFunc limit, size_t& out); espurna::StringView stripNewline(espurna::StringView); diff --git a/code/espurna/wifi.cpp b/code/espurna/wifi.cpp index d1f4a8ea..b2c86039 100644 --- a/code/espurna/wifi.cpp +++ b/code/espurna/wifi.cpp @@ -2526,32 +2526,45 @@ String event(wifi::Event value) { [[gnu::unused]] const char* state(wifi::State value) { + const char* out = "?"; + switch (value) { case wifi::State::Boot: - return "Boot"; + out = PSTR("Boot"); + break; case wifi::State::Connect: - return "Connect"; + out = PSTR("Connect"); + break; case wifi::State::TryConnectBetter: - return "TryConnectBetter"; + out = PSTR("TryConnectBetter"); + break; case wifi::State::Fallback: - return "Fallback"; + out = PSTR("Fallback"); + break; case wifi::State::Connected: - return "Connected"; + out = PSTR("Connected"); + break; case wifi::State::Idle: - return "Idle"; + out = PSTR("Idle"); + break; case wifi::State::Init: - return "Init"; + out = PSTR("Init"); + break; case wifi::State::Timeout: - return "Timeout"; + out = PSTR("Timeout"); + break; case wifi::State::WaitScan: - return "WaitScan"; + out = PSTR("WaitScan"); + break; case wifi::State::WaitScanWithoutCurrent: - return "WaitScanWithoutCurrent"; + out = PSTR("WaitScanWithoutCurrent"); + break; case wifi::State::WaitConnected: - return "WaitConnected"; + out = PSTR("WaitConnected"); + break; } - return ""; + return out; } } // namespace debug