Skip to content

Instantly share code, notes, and snippets.

@tfohlmeister
Last active July 30, 2020 23:17
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save tfohlmeister/5781087 to your computer and use it in GitHub Desktop.
Save tfohlmeister/5781087 to your computer and use it in GitHub Desktop.
[dashing] Google Calendar Event widget for Dashing!

Description

Dashing widget to display a bunch of current and coming-up Google Calendar events.

The widget shows only one event and is based on the text-widget which is default in the Dashing installation. The time is displayed as human-readable time string with the help of MomentJS.

A Dashing job fetches the events of a given public or private calendar url, orders events to match starting time and sends the data to the dashboard. A certain event offset can be set for each calendar widget so that it will show the first (data-pre="0"), the second (data-pre="1") or any other following event based on the offset. For each calendar you'll have to define a name which enables you to assign custom background colors for events of each calendar.

While the main job is called in larger intervals, there is a second job to be run every minute or so, which kicks out already finished events. In that way processing load on the client (dashboard) side is minimized as only little data is transfered.

Google calendar widget

##Usage You'll need nokogiri for processing XML so add gem 'nokogiri' to your Gemfile and run bundle install from terminal.

Download MomentJS and put moment.min.js in your /assets/javascripts directory. It gets included automatically.

The files calendar.coffee, calendar.html and calendar.scss go in the /widget/calendar directory.

The calendar.rb goes into the /jobs directory.

Put the following in your dashingboard.erb file to make the current and following event show up in your dashboard:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-id="calendar_events" data-pre="0" data-view="Calendar"></div>
</li>

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-id="calendar_events" data-pre="1"  data-view="Calendar"></div>
</li>

##Settings (calendar.rb) Set your calendar urls in the calendars variable and give each one an appropriate name. You can obtain your personal calendar urls from the settings page for a certain Google Calendar you have access to. You might also change how often both jobs are called to suit your needs. To change calendar colors simply add a class .calendar-name-YOURCALENDARNAME with appropriate background-color to calendar.scss.

class Dashing.Calendar extends Dashing.Widget
ready: =>
if !@pre? then @set('pre',0)
setInterval(@updateTime, 5000+(1000*@pre))
onData: (data) =>
@events = data.events
@updateEvent()
updateEvent: =>
event = @events[@pre]
@updateContent(event)
@updateTime()
updateContent: (event) =>
@setBackgroundClassBy event.calendar
@set('event',event)
updateTime: =>
if @event?
event = @event
diff = moment(event.when_start).diff(moment())
if diff<0
event.time = "Ends "+moment(event.when_end).fromNow()
else
event.time = moment(event.when_start).calendar()
@unset('event')
@set('event',event)
setBackgroundClassBy: (name) =>
@removeBackgroundClass()
$(@node).addClass "calendar-name-#{name.toLowerCase()}"
removeBackgroundClass: =>
classNames = $(@node).attr("class").split " "
for className in classNames
match = /calendar-name-(.*)/.exec className
$(@node).removeClass match[0] if match
<h1 class="subtitle" data-bind="event.time"></h1>
<h2 class="title" data-bind="event.title"></h2>
<h3 data-bind="event.body | raw"></h3>
<p class="more-info" data-bind="moreinfo | raw"></p>
<p class="updated-at" data-bind="updatedAtMessage | prepend ' | ' | prepend event.calendar"></p>
#!/usr/bin/env ruby
require 'open-uri'
require 'nokogiri'
require 'date'
require 'cgi'
# Config
# make sure your URLs end with /full, not /simple (which is default)!
# ------
calendars = [{name: 'Private', url: 'PUT ANY PRIVATE XML-URL HERE'},
{name: 'Business', url: 'PUT ANY PRIVATE XML-URL HERE'}]
events = Array.new
SCHEDULER.every '10m', :first_in => 0 do |job|
events = Array.new
min = CGI.escape(DateTime.now().to_s)
max = CGI.escape((DateTime.now()+7).to_s)
calendars.each do |calendar|
url = calendar[:url]+"?singleevents=true&orderby=starttime&start-min=#{min}&start-max=#{max}"
reader = Nokogiri::XML(open(url))
reader.remove_namespaces!
reader.xpath("//feed/entry").each do |e|
title = e.at_xpath("./title").text
content = e.at_xpath("./content").text
when_node = e.at_xpath("./when")
events.push({title: title,
body: content ? content : "",
calendar: calendar[:name],
when_start_raw: when_node ? DateTime.iso8601(when_node.attribute('startTime').text).to_time.to_i : 0,
when_end_raw: when_node ? DateTime.iso8601(when_node.attribute('endTime').text).to_time.to_i : 0,
when_start: when_node ? DateTime.iso8601(when_node.attribute('startTime').text).to_s : "No time",
when_end: when_node ? DateTime.iso8601(when_node.attribute('endTime').text).to_s : "No time"
})
end
end
events.sort! { |a,b| a[:when_start_raw] <=> b[:when_start_raw] }
events = events.slice!(0,15) # 15 elements is probably enough...
send_event('calendar_events', { events: events })
end
SCHEDULER.every '1m', :first_in => 0 do |job|
events_tmp = Array.new(events)
events_tmp.delete_if{|event| DateTime.now().to_time.to_i>=event[:when_end_raw]}
if events_tmp.count != events.count
events = events_tmp
send_event('calendar_events', { events: events })
end
end
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color: #ec663c;
$title-color: rgba(255, 255, 255, 1);
$subtitle-color: rgba(255, 255, 255, 0.7);
$moreinfo-color: rgba(255, 255, 255, 0.7);
$private-color: rgb(123, 209, 72);
$business-color: rgb(255, 173, 70);
// ----------------------------------------------------------------------------
// Widget-calendar styles
// ----------------------------------------------------------------------------
.widget-calendar {
transition: background-color 2s linear;
-moz-transition: background-color 2s linear;
-o-transition: background-color 2s linear;
-webkit-transition: background-color 2s linear;
background-color: $background-color;
.subtitle {
color: $subtitle-color;
font-size: 20px;
margin: 15px 0;
}
.title {
color: $title-color;
font-size: 30px;
}
.more-info {
color: $moreinfo-color;
}
.updated-at {
color: rgba(255, 255, 255, 0.7);
}
&.large h3 {
font-size: 65px;
}
}
.calendar-name-private {
background-color: $private-color;
}
.calendar-name-business {
background-color: $business-color;
}
@shushiej
Copy link

It says invalid date at the top , where is should say "toady at 6.00pm" any ideas?

@tfohlmeister
Copy link
Author

Thanks for pointing this out!
Due to changes in MomentJS variables when_start and when_end in calendar.rb need to be changed to:

when_start: when_node ? DateTime.iso8601(when_node.attribute('startTime').text).to_s : "No time",
when_end: when_node ? DateTime.iso8601(when_node.attribute('endTime').text).to_s : "No time"

MomentJS should then be able again to parse the dates correctly.

@ethosrot
Copy link

ethosrot commented Dec 6, 2013

Having the same issue as shushiej, but the code in my calendar.rb already matches your posted fix exactly ( just set up today ). The only other thing I could think of is if the script doesn't handle recurring calendar items well, but not sure.

Any pointers?

@tresni
Copy link

tresni commented Dec 29, 2013

See my revision for an easy way to save a little bandwidth. Just restricts the returned XML to just the fields you want to use.

@sirtimbly
Copy link

I figured out the "invalid date" problem, you have to request the "full" xml feed not just the "basic" feed. At the end of your XML url from GCal change basic to full and it should have the date nodes.

@enocom
Copy link

enocom commented Jan 28, 2014

I also used full in place of basic in the default_calendar_url. And Google provided a when node in the response XML.

@kylejohnson
Copy link

Any chance of getting this to work with caldav?

@cbirkenbeul
Copy link

It shows not the next event in my calendar but the latest i have insert into. How can i fix this?

@UdoK
Copy link

UdoK commented Sep 16, 2014

@kylejohnson Any update on having this widget with caldav?
I'm trying to adapt the RTM with ical widget to work with caldav but didn't succed so far due to deviations of implementing caldav in the radicale calendar server I'm using.

@tfohlmeister
Copy link
Author

@cbirkenbeul: this line url = calendar[:url]+"?singleevents=true&orderby=starttime [...] tells Google to return events ordered by their start time. Events are just pushed into an array from there. Do you afterwards modified the array order in any way?
@kylejohnson @UdoK Sorry I have no plans to implement caldav right now.

@max72at
Copy link

max72at commented Nov 20, 2014

The widget does not show any calendar entries anymore for a week now. Did not change anything in the setup or at my google calendar account. Any ideas what this could be?

@mattrcampbell
Copy link

Are you using /full feeds from Google? I was and they no longer appear to work. Looks like google is changing to a new API version.
"This API is a subject to the Deprecation Policy and will be shutdown on November 17, 2014. Please use APIv3 instead."

@max72at
Copy link

max72at commented Nov 22, 2014

@mattrcampbell Thanks a lot for the hint. I used /basic now instead of /full and this is still working.
@tfohlmeister Are you planning to update the widget to APIv3? This would be really great as it seems that with APIv2 using /basic instead of /full the calendar entries are shown in a twisted order (last event in calendar first)

@patrickdaulie
Copy link

The widget only seems to work for public and shared calendars. Is that right ? Or is there any extra authentication necessary in order to fetch private calendar info in the widget ?

@jmb
Copy link

jmb commented Feb 4, 2015

I don't want to step on this widget's toes, but I have put together a simple APIv3 widget which can fetch events from private calendars: https://gist.github.com/jmb/33ae3a33d6e8dbffd102

@jsyeo
Copy link

jsyeo commented May 27, 2015

I have put together an even simpler widget that fetches events from the calendar's private address: https://gist.github.com/jsyeo/39d3fde3afbffdd31093

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment