Commit acc05041 authored by Eric Duminil's avatar Eric Duminil
Browse files

Remove stuff

parent 90f80906
/**
* IotWebConfMultipleWifi.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2021 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfMultipleWifi_h
#define IotWebConfMultipleWifi_h
#include "IotWebConfOptionalGroup.h"
#include "IotWebConf.h" // for WebRequestWrapper
namespace iotwebconf
{
class ChainedWifiParameterGroup : public ChainedParameterGroup
{
public:
ChainedWifiParameterGroup(const char* id) : ChainedParameterGroup(id, "WiFi connection")
{
// -- Update parameter Ids to have unique ID for all parameters within the application.
snprintf(this->_wifiSsidParameterId, IOTWEBCONF_WORD_LEN, "%s-ssid", this->getId());
snprintf(this->_wifiPasswordParameterId, IOTWEBCONF_WORD_LEN, "%s-pwd", this->getId());
this->addItem(&this->wifiSsidParameter);
this->addItem(&this->wifiPasswordParameter);
}
TextParameter wifiSsidParameter =
TextParameter("WiFi SSID", this->_wifiSsidParameterId, this->wifiSsid, IOTWEBCONF_WORD_LEN);
PasswordParameter wifiPasswordParameter =
PasswordParameter("WiFi password", this->_wifiPasswordParameterId, this->wifiPassword, IOTWEBCONF_PASSWORD_LEN);
char wifiSsid[IOTWEBCONF_WORD_LEN];
char wifiPassword[IOTWEBCONF_PASSWORD_LEN];
WifiAuthInfo wifiAuthInfo = { wifiSsid, wifiPassword};
protected:
private:
char _wifiSsidParameterId[IOTWEBCONF_WORD_LEN];
char _wifiPasswordParameterId[IOTWEBCONF_WORD_LEN];
};
class MultipleWifiAddition
{
public:
MultipleWifiAddition(
IotWebConf* iotWebConf,
ChainedWifiParameterGroup sets[],
size_t setsSize);
/**
* Note, that init() calls setFormValidator, that overwrites existing
* formValidator setup. Thus your setFormValidator should be called
* _after_ multipleWifiAddition.init() .
*/
virtual void init();
virtual bool formValidator(
WebRequestWrapper* webRequestWrapper);
protected:
IotWebConf* _iotWebConf;
ChainedWifiParameterGroup* _firstSet;
ChainedWifiParameterGroup* _currentSet;
iotwebconf::OptionalGroupHtmlFormatProvider _optionalGroupHtmlFormatProvider;
};
}
#endif
\ No newline at end of file
/**
* IotWebConfOptionalGroup.cpp -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*s
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#include "IotWebConfOptionalGroup.h"
namespace iotwebconf
{
OptionalParameterGroup::OptionalParameterGroup(const char* id, const char* label, bool defaultVisible)
: ParameterGroup(id, label)
{
this->_defaultActive = defaultVisible;
}
int OptionalParameterGroup::getStorageSize()
{
return ParameterGroup::getStorageSize() + 1;
}
void OptionalParameterGroup::applyDefaultValue()
{
this->_active = this->_defaultActive;
ParameterGroup::applyDefaultValue();
}
void OptionalParameterGroup::storeValue(
std::function<void(SerializationData* serializationData)> doStore)
{
// -- Store active flag.
byte data[1];
data[0] = (byte)this->_active;
SerializationData serializationData;
serializationData.length = 1;
serializationData.data = data;
doStore(&serializationData);
// -- Store other items.
ParameterGroup::storeValue(doStore);
}
void OptionalParameterGroup::loadValue(
std::function<void(SerializationData* serializationData)> doLoad)
{
// -- Load activity.
byte data[1];
SerializationData serializationData;
serializationData.length = 1;
serializationData.data = data;
doLoad(&serializationData);
this->_active = (bool)data[0];
// -- Load other items.
ParameterGroup::loadValue(doLoad);
}
void OptionalParameterGroup::renderHtml(
bool dataArrived, WebRequestWrapper* webRequestWrapper)
{
if (this->label != nullptr)
{
String content = getStartTemplate();
content.replace("{b}", this->label);
content.replace("{i}", this->getId());
content.replace("{v}", this->_active ? "active" : "inactive");
if (this->_active)
{
content.replace("{cb}", "hide");
content.replace("{cf}", "");
}
else
{
content.replace("{cb}", "");
content.replace("{cf}", "hide");
}
webRequestWrapper->sendContent(content);
}
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
if (current->visible)
{
current->renderHtml(dataArrived, webRequestWrapper);
}
current = this->getNextItemOf(current);
}
if (this->label != nullptr)
{
String content = getEndTemplate();
content.replace("{b}", this->label);
content.replace("{i}", this->getId());
webRequestWrapper->sendContent(content);
}
}
void OptionalParameterGroup::update(WebRequestWrapper* webRequestWrapper)
{
// -- Get active variable
String activeId = String(this->getId());
activeId += 'v';
if (webRequestWrapper->hasArg(activeId))
{
String activeStr = webRequestWrapper->arg(activeId);
this->_active = activeStr.equals("active");
}
// Update other items.
ParameterGroup::update(webRequestWrapper);
}
void OptionalParameterGroup::debugTo(Stream* out)
{
out->print('(');
out->print(this->_active ? "active" : "inactive");
out->print(')');
// Print rest.
ParameterGroup::debugTo(out);
}
///////////////////////////////////////////////////////////////////////////////
String ChainedParameterGroup::getStartTemplate()
{
String result = OptionalParameterGroup::getStartTemplate();
if ((this->_prevGroup != nullptr) && (!this->_prevGroup->isActive()))
{
result.replace("{cb}", "hide");
}
return result;
};
String ChainedParameterGroup::getEndTemplate()
{
String result;
if (this->_nextGroup == nullptr)
{
result = OptionalParameterGroup::getEndTemplate();
}
else
{
result = FPSTR(IOTWEBCONF_HTML_FORM_CHAINED_GROUP_NEXTID);
result.replace("{in}", this->_nextGroup->getId());
result += OptionalParameterGroup::getEndTemplate();
}
return result;
};
}
\ No newline at end of file
/**
* IotWebConfOptionalGroup.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfOptionalGroup_h
#define IotWebConfOptionalGroup_h
#include "IotWebConf.h" // For HtmlFormatProvider ... TODO: should be reorganized
#include "IotWebConfParameter.h"
const char IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_JAVASCRIPT[] PROGMEM =
" function show(id) { var x=document.getElementById(id); x.classList.remove('hide'); }\n"
" function hide(id) { var x=document.getElementById(id); x.classList.add('hide'); }\n"
" function val(id) { var x=document.getElementById(id); return x.value; }\n"
" function setVal(id, val) { var x=document.getElementById(id); x.value = val; }\n"
" function showFs(id) {\n"
" show(id); hide(id + 'b'); setVal(id + 'v', 'active'); var n=document.getElementById(id + 'next');\n"
" if (n) { var nId = n.value; if (val(nId + 'v') == 'inactive') { show(nId + 'b'); }}\n"
" }\n"
" function hideFs(id) {\n"
" hide(id); show(id + 'b'); setVal(id + 'v', 'inactive'); var n=document.getElementById(id + 'next');\n"
" if (n) { var nId = n.value; if (val(nId + 'v') == 'inactive') { hide(nId + 'b'); }}\n"
" }\n";
const char IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_CSS[] PROGMEM =
".hide{display: none;}\n";
const char IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_START[] PROGMEM =
"<button id='{i}b' class='{cb}' onclick=\"showFs('{i}'); return false;\">+ {b}</button>\n"
"<fieldset id='{i}' class='{cf}'><legend>{b}</legend>\n"
"<button onclick=\"hideFs('{i}'); return false;\">Disable {b}</button>\n"
"<input id='{i}v' name='{i}v' type='hidden' value='{v}'/>\n"
"\n";
const char IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_END[] PROGMEM =
"</fieldset>\n";
const char IOTWEBCONF_HTML_FORM_CHAINED_GROUP_NEXTID[] PROGMEM =
"<input type='hidden' id='{i}next' value='{in}'/>\n";
namespace iotwebconf
{
class OptionalGroupHtmlFormatProvider : public HtmlFormatProvider
{
protected:
String getScriptInner() override
{
return
HtmlFormatProvider::getScriptInner() +
String(FPSTR(IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_JAVASCRIPT));
}
String getStyleInner() override
{
return
HtmlFormatProvider::getStyleInner() +
String(FPSTR(IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_CSS));
}
};
/**
* With OptionalParameterGroup buttons will appear in the GUI,
* to show and hide this specific group of parameters. The idea
* behind this feature to add/remove optional parameter set
* in the config portal.
*/
class OptionalParameterGroup : public ParameterGroup
{
public:
OptionalParameterGroup(const char* id, const char* label, bool defaultActive);
bool isActive() { return this->_active; }
void setActive(bool active) { this->_active = active; }
protected:
int getStorageSize() override;
void applyDefaultValue() override;
void storeValue(std::function<void(
SerializationData* serializationData)> doStore) override;
void loadValue(std::function<void(
SerializationData* serializationData)> doLoad) override;
void renderHtml(bool dataArrived, WebRequestWrapper* webRequestWrapper) override;
virtual String getStartTemplate() { return FPSTR(IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_START); };
virtual String getEndTemplate() { return FPSTR(IOTWEBCONF_HTML_FORM_OPTIONAL_GROUP_END); };
void update(WebRequestWrapper* webRequestWrapper) override;
void debugTo(Stream* out) override;
private:
bool _defaultActive;
bool _active;
};
class ChainedParameterGroup;
class ChainedParameterGroup : public OptionalParameterGroup
{
public:
ChainedParameterGroup(const char* id, const char* label, bool defaultActive = false) :
OptionalParameterGroup(id, label, defaultActive) { };
void setNext(ChainedParameterGroup* nextGroup) { this->_nextGroup = nextGroup; nextGroup->_prevGroup = this; };
ChainedParameterGroup* getNext() { return this->_nextGroup; };
protected:
virtual String getStartTemplate() override;
virtual String getEndTemplate() override;
protected:
ChainedParameterGroup* _prevGroup = nullptr;
ChainedParameterGroup* _nextGroup = nullptr;
};
}
#endif
/**
* IotWebConfParameter.cpp -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#include "IotWebConfParameter.h"
namespace iotwebconf
{
ParameterGroup::ParameterGroup(
const char* id, const char* label) :
ConfigItem(id)
{
this->label = label;
}
void ParameterGroup::addItem(ConfigItem* configItem)
{
if (configItem->_parentItem != nullptr)
{
return; // Item must not be added two times.
}
if (this->_firstItem == nullptr)
{
this->_firstItem = configItem;
return;
}
ConfigItem* current = this->_firstItem;
while (current->_nextItem != nullptr)
{
current = current->_nextItem;
}
current->_nextItem = configItem;
configItem->_parentItem = this;
}
int ParameterGroup::getStorageSize()
{
int size = 0;
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
size += current->getStorageSize();
current = current->_nextItem;
}
return size;
}
void ParameterGroup::applyDefaultValue()
{
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
current->applyDefaultValue();
current = current->_nextItem;
}
}
void ParameterGroup::storeValue(
std::function<void(SerializationData* serializationData)> doStore)
{
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
current->storeValue(doStore);
current = current->_nextItem;
}
}
void ParameterGroup::loadValue(
std::function<void(SerializationData* serializationData)> doLoad)
{
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
current->loadValue(doLoad);
current = current->_nextItem;
}
}
void ParameterGroup::renderHtml(
bool dataArrived, WebRequestWrapper* webRequestWrapper)
{
if (this->label != nullptr)
{
String content = getStartTemplate();
content.replace("{b}", this->label);
content.replace("{i}", this->getId());
webRequestWrapper->sendContent(content);
}
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
if (current->visible)
{
current->renderHtml(dataArrived, webRequestWrapper);
}
current = current->_nextItem;
}
if (this->label != nullptr)
{
String content = getEndTemplate();
content.replace("{b}", this->label);
content.replace("{i}", this->getId());
webRequestWrapper->sendContent(content);
}
}
void ParameterGroup::update(WebRequestWrapper* webRequestWrapper)
{
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
current->update(webRequestWrapper);
current = current->_nextItem;
}
}
void ParameterGroup::clearErrorMessage()
{
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
current->clearErrorMessage();
current = current->_nextItem;
}
}
void ParameterGroup::debugTo(Stream* out)
{
out->print('[');
out->print(this->getId());
out->println(']');
// -- Here is some overcomplicated logic to have nice debug output.
bool ownItem = false;
bool lastItem = false;
PrefixStreamWrapper stream =
PrefixStreamWrapper(
out,
[&](Stream* out1)
{
if (ownItem)
{
ownItem = false;
return (size_t)0;
}
if (lastItem)
{
return out1->print(" ");
}
else
{
return out1->print("| ");
}
});
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
if (current->_nextItem == nullptr)
{
out->print("\\-- ");
}
else
{
out->print("|-- ");
}
ownItem = true;
lastItem = (current->_nextItem == nullptr);
current->debugTo(&stream);
current = current->_nextItem;
}
}
#ifdef IOTWEBCONF_ENABLE_JSON
void ParameterGroup::loadFromJson(JsonObject jsonObject)
{
if (jsonObject.containsKey(this->getId()))
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(F("Applying values from JSON for groupId: "));
Serial.println(this->getId());
#endif
JsonObject myObject = jsonObject[this->getId()];
ConfigItem* current = this->_firstItem;
while (current != nullptr)
{
current->loadFromJson(myObject);
current = current->_nextItem;
}
}
else
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(F("Group data not found in JSON. Skipping groupId: "));
Serial.println(this->getId());
#endif
}
}
#endif
///////////////////////////////////////////////////////////////////////////////
Parameter::Parameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue) :
ConfigItem(id)
{
this->label = label;
this->valueBuffer = valueBuffer;
this->_length = length;
this->defaultValue = defaultValue;
this->errorMessage = nullptr;
}
int Parameter::getStorageSize()
{
return this->_length;
}
void Parameter::applyDefaultValue()
{
if (defaultValue != nullptr)
{
strncpy(this->valueBuffer, this->defaultValue, this->getLength());
}
else
{
this->valueBuffer[0] = '\0';
}
}
void Parameter::storeValue(
std::function<void(SerializationData* serializationData)> doStore)
{
SerializationData serializationData;
serializationData.length = this->_length;
serializationData.data = (byte*)this->valueBuffer;
doStore(&serializationData);
}
void Parameter::loadValue(
std::function<void(SerializationData* serializationData)> doLoad)
{
SerializationData serializationData;
serializationData.length = this->_length;
serializationData.data = (byte*)this->valueBuffer;
doLoad(&serializationData);
}
void Parameter::update(WebRequestWrapper* webRequestWrapper)
{
if (webRequestWrapper->hasArg(this->getId()))
{
String newValue = webRequestWrapper->arg(this->getId());
this->update(newValue);
}
}
void Parameter::clearErrorMessage()
{
this->errorMessage = nullptr;
}
#ifdef IOTWEBCONF_ENABLE_JSON
void Parameter::loadFromJson(JsonObject jsonObject)
{
if (jsonObject.containsKey(this->getId()))
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(F("Applying value from JSON for parameterId: "));
Serial.println(this->getId());
#endif
const char* value = jsonObject[this->getId()];
this->update(String(value));
}
else
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(F("No value found in JSON for parameterId: "));
Serial.println(this->getId());
#endif
}
}
#endif
///////////////////////////////////////////////////////////////////////////////
TextParameter::TextParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue,
const char* placeholder,
const char* customHtml)
: Parameter(label, id, valueBuffer, length, defaultValue)
{
this->placeholder = placeholder;
this->customHtml = customHtml;
}
void TextParameter::renderHtml(
bool dataArrived, WebRequestWrapper* webRequestWrapper)
{
String content = this->renderHtml(
dataArrived,
webRequestWrapper->hasArg(this->getId()),
webRequestWrapper->arg(this->getId()));
webRequestWrapper->sendContent(content);
}
String TextParameter::renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost)
{
return this->renderHtml("text", hasValueFromPost, valueFromPost);
}
String TextParameter::renderHtml(
const char* type, bool hasValueFromPost, String valueFromPost)
{
TextParameter* current = this;
char parLength[12];
String pitem = getHtmlTemplate();
pitem.replace("{b}", current->label);
pitem.replace("{t}", type);
pitem.replace("{i}", current->getId());
pitem.replace("{p}", current->placeholder == nullptr ? "" : current->placeholder);
snprintf(parLength, 12, "%d", current->getLength()-1);
pitem.replace("{l}", parLength);
if (hasValueFromPost)
{
// -- Value from previous submit
pitem.replace("{v}", valueFromPost);
}
else
{
// -- Value from config
pitem.replace("{v}", current->valueBuffer);
}
pitem.replace(
"{c}", current->customHtml == nullptr ? "" : current->customHtml);
pitem.replace(
"{s}",
current->errorMessage == nullptr ? "" : "de"); // Div style class.
pitem.replace(
"{e}",
current->errorMessage == nullptr ? "" : current->errorMessage);
return pitem;
}
void TextParameter::update(String newValue)
{
newValue.toCharArray(this->valueBuffer, this->getLength());
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(": ");
Serial.println(this->valueBuffer);
#endif
}
void TextParameter::debugTo(Stream* out)
{
Parameter* current = this;
out->print("'");
out->print(current->getId());
out->print("' with value: '");
out->print(current->valueBuffer);
out->println("'");
}
///////////////////////////////////////////////////////////////////////////////
NumberParameter::NumberParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue,
const char* placeholder,
const char* customHtml)
: TextParameter(label, id, valueBuffer, length, defaultValue,
placeholder, customHtml)
{
}
String NumberParameter::renderHtml(
bool dataArrived,
bool hasValueFromPost, String valueFromPost)
{
return TextParameter::renderHtml("number", hasValueFromPost, valueFromPost);
}
///////////////////////////////////////////////////////////////////////////////
PasswordParameter::PasswordParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue,
const char* placeholder,
const char* customHtml)
: TextParameter(label, id, valueBuffer, length, defaultValue,
placeholder, customHtml)
{
}
String PasswordParameter::renderHtml(
bool dataArrived,
bool hasValueFromPost, String valueFromPost)
{
return TextParameter::renderHtml("password", true, String(this->valueBuffer));
}
void PasswordParameter::debugTo(Stream* out)
{
Parameter* current = this;
out->print("'");
out->print(current->getId());
out->print("' with value: ");
#ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
out->print("'");
out->print(current->valueBuffer);
out->println("'");
#else
out->println(F("<hidden>"));
#endif
}
void PasswordParameter::update(String newValue)
{
Parameter* current = this;
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(": ");
#endif
if (newValue != current->valueBuffer)
{
// -- Value was set.
newValue.toCharArray(current->valueBuffer, current->getLength());
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
Serial.println(current->valueBuffer);
# else
Serial.println("<updated>");
# endif
#endif
}
else
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.println("<was not changed>");
#endif
}
}
///////////////////////////////////////////////////////////////////////////////
CheckboxParameter::CheckboxParameter(
const char* label, const char* id, char* valueBuffer, int length,
bool defaultValue)
: TextParameter(label, id, valueBuffer, length, defaultValue ? "selected" : nullptr,
nullptr, nullptr)
{
}
String CheckboxParameter::renderHtml(
bool dataArrived,
bool hasValueFromPost, String valueFromPost)
{
bool checkSelected = false;
if (dataArrived)
{
if (hasValueFromPost && valueFromPost.equals("selected"))
{
checkSelected = true;
}
}
else
{
if (this->isChecked())
{
checkSelected = true;
}
}
if (checkSelected)
{
this->customHtml = CheckboxParameter::_checkedStr;
}
else
{
this->customHtml = nullptr;
}
return TextParameter::renderHtml("checkbox", true, "selected");
}
void CheckboxParameter::update(WebRequestWrapper* webRequestWrapper)
{
if (webRequestWrapper->hasArg(this->getId()))
{
String newValue = webRequestWrapper->arg(this->getId());
return TextParameter::update(newValue);
}
else if (this->visible)
{
// HTML will not post back unchecked checkboxes.
return TextParameter::update("");
}
}
///////////////////////////////////////////////////////////////////////////////
OptionsParameter::OptionsParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* optionValues, const char* optionNames, size_t optionCount, size_t nameLength,
const char* defaultValue)
: TextParameter(label, id, valueBuffer, length, defaultValue,
nullptr, nullptr)
{
this->_optionValues = optionValues;
this->_optionNames = optionNames;
this->_optionCount = optionCount;
this->_nameLength = nameLength;
}
///////////////////////////////////////////////////////////////////////////////
SelectParameter::SelectParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* optionValues, const char* optionNames, size_t optionCount, size_t nameLength,
const char* defaultValue)
: OptionsParameter(label, id, valueBuffer, length, optionValues, optionNames,
optionCount, nameLength, defaultValue)
{
}
String SelectParameter::renderHtml(
bool dataArrived,
bool hasValueFromPost, String valueFromPost)
{
TextParameter* current = this;
String options = "";
for (size_t i=0; i<this->_optionCount; i++)
{
const char *optionValue = (this->_optionValues + (i*this->getLength()) );
const char *optionName = (this->_optionNames + (i*this->_nameLength) );
String oitem = FPSTR(IOTWEBCONF_HTML_FORM_OPTION);
oitem.replace("{v}", optionValue);
// if (sizeof(this->_optionNames) > i)
{
oitem.replace("{n}", optionName);
}
// else
// {
// oitem.replace("{n}", "?");
// }
if ((hasValueFromPost && (valueFromPost == optionValue)) ||
(strncmp(current->valueBuffer, optionValue, this->getLength()) == 0))
{
// -- Value from previous submit
oitem.replace("{s}", " selected");
}
else
{
// -- Value from config
oitem.replace("{s}", "");
}
options += oitem;
}
String pitem = FPSTR(IOTWEBCONF_HTML_FORM_SELECT_PARAM);
pitem.replace("{b}", current->label);
pitem.replace("{i}", current->getId());
pitem.replace(
"{c}", current->customHtml == nullptr ? "" : current->customHtml);
pitem.replace(
"{s}",
current->errorMessage == nullptr ? "" : "de"); // Div style class.
pitem.replace(
"{e}",
current->errorMessage == nullptr ? "" : current->errorMessage);
pitem.replace("{o}", options);
return pitem;
}
///////////////////////////////////////////////////////////////////////////////
PrefixStreamWrapper::PrefixStreamWrapper(
Stream* originalStream,
std::function<size_t(Stream* stream)> prefixWriter)
{
this->_originalStream = originalStream;
this->_prefixWriter = prefixWriter;
}
size_t PrefixStreamWrapper::write(uint8_t data)
{
size_t sizeOut = checkNewLine();
sizeOut += this->_originalStream->write(data);
if (data == 10) // NewLine
{
this->_newLine = true;
}
return sizeOut;
}
size_t PrefixStreamWrapper::write(const uint8_t *buffer, size_t size)
{
size_t sizeOut = checkNewLine();
sizeOut += this->_originalStream->write(buffer, size);
if (*(buffer + size-1) == 10) // Ends with new line
{
this->_newLine = true;
}
return sizeOut;
}
size_t PrefixStreamWrapper::checkNewLine()
{
if (this->_newLine)
{
this->_newLine = false;
return this->_prefixWriter(this->_originalStream);
}
return 0;
}
} // end namespace
\ No newline at end of file
/**
* IotWebConfParameter.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfParameter_h
#define IotWebConfParameter_h
#include <Arduino.h>
#include <functional>
#include "IotWebConfSettings.h"
#include "IotWebConfWebServerWrapper.h"
#ifdef IOTWEBCONF_ENABLE_JSON
# include <ArduinoJson.h>
#endif
const char IOTWEBCONF_HTML_FORM_GROUP_START[] PROGMEM =
"<fieldset id='{i}'><legend>{b}</legend>\n";
const char IOTWEBCONF_HTML_FORM_GROUP_END[] PROGMEM =
"</fieldset>\n";
const char IOTWEBCONF_HTML_FORM_PARAM[] PROGMEM =
"<div class='{s}'><label for='{i}'>{b}</label><input type='{t}' id='{i}' "
"name='{i}' {l} placeholder='{p}' value='{v}' {c}/>"
"<div class='em'>{e}</div></div>\n";
const char IOTWEBCONF_HTML_FORM_SELECT_PARAM[] PROGMEM =
"<div class='{s}'><label for='{i}'>{b}</label><select id='{i}' "
"name='{i}' {c}/>\n{o}"
"</select><div class='em'>{e}</div></div>\n";
const char IOTWEBCONF_HTML_FORM_OPTION[] PROGMEM =
"<option value='{v}'{s}>{n}</option>\n";
namespace iotwebconf
{
typedef struct SerializationData
{
byte* data;
int length;
} SerializationData;
class ConfigItem
{
public:
bool visible = true;
const char* getId() { return this->_id; }
/**
* Calculate the size of bytes should be stored in the EEPROM.
*/
virtual int getStorageSize() = 0;
/**
* On initial startup (when no data was saved), it may be required to apply a default value
* to the parameter.
*/
virtual void applyDefaultValue() = 0;
/**
* Save data.
* @doStore - A method is passed as a parameter, that will performs the actual EEPROM access.
* The argument 'serializationData' of this referenced method should be pre-filled with
* the size and the serialized data before calling the method.
*/
virtual void storeValue(std::function<void(SerializationData* serializationData)> doStore) = 0;
/**
* Load data.
* @doLoad - A method is passed as a parameter, that will performs the actual EEPROM access.
* The argument 'serializationData' of this referenced method should be pre-filled with
* the size of the expected data, and the data buffer should be allocated with this size.
* The doLoad will fill the data from the EEPROM.
*/
virtual void loadValue(std::function<void(SerializationData* serializationData)> doLoad) = 0;
/**
* This method will create the HTML form item for the config portal.
*
* @dataArrived - True if there was a form post, where (some) form
* data arrived from the client.
* @webRequestWrapper - The webRequestWrapper, that will send the rendered content to the client.
* The webRequestWrapper->sendContent() method should be used in the implementations.
*/
virtual void renderHtml(bool dataArrived, WebRequestWrapper* webRequestWrapper) = 0;
/**
* New value arrived from the form post. The value should be stored in the
* in this config item.
*
* @webRequestWrapper - The webRequestWrapper, that will send the rendered content to the client.
* The webRequestWrapper->hasArg() and webRequestWrapper->arg() methods should be used in the
* implementations.
*/
virtual void update(WebRequestWrapper* webRequestWrapper) = 0;
/**
* Before validating the form post, it is required to clear previous error messages.
*/
virtual void clearErrorMessage() = 0;
/**
* This method should display information to Serial containing the parameter
* ID and the current value of the parameter (if it is confidential).
* Will only be called if debug is enabled.
*/
virtual void debugTo(Stream* out) = 0;
#ifdef IOTWEBCONF_ENABLE_JSON
/**
*
*/
virtual void loadFromJson(JsonObject jsonObject) = 0;
#endif
protected:
ConfigItem(const char* id) { this->_id = id; };
private:
const char* _id = 0;
ConfigItem* _parentItem = nullptr;
ConfigItem* _nextItem = nullptr;
friend class ParameterGroup; // Allow ParameterGroup to access _nextItem.
};
class ParameterGroup : public ConfigItem
{
public:
ParameterGroup(const char* id, const char* label = nullptr);
void addItem(ConfigItem* configItem);
const char *label;
void applyDefaultValue() override;
#ifdef IOTWEBCONF_ENABLE_JSON
virtual void loadFromJson(JsonObject jsonObject) override;
#endif
protected:
int getStorageSize() override;
void storeValue(std::function<void(
SerializationData* serializationData)> doStore) override;
void loadValue(std::function<void(
SerializationData* serializationData)> doLoad) override;
void renderHtml(bool dataArrived, WebRequestWrapper* webRequestWrapper) override;
void update(WebRequestWrapper* webRequestWrapper) override;
void clearErrorMessage() override;
void debugTo(Stream* out) override;
/**
* One can override this method in case a specific HTML template is required
* for a group.
*/
virtual String getStartTemplate() { return FPSTR(IOTWEBCONF_HTML_FORM_GROUP_START); };
/**
* One can override this method in case a specific HTML template is required
* for a group.
*/
virtual String getEndTemplate() { return FPSTR(IOTWEBCONF_HTML_FORM_GROUP_END); };
ConfigItem* _firstItem = nullptr;
ConfigItem* getNextItemOf(ConfigItem* parent) { return parent->_nextItem; };
friend class IotWebConf; // Allow IotWebConf to access protected members.
private:
};
/**
* Parameters is a configuration item of the config portal.
* The parameter will have its input field on the configuration page,
* and the provided value will be saved to the EEPROM.
*/
class Parameter : public ConfigItem
{
public:
/**
* Create a parameter for the config portal.
*
* @label - Displayable label at the config portal.
* @id - Identifier used for HTTP queries and as configuration key. Must not
* contain spaces nor other special characters.
* @valueBuffer - Configuration value will be loaded to this buffer from the
* EEPROM.
* @length - The buffer should have a length provided here.
* @defaultValue - Defalt value set on startup, when no configuration ever saved
* with the current config-version.
*/
Parameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue = nullptr);
const char* label;
char* valueBuffer;
const char* defaultValue;
const char* errorMessage;
int getLength() { return this->_length; }
void applyDefaultValue() override;
#ifdef IOTWEBCONF_ENABLE_JSON
virtual void loadFromJson(JsonObject jsonObject) override;
#endif
protected:
// Overrides
int getStorageSize() override;
void storeValue(std::function<void(SerializationData* serializationData)> doStore) override;
void loadValue(std::function<void(SerializationData* serializationData)> doLoad) override;
virtual void update(WebRequestWrapper* webRequestWrapper) override;
virtual void update(String newValue) = 0;
void clearErrorMessage() override;
private:
int _length;
};
///////////////////////////////////////////////////////////////////////////////
/**
* TexParameters is to store text based parameters.
*/
class TextParameter : public Parameter
{
public:
/**
* Create a text parameter for the config portal.
*
* @placeholder (optional) - Text appear in an empty input box.
* @customHtml (optional) - The text of this parameter will be added into
* the HTML INPUT field.
* (See Parameter for arguments!)
*/
TextParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue = nullptr,
const char* placeholder = nullptr,
const char* customHtml = nullptr);
/**
* This variable is meant to store a value that is displayed in an empty
* (not filled) field.
*/
const char* placeholder;
/**
* Usually this variable is used when rendering the form input field
* so one can customize the rendered outcome of this particular item.
*/
const char* customHtml;
protected:
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost);
// Overrides
virtual void renderHtml(bool dataArrived, WebRequestWrapper* webRequestWrapper) override;
virtual void update(String newValue) override;
virtual void debugTo(Stream* out) override;
/**
* One can override this method in case a specific HTML template is required
* for a parameter.
*/
virtual String getHtmlTemplate() { return FPSTR(IOTWEBCONF_HTML_FORM_PARAM); };
/**
* Renders a standard HTML form INPUT.
* @type - The type attribute of the html input field.
*/
virtual String renderHtml(const char* type, bool hasValueFromPost, String valueFromPost);
private:
friend class IotWebConf;
friend class WifiParameterGroup;
};
///////////////////////////////////////////////////////////////////////////////
/**
* The Password parameter has a special handling, as the value will be
* overwritten in the EEPROM only if value was provided on the config portal.
* Because of this logic, "password" type field with length more then
* IOTWEBCONF_PASSWORD_LEN characters are not supported.
*/
class PasswordParameter : public TextParameter
{
public:
/**
* Create a password parameter for the config portal.
*
* (See TextParameter for arguments!)
*/
PasswordParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue = nullptr,
const char* placeholder = nullptr,
const char* customHtml = "ondblclick=\"pw(this.id)\"");
protected:
// Overrides
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override;
virtual void update(String newValue) override;
virtual void debugTo(Stream* out) override;
private:
friend class IotWebConf;
friend class WifiParameterGroup;
};
///////////////////////////////////////////////////////////////////////////////
/**
* This is just a text parameter, that is rendered with type 'number'.
*/
class NumberParameter : public TextParameter
{
public:
/**
* Create a numeric parameter for the config portal.
*
* (See TextParameter for arguments!)
*/
NumberParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* defaultValue = nullptr,
const char* placeholder = nullptr,
const char* customHtml = nullptr);
protected:
// Overrides
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override;
private:
friend class IotWebConf;
};
///////////////////////////////////////////////////////////////////////////////
/**
* Checkbox parameter is represended as a text parameter but has a special
* handling. As the value is either empty or has the word "selected".
* Note, that form post will not send value if checkbox was not selected.
*/
class CheckboxParameter : public TextParameter
{
public:
/**
* Create a checkbox parameter for the config portal.
*
* (See TextParameter for arguments!)
*/
CheckboxParameter(
const char* label, const char* id, char* valueBuffer, int length,
bool defaultValue = false);
bool isChecked() { return strncmp(this->valueBuffer, "selected", this->getLength()) == 0; }
protected:
// Overrides
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override;
virtual void update(WebRequestWrapper* webRequestWrapper) override;
private:
friend class IotWebConf;
bool _checked;
const char* _checkedStr = "checked='checked'";
};
///////////////////////////////////////////////////////////////////////////////
/**
* Options parameter is a structure, that handles multiple values when redering
* the HTML representation.
*/
class OptionsParameter : public TextParameter
{
public:
/**
* @optionValues - List of values to choose from with, where each value
* can have a maximal size of 'length'. Contains 'optionCount' items.
* @optionNames - List of names to render for the values, where each
* name can have a maximal size of 'nameLength'. Contains 'optionCount'
* items.
* @optionCount - Size of both 'optionValues' and 'optionNames' lists.
* @nameLength - Size of any item in optionNames list.
* (See TextParameter for arguments!)
*/
OptionsParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* optionValues, const char* optionNames, size_t optionCount, size_t nameLength,
const char* defaultValue = nullptr);
protected:
const char* _optionValues;
const char* _optionNames;
size_t _optionCount;
size_t _nameLength;
private:
friend class IotWebConf;
};
///////////////////////////////////////////////////////////////////////////////
/**
* Select parameter is an option parameter, that rendered as HTML SELECT.
* Basically it is a dropdown combobox.
*/
class SelectParameter : public OptionsParameter
{
public:
/**
* Create a select parameter for the config portal.
*
* (See OptionsParameter for arguments!)
*/
SelectParameter(
const char* label, const char* id, char* valueBuffer, int length,
const char* optionValues, const char* optionNames, size_t optionCount, size_t namesLenth,
const char* defaultValue = nullptr);
protected:
// Overrides
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override;
private:
friend class IotWebConf;
};
/**
* This class is here just to make some nice indents on debug output
* for group tree.
*/
class PrefixStreamWrapper : public Stream
{
public:
PrefixStreamWrapper(
Stream* originalStream,
std::function<size_t(Stream* stream)> prefixWriter);
size_t write(uint8_t) override;
size_t write(const uint8_t *buffer, size_t size) override;
int available() override { return _originalStream->available(); };
int read() override { return _originalStream->read(); };
int peek() override { return _originalStream->peek(); };
void flush() override { return _originalStream->flush(); };
private:
Stream* _originalStream;
std::function<size_t(Stream* stream)> _prefixWriter;
bool _newLine = true;
size_t checkNewLine();
};
} // end namespace
#endif
/**
* IotWebConfSettings.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfSettings_h
#define IotWebConfSettings_h
#if __has_include("custom_ampel_iotwebconf.h")
#include "custom_ampel_iotwebconf.h"
#endif
// -- We might want to place the config in the EEPROM in an offset.
#ifndef IOTWEBCONF_CONFIG_START
# define IOTWEBCONF_CONFIG_START 0
#endif
// -- Maximal length of any string used in IotWebConfig configuration (e.g.
// ssid).
#ifndef IOTWEBCONF_WORD_LEN
# define IOTWEBCONF_WORD_LEN 33
#endif
// -- Maximal length of password used in IotWebConfig configuration.
#ifndef IOTWEBCONF_PASSWORD_LEN
# define IOTWEBCONF_PASSWORD_LEN 33
#endif
// -- IotWebConf tries to connect to the local network for an amount of time
// before falling back to AP mode.
#ifndef IOTWEBCONF_DEFAULT_WIFI_CONNECTION_TIMEOUT_MS
# define IOTWEBCONF_DEFAULT_WIFI_CONNECTION_TIMEOUT_MS 30000
#endif
// -- Thing will stay in AP mode for an amount of time on boot, before retrying
// to connect to a WiFi network.
#ifndef IOTWEBCONF_DEFAULT_AP_MODE_TIMEOUT_SECS
# define IOTWEBCONF_DEFAULT_AP_MODE_TIMEOUT_SECS "30"
#endif
// -- mDNS should allow you to connect to this device with a hostname provided
// by the device. E.g. mything.local
// (This is not very likely to work, and MDNS is not very well documented.)
#ifndef IOTWEBCONF_CONFIG_DONT_USE_MDNS
# define IOTWEBCONF_CONFIG_USE_MDNS 80
#endif
// -- Logs progress information to Serial if enabled.
#ifndef IOTWEBCONF_DEBUG_DISABLED
# define IOTWEBCONF_DEBUG_TO_SERIAL
#endif
// -- Logs passwords to Serial if enabled.
//#define IOTWEBCONF_DEBUG_PWD_TO_SERIAL
// -- Helper define for serial debug
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
# define IOTWEBCONF_DEBUG_LINE(MSG) Serial.println(MSG)
#else
# define IOTWEBCONF_DEBUG_LINE(MSG)
#endif
// -- EEPROM config starts with a special prefix of length defined here.
#ifndef IOTWEBCONF_CONFIG_VERSION_LENGTH
# define IOTWEBCONF_CONFIG_VERSION_LENGTH 4
#endif
#ifndef IOTWEBCONF_DNS_PORT
# define IOTWEBCONF_DNS_PORT 53
#endif
#endif
\ No newline at end of file
/**
* IotWebConfTParameter.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2021 Balazs Kelemen <prampec+arduino@gmail.com>
* rovo89
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfTParameter_h
#define IotWebConfTParameter_h
// TODO: This file is a mess. Help wanted to organize thing!
#include "IotWebConfParameter.h"
#include <Arduino.h>
#include <IPAddress.h>
#include <errno.h>
// At least in PlatformIO, strtoimax/strtoumax are defined, but not implemented.
#if 1
#define strtoimax strtoll
#define strtoumax strtoull
#endif
namespace iotwebconf
{
/**
* This class is to hide web related properties from the
* data manipulation.
*/
class ConfigItemBridge : public ConfigItem
{
public:
virtual void update(WebRequestWrapper* webRequestWrapper) override
{
if (webRequestWrapper->hasArg(this->getId()))
{
String newValue = webRequestWrapper->arg(this->getId());
this->update(newValue);
}
}
void debugTo(Stream* out) override
{
out->print("'");
out->print(this->getId());
out->print("' with value: '");
out->print(this->toString());
out->println("'");
}
protected:
ConfigItemBridge(const char* id) : ConfigItem(id) { }
virtual int getInputLength() { return 0; };
virtual bool update(String newValue, bool validateOnly = false) = 0;
virtual String toString() = 0;
};
///////////////////////////////////////////////////////////////////////////
/**
* DataType is the data related part of the parameter.
* It does not care about web and visualization, but takes care of the
* data validation and storing.
*/
template <typename ValueType, typename _DefaultValueType = ValueType>
class DataType : virtual public ConfigItemBridge
{
public:
using DefaultValueType = _DefaultValueType;
DataType(const char* id, DefaultValueType defaultValue) :
ConfigItemBridge(id),
_defaultValue(defaultValue)
{
}
/**
* value() can be used to get the value, but it can also
* be used set it like this: p.value() = newValue
*/
ValueType& value() { return this->_value; }
ValueType& operator*() { return this->_value; }
protected:
int getStorageSize() override
{
return sizeof(ValueType);
}
virtual bool update(String newValue, bool validateOnly = false) = 0;
bool validate(String newValue) { return update(newValue, true); }
virtual String toString() override { return String(this->_value); }
ValueType _value;
const DefaultValueType _defaultValue;
};
///////////////////////////////////////////////////////////////////////////
class StringDataType : public DataType<String>
{
public:
using DataType<String>::DataType;
protected:
virtual bool update(String newValue, bool validateOnly) override {
if (!validateOnly)
{
this->_value = newValue;
}
return true;
}
virtual String toString() override { return this->_value; }
};
///////////////////////////////////////////////////////////////////////////
template <size_t len>
class CharArrayDataType : public DataType<char[len], const char*>
{
public:
using DataType<char[len], const char*>::DataType;
CharArrayDataType(const char* id, const char* defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
DataType<char[len], const char*>::DataType(id, defaultValue) { };
virtual void applyDefaultValue() override
{
strncpy(this->_value, this->_defaultValue, len);
}
protected:
virtual bool update(String newValue, bool validateOnly) override
{
if (newValue.length() + 1 > len)
{
return false;
}
if (!validateOnly)
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(": ");
Serial.println(newValue);
#endif
strncpy(this->_value, newValue.c_str(), len);
}
return true;
}
void storeValue(std::function<void(
SerializationData* serializationData)> doStore) override
{
SerializationData serializationData;
serializationData.length = len;
serializationData.data = (byte*)this->_value;
doStore(&serializationData);
}
void loadValue(std::function<void(
SerializationData* serializationData)> doLoad) override
{
SerializationData serializationData;
serializationData.length = len;
serializationData.data = (byte*)this->_value;
doLoad(&serializationData);
}
virtual int getInputLength() override { return len; };
};
///////////////////////////////////////////////////////////////////////////
/**
* All non-complex types should be inherited from this base class.
*/
template <typename ValueType>
class PrimitiveDataType : public DataType<ValueType>
{
public:
using DataType<ValueType>::DataType;
PrimitiveDataType(const char* id, ValueType defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
DataType<ValueType>::DataType(id, defaultValue) { };
void setMax(ValueType val) { this->_max = val; this->_maxDefined = true; }
void setMin(ValueType val) { this->_min = val; this->_minDefined = true; }
virtual void applyDefaultValue() override
{
this->_value = this->_defaultValue;
}
protected:
virtual bool update(String newValue, bool validateOnly) override
{
errno = 0;
ValueType val = fromString(newValue);
if ((errno == ERANGE)
|| (this->_minDefined && (val < this->_min))
|| (this->_maxDefined && (val > this->_max)))
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(" value not accepted: ");
Serial.println(val);
#endif
return false;
}
if (!validateOnly)
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(": ");
Serial.println((ValueType)val);
#endif
this->_value = (ValueType) val;
}
return true;
}
void storeValue(std::function<void(
SerializationData* serializationData)> doStore) override
{
SerializationData serializationData;
serializationData.length = this->getStorageSize();
serializationData.data =
reinterpret_cast<byte*>(&this->_value);
doStore(&serializationData);
}
void loadValue(std::function<void(
SerializationData* serializationData)> doLoad) override
{
byte buf[this->getStorageSize()];
SerializationData serializationData;
serializationData.length = this->getStorageSize();
serializationData.data = buf;
doLoad(&serializationData);
ValueType* valuePointer = reinterpret_cast<ValueType*>(buf);
this->_value = *valuePointer;
}
virtual ValueType fromString(String stringValue) = 0;
ValueType getMax() { return this->_max; }
ValueType getMin() { return this->_min; }
ValueType isMaxDefined() { return this->_maxDefined; }
ValueType isMinDefined() { return this->_minDefined; }
private:
ValueType _min;
ValueType _max;
bool _minDefined = false;
bool _maxDefined = false;
};
///////////////////////////////////////////////////////////////////////////
template <typename ValueType, int base = 10>
class SignedIntDataType : public PrimitiveDataType<ValueType>
{
public:
SignedIntDataType(const char* id, ValueType defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
PrimitiveDataType<ValueType>::PrimitiveDataType(id, defaultValue) { };
protected:
virtual ValueType fromString(String stringValue)
{
return (ValueType)strtoimax(stringValue.c_str(), nullptr, base);
}
};
template <typename ValueType, int base = 10>
class UnsignedIntDataType : public PrimitiveDataType<ValueType>
{
public:
UnsignedIntDataType(const char* id, ValueType defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
PrimitiveDataType<ValueType>::PrimitiveDataType(id, defaultValue) { };
protected:
virtual ValueType fromString(String stringValue)
{
return (ValueType)strtoumax(stringValue.c_str(), nullptr, base);
}
};
class BoolDataType : public PrimitiveDataType<bool>
{
public:
BoolDataType(const char* id, bool defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
PrimitiveDataType<bool>::PrimitiveDataType(id, defaultValue) { };
protected:
virtual bool fromString(String stringValue)
{
return stringValue.c_str()[0] == 1;
}
};
class FloatDataType : public PrimitiveDataType<float>
{
public:
FloatDataType(const char* id, float defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
PrimitiveDataType<float>::PrimitiveDataType(id, defaultValue) { };
protected:
virtual float fromString(String stringValue)
{
return atof(stringValue.c_str());
}
};
class DoubleDataType : public PrimitiveDataType<double>
{
public:
DoubleDataType(const char* id, double defaultValue) :
ConfigItemBridge::ConfigItemBridge(id),
PrimitiveDataType<double>::PrimitiveDataType(id, defaultValue) { };
protected:
virtual double fromString(String stringValue)
{
return strtod(stringValue.c_str(), nullptr);
}
};
/////////////////////////////////////////////////////////////////////////
class IpDataType : public DataType<IPAddress>
{
using DataType<IPAddress>::DataType;
protected:
virtual bool update(String newValue, bool validateOnly) override
{
if (validateOnly)
{
IPAddress ip;
return ip.fromString(newValue);
}
else
{
return this->_value.fromString(newValue);
}
}
virtual String toString() override { return this->_value.toString(); }
};
///////////////////////////////////////////////////////////////////////////
/**
* Input parameter is the part of the parameter that is responsible
* for the appearance of the parameter in HTML.
*/
class InputParameter : virtual public ConfigItemBridge
{
public:
InputParameter(const char* id, const char* label) :
ConfigItemBridge::ConfigItemBridge(id),
label(label) { }
virtual void renderHtml(
bool dataArrived, WebRequestWrapper* webRequestWrapper) override
{
String content = this->renderHtml(
dataArrived,
webRequestWrapper->hasArg(this->getId()),
webRequestWrapper->arg(this->getId()));
webRequestWrapper->sendContent(content);
}
const char* label;
/**
* This variable is meant to store a value that is displayed in an empty
* (not filled) field.
*/
const char* placeholder = nullptr;
virtual void setPlaceholder(const char* placeholder) { this->placeholder = placeholder; }
/**
* Usually this variable is used when rendering the form input field
* so one can customize the rendered outcome of this particular item.
*/
const char* customHtml = nullptr;
/**
* Used when rendering the input field. Is is overridden by different
* implementations.
*/
virtual String getCustomHtml()
{
return String(customHtml == nullptr ? "" : customHtml);
}
const char* errorMessage = nullptr;
protected:
void clearErrorMessage() override
{
this->errorMessage = nullptr;
}
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost)
{
String pitem = String(this->getHtmlTemplate());
pitem.replace("{b}", this->label);
pitem.replace("{t}", this->getInputType());
pitem.replace("{i}", this->getId());
pitem.replace(
"{p}", this->placeholder == nullptr ? "" : this->placeholder);
int length = this->getInputLength();
if (length > 0)
{
char parLength[11];
snprintf(parLength, 11, "%d", length - 1); // To allow "\0" at the end of the string.
String maxLength = String("maxlength=") + parLength;
pitem.replace("{l}", maxLength);
}
else
{
pitem.replace("{l}", "");
}
if (hasValueFromPost)
{
// -- Value from previous submit
pitem.replace("{v}", valueFromPost);
}
else
{
// -- Value from config
pitem.replace("{v}", this->toString());
}
pitem.replace("{c}", this->getCustomHtml());
pitem.replace(
"{s}",
this->errorMessage == nullptr ? "" : "de"); // Div style class.
pitem.replace(
"{e}",
this->errorMessage == nullptr ? "" : this->errorMessage);
return pitem;
}
/**
* One can override this method in case a specific HTML template is required
* for a parameter.
*/
virtual String getHtmlTemplate() { return FPSTR(IOTWEBCONF_HTML_FORM_PARAM); };
virtual const char* getInputType() = 0;
};
template <size_t len>
class TextTParameter : public CharArrayDataType<len>, public InputParameter
{
public:
using CharArrayDataType<len>::CharArrayDataType;
TextTParameter(const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
CharArrayDataType<len>::CharArrayDataType(id, defaultValue),
InputParameter::InputParameter(id, label) { }
protected:
virtual const char* getInputType() override { return "text"; }
};
class CheckboxTParameter : public BoolDataType, public InputParameter
{
public:
CheckboxTParameter(const char* id, const char* label, const bool defaultValue) :
ConfigItemBridge(id),
BoolDataType::BoolDataType(id, defaultValue),
InputParameter::InputParameter(id, label) { }
bool isChecked() { return this->value(); }
protected:
virtual const char* getInputType() override { return "checkbox"; }
virtual void update(WebRequestWrapper* webRequestWrapper) override
{
bool selected = false;
if (webRequestWrapper->hasArg(this->getId()))
{
String valueFromPost = webRequestWrapper->arg(this->getId());
selected = valueFromPost.equals("selected");
}
// this->update(String(selected ? "1" : "0"));
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(": ");
Serial.println(selected ? "selected" : "not selected");
#endif
this->_value = selected;
}
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override
{
bool checkSelected = false;
if (dataArrived)
{
if (hasValueFromPost && valueFromPost.equals("selected"))
{
checkSelected = true;
}
}
else
{
if (this->isChecked())
{
checkSelected = true;
}
}
if (checkSelected)
{
this->customHtml = CheckboxTParameter::_checkedStr;
}
else
{
this->customHtml = nullptr;
}
return InputParameter::renderHtml(dataArrived, true, String("selected"));
}
private:
const char* _checkedStr = "checked='checked'";
};
template <size_t len>
class PasswordTParameter : public CharArrayDataType<len>, public InputParameter
{
public:
using CharArrayDataType<len>::CharArrayDataType;
PasswordTParameter(const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
CharArrayDataType<len>::CharArrayDataType(id, defaultValue),
InputParameter::InputParameter(id, label)
{
this->customHtml = _customHtmlPwd;
}
void debugTo(Stream* out)
{
out->print("'");
out->print(this->getId());
out->print("' with value: ");
#ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
out->print("'");
out->print(this->_value);
out->println("'");
#else
out->println(F("<hidden>"));
#endif
}
virtual bool update(String newValue, bool validateOnly) override
{
if (newValue.length() + 1 > len)
{
return false;
}
if (validateOnly)
{
return true;
}
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.print(this->getId());
Serial.print(": ");
#endif
if (newValue != this->_value)
{
// -- Value was set.
strncpy(this->_value, newValue.c_str(), len);
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
Serial.println(this->_value);
# else
Serial.println("<updated>");
# endif
#endif
}
else
{
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
Serial.println("<was not changed>");
#endif
}
return true;
}
protected:
virtual const char* getInputType() override { return "password"; }
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override
{
return InputParameter::renderHtml(dataArrived, true, String(this->_value));
}
private:
const char* _customHtmlPwd = "ondblclick=\"pw(this.id)\"";
};
/**
* All non-complex type input parameters should be inherited from this
* base class.
*/
template <typename ValueType>
class PrimitiveInputParameter :
public InputParameter
{
public:
PrimitiveInputParameter(const char* id, const char* label) :
ConfigItemBridge::ConfigItemBridge(id),
InputParameter::InputParameter(id, label) { }
virtual String getCustomHtml() override
{
String modifiers = String(this->customHtml);
if (this->isMinDefined())
{
modifiers += " min='" ;
modifiers += this->getMin();
modifiers += "'";
}
if (this->isMaxDefined())
{
modifiers += " max='";
modifiers += this->getMax();
modifiers += "'";
}
if (this->step != 0)
{
modifiers += " step='";
modifiers += this->step;
modifiers += "'";
}
return modifiers;
}
ValueType step = 0;
void setStep(ValueType step) { this->step = step; }
virtual ValueType getMin() = 0;
virtual ValueType getMax() = 0;
virtual bool isMinDefined() = 0;
virtual bool isMaxDefined() = 0;
};
template <typename ValueType, int base = 10>
class IntTParameter :
public virtual SignedIntDataType<ValueType, base>,
public PrimitiveInputParameter<ValueType>
{
public:
IntTParameter(const char* id, const char* label, ValueType defaultValue) :
ConfigItemBridge(id),
SignedIntDataType<ValueType, base>::SignedIntDataType(id, defaultValue),
PrimitiveInputParameter<ValueType>::PrimitiveInputParameter(id, label) { }
// TODO: somehow organize these methods into common parent.
virtual ValueType getMin() override
{
return PrimitiveDataType<ValueType>::getMin();
}
virtual ValueType getMax() override
{
return PrimitiveDataType<ValueType>::getMax();
}
virtual bool isMinDefined() override
{
return PrimitiveDataType<ValueType>::isMinDefined();
}
virtual bool isMaxDefined() override
{
return PrimitiveDataType<ValueType>::isMaxDefined();
}
protected:
virtual const char* getInputType() override { return "number"; }
};
template <typename ValueType, int base = 10>
class UIntTParameter :
public virtual UnsignedIntDataType<ValueType, base>,
public PrimitiveInputParameter<ValueType>
{
public:
UIntTParameter(const char* id, const char* label, ValueType defaultValue) :
ConfigItemBridge(id),
UnsignedIntDataType<ValueType, base>::UnsignedIntDataType(id, defaultValue),
PrimitiveInputParameter<ValueType>::PrimitiveInputParameter(id, label) { }
// TODO: somehow organize these methods into common parent.
virtual ValueType getMin() override
{
return PrimitiveDataType<ValueType>::getMin();
}
virtual ValueType getMax() override
{
return PrimitiveDataType<ValueType>::getMax();
}
virtual bool isMinDefined() override
{
return PrimitiveDataType<ValueType>::isMinDefined();
}
virtual bool isMaxDefined() override
{
return PrimitiveDataType<ValueType>::isMaxDefined();
}
protected:
virtual const char* getInputType() override { return "number"; }
};
class FloatTParameter :
public FloatDataType,
public PrimitiveInputParameter<float>
{
public:
FloatTParameter(const char* id, const char* label, float defaultValue) :
ConfigItemBridge(id),
FloatDataType::FloatDataType(id, defaultValue),
PrimitiveInputParameter<float>::PrimitiveInputParameter(id, label) { }
virtual float getMin() override
{
return PrimitiveDataType<float>::getMin();
}
virtual float getMax() override
{
return PrimitiveDataType<float>::getMax();
}
virtual bool isMinDefined() override
{
return PrimitiveDataType<float>::isMinDefined();
}
virtual bool isMaxDefined() override
{
return PrimitiveDataType<float>::isMaxDefined();
}
protected:
virtual const char* getInputType() override { return "number"; }
};
/**
* Options parameter is a structure, that handles multiple values when redering
* the HTML representation.
*/
template <size_t len>
class OptionsTParameter : public TextTParameter<len>
{
public:
/**
* @optionValues - List of values to choose from with, where each value
* can have a maximal size of 'length'. Contains 'optionCount' items.
* @optionNames - List of names to render for the values, where each
* name can have a maximal size of 'nameLength'. Contains 'optionCount'
* items.
* @optionCount - Size of both 'optionValues' and 'optionNames' lists.
* @nameLength - Size of any item in optionNames list.
* (See TextParameter for arguments!)
*/
OptionsTParameter(
const char* id, const char* label, const char* defaultValue,
const char* optionValues, const char* optionNames,
size_t optionCount, size_t nameLength) :
ConfigItemBridge(id),
TextTParameter<len>(id, label, defaultValue)
{
this->_optionValues = optionValues;
this->_optionNames = optionNames;
this->_optionCount = optionCount;
this->_nameLength = nameLength;
}
// TODO: make these protected
void setOptionValues(const char* optionValues) { this->_optionValues = optionValues; }
void setOptionNames(const char* optionNames) { this->_optionNames = optionNames; }
void setOptionCount(size_t optionCount) { this->_optionCount = optionCount; }
void setNameLength(size_t nameLength) { this->_nameLength = nameLength; }
protected:
OptionsTParameter(
const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
TextTParameter<len>(id, label, defaultValue)
{
}
const char* _optionValues;
const char* _optionNames;
size_t _optionCount;
size_t _nameLength;
};
///////////////////////////////////////////////////////////////////////////////
/**
* Select parameter is an option parameter, that rendered as HTML SELECT.
* Basically it is a dropdown combobox.
*/
template <size_t len>
class SelectTParameter : public OptionsTParameter<len>
{
public:
/**
* Create a select parameter for the config portal.
*
* (See OptionsParameter for arguments!)
*/
SelectTParameter(
const char* id, const char* label, const char* defaultValue,
const char* optionValues, const char* optionNames,
size_t optionCount, size_t nameLength) :
ConfigItemBridge(id),
OptionsTParameter<len>(
id, label, defaultValue, optionValues, optionNames, optionCount, nameLength)
{ }
// TODO: make this protected
SelectTParameter(
const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
OptionsTParameter<len>(id, label, defaultValue) { }
protected:
// Overrides
virtual String renderHtml(
bool dataArrived, bool hasValueFromPost, String valueFromPost) override
{
String options = "";
for (size_t i=0; i<this->_optionCount; i++)
{
const char *optionValue = (this->_optionValues + (i*len) );
const char *optionName = (this->_optionNames + (i*this->_nameLength) );
String oitem = FPSTR(IOTWEBCONF_HTML_FORM_OPTION);
oitem.replace("{v}", optionValue);
// if (sizeof(this->_optionNames) > i)
{
oitem.replace("{n}", optionName);
}
// else
// {
// oitem.replace("{n}", "?");
// }
if ((hasValueFromPost && (valueFromPost == optionValue)) ||
(strncmp(this->value(), optionValue, len) == 0))
{
// -- Value from previous submit
oitem.replace("{s}", " selected");
}
else
{
// -- Value from config
oitem.replace("{s}", "");
}
options += oitem;
}
String pitem = FPSTR(IOTWEBCONF_HTML_FORM_SELECT_PARAM);
pitem.replace("{b}", this->label);
pitem.replace("{i}", this->getId());
pitem.replace(
"{c}", this->customHtml == nullptr ? "" : this->customHtml);
pitem.replace(
"{s}",
this->errorMessage == nullptr ? "" : "de"); // Div style class.
pitem.replace(
"{e}",
this->errorMessage == nullptr ? "" : this->errorMessage);
pitem.replace("{o}", options);
return pitem;
}
private:
};
///////////////////////////////////////////////////////////////////////////////
/**
* Color chooser.
*/
class ColorTParameter : public CharArrayDataType<8>, public InputParameter
{
public:
using CharArrayDataType<8>::CharArrayDataType;
ColorTParameter(const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
CharArrayDataType<8>::CharArrayDataType(id, defaultValue),
InputParameter::InputParameter(id, label) { }
protected:
virtual const char* getInputType() override { return "color"; }
};
///////////////////////////////////////////////////////////////////////////////
/**
* Date chooser.
*/
class DateTParameter : public CharArrayDataType<11>, public InputParameter
{
public:
using CharArrayDataType<11>::CharArrayDataType;
DateTParameter(const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
CharArrayDataType<11>::CharArrayDataType(id, defaultValue),
InputParameter::InputParameter(id, label) { }
protected:
virtual const char* getInputType() override { return "date"; }
};
///////////////////////////////////////////////////////////////////////////////
/**
* Time chooser.
*/
class TimeTParameter : public CharArrayDataType<6>, public InputParameter
{
public:
using CharArrayDataType<6>::CharArrayDataType;
TimeTParameter(const char* id, const char* label, const char* defaultValue) :
ConfigItemBridge(id),
CharArrayDataType<6>::CharArrayDataType(id, defaultValue),
InputParameter::InputParameter(id, label) { }
protected:
virtual const char* getInputType() override { return "time"; }
};
} // end namespace
#include "IotWebConfTParameterBuilder.h"
#endif
/**
* IotWebConfTParameter.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2021 Balazs Kelemen <prampec+arduino@gmail.com>
* rovo89
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfTParameterBuilder_h
#define IotWebConfTParameterBuilder_h
#include "IotWebConfTParameter.h"
namespace iotwebconf
{
template <typename ParamType> class Builder;
template <typename ParamType>
class AbstractBuilder
{
public:
AbstractBuilder(const char* id) : _id(id) { };
virtual ParamType build() const
{
ParamType instance = std::move(
ParamType(this->_id, this->_label, this->_defaultValue));
this->apply(&instance);
return instance;
}
Builder<ParamType>& label(const char* label)
{ this->_label = label; return static_cast<Builder<ParamType>&>(*this); }
Builder<ParamType>& defaultValue(typename ParamType::DefaultValueType defaultValue)
{ this->_defaultValue = defaultValue; return static_cast<Builder<ParamType>&>(*this); }
protected:
virtual ParamType* apply(ParamType* instance) const
{
return instance;
}
const char* _label;
const char* _id;
typename ParamType::DefaultValueType _defaultValue;
};
template <typename ParamType>
class Builder : public AbstractBuilder<ParamType>
{
public:
Builder(const char* id) : AbstractBuilder<ParamType>(id) { };
};
///////////////////////////////////////////////////////////////////////////
template <typename ValueType, typename ParamType>
class PrimitiveBuilder :
public AbstractBuilder<ParamType>
{
public:
PrimitiveBuilder<ValueType, ParamType>(const char* id) :
AbstractBuilder<ParamType>(id) { };
Builder<ParamType>& min(ValueType min) { this->_minDefined = true; this->_min = min; return static_cast<Builder<ParamType>&>(*this); }
Builder<ParamType>& max(ValueType max) { this->_maxDefined = true; this->_max = max; return static_cast<Builder<ParamType>&>(*this); }
Builder<ParamType>& step(ValueType step) { this->_step = step; return static_cast<Builder<ParamType>&>(*this); }
Builder<ParamType>& placeholder(const char* placeholder) { this->_placeholder = placeholder; return static_cast<Builder<ParamType>&>(*this); }
protected:
virtual ParamType* apply(
ParamType* instance) const override
{
if (this->_minDefined)
{
instance->setMin(this->_min);
}
if (this->_maxDefined)
{
instance->setMax(this->_max);
}
instance->setStep(this->_step);
instance->setPlaceholder(this->_placeholder);
return instance;
}
bool _minDefined = false;
bool _maxDefined = false;
ValueType _min;
ValueType _max;
ValueType _step = 0;
const char* _placeholder = nullptr;
};
template <typename ValueType, int base>
class Builder<IntTParameter<ValueType, base>> :
public PrimitiveBuilder<ValueType, IntTParameter<ValueType, base>>
{
public:
Builder<IntTParameter<ValueType, base>>(const char* id) :
PrimitiveBuilder<ValueType, IntTParameter<ValueType, base>>(id) { };
};
template <typename ValueType, int base>
class Builder<UIntTParameter<ValueType, base>> :
public PrimitiveBuilder<ValueType, UIntTParameter<ValueType, base>>
{
public:
Builder<UIntTParameter<ValueType, base>>(const char* id) :
PrimitiveBuilder<ValueType, UIntTParameter<ValueType, base>>(id) { };
};
template <>
class Builder<FloatTParameter> :
public PrimitiveBuilder<float, FloatTParameter>
{
public:
Builder<FloatTParameter>(const char* id) :
PrimitiveBuilder<float, FloatTParameter>(id) { };
};
template <size_t len>
class Builder<SelectTParameter<len>> :
public AbstractBuilder<SelectTParameter<len>>
{
public:
Builder<SelectTParameter<len>>(const char* id) :
AbstractBuilder<SelectTParameter<len>>(id) { };
virtual SelectTParameter<len> build() const override
{
return SelectTParameter<len>(
this->_id, this->_label, this->_defaultValue,
this->_optionValues, this->_optionNames,
this->_optionCount, this->_nameLength);
}
Builder<SelectTParameter<len>>& optionValues(const char* optionValues)
{ this->_optionValues = optionValues; return *this; }
Builder<SelectTParameter<len>>& optionNames(const char* optionNames)
{ this->_optionNames = optionNames; return *this; }
Builder<SelectTParameter<len>>& optionCount(size_t optionCount)
{ this->_optionCount = optionCount; return *this; }
Builder<SelectTParameter<len>>& nameLength(size_t nameLength)
{ this->_nameLength = nameLength; return *this; }
protected:
virtual SelectTParameter<len>* apply(
SelectTParameter<len>* instance) const override
{
instance->setOptionValues(this->_optionValues);
instance->setOptionNames(this->_optionNames);
instance->setOptionCount(this->_optionCount);
instance->setNameLength(this->_nameLength);
return instance;
}
private:
const char* _optionValues;
const char* _optionNames;
size_t _optionCount;
size_t _nameLength;
};
} // End namespace
#endif
/**
* IotWebConfUsing.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef IotWebConfUsing_h
#define IotWebConfUsing_h
// This "using" lines are just aliases, and should avoided.
using IotWebConfParameterGroup = iotwebconf::ParameterGroup;
using IotWebConfTextParameter = iotwebconf::TextParameter;
using IotWebConfPasswordParameter = iotwebconf::PasswordParameter;
using IotWebConfNumberParameter = iotwebconf::NumberParameter;
using IotWebConfCheckboxParameter = iotwebconf::CheckboxParameter;
using IotWebConfSelectParameter = iotwebconf::SelectParameter;
#endif
\ No newline at end of file
/**
* IotWebConfWebServerWrapper.h -- IotWebConf is an ESP8266/ESP32
* non blocking WiFi/AP web configuration library for Arduino.
* https://github.com/prampec/IotWebConf
*
* Copyright (C) 2020 Balazs Kelemen <prampec+arduino@gmail.com>
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#ifndef WebServerWrapper_h
#define WebServerWrapper_h
#include <Arduino.h>
#include <IPAddress.h>
namespace iotwebconf
{
class WebRequestWrapper
{
public:
virtual const String hostHeader() const;
virtual IPAddress localIP();
virtual uint16_t localPort();
virtual const String uri() const;
virtual bool authenticate(const char * username, const char * password);
virtual void requestAuthentication();
virtual bool hasArg(const String& name);
virtual String arg(const String name);
virtual void sendHeader(const String& name, const String& value, bool first = false);
virtual void setContentLength(const size_t contentLength);
virtual void send(int code, const char* content_type = nullptr, const String& content = String(""));
virtual void sendContent(const String& content);
virtual void stop();
};
class WebServerWrapper
{
public:
virtual void handleClient();
virtual void begin();
};
} // end namespace
#endif
\ No newline at end of file
#ifndef CustomAmpelIotWebConfSettings_h
#define CustomAmpelIotWebConfSettings_h
/***************************************/
/**** CUSTOM AMPEL CODE ****************/
// Disable DEBUG, in order to save some space, at least on ESP8266
#if defined(ESP8266)
#define IOTWEBCONF_DEBUG_DISABLED
#endif
// Change this value (between 0 & 3000) in order to force the config to be reloaded.
#define IOTWEBCONF_CONFIG_START 512
#endif
\ No newline at end of file
NTPClient 3.1.0 - 2016.05.31
* Added functions for changing the timeOffset and updateInterval later. Thanks @SirUli
NTPClient 3.0.0 - 2016.04.19
* Constructors now require UDP instance argument, to add support for non-ESP8266 boards
* Added optional begin API to override default local port
* Added end API to close UDP socket
* Changed return type of update and forceUpdate APIs to bool, and return success or failure
* Change return type of getDay, getHours, getMinutes, and getSeconds to int
Older
* Changes not recorded
/**
* The MIT License (MIT)
* Copyright (c) 2015 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "NTPClient.h"
NTPClient::NTPClient(UDP& udp) {
this->_udp = &udp;
}
NTPClient::NTPClient(UDP& udp, long timeOffset) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
}
NTPClient::NTPClient(UDP& udp, const char* poolServerName) {
this->_udp = &udp;
this->_poolServerName = poolServerName;
}
NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP) {
this->_udp = &udp;
this->_poolServerIP = poolServerIP;
this->_poolServerName = NULL;
}
NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
this->_poolServerName = poolServerName;
}
NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset){
this->_udp = &udp;
this->_timeOffset = timeOffset;
this->_poolServerIP = poolServerIP;
this->_poolServerName = NULL;
}
NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
this->_poolServerName = poolServerName;
this->_updateInterval = updateInterval;
}
NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval) {
this->_udp = &udp;
this->_timeOffset = timeOffset;
this->_poolServerIP = poolServerIP;
this->_poolServerName = NULL;
this->_updateInterval = updateInterval;
}
void NTPClient::begin() {
this->begin(NTP_DEFAULT_LOCAL_PORT);
}
void NTPClient::begin(unsigned int port) {
this->_port = port;
this->_udp->begin(this->_port);
this->_udpSetup = true;
}
bool NTPClient::forceUpdate() {
#ifdef DEBUG_NTPClient
Serial.println("Update from NTP Server");
#endif
// flush any existing packets
while(this->_udp->parsePacket() != 0)
this->_udp->flush();
this->sendNTPPacket();
// Wait till data is there or timeout...
byte timeout = 0;
int cb = 0;
do {
delay ( 10 );
cb = this->_udp->parsePacket();
if (timeout > 100) return false; // timeout after 1000 ms
timeout++;
} while (cb == 0);
this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time
this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE);
unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]);
unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
this->_currentEpoc = secsSince1900 - SEVENZYYEARS;
return true; // return true after successful update
}
bool NTPClient::update() {
if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval
|| this->_lastUpdate == 0) { // Update if there was no update yet.
if (!this->_udpSetup || this->_port != NTP_DEFAULT_LOCAL_PORT) this->begin(this->_port); // setup the UDP client if needed
return this->forceUpdate();
}
return false; // return false if update does not occur
}
bool NTPClient::isTimeSet() const {
return (this->_lastUpdate != 0); // returns true if the time has been set, else false
}
unsigned long NTPClient::getEpochTime() const {
return this->_timeOffset + // User offset
this->_currentEpoc + // Epoch returned by the NTP server
((millis() - this->_lastUpdate) / 1000); // Time since last update
}
int NTPClient::getDay() const {
return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday
}
int NTPClient::getHours() const {
return ((this->getEpochTime() % 86400L) / 3600);
}
int NTPClient::getMinutes() const {
return ((this->getEpochTime() % 3600) / 60);
}
int NTPClient::getSeconds() const {
return (this->getEpochTime() % 60);
}
void NTPClient::end() {
this->_udp->stop();
this->_udpSetup = false;
}
void NTPClient::setTimeOffset(int timeOffset) {
this->_timeOffset = timeOffset;
}
void NTPClient::setUpdateInterval(unsigned long updateInterval) {
this->_updateInterval = updateInterval;
}
void NTPClient::setPoolServerName(const char* poolServerName) {
this->_poolServerName = poolServerName;
}
void NTPClient::sendNTPPacket() {
// set all bytes in the buffer to 0
memset(this->_packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
this->_packetBuffer[0] = 0b11100011; // LI, Version, Mode
this->_packetBuffer[1] = 0; // Stratum, or type of clock
this->_packetBuffer[2] = 6; // Polling Interval
this->_packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
this->_packetBuffer[12] = 49;
this->_packetBuffer[13] = 0x4E;
this->_packetBuffer[14] = 49;
this->_packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
if (this->_poolServerName) {
this->_udp->beginPacket(this->_poolServerName, 123);
} else {
this->_udp->beginPacket(this->_poolServerIP, 123);
}
this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE);
this->_udp->endPacket();
}
void NTPClient::setRandomPort(unsigned int minValue, unsigned int maxValue) {
randomSeed(analogRead(0));
this->_port = random(minValue, maxValue);
}
/*** Custom code for ampel-firmware ***/
void NTPClient::getFormattedTime(char *formatted_time, unsigned long secs) {
unsigned long rawTime = secs ? secs : this->getEpochTime();
unsigned int hours = (rawTime % 86400L) / 3600;
unsigned int minutes = (rawTime % 3600) / 60;
unsigned int seconds = rawTime % 60;
snprintf(formatted_time, 9, "%02d:%02d:%02d", hours, minutes, seconds);
}
#define LEAP_YEAR(Y) ( (Y>0) && !(Y%4) && ( (Y%100) || !(Y%400) ) )
// Based on https://github.com/PaulStoffregen/Time/blob/master/Time.cpp
void NTPClient::getFormattedDate(char *formatted_date, unsigned long secs) {
unsigned long rawTime = (secs ? secs : this->getEpochTime()) / 86400L; // in days
unsigned long days = 0;
unsigned int year = 1970;
uint8_t month;
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31};
while((days += (LEAP_YEAR(year) ? 366 : 365)) <= rawTime)
year++;
rawTime -= days - (LEAP_YEAR(year) ? 366 : 365); // now it is days in this year, starting at 0
days=0;
for (month=0; month<12; month++) {
uint8_t monthLength;
if (month==1) { // february
monthLength = LEAP_YEAR(year) ? 29 : 28;
} else {
monthLength = monthDays[month];
}
if (rawTime < monthLength) break;
rawTime -= monthLength;
}
month++; // jan is month 1
rawTime++; // first day is day 1
char formatted_time[9];
this->getFormattedTime(formatted_time, secs);
snprintf(formatted_date, 23, "%4d-%02d-%02lu %s%+03ld", year, month, rawTime, formatted_time, (this->_timeOffset / 3600) % 100);
}
void NTPClient::setEpochTime(unsigned long secs) {
this->_currentEpoc = secs;
}
/**************************************************************/
\ No newline at end of file
#pragma once
#include "Arduino.h"
#include <Udp.h>
#define SEVENZYYEARS 2208988800UL
#define NTP_PACKET_SIZE 48
#define NTP_DEFAULT_LOCAL_PORT 1337
class NTPClient {
private:
UDP* _udp;
bool _udpSetup = false;
const char* _poolServerName = "pool.ntp.org"; // Default time server
IPAddress _poolServerIP;
unsigned int _port = NTP_DEFAULT_LOCAL_PORT;
long _timeOffset = 0;
unsigned long _updateInterval = 60000; // In ms
unsigned long _currentEpoc = 0; // In ms
unsigned long _lastUpdate = 0; // In ms
byte _packetBuffer[NTP_PACKET_SIZE];
void sendNTPPacket();
public:
NTPClient(UDP& udp);
NTPClient(UDP& udp, long timeOffset);
NTPClient(UDP& udp, const char* poolServerName);
NTPClient(UDP& udp, const char* poolServerName, long timeOffset);
NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval);
NTPClient(UDP& udp, IPAddress poolServerIP);
NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset);
NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval);
/**
* Set time server name
*
* @param poolServerName
*/
void setPoolServerName(const char* poolServerName);
/**
* Set random local port
*/
void setRandomPort(unsigned int minValue = 49152, unsigned int maxValue = 65535);
/**
* Starts the underlying UDP client with the default local port
*/
void begin();
/**
* Starts the underlying UDP client with the specified local port
*/
void begin(unsigned int port);
/**
* This should be called in the main loop of your application. By default an update from the NTP Server is only
* made every 60 seconds. This can be configured in the NTPClient constructor.
*
* @return true on success, false on failure
*/
bool update();
/**
* This will force the update from the NTP Server.
*
* @return true on success, false on failure
*/
bool forceUpdate();
/**
* This allows to check if the NTPClient successfully received a NTP packet and set the time.
*
* @return true if time has been set, else false
*/
bool isTimeSet() const;
int getDay() const;
int getHours() const;
int getMinutes() const;
int getSeconds() const;
/**
* Changes the time offset. Useful for changing timezones dynamically
*/
void setTimeOffset(int timeOffset);
/**
* Set the update interval to another frequency. E.g. useful when the
* timeOffset should not be set in the constructor
*/
void setUpdateInterval(unsigned long updateInterval);
/**
* @return time in seconds since Jan. 1, 1970
*/
unsigned long getEpochTime() const;
/**
* Stops the underlying UDP client
*/
void end();
/*** Custom code for ampel-firmware ***/
/**
* @return secs argument (or 0 for current time) formatted like `hh:mm:ss`
*/
void getFormattedTime(char *formatted_time, unsigned long secs = 0);
/**
* @return secs argument (or 0 for current date) formatted to ISO 8601
* like `2004-02-12T15:19:21+00:00`
*/
void getFormattedDate(char *formatted_date, unsigned long secs = 0);
/**
* Replace the NTP-fetched time with seconds since Jan. 1, 1970
*/
void setEpochTime(unsigned long secs);
/**************************************************************/
};
# NTPClient
[![Check Arduino status](https://github.com/arduino-libraries/NTPClient/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/check-arduino.yml)
[![Compile Examples status](https://github.com/arduino-libraries/NTPClient/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/compile-examples.yml)
[![Spell Check status](https://github.com/arduino-libraries/NTPClient/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/spell-check.yml)
Connect to a NTP server, here is how:
```cpp
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>
const char *ssid = "<SSID>";
const char *password = "<PASSWORD>";
WiFiUDP ntpUDP;
// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP);
// You can specify the time server pool and the offset, (in seconds)
// additionally you can specify the update interval (in milliseconds).
// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
timeClient.begin();
}
void loop() {
timeClient.update();
Serial.println(timeClient.getFormattedTime());
delay(1000);
}
```
## Function documentation
`getEpochTime` returns the Unix epoch, which are the seconds elapsed since 00:00:00 UTC on 1 January 1970 (leap seconds are ignored, every day is treated as having 86400 seconds). **Attention**: If you have set a time offset this time offset will be added to your epoch timestamp.
#######################################
# Datatypes (KEYWORD1)
#######################################
NTPClient KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
update KEYWORD2
forceUpdate KEYWORD2
isTimeSet KEYWORD2
getDay KEYWORD2
getHours KEYWORD2
getMinutes KEYWORD2
getSeconds KEYWORD2
getFormattedTime KEYWORD2
getEpochTime KEYWORD2
setTimeOffset KEYWORD2
setUpdateInterval KEYWORD2
setPoolServerName KEYWORD2
{
"name": "NTPClient",
"keywords": "ntp, client, time",
"description": "A NTPClient to connect to a time server",
"authors":
[
{
"name": "Fabrice Weinberg",
"email": "fabrice@weinberg.me"
},
{
"name": "Sandeep Mistry",
"email": "s.mistry@arduino.cc"
}
],
"repository":
{
"type": "git",
"url": "https://github.com/arduino-libraries/NTPClient.git"
},
"version": "3.1.0",
"frameworks": "arduino",
"platforms": "espressif"
}
name=NTPClient
version=3.2.0
author=Fabrice Weinberg
maintainer=Fabrice Weinberg <fabrice@weinberg.me>
sentence=An NTPClient to connect to a time server
paragraph=Get time from a NTP server and keep it in sync.
category=Timing
url=https://github.com/arduino-libraries/NTPClient
architectures=*
2.8
* Add setBufferSize() to override MQTT_MAX_PACKET_SIZE
* Add setKeepAlive() to override MQTT_KEEPALIVE
* Add setSocketTimeout() to overide MQTT_SOCKET_TIMEOUT
* Added check to prevent subscribe/unsubscribe to empty topics
* Declare wifi mode prior to connect in ESP example
* Use `strnlen` to avoid overruns
* Support pre-connected Client objects
2.7
* Fix remaining-length handling to prevent buffer overrun
* Add large-payload API - beginPublish/write/publish/endPublish
* Add yield call to improve reliability on ESP
* Add Clean Session flag to connect options
* Add ESP32 support for functional callback signature
* Various other fixes
2.4
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
whilst waiting for inbound data
* Fixed return code when publishing >256 bytes
2.3
* Add publish(topic,payload,retained) function
2.2
* Change code layout to match Arduino Library reqs
2.1
* Add MAX_TRANSFER_SIZE def to chunk messages if needed
* Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
2.0
* Add (and default to) MQTT 3.1.1 support
* Fix PROGMEM handling for Intel Galileo/ESP8266
* Add overloaded constructors for convenience
* Add chainable setters for server/callback/client/stream
* Add state function to return connack return code
1.9
* Do not split MQTT packets over multiple calls to _client->write()
* API change: All constructors now require an instance of Client
to be passed in.
* Fixed example to match 1.8 api changes - dpslwk
* Added username/password support - WilHall
* Added publish_P - publishes messages from PROGMEM - jobytaffey
1.8
* KeepAlive interval is configurable in PubSubClient.h
* Maximum packet size is configurable in PubSubClient.h
* API change: Return boolean rather than int from various functions
* API change: Length parameter in message callback changed
from int to unsigned int
* Various internal tidy-ups around types
1.7
* Improved keepalive handling
* Updated to the Arduino-1.0 API
1.6
* Added the ability to publish a retained message
1.5
* Added default constructor
* Fixed compile error when used with arduino-0021 or later
1.4
* Fixed connection lost handling
1.3
* Fixed packet reading bug in PubSubClient.readPacket
1.2
* Fixed compile error when used with arduino-0016 or later
1.1
* Reduced size of library
* Added support for Will messages
* Clarified licensing - see LICENSE.txt
1.0
* Only Quality of Service (QOS) 0 messaging is supported
* The maximum message size, including header, is 128 bytes
* The keepalive interval is set to 30 seconds
* No support for Will messages
Copyright (c) 2008-2020 Nicholas O'Leary
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment