Tag Archives: blink(1)

RGB LED Meeting Warning Light (Lotus Notes Edition)

At the end of my last post I mentioned trying to get the same set up working with my work Lotus Notes calendar.

After a little poking around with the Lotus Notes Java API here is the result:

package uk.me.hardill.notes;

/**
 * NextCalEntry
 * 
 * Sets the RGB values for Blink(1)/Digispark+RGB
 * according to the time to the next meeting in 
 * your Lotus Notes Calendar
 * 
 * This should be run with the JRE that ships with
 * Lotus notes as it has the required classes on 
 * classpath and have Lotus notes directory on 
 * library path e.g.
 * LD_LIBRARY_PATH=/opt/ibm/lotus/notes
 * /opt/ibm/lotus/notes/jvm/bin/java NextCalEntry -d
 */

import java.text.SimpleDateFormat;
import java.util.Date;

import lotus.domino.Database;
import lotus.domino.DateTime;
import lotus.domino.DbDirectory;
import lotus.domino.Document;
import lotus.domino.NotesFactory;
import lotus.domino.NotesThread;
import lotus.domino.Session;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;

public class NextCalEntry extends NotesThread {

   static SimpleDateFormat dateFormat = new SimpleDateFormat(
         "dd/MM/yy H:m:s z");

   static int rgb[] = { 0, 200, 0 };

   static int pollInterval = 300;
   static boolean mqtt = false;
   static String topic = "";
   static boolean digi = true;

   public static void main(String argv[]) {

      for (int i = 0; i < argv.length; i++) {
         if (argv[i].equals("-b")) {

         } else if (argv[i].equals("-d")) {

         } else if (argv[i].equals("-t")) {
            mqtt = true;
            topic = argv[++i];
         }
      }

      NextCalEntry nextCalEntry = new NextCalEntry();
      nextCalEntry.start();
   }

   public void runNotes() {
      try {
         Session s = NotesFactory.createSession();

         DbDirectory dir = s.getDbDirectory(null);
         Database db = dir.getFirstDatabase(DbDirectory.DATABASE);

         db = dir.openMailDatabase();

         if (db.isOpen() == false)
            db.open();

         db = dir.openMailDatabase();
         View calendarView = db.getView("($Calendar)");

         DateTime sdt = s.createDateTime("today");
         sdt.setNow();
         DateTime edt = s.createDateTime("today");
         edt.setNow();
         edt.adjustDay(+1);

         ViewEntryCollection vec = calendarView.getAllEntries();

         ViewEntry entry = vec.getFirstEntry();

         int offset = 3600;
         boolean poisonPill = false;
         while (entry != null) {
            Document caldoc = entry.getDocument();
            String sub = caldoc.getItemValueString("Subject");
            DateTime startDate = null;
            try {
               startDate = (DateTime) caldoc.getItemValueDateTimeArray(
                     "StartDate").firstElement();

            } catch (Exception e) {

            }

            if (startDate != null) {

               for (int i = 0; i < caldoc.getItemValueDateTimeArray(
                     "StartDateTime").size(); i++) {

                  int st = sdt.timeDifference((DateTime) caldoc
                        .getItemValueDateTimeArray("StartDateTime")
                        .get(i));
                  int en = edt.timeDifference((DateTime) caldoc
                        .getItemValueDateTimeArray("EndDateTime")
                        .get(i));

                  Date start = dateFormat.parse(caldoc
                        .getItemValueDateTimeArray("StartDateTime")
                        .get(i).toString());
                  Date end = dateFormat.parse(caldoc
                        .getItemValueDateTimeArray("EndDateTime")
                        .get(i).toString());
                  Date now = new Date();

                  if ((st <= 0) & (en >= 0)) {
                     if ((-1 * st) < offset) {
                        offset = (-1 * st);
                     }

                  } else if (now.after(start) && now.before(end)) {
                     offset = -1;
                     poisonPill = true;
                     break;
                  }
               }
            }
            if (poisonPill) {
               break;
            }
            entry = vec.getNextEntry();
         }

         if (offset > 0 && offset <= 300) {
            // red
            rgb[0] = 20;
            rgb[1] = 0;
            rgb[2] = 0;
         } else if (offset > 300 && offset <= 600) {
            // redish
            rgb[0] = 15;
            rgb[1] = 5;
            rgb[2] = 0;
         } else if (offset > 600 && offset <= 900) {
            // greenish/redish
            rgb[0] = 10;
            rgb[1] = 10;
            rgb[2] = 0;
         } else if (offset > 900 && offset <= 1200) {
            // greenish
            rgb[0] = 5;
            rgb[1] = 15;
            rgb[2] = 0;
         } else if (offset == -1) {
            // blue
            rgb[0] = 0;
            rgb[1] = 0;
            rgb[2] = 20;
         } else {
            // green
            rgb[0] = 0;
            rgb[1] = 20;
            rgb[2] = 0;
         }

         if (mqtt) {
            // TODO
            // connect to broker and publish
         } else {
            Runtime runtime = Runtime.getRuntime();
            String cmd[];
            if (digi) {
               System.out.println(rgb[0] + " " + rgb[1] + " " + rgb[2]);
               cmd = new String[4];
               cmd[0] = "DigiRGB.py";
               cmd[1] = rgb[0] + "";
               cmd[2] = rgb[1] + "";
               cmd[3] = rgb[2] + "";
            } else {
               System.out.println(rgb[0] + "," + rgb[1] + "," + rgb[2]);
               cmd = new String[3];
               cmd[0] = "blink1-tool";
               cmd[1] = "--rgb";
               cmd[2] = rgb[0] + "," + rgb[1] + "," + rgb[2];
            }
            Process proc = runtime.exec(cmd);
            proc.waitFor();
         }

      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

It is also available as gist here (not embedded as there seams to be CSS background colour issue)

Run this in a script every 5 mins either with a sleep loop or as a crontab entry similar to the ones described at the end of the previous post

RGB LED Meeting Warning Light

In my previous post I talked about the Digispark board and the RGB LED shield I had just received.

Digispark and RGB Shield

This afternoon in a spare 30mins I had while some tests ran I built my first little hack to use one of them. It is inspired by this really cool hack that got mentioned on twitter by Dave CJ while we were debating Blink(1) vs Digispark/RGB Shields. The idea of a strip of LEDs that represent the working day with different colours for free time and meetings is cool, but I only have 1 LED to work with, so I thought how about just showing how close to the next meeting I am.

The idea is that if the next entry in my calendar is more than 20mins away then the light is green, as the meeting start time gets closer the LED changes closer to red and finally during the meeting if glows blue.

I started out prototyping this against my google calendar as it’s pretty easy to get at the data, if you poke around in the settings page you can get hold of a URL that points to either a XML or ICAL version of the data.

I’m running this on my Raspberry Pi so python seamed like a good choice to run this up in.

#!/usr/bin/python

import sys
import pycurl
import StringIO
from icalendar import Calendar, Event
from datetime import datetime, timedelta

curl = pycurl.Curl()
curl.setopt(pycurl.URL,sys.argv[1])
body = StringIO.StringIO()
curl.setopt(pycurl.WRITEFUNCTION, body.write)
curl.perform()

cal = Calendar.from_ical(body.getvalue())
body.close()

now = datetime.now()

offset = 3600

for component in cal.walk():
  if component.name == "VEVENT":
    dtstart = component["dtstart"].to_ical()
    dtend = component["dtend"].to_ical()
    if "T" in dtstart:
      dt = datetime.strptime(dtstart, "%Y%m%dT%H%M%SZ")
      end = datetime.strptime(dtend, "%Y%m%dT%H%M%SZ")

      if dt > now:
        delta = dt - now
        if delta.days == 0:
          if delta.seconds < offset:
            offset = delta.seconds
      elif end > now and dt < now:
        offset = -1
        break

if offset > 0 and offset < 300:
  print "200 0 0"
elif offset >= 300 and offset < 600:
  print "150 50 0"
elif offset >= 600 and offset < 900:
  print "100 100 0"
elif offset >= 900 and offset < 1200:
  print "50 150 0"
elif offset == -1:
  print "0 0 200"
else:
  print "0 200 0"

This script outputs RGB values that can be fed directly to DigiBlink.py script like this in a cron job

*/5 * * * * DigiBlink.py `calColur.py <calendar URL>`

Or since I’ve set up a little script to control my DigiSpark via MQTT the following publishes the output of the script on the ‘digiblink’ topic on the local broker.


*/5 * * * * calColur.py <calendar URL> | mosquitto_pub -h localhost -t digiblink -l

This script should also work with the command line tool for the Blink(1)

I still need to tweak the colours for each step and next work out how to get the same feed out of the company calendaring system, but it’s a good start.