Sunday, June 13, 2010

TeamCity (continuous integration) integration with lava lamps

I wanted to hook up some lavalamps to our CI server.

Shopping list:
2 lava lamps (one red and one green, Teknikmagasinet in Sweden, 199:- sek a piece)
1 usb controlled power outlet (Silver Shield Power Manager bought at Kjell och Company in Sweden, 449:- sek)

The usb controlled poweroutlet comes with a command line program. I wrote this groovy script to poll the rss feed for our projects on the TeamCity server. The script has an added power save feature that allows you to configure it to turn off the lamps at weekends, it also allows you to only run the lamps between certain times of the day, say 08:00-19:00.

Have fun!


File: CILampControl.groovy
----------------------------
/**
 * User: Jonas Ekstrand
 * Date: 2010-maj-27
 * Time: 14:43:22
 */
def config = new ConfigSlurper().parse(new File('lampcontrol.config').toURL())

def LampController lampControl = new LampController(config.executable,
        config.deviceName, config.greenSocketName, config.redSocketName)
def TeamCityBuildFeed teamCityFeed = new TeamCityBuildFeed(config.feedUrl);

enum STATE {
  BUILD_SUCCESSFUL, BUILD_FAILED, LIGHTS_OFF
}
STATE lastState
//noinspection GroovyInfiniteLoopStatement
while (true) {
  // Get state, first check if lights should be on or off
  STATE state = getState(teamCityFeed, config.operateHours, config.operateWeekends)
  // Operate lights
  //noinspection GroovyVariableNotAssigned
  if (state != lastState)
    updateLamps(state, lampControl)
  // Update last state
  lastState = state
  // Print state
  println """${Calendar.getInstance().getTime()} $state
    Operate weekends:$config.operateWeekends Operate hours:$config.operateHours"""
  // Sleep
  Thread.sleep config.secondsBetweenChecks * 1000
}

private def updateLamps(STATE state, LampController lampControl) {
  switch (state) {
    case STATE.LIGHTS_OFF: lampControl.lightsOff(); return;
    case STATE.BUILD_SUCCESSFUL: lampControl.greenOnRedOff(); return;
    case STATE.BUILD_FAILED: lampControl.redOnGreenOff(); return;
  }
}

private STATE getState(TeamCityBuildFeed teamCityCI1, String operateHours, boolean operateWeekends) {
  //Should not operate during weekends and it is weekend OR is out of operation hours
  if ((!operateWeekends && !isWeekday()) || !isOperationHours(operateHours))
    return STATE.LIGHTS_OFF
  //Is build ok or not
  return teamCityCI1.getBuildStatus() ? STATE.BUILD_SUCCESSFUL : STATE.BUILD_FAILED
}

boolean isWeekday() {
  int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
  return (day != Calendar.SUNDAY && day != Calendar.SATURDAY)
}

private boolean isOperationHours(String operateHours) {
  int hourNow = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
  def startAndStop = operateHours.split("-")
  int fromHour = startAndStop[0].toInteger()
  int toHour = startAndStop[1].toInteger()
  return hourNow >= fromHour && hourNow < toHour
}

class LampController {
  private String greenOnCmd, greenOffCmd, redOnCmd, redOffCmd

  public LampController(String executable, String deviceName,
                        String greenSocketName, String redSocketName) {
    greenOnCmd = "$executable -on -$deviceName -$greenSocketName"
    greenOffCmd = "$executable  -off -$deviceName -$greenSocketName"
    redOnCmd = "$executable  -on -$deviceName -$redSocketName"
    redOffCmd = "$executable  -off -$deviceName -$redSocketName"
  }

  def redOnGreenOff() {
    runCmd greenOffCmd
    runCmd redOnCmd
  }

  def greenOnRedOff() {
    runCmd greenOnCmd
    runCmd redOffCmd
  }

  def lightsOff() {
    runCmd redOffCmd
    runCmd greenOffCmd
  }

  private runCmd(String cmd) {
    Process process = cmd.execute()
    process.waitFor()
    process.destroy()
  }
}

class TeamCityBuildFeed {
  private String feedUrl;

  def TeamCityBuildFeed(feedUrl) {
    this.feedUrl = feedUrl;
  }

  def boolean getBuildStatus() {
    try {
      def feed = new XmlParser().parse(feedUrl)
      return feed.entry[0].'dc:creator'.text().equals("Successful Build")
    } catch (Exception e) {
      println "Could not get status from CI server $e"
      return false;
    }
  }
}
File: lampcontrol.config
----------------------------
/** How often (seconds) to check the for build state **/ secondsBetweenChecks=10 /** The url to the build server's rss feed for this project**/ feedUrl="http://SERVER_NAME/guestAuth/feed.html?projectId=PROJECT_ID&itemsType=builds&buildStatus=successful&buildStatus=failed&userKey=guest" /** If lights should be on during weekends **/ operateWeekends=false /** Between what hours should the lights be on. This settings assumes 24H clock. Use 0-24 for use round the clock **/ operateHours="8-19" /** Silver Shield Power Manager **/ executable="C:\\Program\\Gembird\\Power Manager\\pm.exe" deviceName="lava" greenSocketName="green" redSocketName="red"

Thursday, April 8, 2010

iCal not adding ics events if not using a local calendar

At my new consulting assignment they use ms exchange and I use apple mail and iCal synced with all my google calendars.

I use the google calendar as my primary calendar in iCal and had even deleted the local one. But, when I received event invites from exchange/outlook with .ics files they wouldn't open in iCal. iCal got the focus but the event was not added. After some experimenting I noticed that if I created a local calendar, the event got added, but only to the local calendar. The workaround for me was at first to manually save all the .ics attachments and use the import action in iCal, but after the 20:th invite, that routine got really old!!

After doing som searching i realized others where feeling the pain: http://www.google.com/support/forum/p/Calendar/thread?tid=1584cffedb7c8c18&hl=en
http://forums.macosxhints.com/archive/index.php/t-98833.html

Soo I created this applescript, you need to have a local calendar soo that events get picked up from apple mail. After running the script all those events are moved to a google calendar (or any other calendar) of your choice. A future improvement could be to make the script run in the background checking and moving events every minute or soo or to trigger it somehow when a local event is created, does anyone know how to do that?

I hope you find this script useful!
tell application "iCal"
 (* Change the calendar name to what ever you google calendar is called *)
 set googleCalendar to calendar "name of google calendar"
 
 (* change "local" below to what ever name you use *)
 tell calendar "local"
  set allLocalEvents to every event
  repeat with currentEvent in allLocalEvents
   
   (* Move the local event to gCal*)
   move currentEvent to the end of events of googleCalendar
   
   (*Del local event*)
   delete currentEvent
   
  end repeat
  
  (*Reload = upload new event to google*)
  reload calendars
  
 end tell
end tell



Wednesday, January 20, 2010

Mac OS X iSync plugin for Nokia 3120 Classic

My iPhone 3GS broke down and I got handed a Nokia 3120 classic as a temporary replacement phone. When i opened the phonebook my lovely iPhone had stored all contacts using phonenumber as name. What to do?

Luckily all my contacts was stored in the Mac's adressbook! I fired up iSync, but the phone was'nt recognized. A quick gooogle led me to nokias web where iSync plugins could be downloaded :) At last I thought. After a few minutes a realized that only the more expensive phones have iSync plugins..

A new google led me to this brilliant blog, but it didn't work and the blog has been closed.
The plugins on that blog probably didn't work because run snow leopard and the latest iSync.

So I put together my own plugin using the nokia 6130 as template, and it worked!

Instructions:
1. Download the plugin from here.
2. Unzip the file and you will have a plugin called Nokia-3120c.phoneplugin
3. In finder, open up your applications folder
4. Right click iSync and select show package contents
5. Open the folder Contents->Plugins->ApplePhoneConduit.syncdevice->Contents->Plugins
6. Drag the file Nokia-3120c.phoneplugin to this folder, you may have to enter your password

Restart iSync and you are good to go, remember this only works using bluetooth, not the usb cable.

Good luck!
/ Jonas