Sunrise and sunset events
authorArjen Baart <arjen@andromeda.nl>
Mon, 7 Sep 2020 06:09:36 +0000 (08:09 +0200)
committerArjen Baart <arjen@andromeda.nl>
Mon, 7 Sep 2020 06:09:36 +0000 (08:09 +0200)
28 files changed:
src/Makefile.am
src/action.h
src/curtain.c
src/event.cpp
src/event.h
src/location.h [new file with mode: 0644]
src/logging.cpp
src/sunrise.cpp
src/wakeup.conf
src/wakeup.cpp
src/wakeup_alarms.xml
test/Makefile.am
test/event_recurrance.cpp
test/lightctrl-fade
test/lightctrl-fade.exp
test/log_retime.cpp [new file with mode: 0644]
test/sunrise_test.cpp
test/sunrise_test.exp
test/sunset_alarms.xml [new file with mode: 0644]
test/sunset_winter [new file with mode: 0755]
test/sunset_winter.log.expect [new file with mode: 0644]
test/wakeup.conf
test/wakeup_alarms.xml
test/wakeup_empty.xml [new file with mode: 0644]
test/wakeup_load_events.cpp
test/wakeup_load_events.exp
test/wakeup_winter [new file with mode: 0755]
test/wakeup_winter.log.expect [new file with mode: 0644]

index 337d117..b92acb1 100644 (file)
@@ -1,13 +1,15 @@
 bin_PROGRAMS = wakeup pwm curtain lightswitch lightcontrol read_serial
 sbin_SCRIPTS = startpwm
 
-wakeup_SOURCES = wakeup.cpp event.cpp logging.cpp
+wakeup_SOURCES = wakeup.cpp event.cpp logging.cpp sunrise.cpp
 pwm_SOURCES = pwm.c
 curtain_SOURCES = curtain.c
 lightswitch_SOURCES = lightswitch.c
 lightcontrol_SOURCES = lightcontrol.cpp logging.cpp
 
-sunrise_SOURCES = sunrise.cpp
 read_serial_SOURCES = read_serial.cpp
 
 LDADD = -lTachyon -lACL
+
+install-exec-hook:
+       chmod u+s $(DESTDIR)$(bindir)/curtain
index d05c423..c1729bb 100644 (file)
@@ -40,6 +40,38 @@ class Lightstep : public Action
    }
 };
 
+class Curtainstep : public Action
+{
+   virtual String command_line(void)
+   {
+      String cmd("curtain ");
+      cmd += parameters;
+      return cmd;
+   }
+
+   virtual int execute()
+   {
+      String cmd("curtain ");
+      cmd += parameters;
+      return system(cmd);
+   }
+};
+
+class Exitstep : public Action
+{
+   virtual String command_line(void)
+   {
+      String cmd("exit");
+      cmd += parameters;
+      return cmd;
+   }
+
+   virtual int execute()
+   {
+      exit(0);
+   }
+};
+
 class Sleepstep : public Action
 {
    virtual String command_line(void)
index 990b4f8..a452372 100644 (file)
@@ -52,44 +52,30 @@ void setup_io();
 
 int main(int argc, char *argv[])
 {
-   int i;
-
-   // Set up gpi pointer for direct register access
-   setup_io();
-   // Initialize the IO pins.
-   INP_GPIO(OPEN_OUT); // must use INP_GPIO before we can use OUT_GPIO
-   OUT_GPIO(OPEN_OUT);
-   INP_GPIO(OPEN_IN);
-
-   INP_GPIO(CLOSE_OUT); // must use INP_GPIO before we can use OUT_GPIO
-   OUT_GPIO(CLOSE_OUT);
-   INP_GPIO(CLOSE_IN);
-
    //  Scan the arguments.
  
-   const char *usage = "Usage: curtain [-c close_seconds] [-o open_seconds]\n";
+   const char *usage = "Usage: curtain [-t] [-c close_seconds] [-o open_seconds]\n";
 
    int     option;
-   int     pulse_time;
 
+   int     option_close = 0;
+   int     option_open  = 0;
+   int     option_test  = 0;
 
-   while ((option = getopt(argc, argv, "c:o:")) != -1)
+   while ((option = getopt(argc, argv, "c:o:t")) != -1)
    {
       switch (option)
       {
       case 'c':
-         pulse_time = atoi(optarg);
-         GPIO_SET = 1 << CLOSE_OUT;   //  Close the switch
-         sleep(pulse_time);
-         GPIO_CLR = 1 << CLOSE_OUT;   //  Open the switch
+         option_close = atoi(optarg);
          break;
 
       case 'o':
-         pulse_time = atoi(optarg);
-         GPIO_SET = 1 << OPEN_OUT;   //  Close the switch
-         sleep(pulse_time);
-         GPIO_CLR = 1 << OPEN_OUT;   //  Open the switch
+         option_open = atoi(optarg);
+         break;
+
+      case 't':
+         option_test = 1;
          break;
 
       case '?':
@@ -100,6 +86,40 @@ int main(int argc, char *argv[])
       }
    }
 
+   if (option_test == 0)
+   {
+      // Set up gpi pointer for direct register access
+      setup_io();
+      // Initialize the IO pins.
+      INP_GPIO(OPEN_OUT); // must use INP_GPIO before we can use OUT_GPIO
+      OUT_GPIO(OPEN_OUT);
+      INP_GPIO(OPEN_IN);
+
+      INP_GPIO(CLOSE_OUT); // must use INP_GPIO before we can use OUT_GPIO
+      OUT_GPIO(CLOSE_OUT);
+      INP_GPIO(CLOSE_IN);
+
+      if (option_close != 0)
+      {
+         GPIO_SET = 1 << CLOSE_OUT;   //  Close the switch
+         sleep(option_close);
+         GPIO_CLR = 1 << CLOSE_OUT;   //  Open the switch
+      }
+      if (option_open != 0)
+      {
+         GPIO_SET = 1 << OPEN_OUT;   //  Close the switch
+         sleep(option_open);
+         GPIO_CLR = 1 << OPEN_OUT;   //  Open the switch
+      }
+   }
+   else
+   {
+      /*  test mode  */
+
+      printf("Curtain close: %d seconds.\n", option_close);
+      printf("Curtain open:  %d seconds.\n", option_open);
+   }
 
    return 0;
 }
index 1009070..78f6655 100644 (file)
@@ -1,12 +1,21 @@
+#include <configuration.h>
+
 #include "event.h"
 #include "logging.h"
 
 extern logstream     Log;
+extern configuration Config;
+
 
 void Event::FromXML(xml_element x)
 {
    std::vector<xml_element> elems;
    std::vector<xml_element> recur;
+   std::map<String, String> attr;
+
+   attr = x.attributes();
+
+   label = attr[String("id")];
 
    elems = x["start"];
    start_time = elems[0].content();
@@ -64,7 +73,7 @@ String Event::ToXML(void)
    xml_text += "'>\n";
 
    xml_text += "    <start>";
-   xml_text += start_time.format();
+   xml_text += start_time;
    xml_text += "</start>\n";
 
    if (pattern != "none")
@@ -120,8 +129,15 @@ void Event::add_action(String command)
    int    separation;
 
    separation = command.index(' ');
-   to_execute = command(0, separation);
-   parameters = command << separation + 1;
+   if (separation > 0)
+   {
+      to_execute = command(0, separation);
+      parameters = command << separation + 1;
+   }
+   else
+   {
+      to_execute = command;
+   }
 
    if (to_execute == "lightcontrol")
    {
@@ -131,6 +147,14 @@ void Event::add_action(String command)
       act->set_parameters(parameters);
       sequence.push_back(act);
    }
+   else if (to_execute == "curtain")
+   {
+      Curtainstep   *act;
+      act = new Curtainstep;
+
+      act->set_parameters(parameters);
+      sequence.push_back(act);
+   }
    else if (to_execute == "sleep")
    {
       Sleepstep   *act;
@@ -139,6 +163,16 @@ void Event::add_action(String command)
       act->set_parameters(parameters);
       sequence.push_back(act);
    }
+   else if (to_execute == "exit")
+   {
+      Exitstep  *act;
+      act = new Exitstep;
+      sequence.push_back(act);
+   }
+   else
+   {
+      Log << "Action " + to_execute + " not recognized";
+   }
 }
 
 void Event::run_sequence()
@@ -152,12 +186,25 @@ void Event::run_sequence()
    }
 }
 
-UTC Event::next_occurance(UTC after)
+UTC Event::next_occurance(UTC after, Location loc)
 {
-   UTC next;
+   UTC    next;
+
 
-   //std::cout << "Next occ after " << after << ", pattern = " << pattern << "\n";
-   next = start_time;
+   if (start_time == "sunrise")
+   {
+      hour sunrise_at = loc.calculate_sunrise(date(after));
+      next = loc.utc_to_localtime(date(after), sunrise_at);
+   }
+   else if (start_time == "sunset")
+   {
+      hour sunset_at = loc.calculate_sunset(date(after));
+      next = loc.utc_to_localtime(date(after), sunset_at);
+   }
+   else
+   {
+      next = start_time;
+   }
 
    if (pattern != "none")
    {
@@ -200,7 +247,7 @@ std::list<Event> read_alarms(const char filename[])
 
    for (unsigned int i = 0; i < alarms.size(); i++)
    {
-      Event   alarm("name");
+      Event   alarm;
 
       alarm.FromXML(alarms[i]);
       collected_alarms.push_back(alarm);
index 2351aa5..00ea1a1 100644 (file)
@@ -4,12 +4,13 @@
 #include <xml.h>
 #include <date.h>
 
+#include "location.h"
 #include "action.h"
 
 class Event
 {
    String              label;
-   UTC                 start_time;
+   String              start_time;
 
    String              pattern;    // for recurrance
    UTC                 recurrance_interval;
@@ -34,10 +35,20 @@ public:
    void   FromXML(xml_element x);
    String ToXML(void);
 
+   String id()
+   {
+      return label;
+   }
+
+   void  start_at(UTC st)
+   {
+      start_time = st.format();
+   }
+
    void  add_action(String command);
    void  run_sequence();
 
-   UTC   next_occurance(UTC after);
+   UTC   next_occurance(UTC after, Location loc);
 };
 
 std::list<Event> read_alarms(const char *filename);
diff --git a/src/location.h b/src/location.h
new file mode 100644 (file)
index 0000000..7eac6ec
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ *  Location dependent sunrise and sunset calculations
+ */
+
+class Location
+{
+   double latitude;
+   double longitude;
+
+public:
+
+   Location(double lat, double lon)
+   {
+      latitude  = lat;
+      longitude = lon;
+   }
+
+   hour calculate_sunrise(date when);
+   hour calculate_sunset(date when);
+   UTC  utc_to_localtime(date when, hour utc_time);
+};
+
index 65241dc..e0ddd20 100644 (file)
@@ -10,6 +10,7 @@ logstream & logstream::operator << (const char *msg)
    timestamp = Now();
 
    destination << timestamp << ": " << msg << "\n";
+   destination.flush();
 
    return *this;
 }
@@ -21,6 +22,7 @@ logstream & logstream::operator << (const String &msg)
    timestamp = Now();
 
    destination << timestamp << ": " << msg << "\n";
+   destination.flush();
 
    return *this;
 }
index 65b943f..1a64abe 100644 (file)
@@ -4,9 +4,11 @@
 #include <date.h>
 #include <iostream>
 
+#include "location.h"
+
 // Calculate the time of sunrise in timezone UTC.
 
-hour calculate_sunrise(double latitude, double longitude, date when)
+hour Location::calculate_sunrise(date when)
 {
    float Declanation;
    float solaroffset;
@@ -39,7 +41,7 @@ hour calculate_sunrise(double latitude, double longitude, date when)
 
 // Calculate the time of sunset in timezone UTC.
 
-hour calculate_sunset(double latitude, double longitude, date when)
+hour Location::calculate_sunset(date when)
 {
    float Declanation;
    float solaroffset;
@@ -70,3 +72,27 @@ hour calculate_sunset(double latitude, double longitude, date when)
 
 }
 
+UTC Location::utc_to_localtime(date when, hour utc_time)
+{
+   struct tm tp_utc;
+   time_t    utc_t;
+   struct tm *local_tm;
+
+
+   tp_utc.tm_year = when.Year() - 1900;
+   tp_utc.tm_mon  = when.Month() - 1;
+   tp_utc.tm_mday = when.Day();
+   tp_utc.tm_hour = utc_time.Hour();
+   tp_utc.tm_min  = utc_time.Minute();
+   tp_utc.tm_sec  = utc_time.Second();
+   tp_utc.tm_isdst = -1;
+
+   utc_t = timegm(&tp_utc);
+
+   local_tm = localtime(&utc_t);
+   UTC local_time(mktime(local_tm));
+
+   return local_time;
+
+}
+
index fe64aa2..40b9de2 100644 (file)
@@ -1,12 +1,16 @@
 <?xml version='1.0'?>
 <wakeup version='0.0.1'>
+   <location>
+      <latitude>51.821366</latitude> 
+      <longitude>4.844401</longitude> 
+   </location>
    <logging>
       <method>file</method>
-      <destination>.</destination>
+      <destination>/var/log</destination>
       <level>0</level>
    </logging>
    <alarms>
-      <filename>wakeup_alarms.xml</filename>
+      <filename>/usr/local/etc/wakeup_alarms.xml</filename>
    </alarms>
 </wakeup>
 
index 5ccbe74..637d189 100644 (file)
@@ -1,4 +1,7 @@
 #include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 
 #include <configuration.h>
 #include <Tachyon.h>
@@ -12,7 +15,7 @@ configuration Config;
 logstream     Log;
 Tachyon       Timebase;
 
-std::list<Event> calculate_occurances(std::list<Event> alarms)
+std::list<Event> calculate_occurances(std::list<Event> alarms, Location loc)
 {
    std::list<Event>           future_alarms;
    std::list<Event>::iterator alarm;
@@ -21,7 +24,7 @@ std::list<Event> calculate_occurances(std::list<Event> alarms)
    {
       UTC next_time;
 
-      next_time = alarm->next_occurance(Now());
+      next_time = alarm->next_occurance(UTC(Timebase.time()), loc);
       //DEBUG
       std::cout << "Next alarm after will be " << next_time << ".\n";
       if ( next_time != UTC(date(0,0,0), hour(0)))
@@ -37,6 +40,7 @@ std::list<Event> calculate_occurances(std::list<Event> alarms)
 
 void reload()
 {
+   Log << "Reloading configuration.";
    Config.read("wakeup");
 }
 
@@ -46,6 +50,7 @@ void reload()
 void sighup_handler(int sig)
 {
    std::cout << "Caught signal " << sig << "\n";
+   Log << "SIGHUP received.";
 }
 
 int main()
@@ -68,35 +73,64 @@ int main()
    runfile << Timebase.name() << "\n";
    runfile.close();
 
+   std::ofstream pidfile("wakeup.pid");
+   pidfile << getpid() << "\n";
+   pidfile.close();
+
    Log << "wakeup started";
 
    std::list<Event>   set_alarms;
    std::list<Event>   pending_alarms;
 
    alarms_file = Config.find_parameter("alarms", "filename");
+   Log << "Reading alarms from " + alarms_file;
    set_alarms = read_alarms(alarms_file);
-   pending_alarms =  calculate_occurances(set_alarms);
-   // DEBUG
-   std::cout << alarms_to_XML(pending_alarms);
 
-   //  Find the next alarm
-
-   Event next_alarm;
-   std::list<Event>::iterator alarm;
+   double    latitude  = Config.find_parameter("location", "latitude");
+   double    longitude = Config.find_parameter("location", "longitude");
+   Location  local(latitude, longitude);
 
-   alarm = pending_alarms.begin();
-   next_alarm = *alarm;
-   while (alarm != pending_alarms.end())
+   while (true)
    {
-      if (alarm->next_occurance(Now()) < next_alarm.next_occurance(Now()))
+      UTC this_moment = UTC(Timebase.time());
+
+      pending_alarms =  calculate_occurances(set_alarms, local);
+      // DEBUG
+      //std::cout << alarms_to_XML(pending_alarms);
+
+      //  Find the next alarm
+
+      Event next_alarm;
+      std::list<Event>::iterator alarm;
+
+      alarm = pending_alarms.begin();
+      next_alarm.start_at(UTC("01-01-2030 00:00:00"));
+
+      while (alarm != pending_alarms.end())
+      {
+         if (alarm->next_occurance(this_moment, local) < next_alarm.next_occurance(this_moment, local))
+         {
+            next_alarm = *alarm;
+         }
+         alarm++;
+      }
+   
+      UTC next_time   = next_alarm.next_occurance(this_moment, local);
+      // DEBUG
+      //std::cout << "Sleep from " << this_moment << " until " << next_time << " (" << next_time - this_moment << " seconds)\n";
+      if (Timebase.nanosleep(next_time - this_moment) == 0)
       {
-         next_alarm = *alarm;
+         Log << "Alarm " + next_alarm.id() + " triggered at " + next_time.format();
+         next_alarm.run_sequence();
       }
-      alarm++;
+      else
+      {
+         alarms_file = Config.find_parameter("alarms", "filename");
+         Log << "Reading alarms from " + alarms_file;
+         set_alarms = read_alarms(alarms_file);
+      }
+
    }
-   // DEBUG
-   std::cout << "Sleep until " << next_alarm.next_occurance(Now()) << "\n";
-   Timebase.nanosleep(3000);
 
    std::cout << "wakeup exitting.\n";
    remove("wakeup.run");
index 6038304..d6ce205 100644 (file)
@@ -1,13 +1,55 @@
 <?xml version='1.0'?>
 <wakeup>
-  <event id='recurr-workdays'>
+  <event id='lightson_workdays'>
     <start>2020-04-02 06:00</start>
     <recurrance>
        <pattern>weekdays</pattern>
        <number>1,2,3,4,5</number>
     </recurrance>
-    <action>lightcontrol -r 100 -f 200</action>
-    <action>lightcontrol -g 100 -f 200</action>
-    <action>lightcontrol -w 100 -f 200</action>
+    <action>lightcontrol -r 100 -f 300</action>
+    <action>lightcontrol -g 100 -f 300</action>
+    <action>lightcontrol -w 100 -f 300</action>
+  </event>
+  <event id='sunrise'>
+    <start>sunrise</start>
+    <action>curtain -o 2</action>
+    <action>lightcontrol -r 0 -g 0 -w 0-f 200</action>
+    <recurrance>
+       <pattern>weekdays</pattern>
+       <number>1,2,3,4,5</number>
+    </recurrance>
+  </event>
+  <event id='open_curtains'>
+    <start>2020-04-02 09:00</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+    <action>curtain -o 1</action>
+  </event>
+  <event id='sunset'>
+    <start>sunset</start>
+    <action>curtain -c 1</action>
+    <action>lightcontrol -b 99 -w 50</action>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+  </event>
+  <event id='evening_dim'>
+    <start>2020-04-02 22:00</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+    <action>lightcontrol -b 50 -w 0 -f 900</action>
+  </event>
+  <event id='lights_off'>
+    <start>2020-04-02 22:30</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+    <action>lightcontrol -b 0 -f 1800</action>
   </event>
 </wakeup>
index 30b6217..220c0d4 100644 (file)
@@ -1,10 +1,16 @@
-TESTS = lightctrl lightctrl-oor lightctrl-fade $(check_PROGRAMS) check_output
+TESTS = lightctrl lightctrl-oor lightctrl-fade \
+        wakeup_load_events sunrise_test event_recurrance event_run_sequence \
+        wakeup_winter sunset_winter \
+        check_output
 
 AM_CPPFLAGS = -I../src
 LDADD = -lTachyon -lACL
-check_PROGRAMS = wakeup_load_events sunrise_test event_recurrance event_run_sequence
+check_PROGRAMS = wakeup_load_events sunrise_test event_recurrance event_run_sequence \
+                 log_retime
 
-wakeup_load_events_SOURCES = wakeup_load_events.cpp ../src/event.cpp ../src/logging.cpp
-event_recurrance_SOURCES = event_recurrance.cpp ../src/event.cpp ../src/logging.cpp
-event_run_sequence_SOURCES = event_run_sequence.cpp ../src/event.cpp ../src/logging.cpp
+wakeup_load_events_SOURCES = wakeup_load_events.cpp ../src/event.cpp ../src/logging.cpp ../src/sunrise.cpp
+event_recurrance_SOURCES = event_recurrance.cpp ../src/event.cpp ../src/logging.cpp ../src/sunrise.cpp
+event_run_sequence_SOURCES = event_run_sequence.cpp ../src/event.cpp ../src/logging.cpp ../src/sunrise.cpp
 sunrise_test_SOURCES = sunrise_test.cpp ../src/sunrise.cpp
+
+log_retime_SOURCES = log_retime.cpp
index 9598113..483bf68 100644 (file)
@@ -1,6 +1,6 @@
 #include "event.h"
-#include "assert.h"
 #include "logging.h"
+#include <assert.h>
 
 logstream  Log;
 
@@ -11,6 +11,7 @@ int main()
    UTC                time_to_check;
    UTC                time_expected;
    UTC                next_time;
+   Location           dummy(0, 0);
 
    Log.open("event_recurrance.log2");
 
@@ -24,14 +25,14 @@ int main()
    // Check before the alarm time
    time_to_check = UTC(date(20, 3, 2020), hour(2, 2, 2));
    time_expected = UTC(date(22, 3, 2020), hour(6, 0, 0));
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n";
    assert(next_time == time_expected);
 
    // Check after the alarm time
    time_to_check = UTC(date(22, 3, 2020), hour(6, 2, 2));
    time_expected = UTC(date(0, 0, 0), hour(0, 0, 0));
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
@@ -44,7 +45,7 @@ int main()
 
    time_to_check = UTC(date(20, 5, 2020), hour(2, 2, 2));
    time_expected = UTC(date(24, 5, 2020), hour(8, 0, 0));
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
@@ -53,7 +54,7 @@ int main()
 
    time_to_check = UTC(date(24, 5, 2020), hour(8, 2, 2));
    time_expected = UTC(date(31, 5, 2020), hour(8, 0, 0));
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
@@ -62,7 +63,7 @@ int main()
 
    time_to_check = UTC(date(25, 11, 2020), hour(2, 2, 2));
    time_expected = UTC(date(29, 11, 2020), hour(8, 0, 0));
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
@@ -75,7 +76,7 @@ int main()
 
    time_to_check = UTC(date(20, 3, 2020), hour(2, 2, 2));
    time_expected = UTC(date( 2, 4, 2020), hour(7, 0, 0)); // the first Thursday
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << ". Expected " << time_expected << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
@@ -84,7 +85,7 @@ int main()
 
    time_to_check = UTC(date( 2, 4, 2020), hour(7, 2, 2));
    time_expected = UTC(date( 3, 4, 2020), hour(7, 0, 0));   // the next Friday
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << ". Expected " << time_expected << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
@@ -93,7 +94,7 @@ int main()
 
    time_to_check = UTC(date( 3, 4, 2020), hour(7, 2, 2));
    time_expected = UTC(date( 7, 4, 2020), hour(7, 0, 0));   // the next Tuesday
-   next_time = alarm->next_occurance(time_to_check);
+   next_time = alarm->next_occurance(time_to_check, dummy);
    std::cout << "Next alarm after " << time_to_check << " will be " << next_time << ". Expected " << time_expected << "\n";
    std::cout.flush();
    assert(next_time == time_expected);
index e423a69..d800251 100755 (executable)
@@ -10,7 +10,7 @@ lightcontrol -r 30 -g 40 -b 20 -w 40 -f 120 &
 sleep 1
 
 read TACHYON_NAME <lightcontrol.run
-#tachyon -a 4.0 $TACHYON_NAME
+tachyon -a 4.0 $TACHYON_NAME
 
 while [ -f lightcontrol.run ]
 do
index 525364a..ab95e95 100644 (file)
@@ -1,19 +1,33 @@
 0 0 0 0
 0 0 0 0
-0 0 0 0
 0 1 0 1
+2 3 1 3
 3 4 2 4
+3 5 2 5
 5 7 3 7
+6 8 4 8
+6 9 4 9
 8 11 5 11
-10 14 7 14
+9 12 6 12
+9 13 6 13
+11 15 7 15
+12 16 8 16
 13 18 9 18
-15 21 10 21
+14 19 9 19
+15 20 10 20
+16 22 11 22
+17 23 11 23
 18 24 12 24
+19 26 13 26
+20 27 13 27
 21 28 14 28
+22 30 15 30
 23 31 15 31
+24 32 16 32
+25 34 17 34
 26 35 17 35
+27 37 18 37
 28 38 19 38
-Receiving message.
-The message is A10.000000.
+29 39 19 39
 30 40 20 40
 PASS lightctrl-fade (exit status: 0)
diff --git a/test/log_retime.cpp b/test/log_retime.cpp
new file mode 100644 (file)
index 0000000..ff7b612
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  Read a log file and change time time stamps to relative timestamps
+ *  from the beginning of the log.
+ */
+
+#include <fstream>
+#include <date.h>
+
+int main(int argc, char *argv[])
+{
+   std::ifstream logfile;
+   String        firstline, logline;
+   String        timefield;
+   UTC           firsttime, logtime;
+
+   logfile.open(argv[1]);
+
+   logfile >> firstline;
+   timefield = firstline(0,19);
+   firsttime = UTC(timefield);
+   logfile.seekg(0);
+
+   while (logfile >> logline)
+   {
+      String logfield;
+
+      timefield = logline(0,19);
+      logfield  = logline << 21;
+
+      logtime   = UTC(timefield);
+
+      std::cout << logtime - firsttime << " " << logfield << "\n";
+   }
+
+   return 0;
+}
index 5f00e82..80d5a82 100644 (file)
@@ -6,35 +6,51 @@
 
 #include <assert.h>
 
+#include "location.h"
+
 // Hardinxveld:
 const double latitude = 51.821366;
 const double longitude = 4.844401;
 
-extern hour calculate_sunrise(double latitude, double longtitude, date when);
-extern hour calculate_sunset(double latitude, double longtitude, date when);
 
 int main()
 {
    date   when;
    hour   sunrise_at, sunset_at;
+   Location loc(latitude, longitude);
+   UTC local_sunrise;
+   UTC local_sunset;
 
    when = date(1, 1, 2020);
 
    std::cout << "On the date " << when << ":\n";
-   sunrise_at = calculate_sunrise(latitude, longitude, when);
+   sunrise_at = loc.calculate_sunrise(when);
    std::cout << "Sunrise : " << sunrise_at << " UTC.\n";
-   sunset_at  = calculate_sunset(latitude, longitude, when);
+
+   local_sunrise = loc.utc_to_localtime(when, sunrise_at);
+   std::cout << "Sunrise : " << local_sunrise << " local time.\n";
+
+   sunset_at  = loc.calculate_sunset(when);
    std::cout << "Sunset : " << sunset_at << " UTC.\n";
+   local_sunset = loc.utc_to_localtime(when, sunset_at);
+   std::cout << "Sunset : " << local_sunset << " local time.\n";
+
    assert(sunrise_at == hour(7, 51, 29));
    assert(sunset_at  == hour(15, 29, 47));
 
    when = date(30, 6, 2020);
 
    std::cout << "On the date " << when << ":\n";
-   sunrise_at = calculate_sunrise(latitude, longitude, when);
+   sunrise_at = loc.calculate_sunrise(when);
    std::cout << "Sunrise : " << sunrise_at << " UTC.\n";
-   sunset_at  = calculate_sunset(latitude, longitude, when);
+   local_sunrise = loc.utc_to_localtime(when, sunrise_at);
+   std::cout << "Sunrise : " << local_sunrise << " local time.\n";
+
+   sunset_at  = loc.calculate_sunset(when);
    std::cout << "Sunset : " << sunset_at << " UTC.\n";
+   local_sunset = loc.utc_to_localtime(when, sunset_at);
+   std::cout << "Sunset : " << local_sunset << " local time.\n";
+
    assert(sunrise_at == hour(3, 29, 3));
    assert(sunset_at  == hour(19, 52, 13));
 }
index 410989f..6154cc0 100644 (file)
@@ -1,7 +1,11 @@
 On the date 01-01-2020:
 Sunrise : 07:51:29 UTC.
+Sunrise : 01-01-2020 08:51:29 local time.
 Sunset : 15:29:47 UTC.
+Sunset : 01-01-2020 16:29:47 local time.
 On the date 30-06-2020:
 Sunrise : 03:29:03 UTC.
+Sunrise : 30-06-2020 05:29:03 local time.
 Sunset : 19:52:13 UTC.
+Sunset : 30-06-2020 21:52:13 local time.
 PASS sunrise_test (exit status: 0)
diff --git a/test/sunset_alarms.xml b/test/sunset_alarms.xml
new file mode 100644 (file)
index 0000000..4ff475d
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version='1.0'?>
+<wakeup>
+  <event id='sunset'>
+    <start>sunset</start>
+    <action>curtain -c 1 -t</action>
+    <action>lightcontrol -b 99 -w 50</action>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+  </event>
+  <event id='evening_dim'>
+    <start>2020-04-02 22:00</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+    <action>lightcontrol -b 50 -w 0 -f 9</action>
+  </event>
+  <event id='lights_off'>
+    <start>2020-04-02 22:30</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+    <action>lightcontrol -b 0 -f 18</action>
+  </event>
+  <event id='end_test'>
+    <start>2020-04-08 11:00</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>7</number>
+    </recurrance>
+    <action>exit</action>
+  </event>
+</wakeup>
diff --git a/test/sunset_winter b/test/sunset_winter
new file mode 100755 (executable)
index 0000000..8f0e32b
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+#  Test the wakeup sequence in winter.
+#
+set -m
+PATH=../src:$PATH
+
+# Reset the lights and clear the log files
+
+lightcontrol -r 0 -g 0 -b 0 -w 0
+> lightcontrol.log
+> wakeup.log
+
+ln -f -s wakeup_empty.xml wakeup_link.xml
+
+wakeup &
+sleep 1
+
+read TACHYON_NAME <wakeup.run
+Time=`date --date '2020-12-17 15:00' +%s`
+tachyon -t $Time $TACHYON_NAME
+tachyon -a 100   $TACHYON_NAME
+
+ln -f -s sunset_alarms.xml wakeup_link.xml
+
+kill -HUP `cat wakeup.pid`
+
+fg %1
+#kill  `cat wakeup.pid`
+
+# Remove timestamps from the log.
+cut -c 22- wakeup.log >sunset_winter.log.test
+
+diff sunset_winter.log.test sunset_winter.log.expect
+
+
+exit $?
diff --git a/test/sunset_winter.log.expect b/test/sunset_winter.log.expect
new file mode 100644 (file)
index 0000000..67f6128
--- /dev/null
@@ -0,0 +1,48 @@
+wakeup started
+Reading alarms from wakeup_link.xml
+SIGHUP received.
+Reading alarms from wakeup_link.xml
+Alarm sunset triggered at 2020-12-17 16:27:00
+Executing curtain -c 1 -t
+Executing lightcontrol -b 99 -w 50
+Alarm evening_dim triggered at 2020-12-17 22:00:00
+Executing lightcontrol -b 50 -w 0 -f 9
+Alarm lights_off triggered at 2020-12-17 22:30:00
+Executing lightcontrol -b 0 -f 18
+Alarm sunset triggered at 2020-12-18 16:27:00
+Executing curtain -c 1 -t
+Executing lightcontrol -b 99 -w 50
+Alarm evening_dim triggered at 2020-12-18 22:00:00
+Executing lightcontrol -b 50 -w 0 -f 9
+Alarm lights_off triggered at 2020-12-18 22:30:00
+Executing lightcontrol -b 0 -f 18
+Alarm sunset triggered at 2020-12-19 16:26:53
+Executing curtain -c 1 -t
+Executing lightcontrol -b 99 -w 50
+Alarm evening_dim triggered at 2020-12-19 22:00:00
+Executing lightcontrol -b 50 -w 0 -f 9
+Alarm lights_off triggered at 2020-12-19 22:30:00
+Executing lightcontrol -b 0 -f 18
+Alarm sunset triggered at 2020-12-20 16:26:48
+Executing curtain -c 1 -t
+Executing lightcontrol -b 99 -w 50
+Alarm evening_dim triggered at 2020-12-20 22:00:00
+Executing lightcontrol -b 50 -w 0 -f 9
+Alarm lights_off triggered at 2020-12-20 22:30:00
+Executing lightcontrol -b 0 -f 18
+Alarm sunset triggered at 2020-12-21 16:26:47
+Executing curtain -c 1 -t
+Executing lightcontrol -b 99 -w 50
+Alarm evening_dim triggered at 2020-12-21 22:00:00
+Executing lightcontrol -b 50 -w 0 -f 9
+Alarm lights_off triggered at 2020-12-21 22:30:00
+Executing lightcontrol -b 0 -f 18
+Alarm sunset triggered at 2020-12-22 16:26:48
+Executing curtain -c 1 -t
+Executing lightcontrol -b 99 -w 50
+Alarm evening_dim triggered at 2020-12-22 22:00:00
+Executing lightcontrol -b 50 -w 0 -f 9
+Alarm lights_off triggered at 2020-12-22 22:30:00
+Executing lightcontrol -b 0 -f 18
+Alarm end_test triggered at 2020-12-23 11:00:00
+Executing exit
index fe64aa2..10395ed 100644 (file)
@@ -1,12 +1,16 @@
 <?xml version='1.0'?>
 <wakeup version='0.0.1'>
+   <location>
+      <latitude>51.821366</latitude> 
+      <longitude>4.844401</longitude> 
+   </location>
    <logging>
       <method>file</method>
       <destination>.</destination>
       <level>0</level>
    </logging>
    <alarms>
-      <filename>wakeup_alarms.xml</filename>
+      <filename>wakeup_link.xml</filename>
    </alarms>
 </wakeup>
 
index 6038304..48cc16a 100644 (file)
@@ -1,13 +1,38 @@
 <?xml version='1.0'?>
 <wakeup>
-  <event id='recurr-workdays'>
+  <event id='lightson_workdays'>
     <start>2020-04-02 06:00</start>
     <recurrance>
        <pattern>weekdays</pattern>
        <number>1,2,3,4,5</number>
     </recurrance>
-    <action>lightcontrol -r 100 -f 200</action>
-    <action>lightcontrol -g 100 -f 200</action>
-    <action>lightcontrol -w 100 -f 200</action>
+    <action>lightcontrol -r 10 -f 2</action>
+    <action>lightcontrol -g 10 -f 2</action>
+    <action>lightcontrol -w 10 -f 2</action>
+  </event>
+  <event id='sunrise'>
+    <start>sunrise</start>
+    <action>curtain -o 2 -t</action>
+    <action>lightcontrol -r 0 -g 0 -w 0-f 2</action>
+    <recurrance>
+       <pattern>weekdays</pattern>
+       <number>1,2,3,4,5</number>
+    </recurrance>
+  </event>
+  <event id='open_curtains'>
+    <start>2020-04-02 09:00</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>1</number>
+    </recurrance>
+    <action>curtain -o 1 -t</action>
+  </event>
+  <event id='end_test'>
+    <start>2020-04-08 11:00</start>
+    <recurrance>
+       <pattern>days</pattern>
+       <number>7</number>
+    </recurrance>
+    <action>exit</action>
   </event>
 </wakeup>
diff --git a/test/wakeup_empty.xml b/test/wakeup_empty.xml
new file mode 100644 (file)
index 0000000..74eaf5a
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version='1.0'?>
+<!--
+An empty list of events to start a test with.
+The test scenario will set an appropriate time and load a different
+list of events.
+-->
+<wakeup>
+</wakeup>
index ed8e2ff..f09e44e 100644 (file)
@@ -10,10 +10,11 @@ int main()
    std::list<Event>   set_alarms;
 
    set_alarms = read_alarms("wakeup-01.xml");
-
    std::cout << alarms_to_XML(set_alarms);
 
    set_alarms = read_alarms("wakeup-02.xml");
+   std::cout << alarms_to_XML(set_alarms);
 
+   set_alarms = read_alarms("wakeup_empty.xml");
    std::cout << alarms_to_XML(set_alarms);
 }
index b277957..b2f57b5 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version='1.0'?>
 <wakeup>
-  <event id='name'>
-    <start>2020-03-22 06:00:00</start>
+  <event id='monday_wakeup'>
+    <start>2020-03-22 06:00</start>
     <action>lightcontrol -r 100 -f 200</action>
     <action>lightcontrol -g 100 -f 200</action>
     <action>lightcontrol -w 100 -f 200</action>
 </wakeup>
 <?xml version='1.0'?>
 <wakeup>
-  <event id='name'>
-    <start>2020-03-22 06:00:00</start>
+  <event id='non-recurring'>
+    <start>2020-03-22 06:00</start>
     <action>lightcontrol -r 100 -f 200</action>
   </event>
-  <event id='name'>
-    <start>2020-05-24 08:00:00</start>
+  <event id='recurr-weekly'>
+    <start>2020-05-24 08:00</start>
     <pattern>days</pattern>
     <number>7</number>
     <action>lightcontrol -g 100 -f 200</action>
   </event>
-  <event id='name'>
-    <start>2020-04-02 07:00:00</start>
+  <event id='recurr-somedays'>
+    <start>2020-04-02 07:00</start>
     <pattern>weekdays</pattern>
     <number>2,4,5</number>
     <action>lightcontrol -g 100 -f 200</action>
   </event>
 </wakeup>
+<?xml version='1.0'?>
+<wakeup>
+</wakeup>
 PASS wakeup_load_events (exit status: 0)
diff --git a/test/wakeup_winter b/test/wakeup_winter
new file mode 100755 (executable)
index 0000000..4970c7c
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+#  Test the wakeup sequence in winter.
+#
+set -m
+PATH=../src:$PATH
+
+# Reset the lights and clear the log files
+
+lightcontrol -r 0 -g 0 -b 0 -w 0
+> lightcontrol.log
+> wakeup.log
+
+ln -f -s wakeup_empty.xml wakeup_link.xml
+
+wakeup &
+sleep 1
+
+read TACHYON_NAME <wakeup.run
+Time=`date --date '2020-12-17 05:00' +%s`
+tachyon -t $Time $TACHYON_NAME
+tachyon -a 100   $TACHYON_NAME
+
+ln -f -s wakeup_alarms.xml wakeup_link.xml
+
+kill -HUP `cat wakeup.pid`
+
+fg %1
+#kill  `cat wakeup.pid`
+
+# Remove timestamps from the log.
+cut -c 22- wakeup.log >wakeup_winter.log.test
+
+diff wakeup_winter.log.test wakeup_winter.log.expect
+
+exit $?
diff --git a/test/wakeup_winter.log.expect b/test/wakeup_winter.log.expect
new file mode 100644 (file)
index 0000000..0c15a91
--- /dev/null
@@ -0,0 +1,55 @@
+wakeup started
+Reading alarms from wakeup_link.xml
+SIGHUP received.
+Reading alarms from wakeup_link.xml
+Alarm lightson_workdays triggered at 2020-12-17 06:00:00
+Executing lightcontrol -r 10 -f 2
+Executing lightcontrol -g 10 -f 2
+Executing lightcontrol -w 10 -f 2
+Alarm sunrise triggered at 2020-12-17 08:54:16
+Executing curtain -o 2 -t
+Executing lightcontrol -r 0 -g 0 -w 0-f 2
+Alarm open_curtains triggered at 2020-12-17 09:00:00
+Executing curtain -o 1 -t
+Alarm lightson_workdays triggered at 2020-12-18 06:00:00
+Executing lightcontrol -r 10 -f 2
+Executing lightcontrol -g 10 -f 2
+Executing lightcontrol -w 10 -f 2
+Alarm sunrise triggered at 2020-12-18 08:54:23
+Executing curtain -o 2 -t
+Executing lightcontrol -r 0 -g 0 -w 0-f 2
+Alarm open_curtains triggered at 2020-12-18 09:00:00
+Executing curtain -o 1 -t
+Alarm open_curtains triggered at 2020-12-19 09:00:00
+Executing curtain -o 1 -t
+Alarm open_curtains triggered at 2020-12-20 09:00:00
+Executing curtain -o 1 -t
+Alarm lightson_workdays triggered at 2020-12-21 06:00:00
+Executing lightcontrol -r 10 -f 2
+Executing lightcontrol -g 10 -f 2
+Executing lightcontrol -w 10 -f 2
+Alarm sunrise triggered at 2020-12-21 08:54:28
+Executing curtain -o 2 -t
+Executing lightcontrol -r 0 -g 0 -w 0-f 2
+Alarm open_curtains triggered at 2020-12-21 09:00:00
+Executing curtain -o 1 -t
+Alarm lightson_workdays triggered at 2020-12-22 06:00:00
+Executing lightcontrol -r 10 -f 2
+Executing lightcontrol -g 10 -f 2
+Executing lightcontrol -w 10 -f 2
+Alarm sunrise triggered at 2020-12-22 08:54:23
+Executing curtain -o 2 -t
+Executing lightcontrol -r 0 -g 0 -w 0-f 2
+Alarm open_curtains triggered at 2020-12-22 09:00:00
+Executing curtain -o 1 -t
+Alarm lightson_workdays triggered at 2020-12-23 06:00:00
+Executing lightcontrol -r 10 -f 2
+Executing lightcontrol -g 10 -f 2
+Executing lightcontrol -w 10 -f 2
+Alarm sunrise triggered at 2020-12-23 08:54:16
+Executing curtain -o 2 -t
+Executing lightcontrol -r 0 -g 0 -w 0-f 2
+Alarm open_curtains triggered at 2020-12-23 09:00:00
+Executing curtain -o 1 -t
+Alarm end_test triggered at 2020-12-23 11:00:00
+Executing exit