Added PIR sensor night time presets.

Added PIR sensor Disable/Enable button.
UI refinements.
Added colorUpdated() on HTTP API in IR JSON.
This commit is contained in:
Blaz Kristan 2021-09-28 23:27:40 +02:00
parent 9092549f07
commit dc9d48850f
8 changed files with 2298 additions and 2233 deletions

View File

@ -72,6 +72,9 @@ private:
// on and off presets
uint8_t m_onPreset = 0;
uint8_t m_offPreset = 0;
// nighttime presets (optional)
uint8_t m_onNighttime = 0;
uint8_t m_offNighttime = 0;
// flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_nightTimeOnly = false;
// flag to send MQTT message only (assuming it is enabled)
@ -88,6 +91,8 @@ private:
static const char _enabled[];
static const char _onPreset[];
static const char _offPreset[];
static const char _onNighttime[];
static const char _offNighttime[];
static const char _nightTime[];
static const char _mqttOnly[];
static const char _offOnly[];
@ -97,24 +102,23 @@ private:
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/
bool isDayTime() {
bool isDayTime = false;
updateLocalTime();
uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime);
if (sunrise && sunset) {
if (hour(sunrise)<hr && hour(sunset)>hr) {
isDayTime = true;
return true;
} else {
if (hour(sunrise)==hr && minute(sunrise)<mi) {
isDayTime = true;
return true;
}
if (hour(sunset)==hr && minute(sunset)>mi) {
isDayTime = true;
return true;
}
}
}
return isDayTime;
return false;
}
/**
@ -124,17 +128,33 @@ private:
{
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
PIRtriggered = switchOn;
if (switchOn && m_onPreset) {
applyPreset(m_onPreset);
} else if (!switchOn && m_offPreset) {
applyPreset(m_offPreset);
} else if (switchOn && bri == 0) {
bri = briLast;
colorUpdated(NotifyUpdateMode);
} else if (!switchOn && bri != 0) {
briLast = bri;
bri = 0;
colorUpdated(NotifyUpdateMode);
if (switchOn) {
if (m_onNighttime && !isDayTime()) {
applyPreset(m_onNighttime);
return;
} else if (m_onPreset) {
applyPreset(m_onPreset);
return;
}
// preset not assigned
if (bri == 0) {
bri = briLast;
colorUpdated(NotifyUpdateMode);
}
} else {
if (m_offNighttime && !isDayTime()) {
applyPreset(m_offNighttime);
return;
} else if (m_offPreset) {
applyPreset(m_offPreset);
return;
}
// preset not assigned
if (bri != 0) {
briLast = bri;
bri = 0;
colorUpdated(NotifyUpdateMode);
}
}
}
@ -248,11 +268,21 @@ public:
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
if (enabled)
{
// off timer
String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
if (enabled) {
uiDomString += F(":false}});loadInfo();\">");
uiDomString += F("PIR <i class=\"icons\">&#xe325;</i>");
} else {
uiDomString += F(":true}});loadInfo();\">");
uiDomString += F("PIR <i class=\"icons\">&#xe08f;</i>");
}
uiDomString += F("</button>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
if (enabled) {
if (m_offTimerStart > 0)
{
uiDomString = "";
@ -282,8 +312,6 @@ public:
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
}
} else {
String uiDomString = F("PIR sensor");
JsonArray infoArr = user.createNestedArray(uiDomString);
infoArr.add(F("disabled"));
}
}
@ -302,11 +330,18 @@ public:
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
/*
void readFromJsonState(JsonObject &root)
{
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) {
if (usermod[FPSTR(_enabled)].is<bool>()) {
enabled = usermod[FPSTR(_enabled)].as<bool>();
}
}
}
*/
/**
* provide the changeable values
@ -314,14 +349,16 @@ public:
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly;
top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_onNighttime)] = m_onNighttime;
top[FPSTR(_offNighttime)] = m_offNighttime;
top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly;
DEBUG_PRINTLN(F("PIR config saved."));
}
@ -336,9 +373,9 @@ public:
bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin;
DEBUG_PRINT(FPSTR(_name));
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
@ -351,15 +388,18 @@ public:
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset));
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset));
m_onNighttime = top[FPSTR(_onNighttime)] | m_onNighttime;
m_onNighttime = max(0,min(250,(int)m_onNighttime));
m_offNighttime = top[FPSTR(_offNighttime)] | m_offNighttime;
m_offNighttime = max(0,min(250,(int)m_offNighttime));
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded."));
@ -385,7 +425,7 @@ public:
DEBUG_PRINTLN(F(" config (re)loaded."));
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_offOnly)].isNull();
return !top[FPSTR(_onNighttime)].isNull();
}
/**
@ -404,6 +444,8 @@ const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
const char PIRsensorSwitch::_onNighttime[] PROGMEM = "on-nighttime";
const char PIRsensorSwitch::_offNighttime[] PROGMEM = "off-nighttime";
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";

View File

@ -128,11 +128,6 @@ button {
font-size: 42px;
}
.infot {
table-layout: fixed;
width: 100%;
}
.segt, .plentry TABLE {
table-layout: fixed;
width: 100%;
@ -455,14 +450,24 @@ button {
}
#kv, #kn {
max-width: 490px;
/*max-width: 490px;*/
display: inline-block;
}
#kv td {
#info table, #nodes table {
table-layout: fixed;
width: 490px;
margin: auto;
}
#info td, #nodes td {
padding-bottom: 8px;
}
#info .btn, #nodes .btn {
margin: 0;
}
#lv {
max-width: 600px;
display: inline-block;
@ -1130,14 +1135,17 @@ input[type="text"].fnd:hover {
@media all and (max-width: 335px) {
.sliderbubble {
display: none;
}
display: none;
}
}
@media all and (max-width: 550px) and (min-width: 374px) {
.infobtn {
#info .btn, #nodes .btn {
width: 155px;
}
#info table, #nodes table {
width: 320px;
}
}
@media all and (max-width: 540px) {

View File

@ -201,18 +201,28 @@
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
</div><br>
<div id="kv">Loading...</div><br>
<button class="btn infobtn" onclick="loadInfo()">Refresh</button>
<button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br>
<table>
<tr>
<td class="keytd"><button class="btn" onclick="loadInfo()">Refresh</button></td>
<td class="valtd"><button class="btn" onclick="toggleInfo()">Close Info</button></td>
</tr>
<tr>
<td class="keytd"><button class="btn" onclick="toggleNodes()">Instance List</button></td>
<td class="valtd"><button class="btn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button></td>
</tr>
</table><br>
<span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by Aircoookie and the WLED community</span>
</div>
<div id="nodes" class="modal">
<div id="ndlt">WLED instances</div>
<div id="kn">Loading...</div><br>
<button class="btn infobtn" onclick="loadNodes()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Close list</button><br>
<table>
<tr>
<td class="keytd"><button class="btn infobtn" onclick="loadNodes()">Refresh</button></td>
<td class="valtd"><button class="btn infobtn" onclick="toggleNodes()">Close list</button></td>
</tr>
</table><br>
</div>
<div id="rover" class="modal">

View File

@ -629,7 +629,7 @@ function populateInfo(i)
if (i.ver.includes("-bl")) vcn = "Ryujin";
if (i.cn) vcn = i.cn;
cn += `v${i.ver} "${vcn}"<br><br><table class="infot">
cn += `v${i.ver} "${vcn}"<br><br><table>
${urows}
${inforow("Build",i.vid)}
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
@ -760,7 +760,7 @@ function populateNodes(i,n)
for (var x=0;x<n.nodes.length;x++) {
var o = n.nodes[x];
if (o.name) {
var url = `<button class="btn infobtn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
nnodes++;
}
@ -768,9 +768,9 @@ function populateNodes(i,n)
}
if (i.ndc < 0) cn += `Instance List is disabled.`;
else if (nnodes == 0) cn += `No other instances found.`;
cn += `<table class="infot">
${urows}
cn += `<table>
${inforow("Current instance:",i.name)}
${urows}
</table>`;
gId('kn').innerHTML = cn;
}

File diff suppressed because it is too large Load Diff

View File

@ -625,6 +625,7 @@ void decodeIRJson(uint32_t code)
cmdStr = "win&" + cmdStr;
}
handleSet(nullptr, cmdStr, false);
colorUpdated(CALL_MODE_BUTTON);
}
} else if (!jsonCmdObj.isNull()) {
// command is JSON object

View File

@ -713,12 +713,10 @@ void WLED::initInterfaces()
// Set up mDNS responder:
if (strlen(cmDNS) > 0) {
#ifndef WLED_DISABLE_OTA
if (!aOtaEnabled) //ArduinoOTA begins mDNS for us if enabled
MDNS.begin(cmDNS);
#else
// "end" must be called before "begin" is called a 2nd time
// see https://github.com/esp8266/Arduino/issues/7213
MDNS.end();
MDNS.begin(cmDNS);
#endif
DEBUG_PRINTLN(F("mDNS started"));
MDNS.addService("http", "tcp", 80);

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2109261
#define VERSION 2109281
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -87,6 +87,7 @@
#include <WiFiUdp.h>
#include <DNSServer.h>
#ifndef WLED_DISABLE_OTA
#define NO_OTA_PORT
#include <ArduinoOTA.h>
#endif
#include <SPIFFSEditor.h>