Skip to content

Instantly share code, notes, and snippets.

@jmb
Last active December 8, 2022 08:41
Show Gist options
  • Save jmb/33ae3a33d6e8dbffd102 to your computer and use it in GitHub Desktop.
Save jmb/33ae3a33d6e8dbffd102 to your computer and use it in GitHub Desktop.
Google Calendar API v3 widget for Dashing

Description

Dashing widget to display the next and some subsequent Google Calendar events using the Google Calendar API v3.

I use this widget to display my shift calendar - see the screenshot below

Set up

This widget works with API v3 and requires a service account to be set up via the Google Developer's Console. Once a project is set up, enable the Calendar API. On the Credentials page create a new OpenID and download the p12 key file - set up the path to this file in the job file and grant the email address access to the relevant calendar.

The job file defines how many events to get from the calendar and when to start the search. My version gets the next 6 events.

Add to Gemfile:

gem 'google-api-client', '>= 0.8'

Run

bundle install

Download the Moment javascript library and add to your javascript assets. Add #= require moment.js to the application.coffee script. You can also use moment.min.js if you've downloaded that.

class Dashing.GoogleCalendar extends Dashing.Widget
onData: (data) =>
event = rest = null
getEvents = (first, others...) ->
event = first
rest = others
getEvents data.events.items...
start = moment(event.start.dateTime)
end = moment(event.end.dateTime)
@set('event',event)
@set('event_date', start.format('dddd Do MMMM'))
@set('event_times', start.format('HH:mm') + " - " + end.format('HH:mm'))
next_events = []
for next_event in rest
start = moment(next_event.start.dateTime)
start_date = start.format('ddd Do MMM')
start_time = start.format('HH:mm')
next_events.push { summary: next_event.summary, start_date: start_date, start_time: start_time }
@set('next_events', next_events)
<h1 class="subtitle" >Next event:</h1>
<h3 class="times" data-bind="event_date"></h3>
<h2 class="title" data-bind="event.summary"></h2>
<h3 class="times" data-bind="event_times"></h3>
<h4 data-bind="next_count"></h4>
<table class="next">
<tr data-foreach-e='next_events'>
<td data-bind="e.start_date"></td>
<td data-bind="e.start_time"></td>
<td data-bind="e.summary"></td>
</tr>
</table>
<div class="updated-at" data-bind="updatedAtMessage"></div>
# encoding: UTF-8
require 'google/api_client'
require 'date'
require 'time'
require 'digest/md5'
require 'active_support'
require 'active_support/all'
require 'json'
# Update these to match your own apps credentials
service_account_email = '...@developer.gserviceaccount.com' # Email of service account
key_file = '/Path/to/keyfile.p12' # File containing your private key
key_secret = 'notasecret' # Password to unlock private key
calendarID = '...@group.calendar.google.com' # Calendar ID.
# Get the Google API client
client = Google::APIClient.new(:application_name => 'Dashing Calendar Widget',
:application_version => '0.0.1')
# Load your credentials for the service account
if not key_file.nil? and File.exists? key_file
key = Google::APIClient::KeyUtils.load_from_pkcs12(key_file, key_secret)
else
key = OpenSSL::PKey::RSA.new ENV['GOOGLE_SERVICE_PK'], key_secret
end
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/calendar.readonly',
:issuer => service_account_email,
:signing_key => key)
# Start the scheduler
SCHEDULER.every '15m', :first_in => 4 do |job|
# Request a token for our service account
client.authorization.fetch_access_token!
# Get the calendar API
service = client.discovered_api('calendar','v3')
# Start and end dates
now = DateTime.now
result = client.execute(:api_method => service.events.list,
:parameters => {'calendarId' => calendarID,
'timeMin' => now.rfc3339,
'orderBy' => 'startTime',
'singleEvents' => 'true',
'maxResults' => 6}) # How many calendar items to get
send_event('google_calendar', { events: result.data })
end
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color: #47bbb3; //#ec663c;
$title-color: rgba(255, 255, 255, 1);
$subtitle-color: rgba(255, 255, 255, 0.7);
$moreinfo-color: rgba(255, 255, 255, 0.7);
// ----------------------------------------------------------------------------
// Widget-calendar styles
// ----------------------------------------------------------------------------
.widget-google-calendar {
background-color: $background-color;
.subtitle {
color: $subtitle-color;
font-size: 0.75em;
margin: 15px 0;
}
.title {
color: $title-color;
font-size: 1.4em;
}
.times {
font-size: 0.9em;
}
.next {
font-size: 0.75em;
margin-top: 30px;
.tr {
border-bottom: 1px solid $subtitle-color;
.td {
text-align: left;
}
}
}
.more-info {
color: $moreinfo-color;
}
.updated-at {
color: $subtitle-color;
bottom: 5px;
right: 5px;
left: auto;
font-size: 0.5em;
}
&.large h3 {
font-size: 65px;
}
}
@kny2tl
Copy link

kny2tl commented Jan 21, 2021

I'm struggling to get this running. Let me summary what's current setup (after changing ie Gemfile to gem 'google-api-client', '< 0.9')
Gemfile:

source 'https://rubygems.org'
gem 'smashing'
#gem 'icalendar'
#gem 'google-api-client', '~> 0.53.0'
#gem 'google-api-client', '>= 0.8'
gem 'google-api-client', '< 0.9'

application.coffee:

dashing.js is located in the dashing framework
It includes jquery & batman for you.
#= require dashing.js
#= require_directory .
#= require_tree ../../widgets
#= require moment.js
and so on

ls assets/javascripts/
application.coffee d3-3.2.8.js dashing.gridster.coffee gridster jquery.knob.js moment.js moment-with-locales.js rickshaw-1.4.3.min.js

google_calendar.rb:

Update these to match your own apps credentials
service_account_email = '@.iam.gserviceaccount.com' # Email of service account
key_file = '/home/pi/my-project/keyfile.p12' # File containing your private key
key_secret = 'notasecret' # Password to unlock private key
calendarID = '@google.com' # Calendar ID.

AFAIK, set up is fine.
API is allowed. Key was generated. Calendar is shared with that service account.

What I am getting is 'Next event:' but that's it. Enabling debugging I was rewarded with following

{ 753569630 rufus-scheduler intercepted an error:
  753569630   job:
  753569630     Rufus::Scheduler::EveryJob "15m" {:first_in=>4}
  753569630   error:
  753569630     753569630
  753569630     ArgumentError
  753569630     header field value cannot include CR/LF

with help of google I found that downgrade of ruby (now ruby 2.5.5p157 (2019-03-15 revision 67260) [i386-linux-gnu]) but this leads me rvm and other funnies - which would be task for upcoming days. Before going this path, I'm wondering if you guys have any hints.
thanks!

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