Calendar

I really like to plan the day in my calendar. Therefore I added a lot of external ical feeds like meetup, open-air cinema and for sure lauchlibrary.

In order to decide on transportation I always have the weather underground page in a separate browser tab. This is very inconvenient, therefore I wrote a small script to get weather predictions via API call from wunderground and export an ical feed and update my google calendar with weather conditions.

Wunderground

Weather Underground is (or at least was for many years) the coolest weather page in the internet. Really great UI and a wonderful API to get current weather conditions and weather predictions for the next 10 days. Further more (and that is why I really really like it) users could send their own weather sensor data to the side to enhance the sensor mash network and get a nice visualization. Unfortunately the service is loosing features on a monthly basis and also the page itself is down for several hours every now and then. Very sad, but I still love it.

As I said they have a nice API to get weather forecast for the next 10 days on an hourly base. OK, we can all discuss how dependable a weather prediction for a certain hour in 8 days is, but at least for the next days it is really helpful. I am using the forecast10day and the hourly10day API endpoints to get a nicely formatted JSON document from wunderground. If you want to run this script for your own area you need an account and an API key as the calls are restricted (but for free).

PWS

My favorite Maker-space (Motionlab.berlin) has an epic weather phalanx (as I love to call it) and sends in local weather conditions to wunderground. Therefore I can ask beside weather conditions in a city for weather conditions based a certain weather reporting station. In our case its the IBERLIN1705 station. Check out current conditions here.

Forecast10day

The API call to

http://api.wunderground.com/api/YOUR-API-KEY-HERE/forecast10day/q/pws:IBERLIN1705.json

returns for each day of the next 10 days information about humidity, temperature (min/max), snow, rain, wind and many more. I take these data and create one calendar entry each morning at 06:00-06:15 with summary information for the day. Specially for days beyond the 4 days boundry this condition is more accurate then an hourly information. Getting this information in python is very easy:

 try:
            data   = json.loads(requests.get("http://api.wunderground.com/api/YOUR-API-HERE/forecast10day/q/pws:IBERLIN1705.json").content)
        except:
            print("Error in Forecast")
            return False

        for e in data['forecast']['simpleforecast']['forecastday']:
            day        = e['date']['day']
            month      = e['date']['month']
            year       = e['date']['year'] 
            conditions = e['conditions']
            humidity   = e['avehumidity']
            high       = e['high']['celsius']
            low        = e['low']['celsius']
            snow       = e['snow_allday']['cm']
            rain       = e['qpf_allday']['mm']

I am using requests to make the REST call and parse the “content” value with json loads. Easy as it looks. The data var contains the dictionary with all weather information on a silver tablet (if the API is not down, happens way to often).

Hourly10day

http://api.wunderground.com/api/YOUR-API-KEY/hourly10day/q/pws:IBERLIN1705.json

contains the weather information on an hourly basis for the next 10 days, So the parsing is very similar to the forcast API call. I am specially interested here in rain, snow, temperature, wind, dewpoint and UV-Index as these are values I want to monitor and add calendar entries when they are outside a certain range.

  • Wind > 23 km/h
  • Temperature > 30 or < -10 C
  • UV-Index > 4 (6 is max)
  • Rain and Snow in general
  • (Temperature – Dew point) < 3

Humidity in general are not so important and highly dependent on the current temperature. But dew point (“the atmospheric temperature (varying according to pressure and humidity) below which water droplets begin to condense and dew can form.”) is very interesting when you want to know if it is getting muggy. Even when it is 10 C a very low difference between temperature and dew point means you really feel the cold crawling into your bones. 🙂

Ical

To create an Ical feed I use the icalendar library in python. Very handy to create events and export them as an ical (XML) feed.

newcal = Calendar()

event = Event()    
event.add('summary', "%s-%sC %s%% Rain:%s Snow:%s %s" % (low, high, humidity, rain, snow, conditions))
event.add('dtstart', datetime(year,month,day,6, 0,0,0,timezone('Europe/Berlin')))
event.add('dtend',   datetime(year,month,day,6,15,0,0,timezone("Europe/Berlin")))
event.add('description', DESC)

newcal.add_component(event)
return newcal.to_ical()

Summary will be the text your calendar program displays when displaying the calendar itself, while description will be displayed then showing calendar entry details. “dtstart” and “dtend” mark the time range. For the timezone I use the pytz library. “to_ical()”. That’s basically all you need to create an ical feed.

Google

The google calendar can import and subscribe to calendars. While import adds the calendar entries to an existing calendar once (great for concerts, public transport booking), subscribe creates a new calendar and updates the feed every > 24 hours. This is great for long lasting events like meetup or rocket starts but weather predictions changes several times per hour. Therefore I added a small feature to the script to actively delete and create calendar entries. So I can do it every 3 hours and keep the calendar up to date.

As always google offers nice and very handy API endpoints to manipulate the data. Beside calling the API Rest endpoint by hand there are libraries for different languages. I use the “googleapiclient” and “oauth2client” to access my calendar. First step is to create a new calendar in google, then active the calendar API in the developer console and create an API key for your app. The googleapiclient takes care of the Oauth dance and stares credentials in a local file.

store = file.Storage('token.json')
creds = store.get()

if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
  creds = tools.run_flow(flow, store)
        
return build('calendar', 'v3', http=creds.authorize(Http()))

If you call this function the very first time to requires the OAuth dance. Basically call a webpage and give access to your google calendar. The secreats are stored in the token.json file and reloaded every call.

Deleting old events

service       = getService()
events_result = service.events().list(calendarId=CALENDAR_ID, maxResults=100, singleEvents=True, orderBy='startTime').execute()
events        = events_result.get('items', [])
        
for e in events:
  service.events().delete(calendarId=CALENDAR_ID, eventId=e['id']).execute()

“getService” calls the upper function to get an access object. “events().list().execute() request a list of the first 100 calendar entries and “events_result.get() returns an array with all calendar entries and their details. “service.events().delete().execute() removes these entries.

Creating new events
ge = {
       'summary'    : '',
       'description': DESC,
       'start': {
                 'dateTime' : '',
                 'timeZone' : 'Europe/Berlin',
                },
       'end':   {
                 'dateTime' : '',
                 'timeZone' : 'Europe/Berlin',
                }
     }

ge['summary']           = "%s-%sC %s%% Rain:%s Snow:%s %s" % (low, high, humidity, rain, snow, conditions)
ge['start']['dateTime'] = '%s-%s-%sT06:00:00' % (year, month, day)
ge['end'  ]['dateTime'] = '%s-%s-%sT06:15:00' % (year, month, day)

service = getService()
service.events().insert(calendarId=CALENDAR_ID, body=ge).execute()

Very similar to the delete calls, the add calls gets the credentials, and calls “events().insert().execute()” with a dictionary containing the detailed information.

Docker container

The docker container is very simple.

FROM python:latest

RUN pip install icalendar requests Flask oauth2client google-api-python-client iso8601

ADD Exporter.py      Exporter.py
ADD credentials.json credentials.json
ADD token.json       token.json

EXPOSE 80

CMD python /Exporter.py

I am using the latest python docker container, installing some libraries with pip and copy the python file, the creadentials and token json files.

The repo

The complete source code can be found in my github repository.

The calendar for Berlin weather conditions can be found and added here.