Skip to content

Instantly share code, notes, and snippets.

@windowfinn windowfinn/ forked from mavimo/
Last active Mar 25, 2016

What would you like to do?
Dashing Jenkins Widget - a fork that adds html5 audio alerts


Dashing widget to display a Jenkins build status and build progress

This extension adds html5 audio alerts to the generated html dashboard. An alert will be played for each instance of the widget, but only if the state has changed. One sound for success, and one for failure.

This widget is a fork of Marco's work

The widget is based on the meter-widget which is default in the Dashing installation

The widget can also see the progress of a "pre-build", i.e if you have a job triggering the actual build you want to define, you can configure this job in the jenkins_build.rb as a prebuild.

For more information, please see Coding Like a tosser


Put the files, jenkins_build.html and jenkins_build.scss goes in the /widget/jenkins_build directory and the jenkins_build.rb in the /jobs directory. The javascript files should be placed in the /assets/javascripts directory. The failure.wav and success.wav (not supplied) should be placed in the /public directory.

Usage (jenkins_build.rb)

Change the JENKINS_URI to your correct uri for Jenkins, eg.:

JENKINS_URI = URI.parse("")

Add jenkins credential (if required) in JENKINS_AUTH, eg.:

  'name' => 'username',
  'password' => 'password'

Put all the jobnames and pre job names in the job_mapping, eg.:

job_mapping = {
  'job1' => { :job => 'Job Name 1'},
  'job2' => { :job => 'Job Name 2', :pre_job => 'Job Name 3'},

Put the following in your dashingboard.erb file to show the status:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-id="job1" data-view="JenkinsBuild" data-title="Build title" data-description="A little description of build" data-min="0" data-max="100"></div>

Multiple jobs can add in dashboard repeating the snippet and changing data-id in accord with job name specified in job_mapping. Widget support title and description via data attributes, this attributest can be omitted if you do not like to display this information.

var failureSoundBuffer = null;
var successSoundBuffer = null;
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
function loadFailureSound() {
var request = new XMLHttpRequest();'GET', 'failure.wav', true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
failureSoundBuffer = buffer;
function loadSuccessSound() {
var request = new XMLHttpRequest();'GET', 'success.wav', true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
successSoundBuffer = buffer;
function playSound(theBuffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = theBuffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.start(0); // play the source now
// note: on older systems, may have to use deprecated noteOn(time);
function playFailureSound() {
function playSuccessSound() {
function jenkinsBuildPrepareAudio() {
// Only do anything if jenkinsBuildPrepareAudio isn't defined
if (typeof jenkinsBuildPrepareAudio == 'undefined') {
function getScript(url) {
var script = document.createElement('script');
script.src = url;
var head = document.getElementsByTagName('head')[0], done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function () {
if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
done = true;
script.onload = script.onreadystatechange = null;
};// jenkinsBuildPrepareAudio was already loaded
class Dashing.JenkinsBuild extends Dashing.Widget
@accessor 'value', Dashing.AnimatedValue
@accessor 'bgColor', ->
if @get('currentResult') == "SUCCESS"
else if @get('currentResult') == "FAILURE"
else if @get('currentResult') == "PREBUILD"
constructor: ->
@observe 'value', (value) ->
ready: ->
meter = $(@node).find(".jenkins-build")
$(@node).fadeOut().css('background-color', @get('bgColor')).fadeIn()
meter.attr("data-bgcolor", meter.css("background-color"))
meter.attr("data-fgcolor", meter.css("color"))
onData: (data) ->
if data.currentResult isnt data.lastResult
$(@node).fadeOut().css('background-color', @get('bgColor')).fadeIn()
if data.currentResult == "FAILURE"
$("#state").html "<script type='text/javascript'>playFailureSound();</script>"
if data.currentResult == "SUCCESS"
$("#state").html "<script type='text/javascript'>playSuccessSound();</script>"
<h2 class="title" data-bind="title"></h2>
<script type="text/javascript">
$.getScript("/assets/", function(){
console.log( "Load was performed" );
<input class="jenkins-build" data-angleOffset=-125 data-angleArc=250 data-width=200 data-readOnly=true data-bind-value="value | shortenedNumber" data-bind-data-min="min" data-bind-data-max="max">
<p class="more-info" data-bind="description"></p>
<p class="last-built" data-bind="lastBuilt"></p>
<p class="updated-at" data-bind="updatedAtMessage"></p>
<span id="state"/>
require 'net/http'
require 'json'
require 'time'
JENKINS_URI = URI.parse("http://localhost:8080")
'name' => nil,
'password' => nil
# the key of this mapping must be a unique identifier for your job, the according value must be the name that is specified in jenkins
job_mapping = {
'JOB' => { :job => 'BUILD', :pre_job => 'PRE_BUILD'}
def get_number_of_failing_tests(job_name)
info = get_json_for_job(job_name, 'lastCompletedBuild')
def get_completion_percentage(job_name)
build_info = get_json_for_job(job_name)
prev_build_info = get_json_for_job(job_name, 'lastCompletedBuild')
return 0 if not build_info["building"]
last_duration = (prev_build_info["duration"] / 1000).round(2)
current_duration = ( - build_info["timestamp"] / 1000).round(2)
return 99 if current_duration >= last_duration
((current_duration * 100) / last_duration).round(0)
def get_json_for_job(job_name, build = 'lastBuild')
job_name = URI.encode(job_name)
http =, JENKINS_URI.port)
request = http.request("/job/#{job_name}/#{build}/api/json"))
if JENKINS_AUTH['name']
request.basic_auth(JENKINS_AUTH['name'], JENKINS_AUTH['password'])
response = http.request(request)
job_mapping.each do |title, jenkins_project|
current_status = nil
SCHEDULER.every '10s', :first_in => 0 do |job|
last_status = current_status
build_info = get_json_for_job(jenkins_project[:job])
current_status = build_info["result"]
if build_info["building"]
current_status = "BUILDING"
percent = get_completion_percentage(jenkins_project[:job])
elsif jenkins_project[:pre_job]
pre_build_info = get_json_for_job(jenkins_project[:pre_job])
current_status = "PREBUILD" if pre_build_info["building"]
percent = get_completion_percentage(jenkins_project[:pre_job])
send_event(title, {
currentResult: current_status,
lastResult: last_status,
timestamp: build_info["timestamp"],
value: percent
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color: #4b5f24;
$title-color: rgba(255, 255, 255, 1);
$moreinfo-color: rgba(255, 255, 255, 0.7);
$meter-background: rgba(20, 20, 20, 1);
// ----------------------------------------------------------------------------
// Widget-jenkins-build styles
// ----------------------------------------------------------------------------
.widget-jenkins-build {
background-color: $background-color;
input.jenkins-build {
background-color: $meter-background;
color: #fff;
.title {
color: $title-color;
.more-info {
color: $moreinfo-color;
.updated-at {
color: rgba(0, 0, 0, 1);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.