Browse Source

a simple scheduler for relays

i18n
Stefano Cotterli 6 years ago
parent
commit
b7ed0ec686
6 changed files with 234 additions and 3 deletions
  1. +101
    -0
      code/espurna/ascheduler.ino
  2. +4
    -0
      code/espurna/config/hardware.h
  3. +6
    -0
      code/espurna/espurna.ino
  4. +7
    -1
      code/html/custom.css
  5. +65
    -2
      code/html/custom.js
  6. +51
    -0
      code/html/index.html

+ 101
- 0
code/espurna/ascheduler.ino View File

@ -0,0 +1,101 @@
/*
A SCHEDULER MODULE
Copyright (C) 2017 by faina09
*/
#if SCHEDULER_SUPPORT
#include <NtpClientLib.h>
void _schWebSocketOnSend(JsonObject &root){
root["maxScheduled"] = MAX_SCHEDULED;
JsonArray &sch = root.createNestedArray("schedule");
for (byte i = 0; i < MAX_SCHEDULED; i++) {
if (!hasSetting("sch_switch", i))
break;
JsonObject &scheduler = sch.createNestedObject();
scheduler["sch_switch"] = getSetting("sch_switch", i, "");
scheduler["sch_operation"] = getSetting("sch_operation", i, "");
scheduler["sch_hour"] = getSetting("sch_hour", i, "");
scheduler["sch_minute"] = getSetting("sch_minute", i, "");
scheduler["sch_weekdays"] = getSetting("sch_weekdays", i, "");
}
}
void schSetup(){
// Update websocket clients
#if WEB_SUPPORT
wsOnSendRegister(_schWebSocketOnSend);
#endif
int i;
for (i = 0; i < MAX_SCHEDULED; i++) {
if (getSetting("sch_switch" + String(i)).length() == 0)
break;
String sch_weekdays = getSetting("sch_weekdays" + String(i));
int sch_switch = getSetting("sch_switch" + String(i)).toInt();
int sch_operation = getSetting("sch_operation" + String(i)).toInt();
int sch_hour = getSetting("sch_hour" + String(i)).toInt();
int sch_minute = getSetting("sch_minute" + String(i)).toInt();
DEBUG_MSG_P(PSTR("[SCH] Turn switch #%d %d AT %02d:%02d ON %s\n"), sch_switch, sch_operation, sch_hour, sch_minute, (char *)sch_weekdays.c_str());
}
}
void schLoop(){
// Check if we should compare scheduled and actual times
// TODO (?) start every minute and 0 seconds
static unsigned long last_update = 0;
static bool r_on = true;
if ((millis() - last_update > SCH_UPDATE_INTERVAL) || (last_update == 0)) {
last_update = millis();
int i;
for (i = 0; i < MAX_SCHEDULED; i++) {
if (getSetting("sch_switch" + String(i)).length() == 0)
break;
String sch_weekdays = getSetting("sch_weekdays" + String(i));
if (isThisWday(sch_weekdays)) {
int sch_switch = getSetting("sch_switch" + String(i)).toInt();
int sch_operation = getSetting("sch_operation" + String(i)).toInt();
int sch_hour = getSetting("sch_hour" + String(i)).toInt();
int sch_minute = getSetting("sch_minute" + String(i)).toInt();
DEBUG_MSG_P(PSTR("[SCH] Today it will turn switch #%d %d @ %02d:%02d\n"), sch_switch, sch_operation, sch_hour, sch_minute);
int minToTrigger = diffTime(sch_hour, sch_minute);
if (minToTrigger == 0) {
relayStatus(sch_switch, sch_operation);
DEBUG_MSG_P(PSTR("[SCH] TRIGGERED!!\n"));
}
if (minToTrigger < 0) {
//DEBUG_MSG_P(PSTR("[SCH] Time now: %s\n"), (char *)ntpDateTime().c_str()); // aaaa/mm/dd hh:mm:ss
DEBUG_MSG_P(PSTR("[SCH] Time now: %s, %d minutes to trigger %02d:%02d\n"), (char *)ntpDateTime().c_str(), minToTrigger, sch_hour, sch_minute);
}
}
}
}
}
bool isThisWday(String weekdays){
//Sunday = 1, Monday = 2, ...
int w = weekday(now());
//DEBUG_MSG_P(PSTR("[SCH] ntp weekday: %d\n"), w);
char * pch;
char * p = (char *)weekdays.c_str();
while ((pch = strtok_r(p, ",", &p)) != NULL) {
//DEBUG_MSG_P(PSTR("[SCH] w found: %d\n"), atoi(pch));
if (atoi(pch) == w) return true;
}
return false;
}
int diffTime(int schhour, int schminute){
if (!ntpConnected())
return 9999; //NTP time not set
String value = NTP.getTimeDateString();
int hour = value.substring(0, 2).toInt();
int minute = value.substring(3, 5).toInt();
//DEBUG_MSG_P(PSTR("[SCH] ntp time: %02d:%02d\n"), hour, minute);
//DEBUG_MSG_P(PSTR("[SCH] cmp time: %02d:%02d\n"), schhour, schminute);
return (hour - schhour) * 60 + minute - schminute;
}
#endif

+ 4
- 0
code/espurna/config/hardware.h View File

@ -39,6 +39,10 @@
#define RELAY1_PIN 12 #define RELAY1_PIN 12
#define RELAY1_TYPE RELAY_TYPE_NORMAL #define RELAY1_TYPE RELAY_TYPE_NORMAL
#define SCHEDULER_SUPPORT 1
#define SCH_UPDATE_INTERVAL 30000
#define MAX_SCHEDULED 10
// LEDs // LEDs
#define LED1_PIN 2 #define LED1_PIN 2
#define LED1_PIN_INVERSE 1 #define LED1_PIN_INVERSE 1


+ 6
- 0
code/espurna/espurna.ino View File

@ -383,6 +383,9 @@ void setup() {
#if SENSOR_SUPPORT #if SENSOR_SUPPORT
sensorSetup(); sensorSetup();
#endif #endif
#if SCHEDULER_SUPPORT
schSetup();
#endif
// Prepare configuration for version 2.0 // Prepare configuration for version 2.0
migrate(); migrate();
@ -438,6 +441,9 @@ void loop() {
#if SENSOR_SUPPORT #if SENSOR_SUPPORT
sensorLoop(); sensorLoop();
#endif #endif
#if SCHEDULER_SUPPORT
schLoop();
#endif
// Power saving delay // Power saving delay
delay(_loopDelay); delay(_loopDelay);


+ 7
- 1
code/html/custom.css View File

@ -62,17 +62,23 @@
margin-left: 5px; margin-left: 5px;
} }
.button-add-network, .button-add-network,
.button-add-schedule,
.button-rfb-learn { .button-rfb-learn {
background: rgb(28, 184, 65); background: rgb(28, 184, 65);
} }
.button-del-network {
.button-del-network,
.button-del-schedule, {
background: rgb(202, 60, 60); background: rgb(202, 60, 60);
letter-spacing: 0px; letter-spacing: 0px;
} }
.button-more-network, .button-more-network,
.button-more-schedule,
.button-rfb-send { .button-rfb-send {
background: rgb(223, 117, 20); background: rgb(223, 117, 20);
} }
#schedules input[type=number]{
max-width: 55px;
}
.button-settings-backup, .button-settings-backup,
.button-settings-restore { .button-settings-restore {
background: rgb(0, 202, 0); background: rgb(0, 202, 0);


+ 65
- 2
code/html/custom.js View File

@ -1,6 +1,7 @@
var websock; var websock;
var password = false; var password = false;
var maxNetworks; var maxNetworks;
var maxSchedules;
var messages = []; var messages = [];
var webhost; var webhost;
@ -43,7 +44,7 @@ function magnitudeType(type) {
var types = [ var types = [
"Temperature", "Humidity", "Pressure", "Temperature", "Humidity", "Pressure",
"Current", "Voltage", "Active Power", "Apparent Power", "Current", "Voltage", "Active Power", "Apparent Power",
"Reactive Power", "Power Factor", "Energy", "Energy (delta)",
"Reactive Power", "Energy", "Energy (delta)", "Power Factor",
"Analog", "Digital", "Events", "Analog", "Digital", "Events",
"PM1.0", "PM2.5", "PM10", "CO2" "PM1.0", "PM2.5", "PM10", "CO2"
]; ];
@ -100,6 +101,7 @@ function validateForm(form) {
// These fields will always be a list of values // These fields will always be a list of values
var is_group = [ var is_group = [
"ssid", "pass", "gw", "mask", "ip", "dns", "ssid", "pass", "gw", "mask", "ip", "dns",
"sch_switch","sch_operation","sch_hour","sch_minute","sch_weekdays",
"relayBoot", "relayPulse", "relayTime", "relayBoot", "relayPulse", "relayTime",
"mqttGroup", "mqttGroupInv", "mqttGroup", "mqttGroupInv",
"dczRelayIdx", "dczRelayIdx",
@ -464,6 +466,39 @@ function moreNetwork() {
$(".more", parent).toggle(); $(".more", parent).toggle();
} }
// -----------------------------------------------------------------------------
// Relays scheduler
// -----------------------------------------------------------------------------
function delSchedule() {
var parent = $(this).parents(".pure-g");
$(parent).remove();
}
function moreSchedule() {
var parent = $(this).parents(".pure-g");
$("div.more", parent).toggle();
}
function addSchedule() {
var numSchedules = $("#schedules > div").length;
if (numSchedules >= maxSchedules) {
alert("Max number of schedules reached");
return;
}
var tabindex = 200 + numSchedules * 10;
var template = $("#scheduleTemplate").children();
var line = $(template).clone();
$(line).find("input").each(function() {
$(this).attr("tabindex", tabindex++);
});
$(line).find(".button-del-schedule").on('click', delSchedule);
$(line).find(".button-more-schedule").on('click', moreSchedule);
line.appendTo("#schedules");
return line;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Relays // Relays
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -817,6 +852,31 @@ function processData(data) {
return; return;
} }
// -----------------------------------------------------------------------------
// Relays scheduler
// -----------------------------------------------------------------------------
if (key == "maxSchedules") {
maxSchedules = parseInt(data.maxSchedules);
return;
}
if (key == "schedule") {
var schedule = data.schedule;
for (var i in schedule) {
// add a new row
var line = addSchedule();
// fill in the blanks
var schedule = data.schedule[i];
Object.keys(schedule).forEach(function(key) {
var element = $("input[name=" + key + "]", line);
if (element.length) element.val(schedule[key]);
var elementsel = $("select[name=" + key + "]", line);
if (elementsel.length) elementsel.prop("value", schedule[key]);
});
}
return;
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Relays // Relays
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -1036,7 +1096,10 @@ $(function() {
$(".button-add-network").on('click', function() { $(".button-add-network").on('click', function() {
$(".more", addNetwork()).toggle(); $(".more", addNetwork()).toggle();
}); });
$(".button-add-schedule").on('click', function() {
$("div.more", addSchedule()).toggle();
});
$(document).on('change', 'input', hasChanged); $(document).on('change', 'input', hasChanged);
$(document).on('change', 'select', hasChanged); $(document).on('change', 'select', hasChanged);


+ 51
- 0
code/html/index.html View File

@ -100,6 +100,10 @@
<a href="#" class="pure-menu-link" data="panel-relay">SWITCHES</a> <a href="#" class="pure-menu-link" data="panel-relay">SWITCHES</a>
</li> </li>
<li class="pure-menu-item module module-relay">
<a href="#" class="pure-menu-link" data="panel-schedule">SCHEDULE</a>
</li>
<li class="pure-menu-item module module-color"> <li class="pure-menu-item module module-color">
<a href="#" class="pure-menu-link" data="panel-color">LIGHTS</a> <a href="#" class="pure-menu-link" data="panel-color">LIGHTS</a>
</li> </li>
@ -525,6 +529,22 @@
</div> </div>
</div> </div>
<div class="panel" id="panel-schedule">
<div class="header">
<h1>SCHEDULE</h1>
<h2>
Define actions based on the current time.
</h2>
</div>
<div class="page">
<fieldset>
<div id="schedules">
</div>
<button type="button" class="pure-button button-add-schedule">Add schedule</button>
</fieldset>
</div>
</div>
<div class="panel" id="panel-mqtt"> <div class="panel" id="panel-mqtt">
<div class="header"> <div class="header">
@ -946,6 +966,37 @@
</div> </div>
<div id="scheduleTemplate" class="template">
<div class="pure-g">
<label class="pure-u-sm-4-24 pure-u-1-3" for="sch_switch">Turn&nbsp;Switch&nbsp;No</label>
<div class="pure-u-sm-3-24 pure-u-1-3">
<input name="sch_switch" type="number" min="0" max="10" step="1" value="0" tabindex="0" />
</div>
<div class="pure-u-sm-3-24 pure-u-1-3">
<select name="sch_operation" tabindex="0">
<option value="0">OFF</option>
<option value="1">ON</option>
</select>
</div>
<label class="pure-u-sm-5-24 pure-u-1-3">at&nbsp;Time&nbsp;HH&nbsp;MM</label>
<div class="pure-u-sm-3-24 pure-u-1-3">
<input name="sch_hour" type="number" min="0" max="23" step="1" tabindex="0" value="0" />
</div>
<div class="pure-u-sm-3-24 pure-u-1-3">
<input name="sch_minute" type="number" min="0" max="59" step="1" tabindex="0" value="0" />
</div>
<div class="pure-u break"></div>
<label class="pure-u-sm-1-4 pure-u-1">on&nbsp;Weekdays:</label>
<div class="pure-u-sm-3-4 pure-u-1">
<div class="pure-u-1 weekDays-selector">
<input type="inbox" name="sch_weekdays" value="1,2,3,4,5,6,7" class="weekday" tabindex="0" />
</div>
<div class="pure-u-1 hint">1 = Sunday, 2 = Monday, ...</div>
</div>
<div class="pure-u-md-1-6 pure-u-1-4"><button type="button" class="pure-button button-del-schedule pure-u-5-6 pure-u-md-5-6">Del</button></div>
</div>
</div>
<div id="relayTemplate" class="template"> <div id="relayTemplate" class="template">
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-4"><label>Switch #<span class="id"></span></label></div> <div class="pure-u-1 pure-u-lg-1-4"><label>Switch #<span class="id"></span></label></div>


Loading…
Cancel
Save