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 = '[email protected]' # Email of service account
key_file = '/Path/to/keyfile.p12' # File containing your private key
key_secret = 'notasecret' # Password to unlock private key
calendarID = '[email protected]' # 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;
}
}
@lanceDamage
Copy link

Newbie here...I'm having trouble understanding the following instructions. Can someone help explain these to me?

"Download the Moment javascript library and add to your javascript assets. Add #= require moment.js to the application.coffee script."

I understand how to download Moment, but I'm not sure where I am suppose to "add to your javascript assets." I am also not sure what is being referred to with "application.coffee script." Is that referring to adding a line of code in google_calendar.coffee? If not, can someone provide the path to this script? Thanks!

@wmreynolds
Copy link

getting a "LoadError: cannot load such file -- google/api_client"

@emilyboda
Copy link

emilyboda commented Mar 19, 2018

@lanceDamage you need to download this thing called moments and add it to /yourproject/assets/javascripts.
cd /yourproject/assets/javascripts
sudo wget https://momentjs.com/downloads/moment.js

Then in the same directory
sudo nano application.coffee
and then add "#= require moment.js" to the top under the other similar line.

@rnhall82
Copy link

@wmreynolds I got that same error. After looking around, I found it was the version of the google api client. From what I read, apparently they made some major changes in it. I was able to fix it by changing that section in the gemfile to read as so:

gem 'google-api-client', '< 0.9'

After I did that, i stopped getting that error message and everything started working for me.

@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