Final Project – #ISelfie

IMG_3258#ISelfie is a data visualization project that brings attention to a festering social phenomenon our generation is grappling with today: self-obsession. This project shows, in real time, every tweet that includes the hashtag, #selfie, and the hashtag, #ISIS. In light of recent events, such as Rand Paul proposing a declaration of war, the Israeli State has been a prominent topic under the public’s eye for months. Despite this, an overwhelming amount of attention is still directed toward the heavily edited, self important snapshots  individuals feel so obligated to share with the twitter-sphere. This project sheds light on the inappropriate allocation of attention we live with everyday, and encourages otherwise.

#ISelfie is composed of two incandescent lightbulbs; one flashes whenever a twitter post containing ‘#selfie’ is tweeted, and the other ‘#ISIS.’ Not surprisingly, the #selfie lightbulb flashes exponentially more hour to hour. The flashes of light represents both the flash of a camera on an iPhone, and the other the light flashes of warfare caused by the Islamic State. Synchronized with the flashes given off by the lightbulbs is the drops of ink that represent destruction, dropping and filling two vases of clean water. Because this is a message about the allocation of attention on twitter, when the ink fills the vase representing #selfie, it shows self deprecation, when the ink fills the vase representing #ISIS, it shows defeat and triumph.

Our circuit consisted of two sets of the same circuit. Each side had an Electric Imp, a pump, a steady-state relay, an incandescent lightbulb, an AC power connector, and a few batteries.

The electric imp would send out logic signals to the relay and the pump whenever the key word was tweeted, turning them off for a specific amount of time, and then turning them off again. We used a relay to turn the lightbulb off and on again in order to have it connected to AC power, but still be able to use the logic given from the imp.

 

Agent code:

// Copyright (c) 2013 Electric Imp

// This file is licensed under the MIT License

// http://opensource.org/licenses/MIT

 

// Twitter Keys

const API_KEY = “5NPSSMQlFGVdB1hMjnWC0xjfD”;

const API_SECRET = “IgBWsw6fOTlF9CnXE1n5khBdoliabQNIqaIPXRxFffgaEcQONH”;

const AUTH_TOKEN = “2900565861-Xe5FcUOQAzsdJSO8pbwoKKCbkunVuoUhgyV3FdC”;

const TOKEN_SECRET = “gNXMCn9D6pvebFsMzZT3d5XWjCjLEQiAr22ktnLjWr5iM”;

 

class Twitter {

// OAuth

_consumerKey = null;

_consumerSecret = null;

_accessToken = null;

_accessSecret = null;

 

// URLso

streamUrl = “https://stream.twitter.com/1.1/”;

tweetUrl = “https://api.twitter.com/1.1/statuses/update.json”;

 

// Streaming

streamingRequest = null;

_reconnectTimeout = null;

_buffer = null;

 

 

constructor (consumerKey, consumerSecret, accessToken, accessSecret) {

this._consumerKey = consumerKey;

this._consumerSecret = consumerSecret;

this._accessToken = accessToken;

this._accessSecret = accessSecret;

 

this._reconnectTimeout = 60;

this._buffer = “”;

}

 

/***************************************************************************

* function: Tweet

*   Posts a tweet to the user’s timeline

*

* Params:

*   status – the tweet

*   cb – an optional callback

*

* Return:

*   bool indicating whether the tweet was successful(if no cb was supplied)

*   nothing(if a callback was supplied)

**************************************************************************/

function tweet(status, cb = null) {

local headers = { };

 

local request = _oAuth1Request(tweetUrl, headers, { “status”: status} );

if (cb == null) {

local response = request.sendsync();

if (response && response.statuscode != 200) {

server.log(format(“Error updating_status tweet. HTTP Status Code %i:\r\n%s”, response.statuscode, response.body));

return false;

} else {

return true;

}

} else {

request.sendasync(cb);

}

}

 

/***************************************************************************

* function: Stream

*   Opens a connection to twitter’s streaming API

*

* Params:

*   searchTerms – what we’re searching for

*   onTweet – callback function that executes whenever there is data

*   onError – callback function that executes whenever there is an error

**************************************************************************/

function stream(searchTerms, onTweet, onError = null) {

server.log(“Opening stream for: ” + searchTerms);

// Set default error handler

if (onError == null) onError = _defaultErrorHandler.bindenv(this);

 

local method = “statuses/filter.json”

local headers = { };

local post = { track = searchTerms };

local request = _oAuth1Request(streamUrl + method, headers, post);

 

 

this.streamingRequest = request.sendasync(

 

function(resp) {

// connection timeout

server.log(“Stream Closed (” + resp.statuscode + “: ” + resp.body +”)”);

// if we have autoreconnect set

if (resp.statuscode == 28 || resp.statuscode == 200) {

stream(searchTerms, onTweet, onError);

} else if (resp.statuscode == 420) {

imp.wakeup(_reconnectTimeout, function() { stream(searchTerms, onTweet, onError); }.bindenv(this));

_reconnectTimeout *= 2;

}

}.bindenv(this),

 

function(body) {

try {

if (body.len() == 2) {

_reconnectTimeout = 60;

_buffer = “”;

return;

}

 

local data = null;

try {

data = http.jsondecode(body);

} catch(ex) {

_buffer += body;

try {

data = http.jsondecode(_buffer);

} catch (ex) {

return;

}

}

if (data == null) return;

 

// if it’s an error

if (“errors” in data) {

server.log(“Got an error”);

onError(data.errors);

return;

}

else {

if (_looksLikeATweet(data)) {

onTweet(data);

return;

}

}

} catch(ex) {

// if an error occured, invoke error handler

onError([{ message = “Squirrel Error – ” + ex, code = -1 }]);

}

}.bindenv(this)

 

);

}

 

/***** Private Function – Do Not Call *****/

function _encode(str) {

return http.urlencode({ s = str }).slice(2);

}

 

function _oAuth1Request(postUrl, headers, data) {

local time = time();

local nonce = time;

local parm_string = http.urlencode({ oauth_consumer_key = _consumerKey });

parm_string += “&” + http.urlencode({ oauth_nonce = nonce });

parm_string += “&” + http.urlencode({ oauth_signature_method = “HMAC-SHA1” });

parm_string += “&” + http.urlencode({ oauth_timestamp = time });

parm_string += “&” + http.urlencode({ oauth_token = _accessToken });

parm_string += “&” + http.urlencode({ oauth_version = “1.0” });

parm_string += “&” + http.urlencode(data);

 

local signature_string = “POST&” + _encode(postUrl) + “&” + _encode(parm_string);

 

local key = format(“%s&%s”, _encode(_consumerSecret), _encode(_accessSecret));

local sha1 = _encode(http.base64encode(http.hash.hmacsha1(signature_string, key)));

 

local auth_header = “oauth_consumer_key=\””+_consumerKey+”\”, “;

auth_header += “oauth_nonce=\””+nonce+”\”, “;

auth_header += “oauth_signature=\””+sha1+”\”, “;

auth_header += “oauth_signature_method=\””+”HMAC-SHA1″+”\”, “;

auth_header += “oauth_timestamp=\””+time+”\”, “;

auth_header += “oauth_token=\””+_accessToken+”\”, “;

auth_header += “oauth_version=\”1.0\””;

 

local headers = {

“Authorization”: “OAuth ” + auth_header

};

 

local url = postUrl + “?” + http.urlencode(data);

local request = http.post(url, headers, “”);

return request;

}

 

function _looksLikeATweet(data) {

return (

“created_at” in data &&

“id” in data &&

“text” in data &&

“user” in data

);

}

 

function _defaultErrorHandler(errors) {

foreach(error in errors) {

server.log(“ERROR ” + error.code + “: ” + error.message);

}

}

 

}

 

function onTweet(tweetData) {

// log the tweet, and who tweeted it (there is a LOT more info in tweetData)

server.log(format(“%s – %s”, tweetData.text, tweetData.user.screen_name));

 

device.send(“catchTweet”, tweetData.text)

}

 

 

twitter <- Twitter(API_KEY, API_SECRET, AUTH_TOKEN, TOKEN_SECRET);

twitter.stream(“selfie”, onTweet);

 

Device Code:

/***** DEVICE CODE *****/

// create a global variabled called led,

// and assign pin9 to it

pump <- hardware.pin9;

lightbulb <- hardware.pin1;

// configure led to be a digital output

pump.configure(DIGITAL_OUT);

lightbulb.configure(DIGITAL_OUT);

 

function catchTweet(tweetData) {

//server.log(“Tweet ” + tweetData);

lightbulb.write(1);

pump.write(1);

 

imp.sleep(0.0043);

pump.write(0);

 

imp.sleep(0.05);

lightbulb.write(0);

 

}

 

agent.on(“catchTweet”, catchTweet); Screen Shot 2014-12-10 at 8.05.40 PM