Added sunrise/sunset triggered presets.

This commit is contained in:
Blaz Kristan 2021-03-07 00:04:46 +01:00
parent f74a45a33e
commit b5abc6c724
8 changed files with 159 additions and 103 deletions

View File

@ -335,7 +335,8 @@ void deserializeConfig() {
JsonArray timers = tm[F("ins")];
uint8_t it = 0;
for (JsonObject timer : timers) {
if (it > 7) break;
if (it > 9) break;
if (it<8 && timer[F("hour")]==255) it=8;
CJSON(timerHours[it], timer[F("hour")]);
CJSON(timerMinutes[it], timer[F("min")]);
CJSON(timerMacro[it], timer[F("macro")]);
@ -645,8 +646,8 @@ void serializeConfig() {
JsonArray timers_ins = timers.createNestedArray("ins");
for (byte i = 0; i < 8; i++) {
if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue;
for (byte i = 0; i < 10; i++) {
if (timerMacro[i] == 0 && (timerHours[i] == 0 || timerHours[i] == 255) && timerMinutes[i] == 0) continue;
JsonObject timers_ins0 = timers_ins.createNestedObject();
timers_ins0[F("en")] = (timerWeekday[i] & 0x01);
timers_ins0[F("hour")] = timerHours[i];

View File

@ -6,41 +6,41 @@
<title>Time Settings</title>
<script>
var d=document;
function H()
function H()
{
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");
}
function B()
{
window.open("/settings","_self");
}
function S()
{
BTa();GetV();Cs();FC();
}
function gId(s)
{
return d.getElementById(s);
}
function Cs()
{
gId("cac").style.display="none";
gId("coc").style.display="block";
gId("ccc").style.display="none";
if (gId("ca").selected)
{
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings");
gId("cac").style.display="block";
}
function B()
if (gId("cc").selected)
{
window.open("/settings","_self");
gId("coc").style.display="none";
gId("ccc").style.display="block";
}
function S()
if (gId("cn").selected)
{
BTa();GetV();Cs();FC();
}
function gId(s)
{
return d.getElementById(s);
}
function Cs()
{
gId("cac").style.display="none";
gId("coc").style.display="block";
gId("ccc").style.display="none";
if (gId("ca").selected)
{
gId("cac").style.display="block";
}
if (gId("cc").selected)
{
gId("coc").style.display="none";
gId("ccc").style.display="block";
}
if (gId("cn").selected)
{
gId("coc").style.display="none";
}
gId("coc").style.display="none";
}
}
function BTa()
{
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
@ -49,19 +49,23 @@
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
}
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>";
ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>";
gId("TMT").innerHTML=ih;
}
function FC()
{
for(j=0;j<8;j++)
{
for(i=0;i<8;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1;
for(i=0;i<10;i++) gId("W"+i+j).checked=gId("W"+i).value>>j&1;
}
}
function Wd()
{
a=[0,0,0,0,0,0,0,0];
for(i=0;i<8;i++)
a=[0,0,0,0,0,0,0,0,0,0];
for(i=0;i<10;i++)
{
m=1;
for(j=0;j<8;j++)
@ -71,10 +75,10 @@
gId("W"+i).value=a[i];
}
}
function GetV()
{
//values injected by server while sending HTML
}
function GetV()
{
//values injected by server while sending HTML
}
</script>
<style>
@import url("style.css");

View File

@ -297,7 +297,7 @@ type="submit">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_time.htm, do not edit!!
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>Time Settings</title><script>
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0],i=0;i<8;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" value="255" type="hidden"></td><td><input name="N8" type="number" min="-59" max="59"></td><td><input name="T8" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W8'+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" value="255" type="hidden"></td><td><input name="N9" type="number" min="-59" max="59"><td><input name="T9" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W9'+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<10;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0,0,0],i=0;i<10;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
%CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="toprow"><div
class="helpB"><button type="button" onclick="H()">?</button></div><button

View File

@ -509,7 +509,15 @@ void serializeInfo(JsonObject root)
root[F("freeheap")] = ESP.getFreeHeap();
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
root[F("isday")] = daytime;
#ifdef WLED_DEBUG
char tmp[30];
sprintf(tmp,"%02d:%02d",hour(sunrise),minute(sunrise));
root[F("sunrise")] = String(tmp);
sprintf(tmp,"%02d:%02d",hour(sunset),minute(sunset));
root[F("sunset")] = String(tmp);
#endif
usermods.addToJsonInfo(root);

View File

@ -219,9 +219,7 @@ void getTimeString(char* out)
if (hr > 11) hr -= 12;
if (hr == 0) hr = 12;
}
sprintf(out,"%i-%i-%i, %i:%s%i:%s%i",year(localTime), month(localTime), day(localTime),
hr,(minute(localTime)<10)?"0":"",minute(localTime),
(second(localTime)<10)?"0":"",second(localTime));
sprintf(out,"%i-%i-%i, %02d:%02d:%02d",year(localTime), month(localTime), day(localTime), hr, minute(localTime), second(localTime));
if (useAMPM)
{
strcat(out,(hour(localTime) > 11)? " PM":" AM");
@ -259,16 +257,37 @@ byte weekdayMondayFirst()
return wd;
}
int getSunriseUTC(int year, int month, int day, float lat, float lon, bool sunset=false);
void checkTimers()
{
if (lastTimerMinute != minute(localTime)) //only check once a new minute begins
{
daytime = isDayTime();
if (prevDaytime != daytime) {
// sunrise or sunset
DEBUG_PRINTLN(daytime?F("Sunrise"):F("Sunset"));
}
lastTimerMinute = minute(localTime);
// calculate sunrise and sunset at midnight (if longitude and latitude are set)
if (((int)longitude || (int)latitude) && ((!hour(localTime) && !minute(localTime)) || !sunrise)) {
struct tm tim_0;
tim_0.tm_year = year(localTime)-1900;
tim_0.tm_mon = month(localTime)-1;
tim_0.tm_mday = day(localTime);
tim_0.tm_sec = 0;
tim_0.tm_isdst = 0;
int minUTC = getSunriseUTC(year(localTime), month(localTime), day(localTime), latitude, longitude);
tim_0.tm_hour = minUTC / 60;
tim_0.tm_min = minUTC % 60;
sunrise = tz->toLocal(mktime(&tim_0) - utcOffsetSecs);
DEBUG_PRINTF("Sunrise: %02d:%02d\n", hour(sunrise),minute(sunrise));
minUTC = getSunriseUTC(year(localTime), month(localTime), day(localTime), latitude, longitude, true);
tim_0.tm_hour = minUTC / 60;
tim_0.tm_min = minUTC % 60;
sunset = tz->toLocal(mktime(&tim_0) - utcOffsetSecs);
DEBUG_PRINTF("Sunset: %02d:%02d\n", hour(sunset),minute(sunset));
}
if (sunrise && sunset) daytime = difftime(localTime, sunrise) > 0 && difftime(localTime, sunset) < 0;
for (uint8_t i = 0; i < 8; i++)
{
if (timerMacro[i] != 0
@ -280,54 +299,77 @@ void checkTimers()
applyPreset(timerMacro[i]);
}
}
// sunrise macro
if (sunrise && timerMacro[8] != 0
&& (hour(sunrise) == hour(localTime))
&& (minute(sunrise) + timerMinutes[8]) == minute(localTime)
&& (timerWeekday[8] & 0x01) //timer is enabled
&& timerWeekday[8] >> weekdayMondayFirst() & 0x01) //timer should activate at current day of week
{
applyPreset(timerMacro[8]);
}
// sunset macro
if (sunset && timerMacro[9] != 0
&& (hour(sunrise) == hour(localTime))
&& (minute(sunrise) + timerMinutes[9]) == minute(localTime)
&& (timerWeekday[9] & 0x01) //timer is enabled
&& timerWeekday[9] >> weekdayMondayFirst() & 0x01) //timer should activate at current day of week
{
applyPreset(timerMacro[9]);
}
}
}
/*
* This program calculates solar positions as a function of location, date, and time.
* The equations are from Jean Meeus, Astronomical Algorithms, Willmann-Bell, Inc., Richmond, VA
* (C) 2015, David Brooks, Institute for Earth Science Research and Education.
* http://www.instesre.org/ArduinoUnoSolarCalculations.pdf
*/
//#define DEG_TO_RAD 0.01745329
//#define PI 3.141592654
#define TWOPI 6.28318531
#define ZENITH -.83
int getSunriseUTC(int year, int month, int day, float lat, float lon, bool sunset) {
//1. first calculate the day of the year
float N1 = floor(275 * month / 9);
float N2 = floor((month + 9) / 12);
float N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3));
float N = N1 - (N2 * N3) + day - 30;
long JulianDate(int year, int month, int day) {
if (month<=2) {
year--; month+=12;
}
int A=year/100;
int B=2-A+A/4;
return (long)(365.25*(year + 4716)) + (int)(30.6001*(month + 1)) + day + B - 1524;
}
bool isDayTime() {
float JD_frac,T,L0,M,C,L_true,GrHrAngle,Obl,RA,Decl,HrAngle,elev;
long JD_whole,JDx;
float Lon = longitude*DEG_TO_RAD;
float Lat = latitude*DEG_TO_RAD;
// calculate elevation of the sun (>0 daytime, <0 nighttime)
JD_whole = JulianDate(year(localTime), month(localTime), day(localTime));
JD_frac = (hour(localTime) + minute(localTime)/60. + second(localTime)/3600.)/24. - .5;
JDx = JD_whole - 2451545;
T = (JDx + JD_frac)/36525.;
L0 = DEG_TO_RAD*fmod(280.46645 + 36000.76983*T, 360);
M = DEG_TO_RAD*fmod(357.5291 + 35999.0503*T, 360);
C = DEG_TO_RAD*((1.9146-0.004847*T)*sin(M) + (0.019993-0.000101*T)*sin(2*M) + 0.00029*sin(3*M));
Obl = DEG_TO_RAD*(23 + 26/60. + 21.448/3600. - 46.815/3600*T);
GrHrAngle = 280.46061837 + (360*JDx)%360 + .98564736629*JDx + 360.98564736629*JD_frac;
GrHrAngle = fmod(GrHrAngle, 360.);
L_true = fmod(C + L0, TWOPI);
RA = atan2(sin(L_true)*cos(Obl), cos(L_true));
Decl = asin(sin(Obl)*sin(L_true));
HrAngle = DEG_TO_RAD*GrHrAngle + Lon - RA;
elev = asin(sin(Lat)*sin(Decl) + cos(Lat)*(cos(Decl)*cos(HrAngle)));
// Azimuth measured eastward from north.
// azimuth = PI+atan2(sin(HrAngle),cos(HrAngle)*sin(Lat)-tan(Decl)*cos(Lat));
return elev > 0.; // if elevation is gt 0 then it is a day
//2. convert the longitude to hour value and calculate an approximate time
float lngHour = lon / 15.0;
float t = N + (((sunset ? 18 : 6) - lngHour) / 24);
//3. calculate the Sun's mean anomaly
float M = (0.9856 * t) - 3.289;
//4. calculate the Sun's true longitude
float L = fmod(M + (1.916 * sin((PI/180)*M)) + (0.020 * sin(2 *(PI/180) * M)) + 282.634,360.0);
//5a. calculate the Sun's right ascension
float RA = fmod(180/PI*atan(0.91764 * tan((PI/180)*L)),360.0);
//5b. right ascension value needs to be in the same quadrant as L
float Lquadrant = floor( L/90) * 90;
float RAquadrant = floor(RA/90) * 90;
RA = RA + (Lquadrant - RAquadrant);
//5c. right ascension value needs to be converted into hours
RA = RA / 15;
//6. calculate the Sun's declination
float sinDec = 0.39782 * sin((PI/180)*L);
float cosDec = cos(asin(sinDec));
//7a. calculate the Sun's local hour angle
float cosH = (sin((PI/180)*ZENITH) - (sinDec * sin((PI/180)*lat))) / (cosDec * cos((PI/180)*lat));
/*
if (cosH > 1) the sun never rises on this location (on the specified date)
if (cosH < -1) the sun never sets on this location (on the specified date)
*/
//7b. finish calculating H and convert into hours
float H = sunset ? (180/PI)*acos(cosH) : 360 - (180/PI)*acos(cosH);
H = H / 15;
//8. calculate local mean time of rising/setting
float T = H + RA - (0.06571 * t) - 6.622;
//9. adjust back to UTC
float UT = fmod(T - lngHour,24.0);
// return in minutes from midnight
return UT*60;
}

View File

@ -328,7 +328,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
macroDoublePress = request->arg(F("MD")).toInt();
char k[3]; k[2] = 0;
for (int i = 0; i<8; i++)
for (int i = 0; i<10; i++)
{
k[1] = i+48;//ascii 0,1,2,3

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2103051
#define VERSION 2103070
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -451,10 +451,10 @@ WLED_GLOBAL bool countdownOverTriggered _INIT(true);
// timer
WLED_GLOBAL byte lastTimerMinute _INIT(0);
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on
WLED_GLOBAL byte timerHours[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL int8_t timerMinutes[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerMacro[] _INIT_N(({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }));
WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 })); // weekdays to activate on
// bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
// blynk
@ -516,9 +516,10 @@ WLED_GLOBAL IPAddress ntpServerIP;
WLED_GLOBAL uint16_t ntpLocalPort _INIT(2390);
WLED_GLOBAL uint16_t rolloverMillis _INIT(0);
WLED_GLOBAL bool daytime _INIT(false);
WLED_GLOBAL bool prevDaytime _INIT(false);
WLED_GLOBAL float longitude _INIT(0.0);
WLED_GLOBAL float latitude _INIT(0.0);
WLED_GLOBAL time_t sunrise _INIT(0);
WLED_GLOBAL time_t sunset _INIT(0);
// Temp buffer
WLED_GLOBAL char* obuf;

View File

@ -480,10 +480,10 @@ void getSettingsJS(byte subPage, char* dest)
char k[4];
k[2] = 0; //Time macros
for (int i = 0; i<8; i++)
for (int i = 0; i<10; i++)
{
k[1] = 48+i; //ascii 0,1,2,3
k[0] = 'H'; sappend('v',k,timerHours[i]);
if (i<8) { k[0] = 'H'; sappend('v',k,timerHours[i]); }
k[0] = 'N'; sappend('v',k,timerMinutes[i]);
k[0] = 'T'; sappend('v',k,timerMacro[i]);
k[0] = 'W'; sappend('v',k,timerWeekday[i]);