Skip to content

Instantly share code, notes, and snippets.

@maclennann
Last active January 5, 2020 03:07
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save maclennann/b084fa409966f22badaf to your computer and use it in GitHub Desktop.
Save maclennann/b084fa409966f22badaf to your computer and use it in GitHub Desktop.
Exchange Availability Dashing Widget

This is a dashing job to find users' availability in their Exchange calendar and puts it on a dashboard.

Note: I am not a rubyist. I'd certainly welcome some idiomadicy lessons in the comments.

Setup

Drop ews-availability.rb in jobs/ (or dashing install b084fa409966f22badaf), and add the following settings to config.ru:

set :ews_uri, 'https://exchange.example.com/ews/Exchange.asmx'
set :ews_user, 'ews-user@example.com'
set :ews_pass, 'super-cool-password'
set :ews_email_list, ['user1@example.com','user2@example.com','user3@example.com']

Note that your user needs access to talk to Exchange via EWS and read free/busy details from specified users.

Then add 'gem 'viewpoint' to your Gemfile and bundle again to install it.

When you run your dashboard, the job will run every 5 minutes and publish availability for each user in the list as ews-availability-[emailaddress] e.g. ews-availability-user1@example.com.

I like to use the Simplemon widget I found here as the dashboard widget for the data:

  <li data-row="1" data-col="4" data-sizex="1" data-sizey="1">
    <div data-id="ews-availability-user1@example.com" data-view="Simplemon" data-title="Example User"></div>
  </li>

Statuses

The job checks back and ahead 30 minutes to see if there are any meetings in progress or coming up.

Valid statuses are:

  • Currently marked busy: "BUSY"
  • Becoming free within 10 minutes: "HERE SOON"
  • Becoming busy within 10 minutes: "BUSY SOON"
  • No meetings within threshold: "HERE"

Screenshots

Busy Soon Busy Here

require 'viewpoint'
require 'time'
include Viewpoint::EWS
# EWS puts things in stupid-complicated XML results.
# The C# SDK makes it easy to work with, the Ruby one doesn't.
# Just feel around in the result hash until we get the info we're looking for
# Oh god, I don't know how to Ruby
def get_user_arrays(hash)
hash.body.first[:get_user_availability_response][:elems].first[:free_busy_response_array][:elems]
end
def get_user_events(user)
if(user == nil)
return nil
end
user[:free_busy_response][:elems][1][:free_busy_view][:elems][1][:calendar_event_array][:elems]
rescue
return nil
end
def get_event_subject(event)
event[:calendar_event][:elems][3][:calendar_event_details][:elems][1][:subject][:text]
end
SCHEDULER.every '5m', :first_in => 0 do |job|
#==== CONFIGURATION: Please define these variables in config.ru ====#
endpoint = nil
if ! defined? settings.ews_uri
print_warning("Please configure ews_uri in config.ru!")
next
else
endpoint = settings.ews_uri
end
user = nil
if defined? settings.ews_user
user = settings.ews_user
end
pass = nil
if defined? settings.ews_pass
pass = settings.ews_pass
end
# Array of email addresses. Who we're asking EWS about
userlist = settings.ews_email_list
#========== /CONFIGURATION ==========#
cli = Viewpoint::EWSClient.new endpoint,user,pass
cli.set_time_zone 'Eastern Standard Time'
# Set our current time, the "...soon" threshold and the
# whole window we are going to request from EWS
now = Time.now #Time.parse("2014-07-15 14:00:00 -0400")
lower = now - (10*60)
upper = now + (10*60)
start = now - (30*60)
endt = now + (30*60)
start_time = start.iso8601
end_time = endt.iso8601
# Make the EWS call. :detailed view returns more data than we need right now
# but who knows, we may use it.
user_free_busy = cli.get_user_availability(userlist,
start_time: start_time,
end_time: end_time,
requested_view: :detailed
)
# Separate free/busy data for each user into an array
users = get_user_arrays(user_free_busy)
# EWS doesn't return email addresses in the huge XML
# they drop on us, but they are in the same order as
# we requested, so we can just use a counter and line up
# with the userlist.
i = 0
users.each do |user|
busy = 'Here'
color = 'green'
user_events = get_user_events(user)
# User has no events in this time range. Not busy.
if(user_events == nil)
# Send event to clients
send_event("ews-availability-#{userlist[i]}",{value: busy, color: color})
i+=1
next
end
user_events.each do |event|
st = Time.parse(cli.event_start_time(event))
et = Time.parse(cli.event_end_time(event))
# User has an event happening now.
# Set busy and stop looking, because
# that's most important status
if (st <= now and et >= now)
busy = 'Busy'
color = 'red'
break
end
# User has an event ending soon.
# Set status, but keep looking in case
# they have another meeting
if (st < lower and et > lower)
busy = 'Here Soon'
color = 'yellow'
end
# User has an event starting soon.
# Set status, but keep looking in case
# they are currently in a meeting
if (st < upper and et > upper)
busy = 'Busy Soon'
color = 'yellow'
end
end
# Send event to clients
send_event("ews-availability-#{userlist[i]}",{value: busy, color: color})
i+=1
end
end
@gerryhd
Copy link

gerryhd commented Jun 8, 2018

I can't get this to work no matter what I try. I'm getting an empty events array, and I've actually checked the response given by get_user_availability, and there's absolutely no reference for the calendar, it's nowhere to be found. I'm using Office 365 online to test this. Can you help me with this?

@jlongman
Copy link

jlongman commented Jan 5, 2020

I can see events in /events which means I'm getting the data, but the panels aren't updating. I may not have a proper installation of simplemon though as I copied the raw widget folder into place rather than installing the rest. (Never mind, got it working, I had the wrong widgets set it is working now. I did need to generate an app-password as I use MFA however.

Additional note, EWS support is ending October 2020.

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