Reid Carlberg

Connected Devices, salesforce.com & Other Adventures

Category Archives: Development

Create a Case in salesforce.com with a Staples Easy Button

5

I’ve mentioned a couple of times  it would be interesting if we could create a case in salesforce.com by pressing something like a Staples “Easy” button.  So I did it using a Raspberry Pi and an Easy Button.  Turns out, it’s not that hard. I did it with a standard Developer Edition and a couple of Node.js libraries.

Bill of Materials

Staples-Easy-Button-Raspberry-Pi.jpg

Approach

I started by following my standard Raspberry Pi configuration steps.  This created a simple environment where I could work with Node.js.

Next, I wanted to find the right library to help me work with the Raspberry Pi’s GPIO board. There are a few out there, and they all have one challenge: you can only use the GPIO if you are a superuser.  The most popular library appears to be “pi-gpio“.  It requires “gpio-admin” to work around the superuser requirement, but it didn’t seem to fail in a friendly way when I didn’t have gpio-admin, so I kept looking (note, this may not be pi-gpio’s fault).  I came across “GpiO“.  GpiO inherits the same superuser requirements, but if doesn’t require gpio-admin and offers some additional approaches, including gpio-admin.  I did a quick test, which succeeded, so I moved on.

Next, I wanted to review a bit about how to connect a button on a Raspberry Pi.  Now, buttons are pretty easy at their core.  You press one, and it completes a circuit.  But there’s always a little wiring required.  Adafruit has a great learning section which describes the basic hardware configuration in detail.  I also needed to double check how to configure an LED. Unsurprisingly, they have that as well.  My simple adaptation is below.  Note that I used a spare half-sized breadboard rather than the full-sized one that comes with the kit.  The button connects to pin 23, the LED to pin 24.

20140312-163526.jpg

The sample code Adafruit shows you is in Python.  Nothing wrong with Python.  However, I was thinking Node.js, which means I couldn’t use Adafruit’s example. No problem, I figured, there are great examples on the Github repos.

I then started to wonder about the best way to integrate with salesforce.com. There are a lot of ways, but I wanted to do the simplest thing that could possibly work and so landed on “Web to Case.”  Not a universally perfect approach, but it’s easy to get started (in fact, I simply used the default configuration), and it led me to the useful request library which you’ll see I’ve used below.

My final code is pretty simple.  I import a couple of libraries, declare a couple of gpio variables for specific pins, and have a request with form data that’s fired off.

var gpio = require("gpio");
var request = require("request");

function sendWebToCase() {
	var fields = {
			'orgid': '00DU0000000YYhQ',
			'name': 'Reidberry Carlberg',
			'email': 'reid.carlberg@gmail.com',
			'phone': '773-870-5554',
			'subject': 'Auto case submission',
			'description': 'Here is the detail -- we could also send a log'
			};

	var r = request.post('https://www.salesforce.com/servlet/servlet.WebToCase?encoding=UTF-8', 
			{ form: fields },
			function (error, response, body) {
				console.log(body);
			}
		);
}

function flashLed(led, state) {
	led.set(state);
	if (state == 1) {
		setInterval(function() { flashLed(led, 0); }, 5000);
	}
}

var gpio23 = gpio.export(23, {
   direction: "in",
   ready: function() {
   }
});
var gpio24 = gpio.export(24, {
   direction: "out",
   ready: function() {
   }
});

gpio23.on("change", function(val) {
   // value will report either 1 or 0 (number) when the value changes
   console.log("23" + val);
	if (val == 0) {
		sendWebToCase();
		flashLed(gpio24,1);
	}
});

That’s it!  All that was left was to modify the Easy button.

There are quite a few guides on line on how to hack the Easy button–turns out this has been popular the entire time it’s been around.  Most of them require some kind of soldering and circuit modification (example), which to my mind was unnecessary.  The button itself has several parts: a speaker, battery case, some weights, a piece of metal to make that clicking sound, a small circuit board and the actual button.

20140312-162021.jpg

The only think I really care about is the button.  As I said earlier, all a button does is complete a circuit, right? If that’s the case, I wondered, why couldn’t I just add a new circuit?  I started by covering the existing circuit with plain old tape, put on two new wires that would form the new circuit and taped them in place.  Voila!  The button is now mine, no soldering required.

20140312-162009.jpg

Now, about that gift box.  Turns out it is just about the perfect size to contain all these guts.  Just about.  I had to turn the Raspberry Pi case upside down,  stack the breadboard on top of it, and feed the power supply through a special hole I added, but it worked.

Conclusions, Questions & Opportunities

Overall, I’m glad I did this, but I’m not convinced I have landed on the perfect solution just yet. I find myself wondering if an Electric Imp wouldn’t be a better choice.  I also have an Arduino Yun that, with its smaller form factor, is an appealing option.  Finally, I wonder how useful the Web to Case approach really is.  It certainly creates Cases, which is the point, but it’s very one-way.  It would be super interesting to to have some sort of bi-directional communication so the button could display the status of the case, or so that the initial case creation could include a richer set of data. This also begs the question of whether cases should move to a completely data-driven predictive model requiring zero human intervention, but that my friends is a subject for another day.

Questions, comments — I’d love to hear em.  @ReidCarlberg

Salesforce Apex RSS Reader Featuring XMLStreamReader

0

For reasons I won’t go into (but which are probably obvious when you look at the code) I needed a simple way of importing RSS feeds into Salesforce. Although I have seen others, I decided to create a new one using XMLStreamReader that includes test coverage and which is installable via an unmanaged package.

Why XMLStreamReader instead of the oft used XMLNode? Well, in the case of RSS, XMLStreamReader is great because it’s a read-only interface, which is all I want to do. This keeps everything super fast and efficient, and pretty easy to test.

The heavy lifting is done in the BlogLog_RssReader class. As a developer, you simply send the raw text representation of the RSS XML into the read method, and it returns a List of Blog_Entry__c objects.

Here’s the meat:

    public List<Blog_Entry__c> read(String document) {
        List<Blog_Entry__c> ret = new List<Blog_Entry__c>();
        boolean isSafeToGetNextXmlElement = true;

        XmlStreamReader reader = new XmlStreamReader(document);

        while(isSafeToGetNextXmlElement) {
            if (reader.getEventType() == XmlTag.START_ELEMENT) {
                System.debug('^^^^' + reader.getLocalName());
                if ('item' == reader.getLocalName()) {
                    Blog_Entry__c item = parseItem(reader);
                    ret.add(item);
                }
            }
            // Always use hasNext() before calling next() to confirm 
            // that we have not reached the end of the stream
            if (reader.hasNext()) {
                reader.next();
            } else {
                isSafeToGetNextXmlElement = false;
                break;
            }
        }        

        return ret;
    }

    Blog_Entry__c parseItem(XmlStreamReader reader) {

        Blog_Entry__c ret = new Blog_Entry__c();
        boolean isSafeToGetNextXmlElement = true;

        while(isSafeToGetNextXmlElement) {
            if (reader.getEventType() == XmlTag.END_ELEMENT &&
               'item' == reader.getLocalName()) {
                isSafeToGetNextXmlElement = false;  
                   break;
            }
            if (reader.getEventType() == XmlTag.START_ELEMENT) {
                System.debug('****' + reader.getLocalName() + '~~~~' + reader.getNamespace());
                if ('title' == reader.getLocalName() && reader.getNamespace() == null) {
                        String title = parseString(reader);
                        ret.Title__c = title;
                }
                if ('link' == reader.getLocalName() && reader.getNamespace() == null) {
                        String link = parseString(reader);
                        ret.Link__c = link;
                }
                if ('origLink' == reader.getLocalName()) {
                        String link = parseString(reader);
                        ret.Link__c = link;
                }
                if ('creator' == reader.getLocalName() ) {
                        String author = parseString(reader);
                        ret.Author__c = author;
                } 
                if ('category' == reader.getLocalName() ) {
                        String category = parseString(reader);
                    if (ret.Category__c != null) {
                        ret.Category__c = ret.Category__c + ', ' + category;
                    } else {
                        ret.Category__c = category;
                    }
                    if (ret.Category__c.length() > 250) {
                        ret.Category__c = ret.Category__c.substring(0,249);
                    }
                }  
                if ('pubDate' == reader.getLocalName() ) {
                        String pubDate = parseString(reader);
                        ret.Published__c = convertRSSDateStringToDate(pubDate);
                } 
                if ('description' == reader.getLocalName() ) {
                        String description = parseString(reader);
                        ret.Lead_Copy__c = description;
                }                 

            }
            // Always use hasNext() before calling next() to confirm 
            // that we have not reached the end of the stream
            if (reader.hasNext()) {
                reader.next();
            } else {
                isSafeToGetNextXmlElement = false;
                break;
            }
        }        

        return ret;

    }

    String parseString(XmlStreamReader reader) {
        String ret = '';

        boolean isSafeToGetNextXmlElement = true;
        while(isSafeToGetNextXmlElement) {
            System.debug('****EVENTTYPE' + reader.getEventType());
            if (reader.getEventType() == XmlTag.END_ELEMENT) {
                break;
            } else if (reader.getEventType() == XmlTag.CHARACTERS) {
                System.debug('****Characters |' + reader.getText() + '|');
                ret = ret + reader.getText();
            } else if (reader.getEventType() == XmlTag.CDATA) {
                System.debug('****CDATA');
                ret = reader.getText();
            }
            // Always use hasNext() before calling next() to confirm 
            // that we have not reached the end of the stream
            if (reader.hasNext()) {
                reader.next();
            } else {
                isSafeToGetNextXmlElement = false;
                break;
            }
        }
        return ret.trim();
    }

You get the idea. Read method takes the whole document and looks for an item. ParseItem looks for the individual fields I care about, and ParseString gets the code out of those fields. Good times.

Code is on Github. As always, you can get a free Developer Edition with a few quick clicks.

Improvements? Comments? Let me know.

Smoothie Charts, Super Cool Way to Visualize Streaming Data in Salesforce, Mobile App or Otherwise.

1

The other day someone asked, How can I get a near real time view of streaming data in Salesforce? They had a good reason, which I won’t go into here, but the question led me to Smoothie Charts.  And I gotta say, I kinda love ’em.  So I’m going to show you a simple example, then I’m going to show you that example in Salesforce1 and a neat trick I noticed.

You can see Smoothie Charts in action by clicking this fabulous link.

In plain old HTML, you can do something like this:

<html>
<head>
<title>SmoothieCharts Demo</title>

</head>
<body>

<p>Time Series One</p>

<canvas id="timeSeriesOneChart" width="600" height="200"></canvas>
<p>Time Series Two</p>

<canvas id="timeSeriesTwoChart" width="600" height="200"></canvas>

<script src="smoothie.js"></script>
<script type="text/javascript">

//two artificial series of streaming data
var timeSeriesOne = new TimeSeries();
var timeSeriesOneData = [50,70,100,90,75,55,10,50,50,50];
var timeSeriesOneCounter = 0;

var timeSeriesTwo = new TimeSeries();
var timeSeriesTwoData = [50,100,50,0,50,50,50,50,50,50];
var timeSeriesTwoCounter = 0;

setInterval(function() {
console.log(timeSeriesOneData[timeSeriesOneCounter]);
timeSeriesOne.append(new Date().getTime(), timeSeriesOneData[timeSeriesOneCounter]);
timeSeriesOneCounter++;
if (timeSeriesOneCounter == timeSeriesOneData.length) {
timeSeriesOneCounter = 0;
}
},500);

setInterval(function() {
console.log(timeSeriesTwoData[timeSeriesTwoCounter]);
timeSeriesTwo.append(new Date().getTime(), timeSeriesTwoData[timeSeriesTwoCounter]);
timeSeriesTwoCounter++;
if (timeSeriesTwoCounter == timeSeriesTwoData.length) {
timeSeriesTwoCounter = 0;
}
},500);

function createTimelineOne() {
var chart = new SmoothieChart();
chart.addTimeSeries(timeSeriesOne, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 });
chart.streamTo(document.getElementById("timeSeriesOneChart"), 500);
}

function createTimelineTwo() {
var chart = new SmoothieChart();
chart.addTimeSeries(timeSeriesTwo, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 });
chart.streamTo(document.getElementById("timeSeriesTwoChart"), 500);
}

createTimelineOne();
createTimelineTwo();
</script>

</body>
</html>

Which results in animated version of this:

SmoothieCharts_Demo

This is pretty much what you would expect given their site, so not that interesting yet. Note the calls to “setInterval”.  These loop through an array and just add data to the Smoothie Charts provided “TimeSeries” object every half second or so.

Now, let’s say you do this as a Visualforce page in Salesforce (note that you’ll need to upload smoothie.js as a static resource first).  The code looks pretty similar:

<apex:page docType="html-5.0" standardController="Contact">

<p>Time Series One</p>

<canvas id="timeSeriesOneChart" width="600" height="200"></canvas>
<p>Time Series Two</p>

<canvas id="timeSeriesTwoChart" width="600" height="200"></canvas>

<script src="{!$Resource.LAB_SmoothieCharts}"></script>
<script type="text/javascript">
//two artificial series of streaming data
var timeSeriesOne = new TimeSeries();
var timeSeriesOneData = [50,70,100,90,75,55,10,50,50,50];
var timeSeriesOneCounter = 0;

var timeSeriesTwo = new TimeSeries();
var timeSeriesTwoData = [50,100,50,0,50,50,50,50,50,50];
var timeSeriesTwoCounter = 0;

setInterval(function() {
console.log(timeSeriesOneData[timeSeriesOneCounter]);
timeSeriesOne.append(new Date().getTime(), timeSeriesOneData[timeSeriesOneCounter]);
timeSeriesOneCounter++;
if (timeSeriesOneCounter == timeSeriesOneData.length) {
timeSeriesOneCounter = 0;
}
},500);

setInterval(function() {
console.log(timeSeriesTwoData[timeSeriesTwoCounter]);
timeSeriesTwo.append(new Date().getTime(), timeSeriesTwoData[timeSeriesTwoCounter]);
timeSeriesTwoCounter++;
if (timeSeriesTwoCounter == timeSeriesTwoData.length) {
timeSeriesTwoCounter = 0;
}
},500);

function createTimelineOne() {
var chart = new SmoothieChart();
chart.addTimeSeries(timeSeriesOne, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 });
chart.streamTo(document.getElementById("timeSeriesOneChart"), 500);
}

function createTimelineTwo() {
var chart = new SmoothieChart();
chart.addTimeSeries(timeSeriesTwo, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 });
chart.streamTo(document.getElementById("timeSeriesTwoChart"), 500);
}

createTimelineOne();
createTimelineTwo();
</script>

</apex:page>

And the basic outcome is predictable as well (note that this is not delivered by Sites, so you see the standard Salesforce UI unlike the fabulous link I mentioned above):

salesforce_com_-_Developer_Edition

However, the astute among you will notice that I declared a “standardController” of “Contact” which means I can now get a page like this with just a few clicks (here’s a more detailed explanation).

Contact__Mr__Tim_Barr___salesforce_com_-_Developer_Edition

So yeah that’s awesome but so ~2009.  Check out this action on Salesforce1. Here’s that same Contact page in the Salesforce1 mobile app, iOS edition (iTunes).

Smoothie Charts in Salesforce 1 Contact Page

And then the really fun part is that when you tap on the smoothie chart you get the full sized version of the page.

image (6)

Which I think is pretty sweet.  And, yes, for the curious, this works great on the Android version of Salesforce1 as well.

The code is available in an unmanaged package suitable for the Developer Edition of your choice.

%d bloggers like this: