mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 17:29:50 +03:00
Merge remote-tracking branch 'origin/v3.4' into dev
This commit is contained in:
@@ -24,6 +24,9 @@ using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uint32_t WebAPIService::api_count_ = 0;
|
||||
uint16_t WebAPIService::api_fails_ = 0;
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS, must use 'Content-Type: application/json' in header
|
||||
@@ -66,6 +69,33 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
is_admin = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
||||
});
|
||||
|
||||
// check for query parameters first, the old style from v2
|
||||
// api?device={device}&cmd={name}&data={value}&id={hc}
|
||||
if (request->url() == "/api") {
|
||||
// get the device
|
||||
if (request->hasParam(F_(device))) {
|
||||
input["device"] = request->getParam(F_(device))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(cmd))) {
|
||||
input["cmd"] = request->getParam(F_(cmd))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(data))) {
|
||||
input["data"] = request->getParam(F_(data))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(value))) {
|
||||
input["value"] = request->getParam(F_(value))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(id))) {
|
||||
input["id"] = Helpers::atoint(request->getParam(F_(id))->value().c_str());
|
||||
}
|
||||
if (request->hasParam(F_(hc))) {
|
||||
input["hc"] = Helpers::atoint(request->getParam(F_(hc))->value().c_str());
|
||||
}
|
||||
if (request->hasParam(F_(wwc))) {
|
||||
input["wwc"] = Helpers::atoint(request->getParam(F_(wwc))->value().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// output json buffer
|
||||
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
JsonObject output = response->getRoot();
|
||||
@@ -81,8 +111,9 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
snprintf(error, sizeof(error), "Call failed with error code (%s)", Command::return_code_string(return_code).c_str());
|
||||
}
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
api_fails_++;
|
||||
} else {
|
||||
emsesp::EMSESP::logger().debug(F("API command called successfully"));
|
||||
// emsesp::EMSESP::logger().debug(F("API command called successfully"));
|
||||
// if there was no json output from the call, default to the output message 'OK'.
|
||||
if (!output.size()) {
|
||||
output["message"] = "OK";
|
||||
@@ -96,6 +127,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
response->setLength();
|
||||
response->setContentType("application/json");
|
||||
request->send(response);
|
||||
api_count_++;
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Serial.print(COLOR_YELLOW);
|
||||
|
||||
@@ -19,14 +19,6 @@
|
||||
#ifndef WebAPIService_h
|
||||
#define WebAPIService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define EMSESP_API_SERVICE_PATH "/api"
|
||||
|
||||
namespace emsesp {
|
||||
@@ -38,10 +30,21 @@ class WebAPIService {
|
||||
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json); // for POSTs
|
||||
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
|
||||
|
||||
static uint32_t api_count() {
|
||||
return api_count_;
|
||||
}
|
||||
|
||||
static uint16_t api_fails() {
|
||||
return api_fails_;
|
||||
}
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
AsyncCallbackJsonWebHandler _apiHandler; // for POSTs
|
||||
|
||||
static uint32_t api_count_;
|
||||
static uint16_t api_fails_;
|
||||
|
||||
void parse(AsyncWebServerRequest * request, JsonObject & input);
|
||||
};
|
||||
|
||||
|
||||
272
src/web/WebCustomizationService.cpp
Normal file
272
src/web/WebCustomizationService.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebCustomization::read,
|
||||
WebCustomization::update,
|
||||
this,
|
||||
server,
|
||||
EMSESP_CUSTOMIZATION_SERVICE_PATH,
|
||||
securityManager,
|
||||
AuthenticationPredicates::IS_AUTHENTICATED)
|
||||
, _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
|
||||
, _exclude_entities_handler(EXCLUDE_ENTITIES_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebCustomizationService::exclude_entities, this, _1, _2),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _device_entities_handler(DEVICE_ENTITIES_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebCustomizationService::device_entities, this, _1, _2),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED)) {
|
||||
server->on(DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebCustomizationService::devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(RESET_CUSTOMIZATION_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&WebCustomizationService::reset_customization, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
_exclude_entities_handler.setMethod(HTTP_POST);
|
||||
_exclude_entities_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_exclude_entities_handler);
|
||||
|
||||
_device_entities_handler.setMethod(HTTP_POST);
|
||||
_device_entities_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_entities_handler);
|
||||
}
|
||||
|
||||
// this creates the customization file, saving to the FS
|
||||
void WebCustomization::read(WebCustomization & settings, JsonObject & root) {
|
||||
// Dallas Sensor customization
|
||||
JsonArray sensorsJson = root.createNestedArray("sensors");
|
||||
for (const SensorCustomization & sensor : settings.sensorCustomizations) {
|
||||
JsonObject sensorJson = sensorsJson.createNestedObject();
|
||||
sensorJson["id_str"] = sensor.id_str; // is
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
}
|
||||
|
||||
// Analog Sensor customization
|
||||
JsonArray analogJson = root.createNestedArray("analogs");
|
||||
for (const AnalogCustomization & sensor : settings.analogCustomizations) {
|
||||
JsonObject sensorJson = analogJson.createNestedObject();
|
||||
sensorJson["id"] = sensor.id; // i
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
sensorJson["factor"] = sensor.factor; // f
|
||||
sensorJson["uom"] = sensor.uom; // u
|
||||
sensorJson["type"] = sensor.type; // t
|
||||
}
|
||||
|
||||
// Exclude entities customization
|
||||
JsonArray exclude_entitiesJson = root.createNestedArray("exclude_entities");
|
||||
for (const EntityCustomization & entityCustomization : settings.entityCustomizations) {
|
||||
JsonObject entityJson = exclude_entitiesJson.createNestedObject();
|
||||
entityJson["product_id"] = entityCustomization.product_id;
|
||||
entityJson["device_id"] = entityCustomization.device_id;
|
||||
|
||||
JsonArray exclude_entityJson = entityJson.createNestedArray("entity_ids");
|
||||
for (uint8_t entity_id : entityCustomization.entity_ids) {
|
||||
exclude_entityJson.add(entity_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call on initialization and also when the page is saved via web
|
||||
// this loads the data into the internal class
|
||||
StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & settings) {
|
||||
// Dallas Sensor customization
|
||||
settings.sensorCustomizations.clear();
|
||||
if (root["sensors"].is<JsonArray>()) {
|
||||
for (const JsonObject sensorJson : root["sensors"].as<JsonArray>()) {
|
||||
// create each of the sensor, overwritting any previous settings
|
||||
SensorCustomization sensor = SensorCustomization();
|
||||
sensor.id_str = sensorJson["id_str"].as<std::string>();
|
||||
sensor.name = sensorJson["name"].as<std::string>();
|
||||
sensor.offset = sensorJson["offset"];
|
||||
settings.sensorCustomizations.push_back(sensor); // add to list
|
||||
}
|
||||
}
|
||||
|
||||
// Analog Sensor customization
|
||||
settings.analogCustomizations.clear();
|
||||
if (root["analogs"].is<JsonArray>()) {
|
||||
for (const JsonObject analogJson : root["analogs"].as<JsonArray>()) {
|
||||
// create each of the sensor, overwritting any previous settings
|
||||
AnalogCustomization sensor = AnalogCustomization();
|
||||
sensor.id = analogJson["id"];
|
||||
sensor.name = analogJson["name"].as<std::string>();
|
||||
sensor.offset = analogJson["offset"];
|
||||
sensor.factor = analogJson["factor"];
|
||||
sensor.uom = analogJson["uom"];
|
||||
sensor.type = analogJson["type"];
|
||||
settings.analogCustomizations.push_back(sensor); // add to list
|
||||
}
|
||||
}
|
||||
|
||||
// load array of entities id's to exclude, building up the object class
|
||||
settings.entityCustomizations.clear();
|
||||
if (root["exclude_entities"].is<JsonArray>()) {
|
||||
for (const JsonObject exclude_entities : root["exclude_entities"].as<JsonArray>()) {
|
||||
EntityCustomization new_entry = EntityCustomization();
|
||||
new_entry.product_id = exclude_entities["product_id"];
|
||||
new_entry.device_id = exclude_entities["device_id"];
|
||||
|
||||
for (const JsonVariant exclude_entity_id : exclude_entities["entity_ids"].as<JsonArray>()) {
|
||||
new_entry.entity_ids.push_back(exclude_entity_id.as<uint8_t>()); // add entity list
|
||||
}
|
||||
settings.entityCustomizations.push_back(new_entry); // save the new object
|
||||
}
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
// deletes the customization file
|
||||
void WebCustomizationService::reset_customization(AsyncWebServerRequest * request) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (LITTLEFS.remove(EMSESP_CUSTOMIZATION_FILE)) {
|
||||
AsyncWebServerResponse * response = request->beginResponse(200); // OK
|
||||
request->send(response);
|
||||
EMSESP::system_.restart_requested(true);
|
||||
return;
|
||||
}
|
||||
// failed
|
||||
AsyncWebServerResponse * response = request->beginResponse(204); // no content error
|
||||
request->send(response);
|
||||
#endif
|
||||
}
|
||||
|
||||
// send back a short list devices used in the customization page
|
||||
void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
for (auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->has_entities()) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
obj["i"] = emsdevice->unique_id(); // a unique id
|
||||
|
||||
// shortname - we prefix the count to make it unique
|
||||
uint8_t device_index = EMSESP::device_index(emsdevice->device_type(), emsdevice->unique_id());
|
||||
if (device_index) {
|
||||
char s[10];
|
||||
obj["s"] = emsdevice->device_type_name() + Helpers::smallitoa(s, device_index);
|
||||
} else {
|
||||
obj["s"] = emsdevice->device_type_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// send back list device entities
|
||||
void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(true, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
JsonArray output = response->getRoot();
|
||||
emsdevice->generate_values_web_all(output);
|
||||
#endif
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// invalid, but send OK anyway
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a list of excluded ids send from the webUI
|
||||
// saves it in the customization service
|
||||
// and updates the entity list real-time
|
||||
void WebCustomizationService::exclude_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
// find the device using the unique_id
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
uint8_t unique_device_id = json["id"];
|
||||
if (emsdevice->unique_id() == unique_device_id) {
|
||||
JsonArray entity_ids = json["entity_ids"];
|
||||
|
||||
std::vector<uint8_t> temp;
|
||||
for (JsonVariant id : entity_ids) {
|
||||
uint8_t entity_id = id.as<int>();
|
||||
emsdevice->exclude_entity(entity_id); // this will have immediate affect
|
||||
temp.push_back(entity_id);
|
||||
}
|
||||
|
||||
// Save the list to the customization file
|
||||
uint8_t product_id = emsdevice->product_id();
|
||||
uint8_t device_id = emsdevice->device_id();
|
||||
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
// if it exists (productid and deviceid match) overwrite it
|
||||
for (auto & entityCustomization : settings.entityCustomizations) {
|
||||
if ((entityCustomization.product_id == product_id) && (entityCustomization.device_id == device_id)) {
|
||||
// already exists, clear the list and add the new values
|
||||
entityCustomization.entity_ids.clear();
|
||||
for (uint8_t i = 0; i < temp.size(); i++) {
|
||||
entityCustomization.entity_ids.push_back(temp[i]);
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
// create a new entry in the list
|
||||
EntityCustomization new_entry;
|
||||
new_entry.product_id = product_id;
|
||||
new_entry.device_id = device_id;
|
||||
for (uint8_t i = 0; i < temp.size(); i++) {
|
||||
new_entry.entity_ids.push_back(temp[i]);
|
||||
}
|
||||
settings.entityCustomizations.push_back(new_entry);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(200); // OK
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// load the settings when the service starts
|
||||
void WebCustomizationService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
106
src/web/WebCustomizationService.h
Normal file
106
src/web/WebCustomizationService.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebCustomizationService_h
|
||||
#define WebCustomizationService_h
|
||||
|
||||
#define EMSESP_CUSTOMIZATION_FILE "/config/emsespCustomization.json"
|
||||
|
||||
// GET
|
||||
#define DEVICES_SERVICE_PATH "/rest/devices"
|
||||
#define EMSESP_CUSTOMIZATION_SERVICE_PATH "/rest/customization"
|
||||
|
||||
// POST
|
||||
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
|
||||
#define EXCLUDE_ENTITIES_PATH "/rest/excludeEntities"
|
||||
#define RESET_CUSTOMIZATION_SERVICE_PATH "/rest/resetCustomizations"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// Customization for dallas sensor
|
||||
class SensorCustomization {
|
||||
public:
|
||||
std::string id_str;
|
||||
std::string name;
|
||||
uint16_t offset;
|
||||
};
|
||||
|
||||
class AnalogCustomization {
|
||||
public:
|
||||
uint8_t id;
|
||||
std::string name;
|
||||
uint16_t offset;
|
||||
float factor;
|
||||
uint8_t uom; // 0 is none
|
||||
int8_t type; // -1 is for deletion
|
||||
|
||||
// used for removing from a list
|
||||
bool operator==(const AnalogCustomization & a) const {
|
||||
return id == a.id;
|
||||
}
|
||||
bool operator!=(const AnalogCustomization & a) const {
|
||||
return !operator==(a);
|
||||
}
|
||||
};
|
||||
|
||||
// we use product_id and device_id to make the device unique
|
||||
class EntityCustomization {
|
||||
public:
|
||||
uint8_t product_id; // device's product id
|
||||
uint8_t device_id; // device's device id
|
||||
std::vector<uint8_t> entity_ids; // array of entity ids to exclude
|
||||
};
|
||||
|
||||
class WebCustomization {
|
||||
public:
|
||||
std::list<SensorCustomization> sensorCustomizations; // for sensor names and offsets
|
||||
std::list<AnalogCustomization> analogCustomizations; // for analog sensors
|
||||
std::list<EntityCustomization> entityCustomizations; // for a list of entities that should be excluded from the device list
|
||||
|
||||
static void read(WebCustomization & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebCustomization & settings);
|
||||
};
|
||||
|
||||
class WebCustomizationService : public StatefulService<WebCustomization> {
|
||||
public:
|
||||
WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
|
||||
// make all functions public so we can test in the debug and standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
private:
|
||||
#endif
|
||||
|
||||
HttpEndpoint<WebCustomization> _httpEndpoint;
|
||||
FSPersistence<WebCustomization> _fsPersistence;
|
||||
|
||||
// GET
|
||||
void devices(AsyncWebServerRequest * request);
|
||||
|
||||
// POST
|
||||
void exclude_entities(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void device_entities(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void reset_customization(AsyncWebServerRequest * request);
|
||||
|
||||
AsyncCallbackJsonWebHandler _exclude_entities_handler, _device_entities_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -23,69 +23,131 @@ namespace emsesp {
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _writesensor_dataHandler(WRITE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
server->on(EMSESP_DATA_SERVICE_PATH,
|
||||
: _device_data_handler(DEVICE_DATA_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _write_value_handler(WRITE_VALUE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_sensor_handler(WRITE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_analog_handler(WRITE_ANALOG_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_analog, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
server->on(CORE_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::core_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(SENSOR_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::sensor_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
_device_dataHandler.setMethod(HTTP_POST);
|
||||
_device_dataHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_dataHandler);
|
||||
_device_data_handler.setMethod(HTTP_POST);
|
||||
_device_data_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_data_handler);
|
||||
|
||||
_writevalue_dataHandler.setMethod(HTTP_POST);
|
||||
_writevalue_dataHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_writevalue_dataHandler);
|
||||
_write_value_handler.setMethod(HTTP_POST);
|
||||
_write_value_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_value_handler);
|
||||
|
||||
_writesensor_dataHandler.setMethod(HTTP_POST);
|
||||
_writesensor_dataHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_writesensor_dataHandler);
|
||||
_write_sensor_handler.setMethod(HTTP_POST);
|
||||
_write_sensor_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_sensor_handler);
|
||||
|
||||
_write_analog_handler.setMethod(HTTP_POST);
|
||||
_write_analog_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_analog_handler);
|
||||
}
|
||||
|
||||
// scan devices service
|
||||
void WebDataService::scan_devices(AsyncWebServerRequest * request) {
|
||||
EMSESP::logger().info(F("Scanning devices..."));
|
||||
EMSESP::scan_devices();
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void WebDataService::all_devices(AsyncWebServerRequest * request) {
|
||||
// this is used in the dashboard and contains all ems device information
|
||||
// /coreData endpoint
|
||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
// Ignore Contoller
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
for (auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice && emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
obj["i"] = emsdevice->unique_id(); // id
|
||||
obj["i"] = emsdevice->unique_id(); // a unique id
|
||||
obj["t"] = emsdevice->device_type_name(); // type
|
||||
obj["b"] = emsdevice->brand_to_string(); // brand
|
||||
obj["n"] = emsdevice->name(); // name
|
||||
obj["d"] = emsdevice->device_id(); // deviceid
|
||||
obj["p"] = emsdevice->product_id(); // productid
|
||||
obj["v"] = emsdevice->version(); // version
|
||||
obj["e"] = emsdevice->count_entities(); // number of entities (device values)
|
||||
}
|
||||
}
|
||||
|
||||
// sensors stuff
|
||||
root["active_sensors"] = EMSESP::dallassensor_.no_sensors() + (EMSESP::analogsensor_.analog_enabled() ? EMSESP::analogsensor_.no_sensors() : 0);
|
||||
root["analog_enabled"] = EMSESP::analogsensor_.analog_enabled();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// sensor data - sends back to web
|
||||
// /sensorData endpoint
|
||||
// the "sensors" and "analogs" are arrays and must exist
|
||||
void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// dallas sensors
|
||||
JsonArray sensors = root.createNestedArray("sensors");
|
||||
if (EMSESP::have_sensors()) {
|
||||
uint8_t i = 1;
|
||||
for (const auto & sensor : EMSESP::sensor_devices()) {
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::dallassensor_.sensors()) {
|
||||
JsonObject obj = sensors.createNestedObject();
|
||||
obj["n"] = i++; // no
|
||||
obj["i"] = sensor.to_string(true); // id
|
||||
obj["t"] = (float)(sensor.temperature_c) / 10; // temp
|
||||
obj["o"] = (float)(sensor.offset()) / 10; // offset
|
||||
obj["is"] = sensor.id_str(); // id
|
||||
obj["n"] = sensor.name(); // name
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
obj["t"] = (float)sensor.temperature_c * 0.18 + 32;
|
||||
}
|
||||
obj["u"] = DeviceValueUOM::FAHRENHEIT;
|
||||
obj["o"] = (float)sensor.offset() * 0.18;
|
||||
} else {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
obj["t"] = (float)sensor.temperature_c / 10;
|
||||
}
|
||||
obj["u"] = DeviceValueUOM::DEGREES;
|
||||
obj["o"] = (float)(sensor.offset()) / 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EMSESP::system_.analog_enabled()) {
|
||||
root["analog"] = EMSESP::system_.analog();
|
||||
// analog sensors
|
||||
// assume list is already sorted by id
|
||||
JsonArray analogs = root.createNestedArray("analogs");
|
||||
if (EMSESP::analog_enabled() && EMSESP::analogsensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
|
||||
// don't send if it's marked for removal
|
||||
if (sensor.type() != AnalogSensor::AnalogType::MARK_DELETED) {
|
||||
JsonObject obj = analogs.createNestedObject();
|
||||
obj["i"] = sensor.id();
|
||||
obj["n"] = sensor.name();
|
||||
obj["u"] = sensor.uom();
|
||||
obj["o"] = sensor.offset();
|
||||
obj["f"] = sensor.factor();
|
||||
obj["t"] = sensor.type();
|
||||
|
||||
if (sensor.type() != AnalogSensor::AnalogType::NOTUSED) {
|
||||
obj["v"] = Helpers::round2(sensor.value(), 1); // is optional and is a float
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
@@ -96,7 +158,7 @@ void WebDataService::all_devices(AsyncWebServerRequest * request) {
|
||||
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
||||
void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXXLARGE_DYN);
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
@@ -106,8 +168,8 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
}
|
||||
EMSESP::wait_validate(0); // reset in case of timeout
|
||||
#ifndef EMSESP_STANDALONE
|
||||
JsonObject root = response->getRoot();
|
||||
emsdevice->generate_values_json_web(root);
|
||||
JsonObject output = response->getRoot();
|
||||
emsdevice->generate_values_web(output);
|
||||
#endif
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
@@ -117,12 +179,13 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
}
|
||||
}
|
||||
|
||||
// invalid
|
||||
// invalid but send ok
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a command and its data value from a specific Device, from the Web
|
||||
|
||||
// takes a command and its data value from a specific EMS Device, from the Web
|
||||
// assumes the service has been checked for admin authentication
|
||||
void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
@@ -180,23 +243,42 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a sensorname and optional offset from the Web
|
||||
// takes a dallas sensor name and optional offset from the WebUI and update the customization settings
|
||||
// via the Dallas service
|
||||
void WebDataService::write_sensor(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
bool ok = false;
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject sensor = json["sensor"];
|
||||
JsonObject sensor = json;
|
||||
|
||||
// if valid add.
|
||||
uint8_t no = sensor["no"];
|
||||
if (no > 0 && no < 100) {
|
||||
char name[20];
|
||||
std::string id = sensor["id"];
|
||||
strlcpy(name, id.c_str(), sizeof(name));
|
||||
float offset = sensor["offset"]; // this will be a float value. We'll convert it to int and * 10 it
|
||||
int16_t offset10 = offset * 10;
|
||||
char idstr[3];
|
||||
ok = EMSESP::dallassensor_.update(Helpers::itoa(idstr, no, 10), name, offset10);
|
||||
std::string id_str = sensor["id_str"]; // this is the key
|
||||
std::string name = sensor["name"];
|
||||
|
||||
// calculate offset. We'll convert it to an int and * 10
|
||||
float offset = sensor["offset"];
|
||||
int16_t offset10 = offset * 10;
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
offset10 = offset / 0.18;
|
||||
}
|
||||
ok = EMSESP::dallassensor_.update(id_str, name, offset10);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// update the analog record, or create a new one
|
||||
void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
bool ok = false;
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject analog = json;
|
||||
|
||||
uint8_t id = analog["id"]; // this is the unique key
|
||||
std::string name = analog["name"];
|
||||
float factor = analog["factor"];
|
||||
int16_t offset = analog["offset"];
|
||||
uint8_t uom = analog["uom"];
|
||||
int8_t type = analog["type"];
|
||||
ok = EMSESP::analogsensor_.update(id, name, offset, factor, uom, type);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
#ifndef WebDataService_h
|
||||
#define WebDataService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define EMSESP_DATA_SERVICE_PATH "/rest/data"
|
||||
// GET
|
||||
#define CORE_DATA_SERVICE_PATH "/rest/coreData"
|
||||
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
||||
#define SENSOR_DATA_SERVICE_PATH "/rest/sensorData"
|
||||
|
||||
// POST
|
||||
#define WRITE_VALUE_SERVICE_PATH "/rest/writeValue"
|
||||
#define WRITE_SENSOR_SERVICE_PATH "/rest/writeSensor"
|
||||
#define WRITE_ANALOG_SERVICE_PATH "/rest/writeAnalog"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -42,15 +42,17 @@ class WebDataService {
|
||||
#endif
|
||||
|
||||
// GET
|
||||
void all_devices(AsyncWebServerRequest * request);
|
||||
void scan_devices(AsyncWebServerRequest * request);
|
||||
void core_data(AsyncWebServerRequest * request);
|
||||
void sensor_data(AsyncWebServerRequest * request);
|
||||
|
||||
// POST
|
||||
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_value(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_sensor(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_analog(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void scan_devices(AsyncWebServerRequest * request);
|
||||
|
||||
AsyncCallbackJsonWebHandler _device_dataHandler, _writevalue_dataHandler, _writesensor_dataHandler;
|
||||
AsyncCallbackJsonWebHandler _device_data_handler, _write_value_handler, _write_sensor_handler, _write_analog_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -27,6 +27,7 @@ WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * security
|
||||
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2), 256) { // for POSTS
|
||||
|
||||
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->addHandler(&events_);
|
||||
server->on(EVENT_SOURCE_LOG_PATH, HTTP_GET, std::bind(&WebLogService::forbidden, this, _1));
|
||||
|
||||
@@ -104,6 +105,14 @@ WebLogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_
|
||||
}
|
||||
|
||||
void WebLogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
||||
/*
|
||||
// special case for trace, show trace and notice messages only
|
||||
// added by mvdp
|
||||
if (log_level() == uuid::log::Level::TRACE && message->level != uuid::log::Level::TRACE && message->level != uuid::log::Level::NOTICE) {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (log_messages_.size() >= maximum_log_messages_) {
|
||||
log_messages_.pop_front();
|
||||
}
|
||||
@@ -184,7 +193,7 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
|
||||
// send the complete log buffer to the API, not filtering on log level
|
||||
void WebLogService::fetchLog(AsyncWebServerRequest * request) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); // 16kb buffer
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN + 192 * log_messages_.size());
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray log = root.createNestedArray("events");
|
||||
|
||||
|
||||
@@ -19,13 +19,6 @@
|
||||
#ifndef WebLogService_h
|
||||
#define WebLogService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
#define EVENT_SOURCE_LOG_PATH "/es/log"
|
||||
#define FETCH_LOG_PATH "/rest/fetchLog"
|
||||
#define LOG_SETTINGS_PATH "/rest/logSettings"
|
||||
|
||||
@@ -50,63 +50,49 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
root["shower_alert"] = settings.shower_alert;
|
||||
root["rx_gpio"] = settings.rx_gpio;
|
||||
root["tx_gpio"] = settings.tx_gpio;
|
||||
root["phy_type"] = settings.phy_type;
|
||||
root["dallas_gpio"] = settings.dallas_gpio;
|
||||
root["dallas_parasite"] = settings.dallas_parasite;
|
||||
root["led_gpio"] = settings.led_gpio;
|
||||
root["hide_led"] = settings.hide_led;
|
||||
root["low_clock"] = settings.low_clock;
|
||||
root["telnet_enabled"] = settings.telnet_enabled;
|
||||
root["notoken_api"] = settings.notoken_api;
|
||||
root["readonly_mode"] = settings.readonly_mode;
|
||||
root["analog_enabled"] = settings.analog_enabled;
|
||||
root["pbutton_gpio"] = settings.pbutton_gpio;
|
||||
root["solar_maxflow"] = settings.solar_maxflow;
|
||||
root["board_profile"] = settings.board_profile;
|
||||
root["dallas_format"] = settings.dallas_format;
|
||||
root["fahrenheit"] = settings.fahrenheit;
|
||||
root["bool_format"] = settings.bool_format;
|
||||
root["enum_format"] = settings.enum_format;
|
||||
root["weblog_level"] = settings.weblog_level;
|
||||
root["weblog_buffer"] = settings.weblog_buffer;
|
||||
root["weblog_compact"] = settings.weblog_compact;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "sensor_id%d", i);
|
||||
root[buf] = settings.sensor[i].id;
|
||||
snprintf(buf, sizeof(buf), "sensor_name%d", i);
|
||||
root[buf] = settings.sensor[i].name;
|
||||
snprintf(buf, sizeof(buf), "sensor_offset%d", i);
|
||||
root[buf] = settings.sensor[i].offset;
|
||||
}
|
||||
root["phy_type"] = settings.phy_type;
|
||||
root["eth_power"] = settings.eth_power;
|
||||
root["eth_phy_addr"] = settings.eth_phy_addr;
|
||||
root["eth_clock_mode"] = settings.eth_clock_mode;
|
||||
}
|
||||
|
||||
// call on initialization and also when settings are updated via web or console
|
||||
StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) {
|
||||
// load default GPIO configuration based on board profile
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button, phy_type
|
||||
std::vector<int8_t> data; // // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
|
||||
String old_board_profile = settings.board_profile;
|
||||
settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE;
|
||||
settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE;
|
||||
if (!System::load_board_profile(data, settings.board_profile.c_str())) {
|
||||
settings.board_profile = EMSESP_DEFAULT_BOARD_PROFILE; // invalid board configuration, override the default in case it has been misspelled
|
||||
}
|
||||
|
||||
uint8_t default_led_gpio = data[0];
|
||||
uint8_t default_dallas_gpio = data[1];
|
||||
uint8_t default_rx_gpio = data[2];
|
||||
uint8_t default_tx_gpio = data[3];
|
||||
uint8_t default_pbutton_gpio = data[4];
|
||||
uint8_t default_phy_type = data[5];
|
||||
|
||||
if (old_board_profile != settings.board_profile) {
|
||||
EMSESP::logger().info(F("EMS-ESP version %s"), EMSESP_APP_VERSION);
|
||||
|
||||
// check to see if we have a settings file, if not it's a fresh install
|
||||
if (!root.size()) {
|
||||
EMSESP::logger().info(F("Initializing configuration with board profile %s"), settings.board_profile.c_str());
|
||||
} else {
|
||||
EMSESP::logger().info(F("Using configuration from board profile %s"), settings.board_profile.c_str());
|
||||
}
|
||||
}
|
||||
uint8_t default_led_gpio = data[0];
|
||||
uint8_t default_dallas_gpio = data[1];
|
||||
uint8_t default_rx_gpio = data[2];
|
||||
uint8_t default_tx_gpio = data[3];
|
||||
uint8_t default_pbutton_gpio = data[4];
|
||||
uint8_t default_phy_type = data[5];
|
||||
uint8_t default_eth_power = data[6];
|
||||
uint8_t default_eth_phy_addr = data[7];
|
||||
uint8_t default_eth_clock_mode = data[8];
|
||||
|
||||
int prev;
|
||||
reset_flags();
|
||||
@@ -135,6 +121,10 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
|
||||
check_flag(prev, settings.syslog_mark_interval, ChangeFlags::SYSLOG);
|
||||
|
||||
prev = settings.syslog_port;
|
||||
settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT;
|
||||
check_flag(prev, settings.syslog_port, ChangeFlags::SYSLOG);
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
String old_syslog_host = settings.syslog_host;
|
||||
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
|
||||
@@ -143,15 +133,6 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
}
|
||||
#endif
|
||||
|
||||
prev = settings.syslog_port;
|
||||
settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT;
|
||||
check_flag(prev, settings.syslog_port, ChangeFlags::SYSLOG);
|
||||
|
||||
// adc
|
||||
prev = settings.analog_enabled;
|
||||
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
|
||||
check_flag(prev, settings.analog_enabled, ChangeFlags::ADC);
|
||||
|
||||
// button
|
||||
prev = settings.pbutton_gpio;
|
||||
settings.pbutton_gpio = root["pbutton_gpio"] | default_pbutton_gpio;
|
||||
@@ -181,15 +162,48 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.hide_led = root["hide_led"] | EMSESP_DEFAULT_HIDE_LED;
|
||||
check_flag(prev, settings.hide_led, ChangeFlags::LED);
|
||||
|
||||
//
|
||||
// next ones are settings that don't need any follow-up actions
|
||||
//
|
||||
// adc
|
||||
prev = settings.analog_enabled;
|
||||
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
|
||||
check_flag(prev, settings.analog_enabled, ChangeFlags::ADC);
|
||||
|
||||
// these need reboots to be applied
|
||||
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
//
|
||||
// these need reboots to be applied...
|
||||
//
|
||||
prev = settings.telnet_enabled;
|
||||
settings.telnet_enabled = root["telnet_enabled"] | EMSESP_DEFAULT_TELNET_ENABLED;
|
||||
check_flag(prev, settings.telnet_enabled, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.ems_bus_id;
|
||||
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
check_flag(prev, settings.ems_bus_id, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.low_clock;
|
||||
settings.low_clock = root["low_clock"] | false;
|
||||
check_flag(prev, settings.low_clock, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.master_thermostat;
|
||||
settings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT;
|
||||
settings.low_clock = root["low_clock"] | false;
|
||||
settings.phy_type = root["phy_type"] | default_phy_type; // use whatever came from the board profile
|
||||
check_flag(prev, settings.master_thermostat, ChangeFlags::RESTART);
|
||||
|
||||
// use whatever came from the board profile
|
||||
prev = settings.phy_type;
|
||||
settings.phy_type = root["phy_type"] | default_phy_type;
|
||||
check_flag(prev, settings.phy_type, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.eth_power;
|
||||
settings.eth_power = root["eth_power"] | default_eth_power;
|
||||
check_flag(prev, settings.eth_power, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.eth_phy_addr;
|
||||
settings.eth_phy_addr = root["eth_phy_addr"] | default_eth_phy_addr;
|
||||
check_flag(prev, settings.eth_phy_addr, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.eth_clock_mode;
|
||||
settings.eth_clock_mode = root["eth_clock_mode"] | default_eth_clock_mode;
|
||||
check_flag(prev, settings.eth_clock_mode, ChangeFlags::RESTART);
|
||||
|
||||
// without checks...
|
||||
|
||||
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
||||
EMSESP::trace_raw(settings.trace_raw);
|
||||
@@ -197,27 +211,25 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
|
||||
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
|
||||
|
||||
settings.dallas_format = root["dallas_format"] | EMSESP_DEFAULT_DALLAS_FORMAT;
|
||||
EMSESP::dallassensor_.dallas_format(settings.dallas_format);
|
||||
settings.fahrenheit = root["fahrenheit"] | false;
|
||||
EMSESP::system_.fahrenheit(settings.fahrenheit);
|
||||
|
||||
settings.readonly_mode = root["readonly_mode"] | false;
|
||||
EMSESP::system_.readonly_mode(settings.readonly_mode);
|
||||
|
||||
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
||||
EMSESP::bool_format(settings.bool_format);
|
||||
EMSESP::system_.bool_format(settings.bool_format);
|
||||
|
||||
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
|
||||
EMSESP::enum_format(settings.enum_format);
|
||||
EMSESP::system_.enum_format(settings.enum_format);
|
||||
|
||||
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
||||
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
||||
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "sensor_id%d", i);
|
||||
settings.sensor[i].id = root[buf] | EMSESP_DEFAULT_SENSOR_NAME;
|
||||
snprintf(buf, sizeof(buf), "sensor_name%d", i);
|
||||
settings.sensor[i].name = root[buf] | EMSESP_DEFAULT_SENSOR_NAME;
|
||||
snprintf(buf, sizeof(buf), "sensor_offset%d", i);
|
||||
settings.sensor[i].offset = root[buf] | 0;
|
||||
// save the settings
|
||||
if (flags_ == WebSettings::ChangeFlags::RESTART) {
|
||||
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
@@ -243,7 +255,7 @@ void WebSettingsService::onUpdate() {
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::ADC)) {
|
||||
EMSESP::system_.adc_init(true); // reload settings
|
||||
EMSESP::analogsensor_.start();
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::BUTTON)) {
|
||||
@@ -270,22 +282,20 @@ void WebSettingsService::board_profile(AsyncWebServerRequest * request, JsonVari
|
||||
if (json.is<JsonObject>()) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM);
|
||||
JsonObject root = response->getRoot();
|
||||
if (json.containsKey("code")) {
|
||||
String board_profile = json["code"];
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
// check for valid board
|
||||
if (System::load_board_profile(data, board_profile.c_str())) {
|
||||
root["led_gpio"] = data[0];
|
||||
root["dallas_gpio"] = data[1];
|
||||
root["rx_gpio"] = data[2];
|
||||
root["tx_gpio"] = data[3];
|
||||
root["pbutton_gpio"] = data[4];
|
||||
root["phy_type"] = data[5];
|
||||
} else {
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.containsKey("board_profile")) {
|
||||
String board_profile = json["board_profile"];
|
||||
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
(void)System::load_board_profile(data, board_profile.c_str());
|
||||
root["led_gpio"] = data[0];
|
||||
root["dallas_gpio"] = data[1];
|
||||
root["rx_gpio"] = data[2];
|
||||
root["tx_gpio"] = data[3];
|
||||
root["pbutton_gpio"] = data[4];
|
||||
root["phy_type"] = data[5];
|
||||
root["eth_power"] = data[6];
|
||||
root["eth_phy_addr"] = data[7];
|
||||
root["eth_clock_mode"] = data[8];
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
@@ -16,25 +16,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebSettingsConfig_h
|
||||
#define WebSettingsConfig_h
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#ifndef WebSettingsService_h
|
||||
#define WebSettingsService_h
|
||||
|
||||
#include "../default_settings.h"
|
||||
|
||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/settings"
|
||||
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
|
||||
|
||||
#define MAX_NUM_SENSOR_NAMES 20
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum { BOOL_FORMAT_ONOFF = 1, BOOL_FORMAT_ONOFF_CAP, BOOL_FORMAT_TRUEFALSE, BOOL_FORMAT_10 }; // matches Web UI settings
|
||||
enum { ENUM_FORMAT_TEXT = 1, ENUM_FORMAT_NUMBER }; // matches Web UI settings
|
||||
|
||||
class WebSettings {
|
||||
public:
|
||||
uint8_t tx_mode;
|
||||
@@ -55,38 +47,39 @@ class WebSettings {
|
||||
uint8_t led_gpio;
|
||||
bool hide_led;
|
||||
bool low_clock;
|
||||
bool telnet_enabled;
|
||||
bool notoken_api;
|
||||
bool readonly_mode;
|
||||
bool analog_enabled;
|
||||
uint8_t pbutton_gpio;
|
||||
uint8_t solar_maxflow;
|
||||
String board_profile;
|
||||
uint8_t phy_type;
|
||||
uint8_t dallas_format;
|
||||
uint8_t bool_format;
|
||||
uint8_t enum_format;
|
||||
int8_t weblog_level;
|
||||
uint8_t weblog_buffer;
|
||||
bool weblog_compact;
|
||||
bool fahrenheit;
|
||||
|
||||
struct {
|
||||
String id;
|
||||
String name;
|
||||
int16_t offset;
|
||||
} sensor[MAX_NUM_SENSOR_NAMES];
|
||||
uint8_t phy_type;
|
||||
int8_t eth_power; // -1 means disabled
|
||||
uint8_t eth_phy_addr;
|
||||
uint8_t eth_clock_mode;
|
||||
|
||||
static void read(WebSettings & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebSettings & settings);
|
||||
|
||||
enum ChangeFlags : uint8_t {
|
||||
|
||||
NONE = 0,
|
||||
UART = (1 << 0), // 1
|
||||
SYSLOG = (1 << 1), // 2
|
||||
ADC = (1 << 2), // 4
|
||||
DALLAS = (1 << 3), // 8
|
||||
SHOWER = (1 << 4), // 16
|
||||
LED = (1 << 5), // 32
|
||||
BUTTON = (1 << 6) // 64
|
||||
NONE = 0,
|
||||
UART = (1 << 0), // 1
|
||||
SYSLOG = (1 << 1), // 2
|
||||
ADC = (1 << 2), // 4 - analog
|
||||
DALLAS = (1 << 3), // 8
|
||||
SHOWER = (1 << 4), // 16
|
||||
LED = (1 << 5), // 32
|
||||
BUTTON = (1 << 6), // 64
|
||||
RESTART = 0xFF
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
||||
void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
EMSESP::logger().info(F("WiFi Disconnected. Reason code=%d"), info.disconnected.reason);
|
||||
EMSESP::logger().info(F("WiFi disconnected. Reason code=%d"), info.disconnected.reason);
|
||||
WiFi.disconnect(true);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
EMSESP::logger().info(F("WiFi connected with IP=%s, hostname=%s"), WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
#endif
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (!networkSettings.enableIPv6) {
|
||||
@@ -52,7 +52,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
EMSESP::logger().info(F("Ethernet initialized"));
|
||||
// EMSESP::logger().info(F("Ethernet initialized"));
|
||||
ETH.setHostname(EMSESP::system_.hostname().c_str());
|
||||
|
||||
// configure for static IP
|
||||
@@ -68,7 +68,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
// prevent double calls
|
||||
if (!EMSESP::system_.ethernet_connected()) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info(F("Ethernet Connected with IP=%s, speed %d Mbps"), ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
EMSESP::logger().info(F("Ethernet connected with IP=%s, speed %d Mbps"), ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
#endif
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (!networkSettings.enableIPv6) {
|
||||
@@ -82,12 +82,12 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
EMSESP::logger().info(F("Ethernet Disconnected"));
|
||||
EMSESP::logger().info(F("Ethernet disconnected"));
|
||||
EMSESP::system_.ethernet_connected(false);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_STOP:
|
||||
EMSESP::logger().info(F("Ethernet Stopped"));
|
||||
EMSESP::logger().info(F("Ethernet stopped"));
|
||||
EMSESP::system_.ethernet_connected(false);
|
||||
break;
|
||||
|
||||
@@ -110,9 +110,9 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
|
||||
case SYSTEM_EVENT_GOT_IP6:
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
EMSESP::logger().info(F("Ethernet Connected with IP=%s, speed %d Mbps"), ETH.localIPv6().toString().c_str(), ETH.linkSpeed());
|
||||
EMSESP::logger().info(F("Ethernet connected with IP=%s, speed %d Mbps"), ETH.localIPv6().toString().c_str(), ETH.linkSpeed());
|
||||
} else {
|
||||
EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
|
||||
EMSESP::logger().info(F("WiFi connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
|
||||
}
|
||||
EMSESP::system_.send_heartbeat();
|
||||
EMSESP::system_.syslog_start();
|
||||
@@ -129,11 +129,34 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
root["rx_received"] = EMSESP::rxservice_.telegram_count();
|
||||
root["tx_sent"] = EMSESP::txservice_.telegram_read_count() + EMSESP::txservice_.telegram_write_count();
|
||||
root["rx_quality"] = EMSESP::rxservice_.quality();
|
||||
root["tx_quality"] = EMSESP::txservice_.quality();
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
root["num_devices"] = EMSESP::count_devices(); // excluding Controller
|
||||
root["num_sensors"] = EMSESP::dallassensor_.no_sensors();
|
||||
root["num_analogs"] = EMSESP::analogsensor_.no_sensors();
|
||||
root["tx_mode"] = EMSESP::txservice_.tx_mode();
|
||||
root["rx_received"] = EMSESP::rxservice_.telegram_count();
|
||||
root["tx_reads"] = EMSESP::txservice_.telegram_read_count();
|
||||
root["tx_writes"] = EMSESP::txservice_.telegram_write_count();
|
||||
root["rx_quality"] = EMSESP::rxservice_.quality();
|
||||
root["tx_read_quality"] = EMSESP::txservice_.read_quality();
|
||||
root["tx_write_quality"] = EMSESP::txservice_.write_quality();
|
||||
root["rx_fails"] = EMSESP::rxservice_.telegram_error_count();
|
||||
root["tx_read_fails"] = EMSESP::txservice_.telegram_read_fail_count();
|
||||
root["tx_write_fails"] = EMSESP::txservice_.telegram_write_fail_count();
|
||||
root["sensor_fails"] = EMSESP::dallassensor_.fails();
|
||||
root["sensor_reads"] = EMSESP::dallassensor_.reads();
|
||||
root["sensor_quality"] = EMSESP::dallassensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::dallassensor_.fails()) / EMSESP::dallassensor_.reads());
|
||||
root["analog_fails"] = EMSESP::analogsensor_.fails();
|
||||
root["analog_reads"] = EMSESP::analogsensor_.reads();
|
||||
root["analog_quality"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads());
|
||||
root["mqtt_fails"] = Mqtt::publish_fails();
|
||||
root["mqtt_count"] = Mqtt::publish_count();
|
||||
root["mqtt_quality"] = Mqtt::publish_count() == 0 ? 100 : 100 - (Mqtt::publish_fails() * 100) / (Mqtt::publish_count() + Mqtt::publish_fails());
|
||||
root["api_calls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
|
||||
root["api_fails"] = WebAPIService::api_fails();
|
||||
root["api_quality"] =
|
||||
WebAPIService::api_count() == 0 ? 100 : 100 - (WebAPIService::api_fails() * 100) / (WebAPIService::api_count() + WebAPIService::api_fails());
|
||||
root["uptime"] = EMSbus::bus_uptime();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
@@ -19,15 +19,9 @@
|
||||
#ifndef WebStatusService_h
|
||||
#define WebStatusService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
|
||||
#include <ESPmDNS.h>
|
||||
|
||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/emsespStatus"
|
||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/status"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user