Merge branch 'enhance/AutoConnectAux' of https://github.com/Hieromon/AutoConnect into enhance/AutoConnectAux

pull/41/head
Hieromon Ikasamo 6 years ago
commit 6ee87dacd7
  1. 2
      .travis.yml
  2. 2
      keywords.txt
  3. 2
      library.json
  4. 4
      src/AutoConnect.cpp
  5. 119
      src/AutoConnectAux.cpp
  6. 8
      src/AutoConnectAux.h
  7. 12
      src/AutoConnectElementBasis.h
  8. 20
      src/AutoConnectElementBasisImpl.h
  9. 3
      src/AutoConnectElementJson.h
  10. 4
      src/AutoConnectElementJsonImpl.h

@ -20,7 +20,7 @@ before_install:
- if [[ "$BOARD" =~ "esp32:esp32:" ]]; then - if [[ "$BOARD" =~ "esp32:esp32:" ]]; then
arduino --install-boards esp32:esp32; arduino --install-boards esp32:esp32;
fi fi
- arduino --install-library PubSubClient,PageBuilder:1.1.1 - arduino --install-library PubSubClient,PageBuilder:1.2.0
- buildExampleSketch() { arduino --verbose-build --verify --board $BOARD $PWD/examples/$1/$1.ino; } - buildExampleSketch() { arduino --verbose-build --verify --board $BOARD $PWD/examples/$1/$1.ino; }
install: install:
- mkdir -p ~/Arduino/libraries - mkdir -p ~/Arduino/libraries

@ -9,6 +9,7 @@ AutoConnectButton KEYWORD1
AutoConnectCheckbox KEYWORD1 AutoConnectCheckbox KEYWORD1
AutoConnectElement KEYWORD1 AutoConnectElement KEYWORD1
AutoConnectInput KEYWORD1 AutoConnectInput KEYWORD1
AutoConnectRadio KEYWORD1
AutoConnectSelect KEYWORD1 AutoConnectSelect KEYWORD1
AutoConnectSubmit KEYWORD1 AutoConnectSubmit KEYWORD1
AutoConnectText KEYWORD1 AutoConnectText KEYWORD1
@ -54,6 +55,7 @@ ACButton PREPROCESSOR
ACCheckbox PREPROCESSOR ACCheckbox PREPROCESSOR
ACElement PREPROCESSOR ACElement PREPROCESSOR
ACInput PREPROCESSOR ACInput PREPROCESSOR
ACRadio PREPROCESSOR
ACSelect PREPROCESSOR ACSelect PREPROCESSOR
ACSubmit PREPROCESSOR ACSubmit PREPROCESSOR
ACText PREPROCESSOR ACText PREPROCESSOR

@ -12,7 +12,7 @@
[ [
{ {
"name": "PageBuilder", "name": "PageBuilder",
"version": ">=1.1.0" "version": ">=1.2.0"
} }
], ],
"frameworks": "arduino", "frameworks": "arduino",

@ -310,6 +310,7 @@ void AutoConnect::_startWebServer() {
_webServer->onNotFound(std::bind(&AutoConnect::_handleNotFound, this)); _webServer->onNotFound(std::bind(&AutoConnect::_handleNotFound, this));
// here, Prepare PageBuilders for captive portal // here, Prepare PageBuilders for captive portal
_responsePage = new PageBuilder(); _responsePage = new PageBuilder();
_responsePage->chunked(PB_ByteStream);
_responsePage->exitCanHandle(std::bind(&AutoConnect::_classifyHandle, this, std::placeholders::_1, std::placeholders::_2)); _responsePage->exitCanHandle(std::bind(&AutoConnect::_classifyHandle, this, std::placeholders::_1, std::placeholders::_2));
_responsePage->insert(*_webServer); _responsePage->insert(*_webServer);
@ -538,10 +539,11 @@ String AutoConnect::_induceConnect(PageArgument& args) {
// Read from EEPROM // Read from EEPROM
AutoConnectCredential credential(_apConfig.boundaryOffset); AutoConnectCredential credential(_apConfig.boundaryOffset);
struct station_config entry; struct station_config entry;
AC_DBG("Load credential:%s\n", args.arg(AUTOCONNECT_PARAMID_CRED).c_str()); // AC_DBG("Load credential:%s\n", args.arg(AUTOCONNECT_PARAMID_CRED).c_str());
credential.load(args.arg(AUTOCONNECT_PARAMID_CRED).c_str(), &entry); credential.load(args.arg(AUTOCONNECT_PARAMID_CRED).c_str(), &entry);
strncpy(reinterpret_cast<char*>(_credential.ssid), reinterpret_cast<const char*>(entry.ssid), sizeof(_credential.ssid)); strncpy(reinterpret_cast<char*>(_credential.ssid), reinterpret_cast<const char*>(entry.ssid), sizeof(_credential.ssid));
strncpy(reinterpret_cast<char*>(_credential.password), reinterpret_cast<const char*>(entry.password), sizeof(_credential.password)); strncpy(reinterpret_cast<char*>(_credential.password), reinterpret_cast<const char*>(entry.password), sizeof(_credential.password));
AC_DBG("Credential loaded:%s %s\n", _credential.ssid, _credential.password);
} }
else { else {
// Credential had by the post parameter. // Credential had by the post parameter.

@ -282,50 +282,43 @@ AutoConnectElement* AutoConnectAux::_createElement(const JsonObject& json) {
* Constructs an AutoConnectAux instance by reading all the * Constructs an AutoConnectAux instance by reading all the
* AutoConnectElements of the specified URI from the elements defined JSON. * AutoConnectElements of the specified URI from the elements defined JSON.
* @param in AutoConnectAux element data which is described by JSON. * @param in AutoConnectAux element data which is described by JSON.
* @param uri AutoConnectAux uri to be loaded.
* @return true The element collection successfully loaded. * @return true The element collection successfully loaded.
* @return false Invalid JSON data occurred. * @return false Invalid JSON data occurred.
*/ */
bool AutoConnectAux::load(const char* in, const String uri) { bool AutoConnectAux::load(const char* in) {
DynamicJsonBuffer jsonBuffer; // DynamicJsonBuffer jsonBuffer();
const size_t bufferSize = 2*JSON_ARRAY_SIZE(2) + 3*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + 9*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 1080;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& jb = jsonBuffer.parseObject(in); JsonObject& jb = jsonBuffer.parseObject(in);
return _load(jb, uri); return _load(jb);
} }
bool AutoConnectAux::load(const __FlashStringHelper* in, const String uri) { bool AutoConnectAux::load(const __FlashStringHelper* in) {
DynamicJsonBuffer jsonBuffer; // DynamicJsonBuffer jsonBuffer();
const size_t bufferSize = 2*JSON_ARRAY_SIZE(2) + 3*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + 9*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 1080;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& jb = jsonBuffer.parseObject(in); JsonObject& jb = jsonBuffer.parseObject(in);
return _load(jb, uri); return _load(jb);
} }
bool AutoConnectAux::load(Stream& in, const String uri) { bool AutoConnectAux::load(Stream& in) {
DynamicJsonBuffer jsonBuffer; // DynamicJsonBuffer jsonBuffer();
const size_t bufferSize = 2*JSON_ARRAY_SIZE(2) + 3*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + 9*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 1080;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& jb = jsonBuffer.parseObject(in); JsonObject& jb = jsonBuffer.parseObject(in);
return _load(jb, uri); return _load(jb);
} }
bool AutoConnectAux::_load(JsonObject& jb, const String uri) { bool AutoConnectAux::_load(JsonObject& jb) {
bool rc = jb.success(); if (!jb.success())
if (rc) { return false;
JsonArray& aux = jb[AUTOCONNECT_JSON_KEY_AUX];
if ((rc = aux.success())) { _title = jb.get<String>(F(AUTOCONNECT_JSON_KEY_TITLE));
rc = false; _uriStr = jb.get<String>(F(AUTOCONNECT_JSON_KEY_URI));
for (JsonObject& page : aux) { _uri = _uriStr.c_str();
const String j_uri = page.get<String>(F(AUTOCONNECT_JSON_KEY_URI)); _menu = jb.get<bool>(F(AUTOCONNECT_JSON_KEY_MENU));
if (uri == j_uri) { (void)_loadElement(jb, "*");
AC_DBG("Loading %s\n", j_uri.c_str()); return true;
_title = page.get<String>(F(AUTOCONNECT_JSON_KEY_TITLE));
_menu = page.get<bool>(F(AUTOCONNECT_JSON_KEY_MENU));
_uriStr = j_uri;
setUri(_uriStr.c_str());
(void)_loadElement(jb, "*");
rc = true;
break;
}
}
}
}
return rc;
} }
/** /**
@ -333,7 +326,7 @@ bool AutoConnectAux::_load(JsonObject& jb, const String uri) {
* described by JSON. Usually, the Stream is specified a storm file of * described by JSON. Usually, the Stream is specified a storm file of
* SD or SPIFFS. The Stream must be opened before invoking the function. * SD or SPIFFS. The Stream must be opened before invoking the function.
* @param in Reference of the Stream which contains the parameter * @param in Reference of the Stream which contains the parameter
* file described by JSON. * data described by JSON.
* @param name The element name to be loaded. '*'specifies that all * @param name The element name to be loaded. '*'specifies that all
* elements are to be loaded. * elements are to be loaded.
* @return A reference of loaded AutoConnectElement instance. * @return A reference of loaded AutoConnectElement instance.
@ -363,41 +356,31 @@ AutoConnectElement& AutoConnectAux::_loadElement(JsonObject& jb, const String na
if (!jb.success()) if (!jb.success())
return _nullElement(); return _nullElement();
JsonArray& aux = jb[AUTOCONNECT_JSON_KEY_AUX]; JsonArray& elements = jb[AUTOCONNECT_JSON_KEY_ELEMENT];
if (!aux.success()) for (JsonObject& element : elements) {
return _nullElement(); String elmName = element.get<String>(F(AUTOCONNECT_JSON_KEY_NAME));
if (wc || name.equalsIgnoreCase(elmName)) {
for (JsonObject& page : aux) { // The specified element is defined in the JSON stream.
if (page[AUTOCONNECT_JSON_KEY_URI].as<String>() == String(uri())) { // Loads from JSON object.
JsonArray& element = page[AUTOCONNECT_JSON_KEY_ELEMENT]; auxElm = _getElement(elmName);
for (JsonObject& elm : element) { // The element is not created yet, create new one.
String elmName = elm.get<String>(F(AUTOCONNECT_JSON_KEY_NAME)); if (!auxElm) {
if (wc || name.equalsIgnoreCase(elmName)) { if ((auxElm = _createElement(element))) {
// The specified element is defined in the JSON stream. AC_DBG("%s<%d> of %s created\n", elmName.c_str(), (int)(auxElm->typeOf()), uri());
// Loads from JSON object. add(*auxElm); // Insert to AutoConnect
const String inType = elm[AUTOCONNECT_JSON_KEY_TYPE].as<String>(); }
auxElm = _getElement(elmName); else {
// The element is not created yet, create new one. AC_DBG("%s unknown element type\n", elmName.c_str());
if (!auxElm) { continue;
if ((auxElm = _createElement(elm))) {
AC_DBG("%s<%d> of %s created\n", elmName.c_str(), (int)(auxElm->typeOf()), uri());
add(*auxElm); // Insert to AutoConnect
}
else {
AC_DBG("%s unknown element type\n", elmName.c_str());
continue;
}
}
if (auxElm->loadElement(elm)) {
AC_DBG("%s<%d> of %s loaded\n", auxElm->name.c_str(), (int)auxElm->typeOf(), uri());
}
else {
// Element type mismatch
AC_DBG("Type of %s element mismatched\n", elmName.c_str());
continue;
}
} }
} }
if (auxElm->loadElement(element))
AC_DBG("%s<%d> of %s loaded\n", auxElm->name.c_str(), (int)auxElm->typeOf(), uri());
else {
// Element type mismatch
AC_DBG("Type of %s element mismatched\n", elmName.c_str());
continue;
}
} }
} }
return auxElm ? *auxElm : _nullElement(); return auxElm ? *auxElm : _nullElement();
@ -416,12 +399,12 @@ size_t AutoConnectAux::saveElement(Stream& out, const AutoConnectElement& elemen
if (!jb.success()) if (!jb.success())
return 0; return 0;
JsonArray& aux = jb[AUTOCONNECT_JSON_KEY_AUX]; JsonArray& aux = jb["aux"];
if (!aux.success()) if (!aux.success())
return 0; return 0;
for (JsonObject& page : aux) { for (JsonObject& page : aux) {
if (page["uri"].as<String>() == String(uri())) { if (page["aux"].as<String>() == String(uri())) {
JsonArray& element_j = page[AUTOCONNECT_JSON_KEY_ELEMENT]; JsonArray& element_j = page[AUTOCONNECT_JSON_KEY_ELEMENT];
for (JsonObject& elm : element_j) { for (JsonObject& elm : element_j) {
if (elm[AUTOCONNECT_JSON_KEY_NAME].as<String>() == element.name) { if (elm[AUTOCONNECT_JSON_KEY_NAME].as<String>() == element.name) {

@ -60,9 +60,9 @@ class AutoConnectAux : public PageBuilder {
void on(const AuxHandlerFunctionT handler, const AutoConnectExitOrder_t order = AC_EXIT_AHEAD) { _handler = handler; _order = order; } /**< Set user handler */ void on(const AuxHandlerFunctionT handler, const AutoConnectExitOrder_t order = AC_EXIT_AHEAD) { _handler = handler; _order = order; } /**< Set user handler */
#ifdef AUTOCONNECT_USE_JSON #ifdef AUTOCONNECT_USE_JSON
bool load(const char* in, const String uri); bool load(const char* in);
bool load(const __FlashStringHelper* in, const String uri); bool load(const __FlashStringHelper* in);
bool load(Stream& in, const String uri); bool load(Stream& in);
AutoConnectElement& loadElement(const char* in, const String name = "*"); AutoConnectElement& loadElement(const char* in, const String name = "*");
AutoConnectElement& loadElement(const __FlashStringHelper* in, const String name = "*"); AutoConnectElement& loadElement(const __FlashStringHelper* in, const String name = "*");
AutoConnectElement& loadElement(Stream& in, const String name = "*"); AutoConnectElement& loadElement(Stream& in, const String name = "*");
@ -78,7 +78,7 @@ class AutoConnectAux : public PageBuilder {
const String _injectMenu(PageArgument& args); const String _injectMenu(PageArgument& args);
#ifdef AUTOCONNECT_USE_JSON #ifdef AUTOCONNECT_USE_JSON
bool _load(JsonObject& in, const String uri); bool _load(JsonObject& in);
AutoConnectElement& _loadElement(JsonObject& in, const String name); AutoConnectElement& _loadElement(JsonObject& in, const String name);
AutoConnectElement* _createElement(const JsonObject& json); AutoConnectElement* _createElement(const JsonObject& json);
AutoConnectElement* _getElement(const String name); AutoConnectElement* _getElement(const String name);

@ -48,7 +48,7 @@ class AutoConnectElementBasis {
String name; /**< Element name */ String name; /**< Element name */
String value; /**< Element value */ String value; /**< Element value */
protected: protected:
ACElement_t _type; /**< Element type identifier */ ACElement_t _type; /**< Element type identifier */
}; };
@ -119,17 +119,17 @@ class AutoConnectInputBasis : virtual public AutoConnectElementBasis {
*/ */
class AutoConnectRadioBasis : virtual public AutoConnectElementBasis { class AutoConnectRadioBasis : virtual public AutoConnectElementBasis {
public: public:
explicit AutoConnectRadioBasis(const char* name = "", std::vector<String> values = {}, const char* label = "", const ACArrange_t order = AC_Vertical, const uint8_t checked = 0) : AutoConnectElementBasis(name, ""), _values(values), label(label), order(order), checked(checked) { explicit AutoConnectRadioBasis(const char* name = "", std::vector<String> values = {}, const char* label = "", const ACArrange_t order = AC_Vertical, const uint8_t checked = 0) : AutoConnectElementBasis(name, ""), label(label), order(order), checked(checked), _values(values) {
_type = AC_Radio; _type = AC_Radio;
} }
virtual ~AutoConnectRadioBasis() {} virtual ~AutoConnectRadioBasis() {}
const String toHTML(void) const; const String toHTML(void) const;
void option(const String value) { _values.push_back(value); } void add(const String value) { _values.push_back(value); }
void empty(void) { _values.clear(); } void empty(void) { _values.clear(); }
String label; /**< A label for a subsequent radio buttons */ String label; /**< A label for a subsequent radio buttons */
ACArrange_t order; /**< layout order */ ACArrange_t order; /**< layout order */
uint8_t checked; /**< Index of check marked item */ uint8_t checked; /**< Index of check marked item */
protected: protected:
std::vector<String> _values; /**< Items in a group */ std::vector<String> _values; /**< Items in a group */
@ -145,12 +145,12 @@ class AutoConnectRadioBasis : virtual public AutoConnectElementBasis {
*/ */
class AutoConnectSelectBasis : virtual public AutoConnectElementBasis { class AutoConnectSelectBasis : virtual public AutoConnectElementBasis {
public: public:
explicit AutoConnectSelectBasis(const char* name = "", std::vector<String> options = {}, const char* label = "") : AutoConnectElementBasis(name, ""), label(String(label)), _options(options) { explicit AutoConnectSelectBasis(const char* name = "", std::vector<String> options = {}, const char* label = "") : AutoConnectElementBasis(name, ""), label(String(label)), _options(options) {
_type = AC_Select; _type = AC_Select;
} }
virtual ~AutoConnectSelectBasis() {} virtual ~AutoConnectSelectBasis() {}
const String toHTML(void) const; const String toHTML(void) const;
void option(const String value) { _options.push_back(value); } void add(const String option) { _options.push_back(option); }
void empty(void) { _options.clear(); } void empty(void) { _options.clear(); }
String label; /**< A label for a subsequent input box */ String label; /**< A label for a subsequent input box */

@ -69,15 +69,17 @@ const String AutoConnectInputBasis::toHTML(void) const {
const String AutoConnectRadioBasis::toHTML(void) const { const String AutoConnectRadioBasis::toHTML(void) const {
String html = String(); String html = String();
if (label.length()) if (label.length()) {
html = String(FPSTR("<label for=\"")) + name + String("\">") + label + String(FPSTR("</label>")); html = label;
if (order == AC_Vertical) if (order == AC_Vertical)
html += String("<br>"); html += String("<br>");
}
for (std::size_t n = 0; n < _values.size(); n++) { for (std::size_t n = 0; n < _values.size(); n++) {
html += String(FPSTR("<input type=\"radio\" id=\"")) + name + String(FPSTR("\" name=\"")) + name + String(FPSTR("\" value=\"")) + _values[n] + String("\""); String value = _values[n];
html += String(FPSTR("<input type=\"radio\" name=\"")) + name + String(FPSTR("\" id=\"")) + value + String(FPSTR("\" value=\"")) + value + String("\"");
if (n == checked - 1) if (n == checked - 1)
html += String(FPSTR(" checked")); html += String(FPSTR(" checked"));
html += String(">") + _values[n]; html += String(FPSTR("><label for=\"")) + value + String("\">") + value + String(FPSTR("</label>"));
if (order == AC_Vertical) if (order == AC_Vertical)
html += String("<br>"); html += String("<br>");
} }
@ -96,14 +98,14 @@ const String AutoConnectSelectBasis::toHTML(void) const {
String html = String(); String html = String();
if (label.length()) if (label.length())
html = String(FPSTR("<label>")) + label + String(FPSTR("</label>")); html = String(FPSTR("<label for=\"")) + name + String("\">") + label + String(FPSTR("</label>"));
html += String(FPSTR("<select name=\"")) + name + String("\">"); html += String(FPSTR("<select name=\"")) + name + String("\" id=\"") + name + String("\">");
std::size_t n = _options.size(); std::size_t n = _options.size();
if (n) { if (n) {
for (std::size_t n = 0; n < _options.size(); n++) for (std::size_t n = 0; n < _options.size(); n++)
html += String(FPSTR("<option value=\"")) + _options[n] + "\">" + _options[n] + String(FPSTR("</option>")); html += String(FPSTR("<option value=\"")) + _options[n] + "\">" + _options[n] + String(FPSTR("</option>"));
} }
html += String(FPSTR("</select><br>")); html += String(FPSTR("</select>"));
return html; return html;
} }

@ -15,7 +15,6 @@
#define AUTOCONNECT_JSON_KEY_ACTION "action" #define AUTOCONNECT_JSON_KEY_ACTION "action"
#define AUTOCONNECT_JSON_KEY_ARRANGE "arrange" #define AUTOCONNECT_JSON_KEY_ARRANGE "arrange"
#define AUTOCONNECT_JSON_KEY_AUX "aux"
#define AUTOCONNECT_JSON_KEY_CHECKED "checked" #define AUTOCONNECT_JSON_KEY_CHECKED "checked"
#define AUTOCONNECT_JSON_KEY_ELEMENT "element" #define AUTOCONNECT_JSON_KEY_ELEMENT "element"
#define AUTOCONNECT_JSON_KEY_HORIZONTAL "horizontal" #define AUTOCONNECT_JSON_KEY_HORIZONTAL "horizontal"
@ -28,7 +27,7 @@
#define AUTOCONNECT_JSON_KEY_TYPE "type" #define AUTOCONNECT_JSON_KEY_TYPE "type"
#define AUTOCONNECT_JSON_KEY_URI "uri" #define AUTOCONNECT_JSON_KEY_URI "uri"
#define AUTOCONNECT_JSON_KEY_VALUE "value" #define AUTOCONNECT_JSON_KEY_VALUE "value"
#define AUTOCONNECT_JSON_KEY_VERTICAL "vertival" #define AUTOCONNECT_JSON_KEY_VERTICAL "vertical"
#define AUTOCONNECT_JSON_TYPE_ACBUTTON "ACButton" #define AUTOCONNECT_JSON_TYPE_ACBUTTON "ACButton"
#define AUTOCONNECT_JSON_TYPE_ACCHECKBOX "ACCheckBox" #define AUTOCONNECT_JSON_TYPE_ACCHECKBOX "ACCheckBox"
#define AUTOCONNECT_JSON_TYPE_ACELEMENT "ACElement" #define AUTOCONNECT_JSON_TYPE_ACELEMENT "ACElement"

@ -105,7 +105,7 @@ bool AutoConnectRadioJson::loadElement(const JsonObject& json) {
empty(); empty();
JsonArray& optionArray = json[AUTOCONNECT_JSON_KEY_VALUE]; JsonArray& optionArray = json[AUTOCONNECT_JSON_KEY_VALUE];
for (auto value : optionArray) for (auto value : optionArray)
option(value.as<String>()); add(value.as<String>());
return true; return true;
} }
return false; return false;
@ -125,7 +125,7 @@ bool AutoConnectSelectJson::loadElement(const JsonObject& json) {
empty(); empty();
JsonArray& optionArray = json[AUTOCONNECT_JSON_KEY_OPTION]; JsonArray& optionArray = json[AUTOCONNECT_JSON_KEY_OPTION];
for (auto value : optionArray) for (auto value : optionArray)
option(value.as<String>()); add(value.as<String>());
return true; return true;
} }
return false; return false;

Loading…
Cancel
Save