Merge pull request #2043 from blazoncek/v2-usermod-settings
Usermod settings v2
This commit is contained in:
commit
9b2a0102be
@ -426,6 +426,8 @@ class Animated_Staircase : public Usermod {
|
||||
|
||||
/*
|
||||
* Reads the configuration to internal flash memory before setup() is called.
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
bool oldUseUSSensorTop = useUSSensorTop;
|
||||
@ -435,49 +437,41 @@ class Animated_Staircase : public Usermod {
|
||||
int8_t oldBottomAPin = bottomPIRorTriggerPin;
|
||||
int8_t oldBottomBPin = bottomEchoPin;
|
||||
|
||||
bool configComplete = true;
|
||||
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (!staircase.isNull()) {
|
||||
if (staircase[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = staircase[FPSTR(_enabled)].as<bool>();
|
||||
} else {
|
||||
String str = staircase[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
enabled = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
segment_delay_ms = min(10000,max(10,staircase[FPSTR(_segmentDelay)].as<int>())); // max delay 10s
|
||||
on_time_ms = min(900,max(10,staircase[FPSTR(_onTime)].as<int>())) * 1000; // min 10s, max 15min
|
||||
|
||||
if (staircase[FPSTR(_useTopUltrasoundSensor)].is<bool>()) {
|
||||
useUSSensorTop = staircase[FPSTR(_useTopUltrasoundSensor)].as<bool>();
|
||||
} else {
|
||||
String str = staircase[FPSTR(_useTopUltrasoundSensor)]; // checkbox -> off or on
|
||||
useUSSensorTop = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
|
||||
topPIRorTriggerPin = min(39,max(-1,staircase[FPSTR(_topPIRorTrigger_pin)].as<int>()));
|
||||
topEchoPin = min(39,max(-1,staircase[FPSTR(_topEcho_pin)].as<int>()));
|
||||
|
||||
if (staircase[FPSTR(_useBottomUltrasoundSensor)].is<bool>()) {
|
||||
useUSSensorBottom = staircase[FPSTR(_useBottomUltrasoundSensor)].as<bool>();
|
||||
} else {
|
||||
String str = staircase[FPSTR(_useBottomUltrasoundSensor)]; // checkbox -> off or on
|
||||
useUSSensorBottom = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
bottomPIRorTriggerPin = min(39,max(-1,staircase[FPSTR(_bottomPIRorTrigger_pin)].as<int>()));
|
||||
bottomEchoPin = min(39,max(-1,staircase[FPSTR(_bottomEcho_pin)].as<int>()));
|
||||
topMaxDist = min(150,max(30,staircase[FPSTR(_topEchoCm)].as<int>())); // max distnace ~1.5m (a lag of 9ms may be expected)
|
||||
bottomMaxDist = min(150,max(30,staircase[FPSTR(_bottomEchoCm)].as<int>())); // max distance ~1.5m (a lag of 9ms may be expected)
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
|
||||
configComplete = false;
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
segment_delay_ms = top[FPSTR(_segmentDelay)] | segment_delay_ms;
|
||||
segment_delay_ms = (unsigned long) min((unsigned long)10000,max((unsigned long)10,(unsigned long)segment_delay_ms)); // max delay 10s
|
||||
|
||||
on_time_ms = top[FPSTR(_onTime)] | on_time_ms/1000;
|
||||
on_time_ms = min(900,max(10,(int)on_time_ms)) * 1000; // min 10s, max 15min
|
||||
|
||||
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
|
||||
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
|
||||
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
|
||||
|
||||
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
|
||||
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
|
||||
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
|
||||
|
||||
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
|
||||
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
|
||||
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
|
||||
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
DEBUG_PRINTLN(F("Staircase config loaded."));
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing paramters from settings page
|
||||
DEBUG_PRINTLN(F("Staircase config (re)loaded."));
|
||||
// changing parameters from settings page
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
bool changed = false;
|
||||
if ((oldUseUSSensorTop != useUSSensorTop) ||
|
||||
(oldUseUSSensorBottom != useUSSensorBottom) ||
|
||||
@ -493,7 +487,8 @@ class Animated_Staircase : public Usermod {
|
||||
}
|
||||
if (changed) setup();
|
||||
}
|
||||
return configComplete;
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -23,8 +23,12 @@
|
||||
//class name. Use something descriptive and leave the ": public Usermod" part :)
|
||||
class MyExampleUsermod : public Usermod {
|
||||
private:
|
||||
// sample usermod default value for variable (you can also use constructor)
|
||||
int userVar0 = 42;
|
||||
|
||||
//Private class members. You can declare variables and functions only accessible to your usermod here
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
@ -140,21 +144,14 @@ class MyExampleUsermod : public Usermod {
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
userVar0 = 42; //set your variables to their boot default value (this can also be done when declaring the variable)
|
||||
|
||||
//set defaults for variables when declaring the variable (class definition or constructor)
|
||||
JsonObject top = root["exampleUsermod"];
|
||||
if (!top.isNull()) {
|
||||
bool configComplete = true;
|
||||
if (!top.isNull()) return false;
|
||||
|
||||
//check if value is there
|
||||
if (top.containsKey("great")) {
|
||||
//convert value to the correct type
|
||||
userVar0 = top["great"].as<int>();
|
||||
} else configComplete = false;
|
||||
userVar0 = top["great"] | userVar0;
|
||||
|
||||
if (configComplete) return true;
|
||||
}
|
||||
return false;
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -155,6 +155,7 @@ Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
|
||||
if (top.isNull()) return false;
|
||||
m_pingDelayMs = top["PingDelayMs"] | m_pingDelayMs;
|
||||
m_pingDelayMs = max(5000UL, min(18000000UL, m_pingDelayMs));
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -222,9 +222,8 @@ public:
|
||||
void loop()
|
||||
{
|
||||
// only check sensors 10x/s
|
||||
unsigned long now = millis();
|
||||
if (now - lastLoop < 100) return;
|
||||
lastLoop = now;
|
||||
if (millis() - lastLoop < 100 || strip.isUpdating()) return;
|
||||
lastLoop = millis();
|
||||
|
||||
if (!updatePIRsensorState()) {
|
||||
handleOffTimer();
|
||||
@ -320,6 +319,8 @@ public:
|
||||
/**
|
||||
* restore the changeable values
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
@ -327,57 +328,31 @@ public:
|
||||
int8_t oldPin = PIRsensorPin;
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) return false;
|
||||
|
||||
if (top["pin"] != nullptr) {
|
||||
PIRsensorPin = min(39,max(-1,top["pin"].as<int>())); // check bounds
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (top[FPSTR(_enabled)] != nullptr) {
|
||||
if (top[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = top[FPSTR(_enabled)].as<bool>(); // reading from cfg.json
|
||||
} else {
|
||||
// change from settings page
|
||||
String str = top[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
enabled = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
}
|
||||
PIRsensorPin = top["pin"] | PIRsensorPin;
|
||||
|
||||
if (top[FPSTR(_switchOffDelay)] != nullptr) {
|
||||
m_switchOffDelay = (top[FPSTR(_switchOffDelay)].as<int>() * 1000);
|
||||
}
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
if (top[FPSTR(_onPreset)] != nullptr) {
|
||||
m_onPreset = max(0,min(250,top[FPSTR(_onPreset)].as<int>()));
|
||||
}
|
||||
m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
|
||||
|
||||
if (top[FPSTR(_offPreset)] != nullptr) {
|
||||
m_offPreset = max(0,min(250,top[FPSTR(_offPreset)].as<int>()));
|
||||
}
|
||||
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
|
||||
m_onPreset = max(0,min(250,(int)m_onPreset));
|
||||
|
||||
if (top[FPSTR(_nightTime)] != nullptr) {
|
||||
if (top[FPSTR(_nightTime)].is<bool>()) {
|
||||
m_nightTimeOnly = top[FPSTR(_nightTime)].as<bool>(); // reading from cfg.json
|
||||
} else {
|
||||
// change from settings page
|
||||
String str = top[FPSTR(_nightTime)]; // checkbox -> off or on
|
||||
m_nightTimeOnly = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
}
|
||||
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
|
||||
m_offPreset = max(0,min(250,(int)m_offPreset));
|
||||
|
||||
if (top[FPSTR(_mqttOnly)] != nullptr) {
|
||||
if (top[FPSTR(_mqttOnly)].is<bool>()) {
|
||||
m_mqttOnly = top[FPSTR(_mqttOnly)].as<bool>(); // reading from cfg.json
|
||||
} else {
|
||||
// change from settings page
|
||||
String str = top[FPSTR(_mqttOnly)]; // checkbox -> off or on
|
||||
m_mqttOnly = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
}
|
||||
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
|
||||
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// reading config prior to setup()
|
||||
DEBUG_PRINTLN(F("PIR config loaded."));
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
if (oldPin != PIRsensorPin || oldEnabled != enabled) {
|
||||
// check if pin is OK
|
||||
@ -396,10 +371,10 @@ public:
|
||||
if (enabled) {
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
}
|
||||
DEBUG_PRINTLN(F("PIR config (re)loaded."));
|
||||
}
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -167,37 +167,28 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*/
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
|
||||
if (!top.isNull())
|
||||
{
|
||||
if (top[FPSTR(_enabled)].is<bool>())
|
||||
{
|
||||
disabled = !top[FPSTR(_enabled)].as<bool>();
|
||||
}
|
||||
else
|
||||
{
|
||||
String str = top[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
disabled = (bool)(str == "off"); // off is guaranteed to be present
|
||||
};
|
||||
|
||||
readingInterval = min(120, max(10, top[FPSTR(_readInterval)].as<int>())) * 1000; // convert to ms
|
||||
referenceVoltage = top[FPSTR(_referenceVoltage)].as<float>();
|
||||
resistorValue = top[FPSTR(_resistorValue)].as<float>();
|
||||
adcPrecision = top[FPSTR(_adcPrecision)].as<float>();
|
||||
offset = top[FPSTR(_offset)].as<int>();
|
||||
DEBUG_PRINTLN(F("Photoresistor config (re)loaded."));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
disabled = !(top[FPSTR(_enabled)] | !disabled);
|
||||
readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms
|
||||
referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage;
|
||||
resistorValue = top[FPSTR(_resistorValue)] | resistorValue;
|
||||
adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision;
|
||||
offset = top[FPSTR(_offset)] | offset;
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -17,11 +17,6 @@
|
||||
#define USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL 60000
|
||||
#endif
|
||||
|
||||
// how many seconds after boot to take first measurement, 20 seconds
|
||||
#ifndef USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT
|
||||
#define USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT 20000
|
||||
#endif
|
||||
|
||||
class UsermodTemperature : public Usermod {
|
||||
|
||||
private:
|
||||
@ -32,9 +27,12 @@ class UsermodTemperature : public Usermod {
|
||||
int8_t temperaturePin = TEMPERATURE_PIN;
|
||||
// measurement unit (true==°C, false==°F)
|
||||
bool degC = true;
|
||||
// using parasite power on the sensor
|
||||
bool parasite = false;
|
||||
// how often do we read from sensor?
|
||||
unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
|
||||
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
|
||||
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT);
|
||||
unsigned long lastMeasurement = UINT32_MAX - USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
|
||||
// last time requestTemperatures was called
|
||||
// used to determine when we can read the sensors temperature
|
||||
// we have to wait at least 93.75 ms after requestTemperatures() is called
|
||||
@ -42,37 +40,32 @@ class UsermodTemperature : public Usermod {
|
||||
float temperature = -100; // default to -100, DS18B20 only goes down to -50C
|
||||
// indicates requestTemperatures has been called but the sensor measurement is not complete
|
||||
bool waitingForConversion = false;
|
||||
// flag to indicate we have finished the first readTemperature call
|
||||
// allows this library to report to the user how long until the first
|
||||
// measurement
|
||||
bool readTemperatureComplete = false;
|
||||
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
|
||||
// temperature if flashed to a board without a sensor attached
|
||||
bool disabled = false;
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _readInterval[];
|
||||
static const char _parasite[];
|
||||
|
||||
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
|
||||
int16_t readDallas() {
|
||||
float readDallas() {
|
||||
byte i;
|
||||
byte data[2];
|
||||
int16_t result; // raw data from sensor
|
||||
oneWire->reset();
|
||||
oneWire->write(0xCC); // skip ROM
|
||||
oneWire->write(0xBE); // read (temperature) from EEPROM
|
||||
int16_t result; // raw data from sensor
|
||||
if (!oneWire->reset()) return -127.0f; // send reset command and fail fast
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0xBE); // read (temperature) from EEPROM
|
||||
for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature
|
||||
for (i=2; i < 8; i++) oneWire->read(); // read unused bytes
|
||||
result = (data[1]<<8) | data[0];
|
||||
result >>= 4; // 9-bit precision accurate to 1°C (/16)
|
||||
if (data[1]&0x80) result |= 0xF000; // fix negative value
|
||||
//if (data[0]&0x08) ++result;
|
||||
result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
|
||||
if (data[1]&0x80) result |= 0xFF00; // fix negative value
|
||||
oneWire->reset();
|
||||
oneWire->write(0xCC); // skip ROM
|
||||
oneWire->write(0x44,0); // request new temperature reading (without parasite power)
|
||||
return result;
|
||||
oneWire->skip(); // skip ROM
|
||||
oneWire->write(0x44,parasite); // request new temperature reading (without parasite power)
|
||||
return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f);
|
||||
}
|
||||
|
||||
void requestTemperatures() {
|
||||
@ -86,7 +79,6 @@ class UsermodTemperature : public Usermod {
|
||||
temperature = readDallas();
|
||||
lastMeasurement = millis();
|
||||
waitingForConversion = false;
|
||||
readTemperatureComplete = true;
|
||||
DEBUG_PRINTF("Read temperature %2.1f.\n", temperature);
|
||||
}
|
||||
|
||||
@ -118,23 +110,23 @@ class UsermodTemperature : public Usermod {
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (!pinManager.allocatePin(temperaturePin,false)) {
|
||||
temperaturePin = -1; // allocation failed
|
||||
disabled = true;
|
||||
enabled = false;
|
||||
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
|
||||
} else {
|
||||
if (!disabled) {
|
||||
if (enabled) {
|
||||
// config says we are enabled
|
||||
oneWire = new OneWire(temperaturePin);
|
||||
if (!oneWire->reset())
|
||||
disabled = true; // resetting 1-Wire bus yielded an error
|
||||
enabled = false; // resetting 1-Wire bus yielded an error
|
||||
else
|
||||
while ((disabled=!findSensor()) && retries--) delay(25); // try to find sensor
|
||||
while ((enabled=findSensor()) && retries--) delay(25); // try to find sensor
|
||||
}
|
||||
}
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (disabled || strip.isUpdating()) return;
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
@ -189,7 +181,7 @@ class UsermodTemperature : public Usermod {
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
// dont add temperature to info if we are disabled
|
||||
if (disabled) return;
|
||||
if (!enabled) return;
|
||||
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
@ -197,14 +189,6 @@ class UsermodTemperature : public Usermod {
|
||||
JsonArray temp = user.createNestedArray(FPSTR(_name));
|
||||
//temp.add(F("Loaded."));
|
||||
|
||||
if (!readTemperatureComplete) {
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
temp.add((USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
temp.add(F(" sec until read"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (temperature <= -100) {
|
||||
temp.add(0);
|
||||
temp.add(F(" Sensor Error!"));
|
||||
@ -239,49 +223,42 @@ class UsermodTemperature : public Usermod {
|
||||
void addToConfig(JsonObject &root) {
|
||||
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = !disabled;
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top["pin"] = temperaturePin; // usermodparam
|
||||
top["degC"] = degC; // usermodparam
|
||||
top[FPSTR(_readInterval)] = readingInterval / 1000;
|
||||
top[FPSTR(_parasite)] = parasite;
|
||||
DEBUG_PRINTLN(F("Temperature config saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
// we look for JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
int8_t newTemperaturePin = temperaturePin;
|
||||
|
||||
bool configComplete = true;
|
||||
|
||||
if (!top.isNull() && top["pin"] != nullptr) {
|
||||
if (top[FPSTR(_enabled)].is<bool>()) {
|
||||
disabled = !top[FPSTR(_enabled)].as<bool>();
|
||||
} else {
|
||||
String str = top[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
disabled = (bool)(str=="off"); // off is guaranteed to be present
|
||||
}
|
||||
newTemperaturePin = min(39,max(-1,top["pin"].as<int>()));
|
||||
if (top["degC"].is<bool>()) {
|
||||
// reading from cfg.json
|
||||
degC = top["degC"].as<bool>();
|
||||
} else {
|
||||
// new configuration from set.cpp
|
||||
String str = top["degC"]; // checkbox -> off or on
|
||||
degC = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
readingInterval = min(120,max(10,top[FPSTR(_readInterval)].as<int>())) * 1000; // convert to ms
|
||||
DEBUG_PRINTLN(F("Temperature config (re)loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
|
||||
configComplete = false;
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
newTemperaturePin = top["pin"] | newTemperaturePin;
|
||||
degC = top["degC"] | degC;
|
||||
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
|
||||
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
|
||||
parasite = top[FPSTR(_parasite)] | parasite;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
temperaturePin = newTemperaturePin;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing parameters from settings page
|
||||
if (newTemperaturePin != temperaturePin) {
|
||||
@ -292,8 +269,10 @@ class UsermodTemperature : public Usermod {
|
||||
// initialise
|
||||
setup();
|
||||
}
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
return configComplete;
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_parasite)].isNull();
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
@ -306,3 +285,4 @@ class UsermodTemperature : public Usermod {
|
||||
const char UsermodTemperature::_name[] PROGMEM = "Temperature";
|
||||
const char UsermodTemperature::_enabled[] PROGMEM = "enabled";
|
||||
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
|
||||
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";
|
||||
|
@ -35,7 +35,7 @@ class MultiRelay : public Usermod {
|
||||
// switch timer start time
|
||||
uint32_t _switchTimerStart = 0;
|
||||
// old brightness
|
||||
uint8_t _oldBrightness = 0;
|
||||
bool _oldBrightness = 0;
|
||||
|
||||
// usermod enabled
|
||||
bool enabled = false; // needs to be configured (no default config)
|
||||
@ -265,7 +265,7 @@ class MultiRelay : public Usermod {
|
||||
_relay[i].active = false;
|
||||
}
|
||||
}
|
||||
_oldBrightness = bri;
|
||||
_oldBrightness = (bool)bri;
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
@ -288,8 +288,8 @@ class MultiRelay : public Usermod {
|
||||
lastUpdate = millis();
|
||||
|
||||
//set relay when LEDs turn on
|
||||
if (_oldBrightness != bri) {
|
||||
_oldBrightness = bri;
|
||||
if (_oldBrightness != (bool)bri) {
|
||||
_oldBrightness = (bool)bri;
|
||||
_switchTimerStart = millis();
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin>=0) _relay[i].active = true;
|
||||
@ -347,55 +347,35 @@ class MultiRelay : public Usermod {
|
||||
/**
|
||||
* restore the changeable values
|
||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject &root) {
|
||||
int8_t oldPin[MULTI_RELAY_MAX_RELAYS];
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) return false;
|
||||
|
||||
if (top[FPSTR(_enabled)] != nullptr) {
|
||||
if (top[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = top[FPSTR(_enabled)].as<bool>(); // reading from cfg.json
|
||||
} else {
|
||||
// change from settings page
|
||||
String str = top[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
enabled = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-";
|
||||
|
||||
oldPin[i] = _relay[i].pin;
|
||||
if (top[parName+"pin"] != nullptr) _relay[i].pin = min(39,max(-1,top[parName+"pin"].as<int>()));
|
||||
|
||||
if (top[parName+FPSTR(_activeHigh)] != nullptr) {
|
||||
if (top[parName+FPSTR(_activeHigh)].is<bool>()) {
|
||||
_relay[i].mode = top[parName+FPSTR(_activeHigh)].as<bool>(); // reading from cfg.json
|
||||
} else {
|
||||
// change from settings page
|
||||
String str = top[parName+FPSTR(_activeHigh)]; // checkbox -> off or on
|
||||
_relay[i].mode = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
}
|
||||
|
||||
if (top[parName+FPSTR(_external)] != nullptr) {
|
||||
if (top[parName+FPSTR(_external)].is<bool>()) {
|
||||
_relay[i].external = top[parName+FPSTR(_external)].as<bool>(); // reading from cfg.json
|
||||
} else {
|
||||
// change from settings page
|
||||
String str = top[parName+FPSTR(_external)]; // checkbox -> off or on
|
||||
_relay[i].external = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
}
|
||||
|
||||
_relay[i].delay = min(600,max(0,abs(top[parName+FPSTR(_delay_str)].as<int>())));
|
||||
oldPin[i] = _relay[i].pin;
|
||||
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
|
||||
_relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode;
|
||||
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
|
||||
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
|
||||
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
|
||||
}
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// reading config prior to setup()
|
||||
DEBUG_PRINTLN(F("MultiRelay config loaded."));
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// deallocate all pins 1st
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++)
|
||||
@ -411,8 +391,9 @@ class MultiRelay : public Usermod {
|
||||
}
|
||||
_relay[i].active = false;
|
||||
}
|
||||
DEBUG_PRINTLN(F("MultiRelay config (re)loaded."));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -33,12 +33,12 @@ class AutoSaveUsermod : public Usermod {
|
||||
bool enabled = true;
|
||||
|
||||
// configurable parameters
|
||||
unsigned long autoSaveAfterSec = 15; // 15s by default
|
||||
uint16_t autoSaveAfterSec = 15; // 15s by default
|
||||
uint8_t autoSavePreset = 250; // last possible preset
|
||||
bool applyAutoSaveOnBoot = false; // do we load auto-saved preset on boot?
|
||||
|
||||
// If we've detected the need to auto save, this will be non zero.
|
||||
unsigned long autoSaveAfter = 0;
|
||||
uint16_t autoSaveAfter = 0;
|
||||
|
||||
uint8_t knownBrightness = 0;
|
||||
uint8_t knownEffectSpeed = 0;
|
||||
@ -97,7 +97,7 @@ class AutoSaveUsermod : public Usermod {
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (!autoSaveAfterSec || !enabled) return; // setting 0 as autosave seconds disables autosave
|
||||
if (!autoSaveAfterSec || !enabled || strip.isUpdating()) return; // setting 0 as autosave seconds disables autosave
|
||||
|
||||
unsigned long now = millis();
|
||||
uint8_t currentMode = strip.getMode();
|
||||
@ -197,34 +197,28 @@ class AutoSaveUsermod : public Usermod {
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
// we look for JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}}
|
||||
// we look for JSON object: {"Autosave": {"enabled": true, "autoSaveAfterSec": 10, "autoSavePreset": 250, ...}}
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (top[FPSTR(_autoSaveEnabled)].is<bool>()) {
|
||||
// reading from cfg.json
|
||||
enabled = top[FPSTR(_autoSaveEnabled)].as<bool>();
|
||||
} else {
|
||||
// reading from POST message
|
||||
String str = top[FPSTR(_autoSaveEnabled)]; // checkbox -> off or on
|
||||
enabled = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
autoSaveAfterSec = min(3600,max(10,top[FPSTR(_autoSaveAfterSec)].as<int>()));
|
||||
autoSavePreset = min(250,max(100,top[FPSTR(_autoSavePreset)].as<int>()));
|
||||
if (top[FPSTR(_autoSaveApplyOnBoot)].is<bool>()) {
|
||||
// reading from cfg.json
|
||||
applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)].as<bool>();
|
||||
} else {
|
||||
// reading from POST message
|
||||
String str = top[FPSTR(_autoSaveApplyOnBoot)]; // checkbox -> off or on
|
||||
applyAutoSaveOnBoot = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
DEBUG_PRINTLN(F("Autosave config (re)loaded."));
|
||||
enabled = top[FPSTR(_autoSaveEnabled)] | enabled;
|
||||
autoSaveAfterSec = top[FPSTR(_autoSaveAfterSec)] | autoSaveAfterSec;
|
||||
autoSaveAfterSec = (uint16_t) min(3600,max(10,(int)autoSaveAfterSec)); // bounds checking
|
||||
autoSavePreset = top[FPSTR(_autoSavePreset)] | autoSavePreset;
|
||||
autoSavePreset = (uint8_t) min(250,max(100,(int)autoSavePreset)); // bounds checking
|
||||
applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)] | applyAutoSaveOnBoot;
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -54,18 +54,21 @@
|
||||
#define LINE_BUFFER_SIZE 16+1
|
||||
|
||||
typedef enum {
|
||||
FLD_LINE_4_BRIGHTNESS = 0,
|
||||
FLD_LINE_4_EFFECT_SPEED,
|
||||
FLD_LINE_4_EFFECT_INTENSITY,
|
||||
FLD_LINE_4_MODE,
|
||||
FLD_LINE_4_PALETTE
|
||||
FLD_LINE_BRIGHTNESS = 0,
|
||||
FLD_LINE_EFFECT_SPEED,
|
||||
FLD_LINE_EFFECT_INTENSITY,
|
||||
FLD_LINE_MODE,
|
||||
FLD_LINE_PALETTE,
|
||||
FLD_LINE_TIME
|
||||
} Line4Type;
|
||||
|
||||
typedef enum {
|
||||
NONE = 0,
|
||||
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
|
||||
SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C
|
||||
SSD1306_64 // U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
|
||||
SSD1305_64 // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
|
||||
} DisplayType;
|
||||
|
||||
class FourLineDisplayUsermod : public Usermod {
|
||||
@ -87,9 +90,6 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
bool sleepMode = true; // allow screen sleep?
|
||||
bool clockMode = false; // display clock
|
||||
|
||||
// needRedraw marks if redraw is required to prevent often redrawing.
|
||||
bool needRedraw = true;
|
||||
|
||||
// Next variables hold the previous known values to determine if redraw is
|
||||
// required.
|
||||
String knownSsid = "";
|
||||
@ -106,7 +106,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
unsigned long lastUpdate = 0;
|
||||
unsigned long lastRedraw = 0;
|
||||
unsigned long overlayUntil = 0;
|
||||
Line4Type lineFourType = FLD_LINE_4_BRIGHTNESS;
|
||||
Line4Type lineType = FLD_LINE_BRIGHTNESS;
|
||||
// Set to 2 or 3 to mark lines 2 or 3. Other values ignored.
|
||||
byte markLineNum = 0;
|
||||
|
||||
@ -130,7 +130,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// gets called once at boot. Do all initialization that doesn't depend on
|
||||
// network here
|
||||
void setup() {
|
||||
if (type==NONE) return;
|
||||
if (type == NONE) return;
|
||||
if (!pinManager.allocatePin(sclPin)) { sclPin = -1; type = NONE; return;}
|
||||
if (!pinManager.allocatePin(sdaPin)) { pinManager.deallocatePin(sclPin); sclPin = sdaPin = -1; type = NONE; return; }
|
||||
switch (type) {
|
||||
@ -141,6 +141,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SH1106:
|
||||
#ifdef ESP8266
|
||||
@ -149,6 +150,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1306_64:
|
||||
#ifdef ESP8266
|
||||
@ -157,6 +159,25 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
case SSD1305:
|
||||
#ifdef ESP8266
|
||||
if (!(sclPin==5 && sdaPin==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 1;
|
||||
break;
|
||||
case SSD1305_64:
|
||||
#ifdef ESP8266
|
||||
if (!(sclPin==5 && sdaPin==4))
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset
|
||||
else
|
||||
#endif
|
||||
u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA
|
||||
lineHeight = 2;
|
||||
break;
|
||||
default:
|
||||
u8x8 = nullptr;
|
||||
@ -179,9 +200,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* Da loop.
|
||||
*/
|
||||
void loop() {
|
||||
if (millis() - lastUpdate < (clockMode?1000:refreshRate)) {
|
||||
return;
|
||||
}
|
||||
if (millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return;
|
||||
lastUpdate = millis();
|
||||
|
||||
redraw(false);
|
||||
@ -233,9 +252,12 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
* or if forceRedraw).
|
||||
*/
|
||||
void redraw(bool forceRedraw) {
|
||||
static bool showName = false;
|
||||
unsigned long now = millis();
|
||||
|
||||
if (type==NONE) return;
|
||||
if (overlayUntil > 0) {
|
||||
if (millis() >= overlayUntil) {
|
||||
if (now >= overlayUntil) {
|
||||
// Time to display the overlay has elapsed.
|
||||
overlayUntil = 0;
|
||||
forceRedraw = true;
|
||||
@ -247,74 +269,63 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
}
|
||||
|
||||
// Check if values which are shown on display changed from the last time.
|
||||
if (forceRedraw) {
|
||||
needRedraw = true;
|
||||
} else if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) {
|
||||
needRedraw = true;
|
||||
} else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) {
|
||||
needRedraw = true;
|
||||
} else if (knownBrightness != bri) {
|
||||
needRedraw = true;
|
||||
} else if (knownEffectSpeed != effectSpeed) {
|
||||
needRedraw = true;
|
||||
} else if (knownEffectIntensity != effectIntensity) {
|
||||
needRedraw = true;
|
||||
} else if (knownMode != strip.getMode()) {
|
||||
needRedraw = true;
|
||||
} else if (knownPalette != strip.getSegment(0).palette) {
|
||||
needRedraw = true;
|
||||
}
|
||||
|
||||
if (!needRedraw) {
|
||||
if (forceRedraw ||
|
||||
(((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
|
||||
(knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
|
||||
(knownBrightness != bri) ||
|
||||
(knownEffectSpeed != effectSpeed) ||
|
||||
(knownEffectIntensity != effectIntensity) ||
|
||||
(knownMode != strip.getMode()) ||
|
||||
(knownPalette != strip.getSegment(0).palette)) {
|
||||
knownHour = 99; // force time update
|
||||
clear();
|
||||
} else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) {
|
||||
// change line every 5s
|
||||
showName = !showName;
|
||||
switch (lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
lineType = FLD_LINE_EFFECT_SPEED;
|
||||
break;
|
||||
case FLD_LINE_MODE:
|
||||
lineType = FLD_LINE_BRIGHTNESS;
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
lineType = clockMode ? FLD_LINE_MODE : FLD_LINE_BRIGHTNESS;
|
||||
break;
|
||||
case FLD_LINE_EFFECT_SPEED:
|
||||
lineType = FLD_LINE_EFFECT_INTENSITY;
|
||||
break;
|
||||
case FLD_LINE_EFFECT_INTENSITY:
|
||||
lineType = FLD_LINE_PALETTE;
|
||||
break;
|
||||
default:
|
||||
lineType = FLD_LINE_MODE;
|
||||
break;
|
||||
}
|
||||
knownHour = 99; // force time update
|
||||
} else {
|
||||
// Nothing to change.
|
||||
// Turn off display after 3 minutes with no change.
|
||||
if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) {
|
||||
// We will still check if there is a change in redraw()
|
||||
// and turn it back on if it changed.
|
||||
knownHour = 99; // force screen clear
|
||||
clear(); // force screen clear
|
||||
sleepOrClock(true);
|
||||
} else if (displayTurnedOff && clockMode) {
|
||||
showTime();
|
||||
} else if ((millis() - lastRedraw)/1000%3 == 0) {
|
||||
// change 4th line every 3s
|
||||
switch (lineFourType) {
|
||||
case FLD_LINE_4_BRIGHTNESS:
|
||||
setLineFourType(FLD_LINE_4_EFFECT_SPEED);
|
||||
break;
|
||||
case FLD_LINE_4_MODE:
|
||||
setLineFourType(FLD_LINE_4_BRIGHTNESS);
|
||||
break;
|
||||
case FLD_LINE_4_PALETTE:
|
||||
setLineFourType(clockMode ? FLD_LINE_4_MODE : FLD_LINE_4_BRIGHTNESS);
|
||||
break;
|
||||
case FLD_LINE_4_EFFECT_SPEED:
|
||||
setLineFourType(FLD_LINE_4_EFFECT_INTENSITY);
|
||||
break;
|
||||
case FLD_LINE_4_EFFECT_INTENSITY:
|
||||
setLineFourType(FLD_LINE_4_PALETTE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
drawLineFour();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
knownHour = 99; // force time display
|
||||
clear();
|
||||
}
|
||||
|
||||
needRedraw = false;
|
||||
lastRedraw = millis();
|
||||
// do not update lastRedraw marker if just switching row contenet
|
||||
if (((now - lastRedraw)/1000)%5 != 0) lastRedraw = now;
|
||||
|
||||
if (displayTurnedOff) {
|
||||
// Turn the display back on
|
||||
sleepOrClock(false);
|
||||
}
|
||||
// Turn the display back on
|
||||
if (displayTurnedOff) sleepOrClock(false);
|
||||
|
||||
// Update last known values.
|
||||
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
|
||||
knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
|
||||
knownBrightness = bri;
|
||||
knownMode = strip.getMode();
|
||||
knownPalette = strip.getSegment(0).palette;
|
||||
@ -324,7 +335,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// Do the actual drawing
|
||||
|
||||
// First row with Wifi name
|
||||
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // wifi icon
|
||||
drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon
|
||||
String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0);
|
||||
drawString(1, 0, ssidString.c_str());
|
||||
// Print `~` char to indicate that SSID is longer, than our display
|
||||
@ -333,46 +344,54 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
}
|
||||
|
||||
// Second row with IP or Psssword
|
||||
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // home icon
|
||||
drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0) {
|
||||
drawString(1, lineHeight, apPass);
|
||||
} else {
|
||||
drawString(1, lineHeight, (knownIp.toString()).c_str());
|
||||
// alternate IP address and server name
|
||||
String secondLine = knownIp.toString();
|
||||
if (showName && strcmp(serverDescription, "WLED") != 0) {
|
||||
secondLine = serverDescription;
|
||||
}
|
||||
for (uint8_t i=secondLine.length(); i<getCols()-1; i++) secondLine += ' ';
|
||||
drawString(1, lineHeight, secondLine.c_str());
|
||||
}
|
||||
|
||||
// Third row with mode name or current time
|
||||
if (clockMode) showTime(false);
|
||||
else showCurrentEffectOrPalette(knownMode, JSON_mode_names, 2);
|
||||
|
||||
// Fourth row
|
||||
drawLineFour();
|
||||
// draw third and fourth row
|
||||
drawLine(2, clockMode ? lineType : FLD_LINE_MODE);
|
||||
drawLine(3, clockMode ? FLD_LINE_TIME : lineType);
|
||||
|
||||
drawGlyph(0, 2*lineHeight, 66 + (bri > 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon
|
||||
//if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon
|
||||
}
|
||||
|
||||
void drawLineFour() {
|
||||
void drawLine(uint8_t line, Line4Type lineType) {
|
||||
char lineBuffer[LINE_BUFFER_SIZE];
|
||||
switch(lineFourType) {
|
||||
case FLD_LINE_4_BRIGHTNESS:
|
||||
switch(lineType) {
|
||||
case FLD_LINE_BRIGHTNESS:
|
||||
sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri);
|
||||
drawString(2, 3*lineHeight, lineBuffer);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_4_EFFECT_SPEED:
|
||||
case FLD_LINE_EFFECT_SPEED:
|
||||
sprintf_P(lineBuffer, PSTR("FX Speed %3d"), effectSpeed);
|
||||
drawString(2, 3*lineHeight, lineBuffer);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_4_EFFECT_INTENSITY:
|
||||
case FLD_LINE_EFFECT_INTENSITY:
|
||||
sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity);
|
||||
drawString(2, 3*lineHeight, lineBuffer);
|
||||
drawString(2, line*lineHeight, lineBuffer);
|
||||
break;
|
||||
case FLD_LINE_4_MODE:
|
||||
showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3);
|
||||
case FLD_LINE_MODE:
|
||||
showCurrentEffectOrPalette(knownMode, JSON_mode_names, line);
|
||||
break;
|
||||
case FLD_LINE_PALETTE:
|
||||
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, line);
|
||||
break;
|
||||
case FLD_LINE_TIME:
|
||||
showTime(false);
|
||||
break;
|
||||
case FLD_LINE_4_PALETTE:
|
||||
default:
|
||||
showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 3);
|
||||
// unknown type, do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -447,23 +466,6 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
overlayUntil = millis() + showHowLong;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify what data should be defined on line 4
|
||||
* (the last line).
|
||||
*/
|
||||
void setLineFourType(Line4Type newLineFourType) {
|
||||
if (newLineFourType == FLD_LINE_4_BRIGHTNESS ||
|
||||
newLineFourType == FLD_LINE_4_EFFECT_SPEED ||
|
||||
newLineFourType == FLD_LINE_4_EFFECT_INTENSITY ||
|
||||
newLineFourType == FLD_LINE_4_MODE ||
|
||||
newLineFourType == FLD_LINE_4_PALETTE) {
|
||||
lineFourType = newLineFourType;
|
||||
} else {
|
||||
// Unknown value
|
||||
lineFourType = FLD_LINE_4_BRIGHTNESS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Line 3 or 4 (last two lines) can be marked with an
|
||||
* arrow in the first column. Pass 2 or 3 to this to
|
||||
@ -487,8 +489,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (clockMode) showTime();
|
||||
else setPowerSave(1);
|
||||
displayTurnedOff = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setPowerSave(0);
|
||||
displayTurnedOff = false;
|
||||
}
|
||||
@ -504,13 +505,13 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
|
||||
updateLocalTime();
|
||||
byte minuteCurrent = minute(localTime);
|
||||
byte hourCurrent = hour(localTime);
|
||||
byte hourCurrent = hour(localTime);
|
||||
byte secondCurrent = second(localTime);
|
||||
if (knownMinute == minuteCurrent && knownHour == hourCurrent) {
|
||||
// Time hasn't changed.
|
||||
if (!fullScreen) return;
|
||||
} else {
|
||||
if (fullScreen) clear();
|
||||
//if (fullScreen) clear();
|
||||
}
|
||||
knownMinute = minuteCurrent;
|
||||
knownHour = hourCurrent;
|
||||
@ -520,7 +521,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (fullScreen)
|
||||
draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays
|
||||
else
|
||||
drawString(2, lineHeight*2, lineBuffer);
|
||||
drawString(2, lineHeight*3, lineBuffer);
|
||||
|
||||
byte showHour = hourCurrent;
|
||||
boolean isAM = false;
|
||||
@ -544,11 +545,12 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (fullScreen) {
|
||||
draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer);
|
||||
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
|
||||
if (!useAMPM) drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true);
|
||||
else drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
|
||||
} else {
|
||||
drawString(9+(useAMPM?0:2), lineHeight*2, lineBuffer);
|
||||
drawString(9+(useAMPM?0:2), lineHeight*3, lineBuffer);
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*3, (isAM ? "AM" : "PM"), true);
|
||||
}
|
||||
if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -621,55 +623,34 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
int8_t newScl = sclPin;
|
||||
int8_t newSda = sdaPin;
|
||||
|
||||
bool configComplete = true;
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (!top.isNull() && top["pin"] != nullptr) {
|
||||
newScl = top["pin"][0];
|
||||
newSda = top["pin"][1];
|
||||
newType = top["type"];
|
||||
if (top[FPSTR(_flip)].is<bool>()) {
|
||||
flip = top[FPSTR(_flip)].as<bool>();
|
||||
} else {
|
||||
String str = top[FPSTR(_flip)]; // checkbox -> off or on
|
||||
flip = (bool)(str!="off"); // off is guaranteed to be present
|
||||
needRedraw |= true;
|
||||
}
|
||||
contrast = top[FPSTR(_contrast)].as<int>();
|
||||
refreshRate = top[FPSTR(_refreshRate)].as<int>() * 1000;
|
||||
screenTimeout = top[FPSTR(_screenTimeOut)].as<int>() * 1000;
|
||||
if (top[FPSTR(_sleepMode)].is<bool>()) {
|
||||
sleepMode = top[FPSTR(_sleepMode)].as<bool>();
|
||||
} else {
|
||||
String str = top[FPSTR(_sleepMode)]; // checkbox -> off or on
|
||||
sleepMode = (bool)(str!="off"); // off is guaranteed to be present
|
||||
needRedraw |= true;
|
||||
}
|
||||
if (top[FPSTR(_clockMode)].is<bool>()) {
|
||||
clockMode = top[FPSTR(_clockMode)].as<bool>();
|
||||
} else {
|
||||
String str = top[FPSTR(_clockMode)]; // checkbox -> off or on
|
||||
clockMode = (bool)(str!="off"); // off is guaranteed to be present
|
||||
needRedraw |= true;
|
||||
}
|
||||
DEBUG_PRINTLN(F("4 Line Display config (re)loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
|
||||
configComplete = false;
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
newScl = top["pin"][0] | newScl;
|
||||
newSda = top["pin"][1] | newSda;
|
||||
newType = top["type"] | newType;
|
||||
flip = top[FPSTR(_flip)] | flip;
|
||||
contrast = top[FPSTR(_contrast)] | contrast;
|
||||
refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000;
|
||||
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
|
||||
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
|
||||
clockMode = top[FPSTR(_clockMode)] | clockMode;
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
sclPin = newScl;
|
||||
sdaPin = newSda;
|
||||
type = newType;
|
||||
lineHeight = type==SSD1306 ? 1 : 2;
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing paramters from settings page
|
||||
// changing parameters from settings page
|
||||
if (sclPin!=newScl || sdaPin!=newSda || type!=newType) {
|
||||
if (type==SSD1306) delete (static_cast<U8X8*>(u8x8));
|
||||
if (type==SH1106) delete (static_cast<U8X8*>(u8x8));
|
||||
if (type==SSD1306_64) delete (static_cast<U8X8*>(u8x8));
|
||||
if (type != NONE) delete (static_cast<U8X8*>(u8x8));
|
||||
pinManager.deallocatePin(sclPin);
|
||||
pinManager.deallocatePin(sdaPin);
|
||||
sclPin = newScl;
|
||||
@ -677,18 +658,17 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
if (newScl<0 || newSda<0) {
|
||||
type = NONE;
|
||||
return true;
|
||||
} else
|
||||
type = newType;
|
||||
lineHeight = type==SSD1306 ? 1 : 2;
|
||||
} else type = newType;
|
||||
setup();
|
||||
needRedraw |= true;
|
||||
needsRedraw |= true;
|
||||
}
|
||||
setContrast(contrast);
|
||||
setFlipMode(flip);
|
||||
if (needsRedraw && !wakeDisplay()) redraw(true);
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -29,7 +29,10 @@
|
||||
if (numM > 0 || locip) ldS();
|
||||
else gId("um").innerHTML = "No Usermods installed.";
|
||||
}
|
||||
function check(o,k) {
|
||||
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
|
||||
function isF(n) { return n === +n && n !== (n|0); }
|
||||
function isI(n) { return n === +n && n === (n|0); }
|
||||
function check(o,k) { // input object, pin owner key
|
||||
var n = o.name.replace("[]","").substr(-3);
|
||||
if (o.type=="number" && n.substr(0,3)=="pin") {
|
||||
for (var i=0; i<pins.length; i++) {
|
||||
@ -58,10 +61,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
function addField(k,f,o,a=false) {
|
||||
function addField(k,f,o,a=false) { //key, field, (sub)object, isArray
|
||||
if (isO(o)) {
|
||||
for (const [s,v] of Object.entries(o)) {
|
||||
addField(k,s,v);
|
||||
// possibility to nest objects (only 1 level)
|
||||
if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v);
|
||||
else addField(k,s,v);
|
||||
}
|
||||
} else if (Array.isArray(o)) {
|
||||
for (var j=0; j<o.length; j++) {
|
||||
@ -71,17 +76,29 @@
|
||||
var t,c;
|
||||
switch (typeof o) {
|
||||
case "boolean":
|
||||
t = "checkbox"; c = o ? `checked value="on"` : ""; break;
|
||||
t = "checkbox"; c = o ? `checked value="true"` : "";
|
||||
break;
|
||||
case "number":
|
||||
t = "number"; c = `value="${parseInt(o,10)}"`; break;
|
||||
case "string":
|
||||
t = "text"; c = `value="${o}"`; break;
|
||||
c = `value="${o}"`;
|
||||
if (isF(o)) {
|
||||
c += ` step="0.01" class="xxl"`;
|
||||
t = "float";
|
||||
} else {
|
||||
if (f.substr(-3)==="pin") c += ' max="39" min="-1" class="small"';
|
||||
else c += ` class="big"`;
|
||||
t = "int";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
t = "text"; c = `value="${o}"`; break;
|
||||
t = "text"; c = `value="${o}" style="width:150px;"`;
|
||||
break;
|
||||
}
|
||||
if (k.includes(":")) urows += k.substr(k.indexOf(":")+1);
|
||||
urows += ` ${f}: `;
|
||||
// https://stackoverflow.com/questions/11657123/posting-both-checked-and-unchecked-checkboxes
|
||||
if (t=="checkbox") urows += `<input type="hidden" name="${k}_${f}${a?"[]":""}" value="off">`;
|
||||
urows += `${f}: <input type="${t}" name="${k}_${f}${a?"[]":""}" ${c} oninput="check(this,'${k}')"><br>`;
|
||||
if (t=="checkbox") urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="false">`;
|
||||
else if (!a) urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="${t}">`;
|
||||
urows += `<input type="${t==="float"||t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
|
||||
}
|
||||
}
|
||||
function ldS() {
|
||||
|
@ -3,7 +3,7 @@ body {
|
||||
text-align: center;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
line-height: 200%;
|
||||
line-height: 200%%; /* %% because of AsyncWebServer */
|
||||
margin: 0;
|
||||
}
|
||||
hr {
|
||||
@ -33,6 +33,23 @@ input {
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 4em;
|
||||
margin: 2px;
|
||||
}
|
||||
input[type="number"].xxl {
|
||||
width: 100px;
|
||||
}
|
||||
input[type="number"].big {
|
||||
width: 85px;
|
||||
}
|
||||
input[type="number"].med {
|
||||
width: 55px;
|
||||
}
|
||||
input[type="number"].small {
|
||||
width: 40px;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.5);
|
||||
margin-right: 10px;
|
||||
}
|
||||
select {
|
||||
background: #333;
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
// Autogenerated from wled00/data/style.css, do not edit!!
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px;cursor:pointer}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=number]{width:4em}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}</style>)=====";
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px;cursor:pointer}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=number]{width:4em;margin:2px}input[type=number].xxl{width:100px}input[type=number].big{width:85px}input[type=number].med{width:55px}input[type=number].small{width:40px}input[type=checkbox]{transform:scale(1.5);margin-right:10px}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}</style>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings.htm, do not edit!!
|
||||
@ -394,7 +394,7 @@ type="submit">Save & Reboot</button></form></body></html>)=====";
|
||||
// Autogenerated from wled00/data/settings_um.htm, do not edit!!
|
||||
const char PAGE_settings_um[] PROGMEM = R"=====(<!DOCTYPE html><html><head lang="en"><meta charset="utf-8"><meta
|
||||
name="viewport" content="width=500"><title>Usermod Settings</title><script>
|
||||
var owner,locip,urows,d=document,umCfg={},pins=[6,7,8,9,10,11],pinO=["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"],loc=!1,numM=0;function gId(e){return d.getElementById(e)}function isO(e){return e&&"object"==typeof e&&!Array.isArray(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings")}function B(){window.open("/settings","_self")}function S(){"file:"==window.location.protocol&&(loc=!0,(locip=localStorage.getItem("locIp"))||(locip=prompt("File Mode. Please enter WLED IP!"),localStorage.setItem("locIp",locip))),GetV(),numM>0||locip?ldS():gId("um").innerHTML="No Usermods installed."}function check(e,n){var o=e.name.replace("[]","").substr(-3);if("number"==e.type&&"pin"==o.substr(0,3))for(var i=0;i<pins.length;i++)if(n!=pinO[i]){if(e.value==pins[i]||e.value<-1||e.value>39){e.style.color="red";break}e.style.color=e.value>33?"orange":"#fff"}}function getPins(e){if(isO(e))for(const[o,i]of Object.entries(e))if(isO(i))owner=o,getPins(i);else if("pin"==o.replace("[]","").substr(-3))if(Array.isArray(i))for(var n=0;n<i.length;n++)i[n]>=0&&(pins.push(i[n]),pinO.push(owner));else i>=0&&(pins.push(i),pinO.push(owner));else if(Array.isArray(i))for(n=0;n<i.length;n++)getPins(i[n])}function addField(e,n,o,i=!1){if(isO(o))for(const[n,i]of Object.entries(o))addField(e,n,i);else if(Array.isArray(o))for(var t=0;t<o.length;t++)addField(e,n,o[t],!0);else{var r,s;switch(typeof o){case"boolean":r="checkbox",s=o?'checked value="on"':"";break;case"number":r="number",s=`value="${parseInt(o,10)}"`;break;case"string":default:r="text",s=`value="${o}"`}"checkbox"==r&&(urows+=`<input type="hidden" name="${e}_${n}${i?"[]":""}" value="off">`),urows+=`${n}: <input type="${r}" name="${e}_${n}${i?"[]":""}" ${s} oninput="check(this,'${e}')"><br>`}}function ldS(){fetch((loc?"http://"+locip:"")+"/cfg.json",{method:"get"}).then(e=>(e.ok||(gId("lserr").style.display="inline"),e.json())).then(e=>{if(umCfg=e.um,getPins(e),urows="",isO(umCfg))for(const[e,n]of Object.entries(umCfg))urows+=`<hr><h3>${e}</h3>`,addField(e,"unknown",n);""===urows&&(urows="Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults."),gId("um").innerHTML=urows}).catch((function(e){gId("lserr").style.display="inline",console.log(e)}))}function svS(e){e.preventDefault(),console.log(d.Sf),d.Sf.checkValidity()&&d.Sf.submit()}function GetV() {var d=document;
|
||||
var owner,locip,urows,d=document,umCfg={},pins=[6,7,8,9,10,11],pinO=["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"],loc=!1,numM=0;function gId(e){return d.getElementById(e)}function isO(e){return e&&"object"==typeof e&&!Array.isArray(e)}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings")}function B(){window.open("/settings","_self")}function S(){"file:"==window.location.protocol&&(loc=!0,(locip=localStorage.getItem("locIp"))||(locip=prompt("File Mode. Please enter WLED IP!"),localStorage.setItem("locIp",locip))),GetV(),numM>0||locip?ldS():gId("um").innerHTML="No Usermods installed."}function isF(e){return e===+e&&e!==(0|e)}function isI(e){return e===+e&&e===(0|e)}function check(e,n){var i=e.name.replace("[]","").substr(-3);if("number"==e.type&&"pin"==i.substr(0,3))for(var s=0;s<pins.length;s++)if(n!=pinO[s]){if(e.value==pins[s]||e.value<-1||e.value>39){e.style.color="red";break}e.style.color=e.value>33?"orange":"#fff"}}function getPins(e){if(isO(e))for(const[i,s]of Object.entries(e))if(isO(s))owner=i,getPins(s);else if("pin"==i.replace("[]","").substr(-3))if(Array.isArray(s))for(var n=0;n<s.length;n++)s[n]>=0&&(pins.push(s[n]),pinO.push(owner));else s>=0&&(pins.push(s),pinO.push(owner));else if(Array.isArray(s))for(n=0;n<s.length;n++)getPins(s[n])}function addField(e,n,i,s=!1){if(isO(i))for(const[s,t]of Object.entries(i))"unknown"===n||e.includes(":")?addField(e,s,t):addField(e+":"+n,s,t);else if(Array.isArray(i))for(var t=0;t<i.length;t++)addField(e,n,i[t],!0);else{var o,r;switch(typeof i){case"boolean":o="checkbox",r=i?'checked value="true"':"";break;case"number":r=`value="${i}"`,isF(i)?(r+=' step="0.01" class="xxl"',o="float"):("pin"===n.substr(-3)?r+=' max="39" min="-1" class="small"':r+=' class="big"',o="int");break;default:o="text",r=`value="${i}" style="width:150px;"`}e.includes(":")&&(urows+=e.substr(e.indexOf(":")+1)),urows+=` ${n}: `,"checkbox"==o?urows+=`<input type="hidden" name="${e}:${n}${s?"[]":""}" value="false">`:s||(urows+=`<input type="hidden" name="${e}:${n}${s?"[]":""}" value="${o}">`),urows+=`<input type="${"float"===o||"int"===o?"number":o}" name="${e}:${n}${s?"[]":""}" ${r} oninput="check(this,'${e.substr(e.indexOf(":")+1)}')"><br>`}}function ldS(){fetch((loc?"http://"+locip:"")+"/cfg.json",{method:"get"}).then(e=>(e.ok||(gId("lserr").style.display="inline"),e.json())).then(e=>{if(umCfg=e.um,getPins(e),urows="",isO(umCfg))for(const[e,n]of Object.entries(umCfg))urows+=`<hr><h3>${e}</h3>`,addField(e,"unknown",n);""===urows&&(urows="Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults."),gId("um").innerHTML=urows}).catch((function(e){gId("lserr").style.display="inline",console.log(e)}))}function svS(e){e.preventDefault(),console.log(d.Sf),d.Sf.checkValidity()&&d.Sf.submit()}function GetV() {var d=document;
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post" onsubmit="svS(event)"><div class="toprow">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div><button
|
||||
|
@ -431,8 +431,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
String value = request->arg(i);
|
||||
|
||||
// POST request parameters are combined as <usermodname>_<usermodparameter>
|
||||
uint8_t umNameEnd = name.indexOf("_");
|
||||
if (!umNameEnd) break; // parameter does not contain "_" -> wrong
|
||||
int umNameEnd = name.indexOf(":");
|
||||
if (umNameEnd<1) break; // parameter does not contain ":" or on 1st place -> wrong
|
||||
|
||||
JsonObject mod = um[name.substring(0,umNameEnd)]; // get a usermod JSON object
|
||||
if (mod.isNull()) {
|
||||
@ -442,30 +442,57 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
DEBUG_PRINT(":");
|
||||
name = name.substring(umNameEnd+1); // remove mod name from string
|
||||
|
||||
// if the resulting name still contains ":" this means nested object
|
||||
JsonObject subObj;
|
||||
int umSubObj = name.indexOf(":");
|
||||
DEBUG_PRINTF("(%d):",umSubObj);
|
||||
if (umSubObj>0) {
|
||||
subObj = mod[name.substring(0,umSubObj)];
|
||||
if (subObj.isNull())
|
||||
subObj = mod.createNestedObject(name.substring(0,umSubObj));
|
||||
name = name.substring(umSubObj+1); // remove nested object name from string
|
||||
} else {
|
||||
subObj = mod;
|
||||
}
|
||||
DEBUG_PRINT(name);
|
||||
|
||||
// check if parameters represent array
|
||||
if (name.endsWith("[]")) {
|
||||
name.replace("[]","");
|
||||
if (!mod[name].is<JsonArray>()) {
|
||||
JsonArray ar = mod.createNestedArray(name);
|
||||
ar.add(value);
|
||||
if (!subObj[name].is<JsonArray>()) {
|
||||
JsonArray ar = subObj.createNestedArray(name);
|
||||
ar.add(value.toInt());
|
||||
j=0;
|
||||
} else {
|
||||
mod[name].add(value);
|
||||
subObj[name].add(value.toInt());
|
||||
j++;
|
||||
}
|
||||
DEBUG_PRINT(name);
|
||||
DEBUG_PRINT("[");
|
||||
DEBUG_PRINT(j);
|
||||
DEBUG_PRINT("] = ");
|
||||
DEBUG_PRINTLN(value);
|
||||
} else {
|
||||
mod.remove(name); // checkboxes get two fields (first is always "off", existence of second depends on checkmark and may be "on")
|
||||
mod[name] = value;
|
||||
DEBUG_PRINT(name);
|
||||
// we are using a hidden field with the same name as our parameter (!before the actual parameter!)
|
||||
// to describe the type of parameter (text,float,int), for boolean patameters the first field contains "off"
|
||||
// so checkboxes have one or two fields (first is always "false", existence of second depends on checkmark and may be "true")
|
||||
if (subObj[name].isNull()) {
|
||||
// the first occurence of the field describes the parameter type (used in next loop)
|
||||
if (value == "false") subObj[name] = false; // checkboxes may have only one field
|
||||
else subObj[name] = value;
|
||||
} else {
|
||||
String type = subObj[name].as<String>(); // get previously stored value as a type
|
||||
if (subObj[name].is<bool>()) subObj[name] = true; // checkbox/boolean
|
||||
else if (type == "float") subObj[name] = value.toDouble();
|
||||
else if (type == "int") subObj[name] = value.toInt();
|
||||
else subObj[name] = value; // text fields
|
||||
}
|
||||
DEBUG_PRINT(" = ");
|
||||
DEBUG_PRINTLN(value);
|
||||
}
|
||||
}
|
||||
#ifdef WLED_DEBUG
|
||||
serializeJson(um,Serial);
|
||||
#endif
|
||||
usermods.readFromConfig(um); // force change of usermod parameters
|
||||
}
|
||||
|
||||
@ -643,7 +670,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
nightlightActive = false; //always disable nightlight when toggling
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//set hue
|
||||
|
Loading…
Reference in New Issue
Block a user