Browse Source

api: rework storage & json calls, drop std::function

- reduce overall size of the structure, store a single required entity
  inside of Api object itself.
- tweak internal functions to expect a certain path size
- (kind of a hack) add manual calls to vector<Api>::reserve() as we go over
  the current capacity and vector increases it's size times 2.
  e.g. for 9 relays, we would allocate space for 32 Api objects
  when vector size goes from 16 to 32, after we add 18 Api objects with relay + pulse
- (breaking) json calls on a separate path, don't waste time encoding
  a single entity as json object when we can encode more things
mcspr-patch-1
Maxim Prokhorov 4 years ago
parent
commit
f3c185cc73
6 changed files with 396 additions and 226 deletions
  1. +131
    -81
      code/espurna/api.cpp
  2. +61
    -4
      code/espurna/api.h
  3. +67
    -52
      code/espurna/light.cpp
  4. +54
    -42
      code/espurna/relay.cpp
  5. +45
    -32
      code/espurna/rfbridge.cpp
  6. +38
    -15
      code/espurna/sensor.cpp

+ 131
- 81
code/espurna/api.cpp View File

@ -21,19 +21,8 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <ESPAsyncTCP.h>
#include <ArduinoJson.h>
struct web_api_t {
explicit web_api_t(const String& key, api_get_callback_f getFn, api_put_callback_f putFn) :
key(key),
getFn(getFn),
putFn(putFn)
{}
web_api_t() = delete;
const String key;
api_get_callback_f getFn;
api_put_callback_f putFn;
};
std::vector<web_api_t> _apis;
constexpr size_t ApiPathSizeMax { 64ul };
std::vector<Api> _apis;
// -----------------------------------------------------------------------------
// API
@ -50,16 +39,10 @@ bool _asJson(AsyncWebServerRequest *request) {
void _onAPIsText(AsyncWebServerRequest *request) {
AsyncResponseStream *response = request->beginResponseStream("text/plain");
String output;
output.reserve(48);
char buffer[ApiPathSizeMax] = {0};
for (auto& api : _apis) {
output = "";
output += api.key;
output += " -> ";
output += "/api/";
output += api.key;
output += '\n';
response->write(output.c_str());
sprintf_P(buffer, PSTR("/api/%s\n"), api.path.c_str());
response->write(buffer);
}
request->send(response);
}
@ -69,19 +52,14 @@ constexpr size_t ApiJsonBufferSize = 1024;
void _onAPIsJson(AsyncWebServerRequest *request) {
DynamicJsonBuffer jsonBuffer(ApiJsonBufferSize);
JsonObject& root = jsonBuffer.createObject();
JsonArray& root = jsonBuffer.createArray();
constexpr const int BUFFER_SIZE = 48;
for (unsigned int i=0; i < _apis.size(); i++) {
char buffer[BUFFER_SIZE] = {0};
int res = snprintf(buffer, sizeof(buffer), "/api/%s", _apis[i].key.c_str());
if ((res < 0) || (res > (BUFFER_SIZE - 1))) {
request->send(500);
return;
}
root[_apis[i].key] = buffer;
char buffer[ApiPathSizeMax] = {0};
for (auto& api : _apis) {
sprintf(buffer, "/api/%s", api.path.c_str());
root.add(buffer);
}
AsyncResponseStream *response = request->beginResponseStream("application/json");
root.printTo(*response);
request->send(response);
@ -129,87 +107,159 @@ void _onRPC(AsyncWebServerRequest *request) {
}
bool _apiRequestCallback(AsyncWebServerRequest *request) {
struct ApiMatch {
Api* api { nullptr };
Api::Type type { Api::Type::Basic };
};
String url = request->url();
ApiMatch _apiMatch(const String& url, AsyncWebServerRequest* request) {
// Main API entry point
if (url.equals("/api") || url.equals("/apis")) {
_onAPIs(request);
return true;
ApiMatch result;
char buffer[ApiPathSizeMax] = {0};
for (auto& api : _apis) {
sprintf_P(buffer, PSTR("/api/%s"), api.path.c_str());
if (url != buffer) {
continue;
}
auto type = _asJson(request)
? Api::Type::Json
: Api::Type::Basic;
result.api = &api;
result.type = type;
break;
}
// Main RPC entry point
if (url.equals("/rpc")) {
_onRPC(request);
return result;
}
bool _apiDispatchRequest(const String& url, AsyncWebServerRequest* request) {
auto match = _apiMatch(url, request);
if (!match.api) {
return false;
}
if (match.type != match.api->type) {
DEBUG_MSG_P(PSTR("[API] Cannot handle the request type\n"));
request->send(404);
return true;
}
// Not API request
if (!url.startsWith("/api/")) return false;
const bool is_put = (
(!apiRestFul() || (request->method() == HTTP_PUT))
&& request->hasParam("value", request->method() == HTTP_PUT)
);
for (auto& api : _apis) {
ApiBuffer buffer;
// Search API url for the exact match
if (!url.endsWith(api.key)) continue;
switch (match.api->type) {
// Log and check credentials
webLog(request);
if (!apiAuthenticate(request)) return false;
case Api::Type::Basic: {
if (!match.api->get.basic) {
break;
}
// Check if its a PUT
if (api.putFn != NULL) {
if (!apiRestFul() || (request->method() == HTTP_PUT)) {
if (request->hasParam("value", request->method() == HTTP_PUT)) {
AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
(api.putFn)((p->value()).c_str());
}
if (is_put) {
if (!match.api->put.basic) {
break;
}
auto value = request->getParam("value", request->method() == HTTP_PUT)->value();
//memcpy(buffer.data, value.c_str(), value.length());
std::copy(value.c_str(), value.c_str() + value.length(), buffer.data);
match.api->get.basic(*match.api, buffer);
buffer.erase();
}
// Get response from callback
char value[API_BUFFER_SIZE] = {0};
(api.getFn)(value, API_BUFFER_SIZE);
match.api->get.basic(*match.api, buffer);
request->send(200, "text/plain", buffer.data);
// The response will be a 404 NOT FOUND if the resource is not available
if (0 == value[0]) {
DEBUG_MSG_P(PSTR("[API] Sending 404 response\n"));
request->send(404);
return false;
return true;
}
// TODO: pass the body instead of `value` param
// TODO: handle HTTP_PUT
case Api::Type::Json: {
if (!match.api->get.json || is_put) {
break;
}
DEBUG_MSG_P(PSTR("[API] Sending response '%s'\n"), value);
DynamicJsonBuffer jsonBuffer(API_BUFFER_SIZE);
JsonObject& root = jsonBuffer.createObject();
// Format response according to the Accept header
if (_asJson(request)) {
char buffer[64];
if (isNumber(value)) {
snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": %s }"), api.key.c_str(), value);
} else {
snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": \"%s\" }"), api.key.c_str(), value);
}
request->send(200, "application/json", buffer);
} else {
request->send(200, "text/plain", value);
}
match.api->get.json(*match.api, root);
AsyncResponseStream *response = request->beginResponseStream("application/json", root.measureLength() + 1);
root.printTo(*response);
request->send(response);
return true;
}
}
return false;
DEBUG_MSG_P(PSTR("[API] Method not supported\n"));
request->send(405);
return true;
}
bool _apiRequestCallback(AsyncWebServerRequest* request) {
String url = request->url();
if (url.equals("/rpc")) {
_onRPC(request);
return true;
}
if (url.equals("/api") || url.equals("/apis")) {
_onAPIs(request);
return true;
}
if (!url.startsWith("/api/")) return false;
if (!apiAuthenticate(request)) return false;
return _apiDispatchRequest(url, request);
}
// -----------------------------------------------------------------------------
void apiRegister(const String& key, api_get_callback_f getFn, api_put_callback_f putFn) {
_apis.emplace_back(key, std::move(getFn), std::move(putFn));
void apiReserve(size_t size) {
_apis.reserve(_apis.size() + size);
}
void apiRegister(const Api& api) {
if (api.path.length() >= (ApiPathSizeMax - strlen("/api/") - 1ul)) {
return;
}
_apis.push_back(api);
}
void apiSetup() {
webRequestRegister(_apiRequestCallback);
}
void apiOk(const Api&, ApiBuffer& buffer) {
buffer.data[0] = 'O';
buffer.data[1] = 'K';
buffer.data[2] = '\0';
}
void apiError(const Api&, ApiBuffer& buffer) {
buffer.data[0] = '-';
buffer.data[1] = 'E';
buffer.data[2] = 'R';
buffer.data[3] = 'R';
buffer.data[4] = 'O';
buffer.data[5] = 'R';
buffer.data[6] = '\0';
}
#endif // API_SUPPORT

+ 61
- 4
code/espurna/api.h View File

@ -22,14 +22,71 @@ String apiKey();
#if WEB_SUPPORT && API_SUPPORT
#include <functional>
#include <vector>
using api_get_callback_f = std::function<void(char * buffer, size_t size)>;
using api_put_callback_f = std::function<void(const char * payload)> ;
constexpr unsigned char ApiUnusedArg = 0u;
void apiRegister(const String& key, api_get_callback_f getFn, api_put_callback_f putFn = nullptr);
struct ApiBuffer {
constexpr static size_t size = API_BUFFER_SIZE;
char data[size];
void erase() {
std::fill(data, data + size, '\0');
}
};
struct Api {
using BasicHandler = void(*)(const Api& api, ApiBuffer& buffer);
using JsonHandler = void(*)(const Api& api, JsonObject& root);
enum class Type {
Basic,
Json
};
Api() = delete;
Api(const String& path_, Type type_, unsigned char arg_, BasicHandler get_, BasicHandler put_ = nullptr) :
path(path_),
type(type_),
arg(arg_)
{
get.basic = get_;
put.basic = put_;
}
Api(const String& path_, Type type_, unsigned char arg_, JsonHandler get_, JsonHandler put_ = nullptr) :
path(path_),
type(type_),
arg(arg_)
{
get.json = get_;
put.json = put_;
}
String path;
Type type;
unsigned char arg;
union {
BasicHandler basic;
JsonHandler json;
} get;
union {
BasicHandler basic;
JsonHandler json;
} put;
};
void apiRegister(const Api& api);
void apiCommonSetup();
void apiSetup();
void apiReserve(size_t);
void apiError(const Api&, ApiBuffer& buffer);
void apiOk(const Api&, ApiBuffer& buffer);
#endif // API_SUPPORT == 1

+ 67
- 52
code/espurna/light.cpp View File

@ -853,86 +853,101 @@ void lightBroker() {
#if API_SUPPORT
void _lightAPISetup() {
void _lightApiSetup() {
// Note that we expect a fixed number of entries.
// Otherwise, underlying vector will reserve more than we need (likely, *2 of the current size)
apiReserve(
(_light_has_color ? 4u : 0u) + 2u + _light_channels.size()
);
if (_light_has_color) {
apiRegister(MQTT_TOPIC_COLOR_RGB,
[](char * buffer, size_t len) {
apiRegister({
MQTT_TOPIC_COLOR_RGB, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
if (getSetting("useCSS", 1 == LIGHT_USE_CSS)) {
_toRGB(buffer, len, true);
_toRGB(buffer.data, buffer.size, true);
} else {
_toLong(buffer, len, true);
_toLong(buffer.data, buffer.size, true);
}
},
[](const char * payload) {
lightColor(payload, true);
[](const Api&, ApiBuffer& buffer) {
lightColor(buffer.data, true);
lightUpdate(true, true);
}
);
});
apiRegister(MQTT_TOPIC_COLOR_HSV,
[](char * buffer, size_t len) {
_toHSV(buffer, len);
apiRegister({
MQTT_TOPIC_COLOR_HSV, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
_toHSV(buffer.data, buffer.size);
},
[](const char * payload) {
lightColor(payload, false);
lightUpdate(true, true);
}
);
apiRegister(MQTT_TOPIC_KELVIN,
[](char * buffer, size_t len) {},
[](const char * payload) {
_lightAdjustKelvin(payload);
[](const Api&, ApiBuffer& buffer) {
lightColor(buffer.data, false);
lightUpdate(true, true);
}
);
});
apiRegister(MQTT_TOPIC_MIRED,
[](char * buffer, size_t len) {},
[](const char * payload) {
_lightAdjustMireds(payload);
apiRegister({
MQTT_TOPIC_MIRED, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
sprintf(buffer.data, PSTR("%d"), _light_mireds);
},
[](const Api&, ApiBuffer& buffer) {
_lightAdjustMireds(buffer.data);
lightUpdate(true, true);
}
);
}
});
for (unsigned int id=0; id<_light_channels.size(); id++) {
char key[15];
snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_CHANNEL, id);
apiRegister(key,
[id](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _light_channels[id].target);
apiRegister({
MQTT_TOPIC_KELVIN, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
sprintf(buffer.data, PSTR("%d"), _toKelvin(_light_mireds));
},
[id](const char * payload) {
_lightAdjustChannel(id, payload);
[](const Api&, ApiBuffer& buffer) {
_lightAdjustKelvin(buffer.data);
lightUpdate(true, true);
}
);
});
}
apiRegister(MQTT_TOPIC_TRANSITION,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), lightTransitionTime());
apiRegister({
MQTT_TOPIC_TRANSITION, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
snprintf_P(buffer.data, buffer.size, PSTR("%u"), lightTransitionTime());
},
[](const char * payload) {
lightTransitionTime(atol(payload));
[](const Api&, ApiBuffer& buffer) {
lightTransitionTime(atol(buffer.data));
}
);
});
apiRegister(MQTT_TOPIC_BRIGHTNESS,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _light_brightness);
apiRegister({
MQTT_TOPIC_BRIGHTNESS, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
snprintf_P(buffer.data, buffer.size, PSTR("%u"), _light_brightness);
},
[](const char * payload) {
_lightAdjustBrightness(payload);
[](const Api&, ApiBuffer& buffer) {
_lightAdjustBrightness(buffer.data);
lightUpdate(true, true);
}
);
});
char path[32] = {0};
for (unsigned char id = 0; id < _light_channels.size(); ++id) {
snprintf_P(path, sizeof(path), PSTR(MQTT_TOPIC_CHANNEL "/%u"), id);
apiRegister({
path, Api::Type::Basic, id,
[](const Api& api, ApiBuffer& buffer) {
snprintf_P(buffer.data, buffer.size, PSTR("%u"), _light_channels[api.arg].target);
},
[](const Api& api, ApiBuffer& buffer) {
_lightAdjustChannel(api.arg, buffer.data);
lightUpdate(true, true);
}
});
}
}
@ -1421,7 +1436,7 @@ void lightSetup() {
#endif
#if API_SUPPORT
_lightAPISetup();
_lightApiSetup();
#endif
#if MQTT_SUPPORT


+ 54
- 42
code/espurna/relay.cpp View File

@ -1022,60 +1022,72 @@ void relaySetupWS() {
void relaySetupAPI() {
char key[20];
// API entry points (protected with apikey)
for (unsigned int relayID=0; relayID<relayCount(); relayID++) {
// Note that we expect a fixed number of entries.
// Otherwise, underlying vector will reserve more than we need (likely, *2 of the current size)
apiReserve(2u + (relayCount() * 2u));
apiRegister({
MQTT_TOPIC_RELAY, Api::Type::Json, ApiUnusedArg,
[](const Api&, JsonObject& root) {
JsonArray& relays = root.createNestedArray("relayStatus");
for (unsigned char id = 0; id < relayCount(); ++id) {
relays.add(_relays[id].target_status ? 1 : 0);
}
}
});
snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_RELAY, relayID);
apiRegister(key,
[relayID](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("%d"), _relays[relayID].target_status ? 1 : 0);
#if defined(ITEAD_SONOFF_IFAN02)
apiRegister({
MQTT_TOPIC_SPEED, Api::Type::Basic, ApiUnusedArg,
[](const Api&, ApiBuffer& buffer) {
snprintf(buffer.data, buffer.size, "%u", getSpeed());
},
[relayID](const char * payload) {
[](const Api&, ApiBuffer& buffer) {
setSpeed(atoi(buffer.data));
snprintf(buffer.data, buffer.size, "%u", getSpeed());
}
});
#endif
if (!_relayHandlePayload(relayID, payload)) {
DEBUG_MSG_P(PSTR("[RELAY] Wrong payload (%s)\n"), payload);
char path[64] = {0};
for (unsigned char id = 0; id < relayCount(); ++id) {
sprintf_P(path, PSTR(MQTT_TOPIC_RELAY "/%u"), id);
apiRegister({
path, Api::Type::Basic, id,
[](const Api& api, ApiBuffer& buffer) {
snprintf_P(buffer.data, buffer.size, PSTR("%d"), _relays[api.arg].target_status ? 1 : 0);
},
[](const Api& api, ApiBuffer& buffer) {
if (!_relayHandlePayload(api.arg, buffer.data)) {
DEBUG_MSG_P(PSTR("[RELAY] Invalid API payload (%s)\n"), buffer.data);
return;
}
}
);
});
snprintf_P(key, sizeof(key), PSTR("%s/%d"), MQTT_TOPIC_PULSE, relayID);
apiRegister(key,
[relayID](char * buffer, size_t len) {
dtostrf((double) _relays[relayID].pulse_ms / 1000, 1, 3, buffer);
sprintf_P(path, PSTR(MQTT_TOPIC_PULSE "/%u"), id);
apiRegister({
path, Api::Type::Basic, id,
[](const Api& api, ApiBuffer& buffer) {
dtostrf((double) _relays[api.arg].pulse_ms / 1000, 1, 3, buffer.data);
},
[relayID](const char * payload) {
unsigned long pulse = 1000 * atof(payload);
if (0 == pulse) return;
if (RELAY_PULSE_NONE != _relays[relayID].pulse) {
DEBUG_MSG_P(PSTR("[RELAY] Overriding relay #%d pulse settings\n"), relayID);
[](const Api& api, ApiBuffer& buffer) {
unsigned long pulse = 1000 * atof(buffer.data);
if (0 == pulse) {
return;
}
_relays[relayID].pulse_ms = pulse;
_relays[relayID].pulse = relayStatus(relayID) ? RELAY_PULSE_ON : RELAY_PULSE_OFF;
relayToggle(relayID, true, false);
}
);
#if defined(ITEAD_SONOFF_IFAN02)
apiRegister(MQTT_TOPIC_SPEED,
[relayID](char * buffer, size_t len) {
snprintf(buffer, len, "%u", getSpeed());
},
[relayID](const char * payload) {
setSpeed(atoi(payload));
if (RELAY_PULSE_NONE != _relays[api.arg].pulse) {
DEBUG_MSG_P(PSTR("[RELAY] Overriding relay #%d pulse settings\n"), api.arg);
}
);
#endif
_relays[api.arg].pulse_ms = pulse;
_relays[api.arg].pulse = relayStatus(api.arg)
? RELAY_PULSE_ON
: RELAY_PULSE_OFF;
relayToggle(api.arg, true, false);
}
});
}
}


+ 45
- 32
code/espurna/rfbridge.cpp View File

@ -539,47 +539,60 @@ void _rfbMqttCallback(unsigned int type, const char * topic, char * payload) {
#if API_SUPPORT
void _rfbAPISetup() {
apiRegister(MQTT_TOPIC_RFOUT,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("OK"));
},
[](const char * payload) {
_rfbParseCode((char *) payload);
void _rfbApiSetup() {
apiReserve(3u);
apiRegister({
MQTT_TOPIC_RFOUT, Api::Type::Basic, ApiUnusedArg,
apiOk, // just a stub, nothing to return
[](const Api&, ApiBuffer& buffer) {
_rfbParseCode(buffer.data);
}
);
});
apiRegister(MQTT_TOPIC_RFLEARN,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("OK"));
},
[](const char * payload) {
apiRegister({
MQTT_TOPIC_RFLEARN, Api::Type::Basic, ApiUnusedArg,
apiOk, // just a stub, nothing to return
[](const Api&, ApiBuffer& buffer) {
// The payload must be the relayID plus the mode (0 or 1)
char * tok = strtok((char *) payload, ",");
if (NULL == tok) return;
if (!isNumber(tok)) return;
_learnId = atoi(tok);
char* sep = strchr(buffer.data, ',');
if (NULL == sep) {
return;
}
char relay[3] {0, 0, 0};
if ((sep - buffer) > 2) {
return;
}
std::copy(buffer.data, sep, relay);
if (!isNumber(relay)) {
return;
}
_learnId = atoi(relay);
if (_learnId >= relayCount()) {
DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
return;
}
tok = strtok(NULL, ",");
if (NULL == tok) return;
_learnStatus = (char) tok[0] != '0';
_rfbLearnImpl();
++sep;
if ((*sep == '0') || (*sep == '1')) {
_learnStatus = (*sep != '0');
_rfbLearnImpl();
}
}
);
});
#if !RFB_DIRECT
apiRegister(MQTT_TOPIC_RFRAW,
[](char * buffer, size_t len) {
snprintf_P(buffer, len, PSTR("OK"));
},
[](const char * payload) {
_rfbParseRaw((char *)payload);
#if not RFB_DIRECT
apiRegister({
MQTT_TOPIC_RFRAW, Api::Type::Basic, ApiUnusedArg,
apiOk, // just a stub, nothing to return
[](const Api&, ApiBuffer& buffer) {
_rfbParseRaw(buffer.data);
}
);
});
#endif
}
@ -723,7 +736,7 @@ void rfbSetup() {
#endif
#if API_SUPPORT
_rfbAPISetup();
_rfbApiSetup();
#endif
#if WEB_SUPPORT


+ 38
- 15
code/espurna/sensor.cpp View File

@ -1403,27 +1403,50 @@ void _sensorWebSocketOnConnected(JsonObject& root) {
#if API_SUPPORT
void _sensorAPISetup() {
String _sensorApiMagnitudeName(sensor_magnitude_t& magnitude) {
String name = magnitudeTopic(magnitude.type);
if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) name = name + "/" + String(magnitude.index_global);
return name;
}
void _sensorApiJsonCallback(const Api&, JsonObject& root) {
JsonArray& magnitudes = root.createNestedArray("magnitudes");
for (auto& magnitude : _magnitudes) {
JsonArray& data = magnitudes.createNestedArray();
data.add(_sensorApiMagnitudeName(magnitude));
data.add(magnitude.last);
data.add(magnitude.reported);
}
}
String topic = magnitudeTopic(magnitude.type);
if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) topic = topic + "/" + String(magnitude.index_global);
void _sensorApiGetValue(const Api& api, ApiBuffer& buffer) {
auto& magnitude = _magnitudes[api.arg];
double value = _sensor_realtime ? magnitude.last : magnitude.reported;
dtostrf(value, 1, magnitude.decimals, buffer.data);
}
api_get_callback_f get_cb = [&magnitude](char * buffer, size_t len) {
double value = _sensor_realtime ? magnitude.last : magnitude.reported;
dtostrf(value, 1, magnitude.decimals, buffer);
};
api_put_callback_f put_cb = nullptr;
void _sensorApiResetEnergyPutCallback(const Api& api, ApiBuffer& buffer) {
_sensorApiResetEnergy(_magnitudes[api.arg], buffer.data);
}
if (magnitude.type == MAGNITUDE_ENERGY) {
put_cb = [&magnitude](const char* payload) {
_sensorApiResetEnergy(magnitude, payload);
};
}
void _sensorApiSetup() {
apiRegister(topic.c_str(), get_cb, put_cb);
apiReserve(
_magnitudes.size() + sensor_magnitude_t::counts(MAGNITUDE_ENERGY) + 1u
);
apiRegister({"magnitudes", Api::Type::Json, ApiUnusedArg, _sensorApiJsonCallback});
for (unsigned char id = 0; id < _magnitudes.size(); ++id) {
apiRegister({
_sensorApiMagnitudeName(_magnitudes[id]).c_str(),
Api::Type::Basic, id,
_sensorApiGetValue,
(_magnitudes[id].type == MAGNITUDE_ENERGY)
? _sensorApiResetEnergyPutCallback
: nullptr
});
}
}
@ -2642,7 +2665,7 @@ void sensorSetup() {
// API
#if API_SUPPORT
_sensorAPISetup();
_sensorApiSetup();
#endif
// Terminal


Loading…
Cancel
Save