Kodi

Using Kodi for Office Dashboards

Around a year ago my team started making decent use of Grafana – we had just created an integration API and needed to monitor it to ensure things were running smoothly. Simple beginnings.

The idea was to get these web-based graphs and metrics in-front of our IT team so issues were anticipated ideally or at least understood better & quicker as they happened. The problem was it was never really looked at because it's just another browser tab to keep open – and being honest who regularly cycles through their browser just to check? I wanted to get these dashboards infront of people, challenge number 1, we're a hot desking office, so no IT corner or somewhere i could quietly blag a big screen on the wall.

Speaking with our facilities guy it turned out there was no resistance from senior management to us putting some screens up in key locations on proper wall brackets, and running the necessary power and network outlets. This was actually completely unexpected as we've just moved into a new building and had it kitted out professionally. Having worked in similar circumstances before there can be a general feeling of "it's perfect – don't change anything". I don't work in a usual office!

This is an idea i've had in the back of my head for years, allbeit in a slightly different context but it was fairly obvious to me which route to take.

Get kodi on cheap hardware on big beautiful screens around the office! 

The main reason i chose Kodi was its JSON Rest API – nearly every aspect of Kodi can be controlled remotely by another machine, in this case Jenkins. Why Jenkins? It's a build environment that allows me to run pretty much any code or send any HTTP Request on a CRON schedule with a nice web-based GUI for manual intervention. I wrote scripts that captures real-time screenshots of our dashboard in their native applications or webservices. At the moment everything is in PhantomJS – but that might change for some sources – and Jenkins will be able to handle it all. Using a HTTP Request plugin Jenkins can also send commands to trigger pretty much any action in Kodi.

From one GUI (Jenkins) i can control everything, including rebooting and shutting down systems at the end of the day. We have a strange issue on our network at present, so i have the system set up to capture images centrally on the jenkins server and then distribute them out to the various instances of Kodi – some running on old x86 Intel hardware, recycled from old office PC's, others RaspberryPi's – all connected to second hand displays of varying sizes but all 16:9 widescreen. This means that if the network drops, the dashboards still display recent data – or just if the network takes a while to come up after a reboot there is local data to display rather than a blank Kodi instance – which raises a few eyebrows…

I have a few manual overide jobs loaded up in Jenkins such as Delete all Kodi Content, which empties the remote folders in preparation for a new set of images. This is used most often when i'm clearing out old notification images, photos or if i've had to change a filename for some reason. its a quick clearing of the slate. I also have a Distribute job, which doesn't capture any new content, but just redistributes the cached content on the server out to each remote kodi instance.

In order to serve individual screens with content i have a fairly intricate set of scripts that process images into a set of folders which ultimate end up in structure shown below

Screen1
-prep
-playlist

Screen2
-prep
-playlist

The playlist folder is then copied out to each instance. This allows me to have a capture job per screen in Jenkins which can run on its own CRON and have its own screenshot scripts associated.

At the moment i've limited content to picture slide-shows of folders, video was too much to keep synchronized with a back-end unaware of what it's really controlling, but these features aren't too far out of reach for even a basic developer familiar with writing code to interact with an API. 

The Code:

In the vein of "sharing's caring" and Open Source in general below is the basis of the code i'm using. It's not particularly clever, but it gets the job done. I replicate this code as .js files for each dashboard i need to display. I'm sharing our Air Pollution dashboard as it's one of a few that don't contain private data or information – and is based on a public URL – but gives you an idea of how we are achieving the end result.

var page = new WebPage();
page.open('https://uk-air.defra.gov.uk/forecasting/locations?q=London', function (status) {
 just_wait();
});
//viewportSize being the actual size of the headless browser
page.viewportSize = { width: 800, height: 600 };
//the clipRect is the portion of the page you are taking a screenshot of
page.clipRect = { top: 480, left: 250, width: 620, height: 350 };
function just_wait() {
 setTimeout(function() {
 page.render('C:/dashboard/capture/uk-air-and-weather-map.png');
 phantom.exit();
 }, 5000);
}

Things of note in this code,

page.open – this is the URL you want to screenshot
page.viewportSize – you are opening a virtual (headless) browser to take this screenshot, how big should that browser be?
page.clipReact – i only want a small part of that browser windows, so i specify how far in pixels from the top and the left the capture should start, and then the width & height of the capture.
page.render – where i want the captured file to be saved locally.
5000 – the amount of milliseconds to wait from opening the browser to taking the screenshot. (The internet can be slow)

Still with me?
This code ends up with this screenshot:

Amsterdam was having a bad day..

uk air and weather

I also run the below code to capture a map of London Air Monitoring stations:

var page = new WebPage();
page.open('https://uk-air.defra.gov.uk/forecasting/locations?q=London', function (status) {
 just_wait();
});
//viewportSize being the actual size of the headless browser
page.viewportSize = { width: 800, height: 600 };
//the clipRect is the portion of the page you are taking a screenshot of
page.clipRect = { top: 480, left: 250, width: 620, height: 350 };
function just_wait() {
 setTimeout(function() {
 page.render('C:/dashboard/capture/uk-air-and-weather-map.png');
 phantom.exit();
 }, 5000);
}

See?

Same code – different URL and render file – easy.

I then run these rendered files through ImageMagick a few times to process them onto a background so it display nicely on-screen.

magick composite -gravity center image1.jpg bg1.jpg airquality.jpg

uk_air_and_weather_map uk_air_and_weather Occasionally we use something like this, template which is manually stitched together and looks better – but i've currently lost the code on how I achieved it using ImageMagick and don't have time to look into it again at the moment.

The dangers of coding late at night.

Control of all of this used to be done via API calls from Jenkins out to Kodi, some examples of the calls; (these should be URLEncoded)

Video Playlist of a SMB share
http://IPAddress:Port/jsonrpc?request={"jsonrpc":"2.0","id":"1","method":"Player.Open","params":{"item":{"directory":"smb://server/networkshare/"}}}

Picture Playlist of a SMB share
http://IPAddress:Port/jsonrpc?request={"jsonrpc":"2.0","id":"2","method":"Player.Open","params":{"item":{"directory":"smb://server/networkshare/"}}}

The distribution scripts I spoke about are just robocopy commands within batch files – again run by Jenkins. Lots of fun with exit codes and error handling – yay!

In terms of content – we started with very basic data from AWS Cloudwatch displayed through Hosted Graphite (Grafana) but has since introduced a world of metrics and stats from a large number of sources, we're currently monitoring; Windows & Linux Servers (CPU, RAM, HDD, Network) SQL Servers present an overview of their health and key metrics such as long running transactions and any locks that would be impacting our CRM Application. 

We also sync a pretty large amount of data with some 3rd party services each night, and we monitor each stage of this process and graph everything. We looked to bring Google Analytics data in from our website, but found Google Data Studio made this job a lot quicker and easier than trying to pipe it into Grafana. Im struggling to screenshot these live at the moment so these are on a manual 30 day refresh – but when i get my developer back i hope to automate this as well.

I've also collaborated with our Creative Media team to produce some high quality imagery (photos, logos etc) to complete the package of content.

The screens give an overview of our company at any given time, through the use of raw data and creative multimedia elements, and this is something that has only recently been realised. we have a lot of external companies and visitors move through our offices and i often find visitors standing watching the more publically visible dashboards – today i actually spoke to some of them and they were mesmerised, they'd been watching for ten minutes and had lost themselves in the photos. 

We have 6 screens across 2 offices, and their content varies on location. It tooks us a while to consider the audience properly at first as we had such a small amount of quite techy content to play with – in the last few months we've added content that affects everyone but also presents a considered approach to visitors in more public areas.

My next, and probably final – for a while, step is to add some physical buttons to the public screens. I've found some Sanwa Arcade style buttons on ebay for next to nothing, i'm going to mount them on a peice of acryllic (or something a bit more eco hopefully) so people can interact with them more – or trigger an addon to show the news, or similar. Seeing as the public boxes are based on Raspberry Pi's adding some hardware buttons shouldn't be hard, and with a bit of Python i think i can interact with the local Kodi API easily – we'll see.

Off to London Hackspace to test the theory soon.

Links:
PhantomJS
ImageMagick
Jenkins
Kodi Forum Thread