Last active
August 29, 2015 14:21
-
-
Save blindman2k/609b165c5d134b555bab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#require "Rocky.class.nut:1.1.1" | |
class RockyII extends Rocky { | |
function _addAccessControl(res) { | |
res.header("Access-Control-Allow-Origin", "*") | |
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); | |
res.header("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, PATCH, DELETE"); | |
res.header("Access-Control-Expose-Headers", "X-ImpBase-Version"); | |
} | |
} | |
//We have static variables, so only one version of ImpBase can be instantiated per agent | |
class ImpBase { | |
//static variables shared between all instances of ImpBase | |
_waiting = {}; | |
_ib_data = {}; | |
_rocky = null; | |
_root = null; | |
_path = null; | |
static version = [1, 0, 0]; | |
constructor(rocky, urlbase = "/data", initialize = true) { | |
this._rocky = rocky; | |
this._path = urlbase; | |
this._root = this; | |
if(initialize) init(); | |
} | |
function init() { | |
// Make sure Rocky is v1.1.1 or newer. | |
assert_version(_rocky, [1, 1, 1]); | |
//get impbase table from server | |
_ib_restore(); | |
//set up rocky listeners | |
_rocky.on("GET", _path + "/impbase.js", _ib_javascript.bindenv(this)); | |
_rocky.on("GET", _path, ib_get.bindenv(this)).onTimeout(_ib_get_timeout.bindenv(this), 60); | |
_rocky.on("GET", _path + "/(.*)", ib_get.bindenv(this)).onTimeout(_ib_get_timeout.bindenv(this), 60); | |
_rocky.on("POST", _path, ib_post.bindenv(this)); | |
_rocky.on("POST", _path + "/.*", ib_post.bindenv(this)); | |
_rocky.on("PUT", _path, ib_put.bindenv(this)); | |
_rocky.on("PUT", _path + "/.*", ib_put.bindenv(this)); | |
_rocky.on("PATCH", _path, ib_patch.bindenv(this)); | |
_rocky.on("PATCH", _path + "/.*", ib_patch.bindenv(this)); | |
_rocky.on("DELETE", _path, ib_delete.bindenv(this)); | |
_rocky.on("DELETE", _path + "/.*", ib_delete.bindenv(this)); | |
_rocky.on("OPTIONS", _path, function(context) { context.send("OK"); }); | |
_rocky.on("OPTIONS", _path + "/.*", function(context) { context.send("OK"); }); | |
} | |
function assert_version(object, version) { | |
assert(typeof version == "array" && version.len() == 3); | |
assert("version" in object); | |
assert( (object.version[0] > version[0]) | |
|| (object.version[0] == version[0] && object.version[1] > version[1]) | |
|| (object.version[0] == version[0] && object.version[1] == version[1] && object.version[2] >= version[2])); | |
} | |
//--------------------[ Remote handlers ]---------------------------------- | |
// GET - Reading Data | |
function ib_get(context) { | |
local path = _map_path(context.req.path); | |
local last_version = null; | |
if ("version" in context.req.query) { | |
try { | |
if (context.req.query.version == "*") { | |
last_version = _ib_data.version; | |
} else { | |
last_version = context.req.query.version.tointeger(); | |
} | |
} catch (e) { | |
// Do nothing. Just assume version 0. | |
} | |
} | |
if (last_version == null || last_version != _ib_data.version) { | |
// We don't have a version match so serve the results immediately | |
context.setHeader("X-ImpBase-Version", _ib_data.version); | |
context.send(200, _ib_get_path(path), true); | |
return; | |
} | |
} | |
// PUT - Writing Data | |
function ib_put(context) { | |
// Clobber the data store with the new data | |
child(context.req.path).set(context.req.body); | |
} | |
// PATCH - Updating Data | |
function ib_patch(context) { | |
if (context.req.body != null && typeof context.req.body != "table") { | |
// Check the input data is a table | |
context.send(400, "Body must be a JSON object"); | |
} else { | |
local ptr = _ib_get_path(context.req.path); | |
if (ptr != null && typeof ptr != "table") { | |
context.send(400, "Existing data must be a table"); | |
} else { | |
// Update the target node with the specified changes | |
child(context.req.path).update(context.req.body); | |
} | |
} | |
} | |
// DELETE - Removing Data | |
function ib_delete(context) { | |
// Delete the data store contents | |
child(context.req.path).remove(); | |
} | |
// POST - Pushing Data | |
function ib_post(context) { | |
local autoid = child(context.req.path).push(context.req.body); | |
// Send a unique result to this context | |
context.setHeader("X-ImpBase-Version", _ib_data.version); | |
context.send(200, {"name": autoid}, true); | |
} | |
//--------------------[ Local handlers ]---------------------------------- | |
// GET - Reading Data | |
function once(eventtype, callback) { | |
local path = _map_path(_path); | |
switch (eventtype) { | |
case "value" : | |
// Retrieve the data from the data store | |
local val = _ib_get_path(path); | |
callback(DataSnapshot(this, val)); | |
break; | |
default: | |
callback(null); | |
server.error("event type not recognized"); | |
} | |
} | |
function on(eventtype, callback) { | |
local path = _map_path(_path); | |
switch (eventtype) { | |
case "value" : | |
// Register the callback for future changes | |
if (!(path in _waiting)) _waiting[path] <- {}; | |
if (!(eventtype in _waiting[path])) _waiting[path][eventtype] <- []; | |
_waiting[path][eventtype].push(callback); | |
// Call the callback for the initial value | |
local val = _ib_get_path(path); | |
callback(DataSnapshot(this, val)); | |
break; | |
default: | |
callback(null); | |
server.error("event type not recognized"); | |
} | |
// Returns the callback for off() requests | |
return callback; | |
} | |
function off(eventtype = null, callback = null) { | |
local path = _map_path(_path); | |
if (path == null) { | |
_waiting <- {}; | |
} else if (eventtype == null) { | |
_waiting[path] <- {}; | |
} else if (callback == null) { | |
_waiting[path][eventtype] <- []; | |
} else { | |
foreach (path, types in _waiting) { | |
foreach (type, callbacks in _waiting[path]) { | |
for (local i = callbacks.len()-1; i >= 0; i--) { | |
if (callbacks[i] == callback) { | |
callbacks.remove(i); | |
} | |
} | |
} | |
} | |
} | |
} | |
// PUT - Writing Data | |
function set(newData, callback=null) { | |
local path = _map_path(_path); | |
// Clobber the data store with the new data | |
_ib_set_path(path, newData); | |
// Persist to disk | |
_ib_persist(); | |
// Distribute to all the waiting clients | |
_ib_broadcast(path); | |
if (callback) callback(); | |
} | |
// UPDATE - Updating Data | |
function update(newData, callback=null) { | |
local path = _map_path(_path); | |
if (newData != null && typeof newData != "table") { | |
// Check the input data is a table | |
throw "Body must be a JSON object"; | |
} else { | |
local ptr = _ib_get_path(path); | |
if (ptr == null || newData == null) { | |
// If the path isn't pointing to anything or the new data is null then treat it like a put/set | |
_ib_set_path(path, newData); | |
// Set the return value | |
ptr = newData; | |
} else if (typeof ptr == "table") { | |
// Patch the data store with the new changes | |
foreach (k,v in newData) ptr[k] <- v; | |
} else { | |
// The existing data isn't a table | |
throw "Existing data must be a table"; | |
} | |
} | |
// Persist to disk | |
_ib_persist(); | |
// Distribute to all the waiting clients | |
_ib_broadcast(path); | |
if (callback) callback(); | |
} | |
// DELETE - Removing Data | |
function remove(callback=null) { | |
local path = _map_path(_path); | |
// Delete the data store contents | |
child(path).set(null); | |
if (callback) callback(); | |
} | |
// POST - Pushing Data | |
function push(newData, callback=null) { | |
local path = _map_path(_path); | |
local ptr = _ib_get_path(path); | |
// Make sure the existing data is clean | |
if (typeof ptr != "table") { | |
ptr = {}; | |
} | |
// Append the new data to the old | |
local autoid = format("~%08x-%04x", time(), math.rand()%0x10000); | |
ptr[autoid] <- newData; | |
// Update the data store | |
_ib_set_path(path, ptr); | |
// Persist to disk | |
_ib_persist(); | |
// Distribute to all the waiting clients | |
_ib_broadcast(path); | |
if (callback) callback(); | |
return autoid; | |
} | |
function root() { | |
return _root; | |
} | |
function child(path) { | |
if(path && path != "/") { | |
while (path.slice(0, 1) == "/") path = path.slice(1); | |
while (path.slice(-1) == "/") path = path.slice(0, -1); | |
if (path == "") return this; | |
local newPath = "/" + path; | |
local newRef = ImpBase(_rocky, newPath, false); | |
newRef._setRoot(_root); | |
return newRef; | |
} else { | |
return this; | |
} | |
} | |
function parent() { | |
if(_isRoot()) return this; | |
local lastNode = split(_path, "/").pop(); | |
local newPath = _path.slice(0, _path.find(lastNode) - 1); | |
local newRef = ImpBase(_rocky, newPath, false); | |
newRef._setRoot(_root); | |
return newRef; | |
} | |
function key() { | |
if (_isRoot()) return null; | |
return split(_path, "/").slice(-1)[0]; | |
} | |
function toString() { | |
return _path; | |
} | |
//--------------------[ Private functions ]--------------------------------- | |
// Timeout handler for GET long polling requests | |
function _ib_get_timeout(context) { | |
// Send the latest, unchanged values | |
context.setHeader("X-ImpBase-Version", _ib_data.version); | |
context.send(204, ""); | |
} | |
// Removes null nodes from the data tree | |
// This may become a memory/stack issue in larger trees. Redo this function | |
// when that happens. | |
function _ib_clean_data(ptr) { | |
local changed = false; | |
if (typeof ptr == "table" || typeof ptr == "array") { | |
foreach (k,v in ptr) { | |
if (v == null) { | |
// Remove this null node | |
changed = true; | |
delete ptr[k]; | |
} else if ((typeof v == "table" || typeof v == "array") && v.len() == 0) { | |
// Remove this empty array/table | |
changed = true; | |
delete ptr[k]; | |
} else if (typeof v == "array") { | |
// Convert this array into a table | |
changed = true; | |
local newtable = {}; | |
for (local i = 0; i < v.len(); i++) { | |
newtable[i.tostring()] <- v[i]; | |
} | |
ptr[k] <- newtable; | |
_ib_clean_data(ptr[k]); | |
} else { | |
// Move into the next node | |
changed = changed || _ib_clean_data(v); | |
} | |
} | |
} | |
return changed; | |
} | |
// Persist the current data to agent storage | |
function _ib_persist() { | |
// Remove null nodes for the tree | |
while (_ib_clean_data(_ib_data.root)); | |
// Increment the version number | |
_ib_data.version++; | |
// Read the latest stored data and update it | |
local persist = server.load(); | |
persist.impbase <- _ib_data; | |
server.save(persist); | |
} | |
// Restores the previously persisted data from disk | |
function _ib_restore() { | |
local persist = server.load(); | |
if ("impbase" in persist) { | |
_ib_data.root <- persist.impbase.root; | |
_ib_data.version <- persist.impbase.version; | |
} else { | |
_ib_data.root <- null; | |
_ib_data.version <- -1; | |
} | |
} | |
// Set the subitem based on the path | |
function _ib_set_path(req_path, new_data) { | |
// Split out the path parts | |
local pathparts = split(req_path, "/"); | |
// server.log("Setting " + req_path + " to " + new_data); | |
// Loop through the data searching for the node | |
local parent = _ib_data, ptr = _ib_data.root, p = 0, part = null, last_part = null; | |
for (p = 0; p < pathparts.len(); p++) { | |
part = pathparts[p]; | |
// server.log(format("Stepping into %s/%s (%s)", (last_part ? last_part : "~"), part, typeof ptr)); | |
if (new_data == null) { | |
// Handle deletions slightly differently | |
if (!(part in ptr)) { | |
// Can't delete a node that doesn't exist | |
return new_data; | |
} else if (typeof ptr[part] != "table" && p < pathparts.len()-1) { | |
// We aren't at the last node in the path but are at the last node of the data | |
return new_data; | |
} | |
} else { | |
if (typeof ptr != "table") { | |
if (last_part && parent) { | |
// We are missing or overriding a node | |
ptr = parent[last_part] <- {}; | |
} else { | |
// We are missing or overriding the root node | |
ptr = _ib_data.root = {}; | |
} | |
} | |
if (!(part in ptr) || typeof ptr[part] != "table") { | |
// But we can create a new node that doesn't exist | |
ptr[part] <- {}; | |
} | |
} | |
parent = ptr; | |
ptr = ptr[part]; | |
last_part = part; | |
} | |
if (parent == _ib_data) { | |
_ib_data.root = new_data; | |
} else { | |
parent[pathparts.pop()] <- new_data; | |
} | |
// We have set the item | |
return new_data; | |
} | |
// Extract the subitem based on the path | |
function _ib_get_path(req_path) { | |
// Parse out the path parts | |
local pathparts = split(req_path, "/"); | |
// Loop through the data searching for the node | |
local ptr = _ib_data.root; | |
foreach (part in pathparts) { | |
if (part in ptr) ptr = ptr[part]; | |
else return null; | |
} | |
// We have the item | |
return ptr; | |
} | |
// Broadcast the results specific to the caller's request | |
function _ib_broadcast(changedPath = null) { | |
imp.wakeup(0, function() { | |
// Broadcast to all waiting connections | |
// Clone the _contexts list as it will be changing | |
local contexts = clone Rocky.Context._contexts; | |
foreach (id, context in contexts) { | |
local path = _map_path(context.req.path); | |
if (path.find(changedPath) != null || changedPath.find(path) != null) { | |
context.setHeader("X-ImpBase-Version", _ib_data.version); | |
context.send(200, _ib_get_path(path), true); | |
} | |
} | |
// And to all waiting callback functions | |
foreach (path, types in _waiting) { | |
if (path.find(changedPath) != null || changedPath.find(path) != null) { | |
foreach (type, callbacks in _waiting[path]) { | |
foreach (callback in callbacks) { | |
local val = _ib_get_path(path); | |
callback(DataSnapshot(this, val)); | |
} | |
} | |
} | |
} | |
}.bindenv(this)) | |
} | |
// Converts a HTTP URL to a local path | |
function _map_path(path) { | |
local newPath = path; | |
if (path.find(_root._path) != null) newPath = path.slice(_root._path.len()); | |
if (newPath == "") newPath = "/"; | |
return newPath; | |
} | |
function _isRoot() { | |
return (_root.toString() == _path); | |
} | |
function _setRoot(newRoot) { | |
return _root = newRoot; | |
} | |
// Responds with the Javascript library | |
function _ib_javascript(context) { | |
context.setHeader("Content-Type", "application/javascript"); | |
context.send(200, @" | |
function ImpBase (rootPath) { | |
// Constants | |
const MIN_TIMEOUT = 500; | |
const MAX_TIMEOUT = 5000; | |
// Properties | |
var _root = this; | |
var _path = null; | |
var _timeout = MIN_TIMEOUT; | |
// Private methods | |
var _constructor = function(rootPath) { | |
// Setup the static _requests object for holding the callbacks | |
if (typeof ImpBase._requests == 'undefined') { | |
ImpBase._requests = {}; | |
} | |
if (!rootPath) { | |
// Use the current window URL as the basis for the path, assuming its running in an agent | |
_path = window.location.protocol + '//' + window.location.hostname + '/' + window.location.pathname.split('/')[1] + '/data'; | |
} else { | |
// Strip any trailing slashes | |
while (rootPath.slice(-1) == '/') rootPath = rootPath.slice(0, -1); | |
_path = rootPath; | |
} | |
} | |
var _isRoot = function() { | |
return (_root.toString() == _path); | |
} | |
var _ajax = function(method, url, data, callback) { | |
if (typeof data == 'function') { | |
callback = data; | |
data = null; | |
} | |
var method = method.toUpperCase(); | |
var xmlhttp = new XMLHttpRequest(); | |
xmlhttp.onreadystatechange = function(){ | |
if (xmlhttp.readyState == XMLHttpRequest.DONE) { | |
if (typeof callback == 'function') { | |
var response = null; | |
if (xmlhttp.status == 200 && xmlhttp.getResponseHeader('Content-Type').indexOf('json') >= 0) { | |
response = JSON.parse(xmlhttp.responseText); | |
callback(xmlhttp.status, response, xmlhttp.getResponseHeader('X-ImpBase-Version')); | |
_timeout = MIN_TIMEOUT; | |
} else { | |
// console.log('Ajax request failed: ' + xmlhttp.status + ': ' + xmlhttp.responseText) | |
setTimeout(function() { | |
_ajax(method, url, data, callback); | |
}, _timeout); | |
_timeout = _timeout * 1.5; | |
if (_timeout > MAX_TIMEOUT) _timeout = MAX_TIMEOUT; | |
} | |
} | |
} | |
} | |
xmlhttp.open(method, url, true); | |
if (method == 'GET') { | |
xmlhttp.send(); | |
} else { | |
xmlhttp.setRequestHeader('Content-Type', 'application/json'); | |
// xmlhttp.setRequestHeader('Access-Control-Expose-Headers', 'X-ImpBase-Version'); | |
xmlhttp.send(JSON.stringify(data)); | |
} | |
return xmlhttp; | |
} | |
var _randomInt = function(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
// Protected methods | |
this._setRoot = function(newRoot) { | |
return _root = newRoot; | |
} | |
// Public methods | |
this.root = function() { | |
return _root; | |
} | |
this.child = function(path) { | |
if (path) { | |
// Strip the leading and trailing slashes off the new path and trailing slashes off the old path | |
while (path.slice(0, 1) == '/') path = path.slice(1); | |
while (path.slice(-1) == '/') path = path.slice(0, -1); | |
if (path == '') return this; | |
// Append the new path to the old path | |
var newPath = _path + '/' + path; | |
var newRef = new ImpBase(newPath); | |
newRef._setRoot(_root); | |
return newRef; | |
} else { | |
return this; | |
} | |
} | |
this.parent = function() { | |
if (_isRoot()) return this; | |
var newPath = _path.split('/').slice(0, -1).join('/'); | |
var newRef = new ImpBase(newPath); | |
newRef._setRoot(_root); | |
return newRef; | |
} | |
this.key = function() { | |
if (_isRoot()) return null; | |
return _path.split('/').slice(-1)[0]; | |
} | |
this.toString = function() { | |
return _path; | |
} | |
this.set = function(value, onComplete) { | |
_ajax('PUT', _path, value, onComplete); | |
} | |
this.update = function(value, onComplete) { | |
_ajax('PATCH', _path, value, onComplete); | |
} | |
this.remove = function(onComplete) { | |
_ajax('PUT', _path, null, onComplete); | |
} | |
this.push = function(value, onComplete) { | |
// Generate the autoid | |
var time = ('00000000' + Math.floor(Date.now() / 1000).toString(16)).slice(-8); | |
var rand = ('0000' + _randomInt(1, 0x10000).toString(16)).slice(-4); | |
var autoid = '~' + time + '-' + rand; | |
if (value != undefined) { | |
// Post the provided value as a new node | |
_ajax('PUT', _path + '/' + autoid, value, onComplete); | |
} | |
// Return an empty child node | |
return this.child(autoid); | |
} | |
this.on = function(eventtype, callback, cancelCallback, context, _version) { | |
var request = null; | |
switch (eventtype) { | |
case 'value': | |
var url = _path; | |
var context = context ? context : this; | |
if (_version != undefined) url += '?version=' + _version; | |
request = _ajax('GET', url, function(status, response, version) { | |
if (status == 200) { | |
callback.bind(context)(new DataSnapshot(this, response)); | |
this.on(eventtype, callback, cancelCallback, context, version); | |
} else if (status == 204) { | |
this.on(eventtype, callback, cancelCallback, context, version); | |
} else if (cancelCallback) { | |
if (eventtype in ImpBase._requests && callback in ImpBase._requests[eventtype]) { | |
delete ImpBase._requests[eventtype][callback]; | |
} | |
cancelCallback.bind(context)(status); | |
} | |
}.bind(this)); | |
} | |
// Store the request for later cancelling with off() | |
if (request) { | |
if (!(eventtype in ImpBase._requests)) ImpBase._requests[eventtype] = {}; | |
ImpBase._requests[eventtype][callback] = request; | |
} | |
return callback; | |
} | |
this.off = function(eventtype, callback) { | |
if (eventtype in ImpBase._requests && callback in ImpBase._requests[eventtype]) { | |
ImpBase._requests[eventtype][callback].abort(); | |
} | |
} | |
this.once = function(eventtype, successCallback, failureCallback, context) { | |
var request = null; | |
switch (eventtype) { | |
case 'value': | |
var url = _path; | |
var context = context ? context : this; | |
request = _ajax('GET', url, function(status, response, version) { | |
if (status == 200) { | |
successCallback.bind(context)(new DataSnapshot(this, response)); | |
} else if (failureCallback) { | |
failureCallback.bind(context)(status); | |
} | |
}.bind(this)); | |
} | |
} | |
// Call the constructor | |
_constructor(rootPath); | |
} | |
function DataSnapshot(ref, val) { | |
// Properties | |
this._ref = ref; | |
this._val = val; | |
// Private methods | |
var _constructor = function() { | |
} | |
// Public methods | |
this.exists = function() { | |
return this._val != null; | |
} | |
this.val = function() { | |
return this._val; | |
} | |
this.child = function(childPath) { | |
var newRef = this._ref.child(childPath); | |
var nodes = childPath.split('/'); | |
var newVal = this._val; | |
for (var nodeid in nodes) { | |
var node = nodes[nodeid]; | |
if (node == '') { | |
continue; | |
} else if (newVal != null && typeof newVal == 'object' && node in newVal) { | |
newVal = newVal[node]; | |
} else { | |
newVal = null; | |
break; | |
} | |
} | |
return new DataSnapshot(newRef, newVal); | |
} | |
this.forEach = function(childAction) { | |
for (var key in this._val) { | |
var ret = childAction(this.child(key)); | |
if (ret === true) return true; | |
} | |
return false; | |
} | |
this.hasChild = function(childPath) { | |
return this.child(childPath).exists(); | |
} | |
this.hasChildren = function() { | |
return this.numChildren() > 0; | |
} | |
this.numChildren = function() { | |
if ((this._val != null) && (typeof this._val == 'object')) { | |
return Object.keys(this._val).length; | |
} else { | |
return 0; | |
} | |
} | |
this.key = function() { | |
return this._ref.key(); | |
} | |
this.ref = function() { | |
return this._ref; | |
} | |
// Call the constructor | |
_constructor(); | |
} | |
"); | |
} | |
} | |
class DataSnapshot { | |
_ref = null; | |
_val = null; | |
constructor(ref, val) { | |
this._ref = ref; | |
this._val = val; | |
} | |
function exists() { | |
return _val != null; | |
} | |
function val() { | |
return _val; | |
} | |
function child(childPath) { | |
local newRef = _ref.child(childPath); | |
local nodes = split(childPath, "/"); | |
local newVal = _val; | |
foreach (node in nodes) { | |
if(newVal != null && typeof newVal == "table" && node in newVal ) { | |
newVal = newVal[node]; | |
} else { | |
newVal = null; | |
break; | |
} | |
} | |
return DataSnapshot(newRef, newVal); | |
} | |
function forEach(childAction) { | |
foreach(key, value in _val) { | |
local ret = childAction(this.child(key)) | |
if (ret == true) return true; | |
} | |
return false; | |
} | |
function hasChild(childPath) { | |
return this.child(childPath).exists(); | |
} | |
function numChildren() { | |
if( (_val != null) && (typeof _val == "table") ) { | |
return _val.len(); | |
} else { | |
return 0; | |
} | |
} | |
function hasChildren() { | |
return this.numChildren() > 0; | |
} | |
function key() { | |
return _ref.key(); | |
} | |
function ref() { | |
return _ref; | |
} | |
} | |
server.log("AGENT RUNNING") | |
app <- RockyII(); //create an instance of rocky for impbase and webpage rendering | |
impbase <- ImpBase(app, "/data"); //create an instance of impbase - db for realtime functionality | |
led <- { "state" : 0 }; //sets default LED state to OFF | |
impbase.child("/led").on("value", function(snapshot) { | |
server.log("impbase change "+http.jsonencode(snapshot.val())); | |
if ("state" in snapshot.val()) { | |
device.send("state", snapshot.val().state); | |
} else { | |
impbase.child("/led").set(led); //stores default to impbase | |
} | |
}); | |
device.on("getState" function(msg) { | |
impbase.child("/led/state").once("value", function(snapshot) { | |
server.log("in get state " + snapshot.val()) | |
if(typeof snapshot.val() == "float" || typeof snapshot.val() == "integer") { | |
device.send("state", snapshot.val()); //sends state to device | |
} | |
}) | |
}); | |
impbase.child("/test").remove(); | |
testOFF <- null; | |
imp.wakeup(5, function() { | |
server.log("5 sec") | |
impbase.child("/led/state").set(1); | |
// impbase.child("/test").set("setting"); | |
// impbase.update({"test" : "updating"}); | |
// impbase.child("/test").once("value", function(s) { | |
// server.log( "/test once " + http.jsonencode(s.val()) ) | |
// }) | |
testOFF = impbase.child("/test").on("value", function(snapshot) { | |
server.log( "/test on "+http.jsonencode(snapshot.val()) ); | |
}) | |
// impbase.child("/test").on("value", function(snapshot) { | |
// server.log( "/test on 2 "+http.jsonencode(snapshot.val()) ); | |
// }) | |
// impbase.update({"test" : {"update_table" : "updating"}}); | |
}) | |
imp.wakeup(10, function() { | |
server.log("10 sec") | |
impbase.child("/led/state").set(0); | |
// impbase.child("/led/state").parent().update({"state" : 0}) | |
// impbase.child("/led").update({"state" : 0}) | |
impbase.child("/test").push("pushing"); | |
}) | |
imp.wakeup(15, function() { | |
server.log("15 sec") | |
impbase.child("/test").push("pushing2"); | |
impbase.child("/test").off("value", testOFF); | |
}) | |
imp.wakeup(20, function() { | |
server.log("20 sec") | |
impbase.child("/test").push("pushing3"); | |
}) | |
// imp.wakeup(25, function() { | |
// server.log("25 sec") | |
// impbase.child("/test").push("pushing4"); | |
// }) | |
app.get("/", function(context) { | |
context.send(200, html); //render HTML to agent's URL | |
}); | |
html <- @" | |
<!DOCTYPE html> | |
<html lang='en'> | |
<head> | |
<meta charset='utf-8'> | |
<meta http-equiv='X-UA-Compatible' content='IE=edge'> | |
<meta name='viewport' content='width=device-width, initial-scale=1'> | |
<meta name='description' content> | |
<meta name='author' content> | |
<title>Test</title> | |
</head> | |
<style> | |
h1 { | |
text-align: center; | |
font-size: 36px; | |
} | |
button { | |
font-size: 36px; | |
border-radius: 4px; | |
padding: 2% 5%; | |
margin: 0 5%; | |
} | |
.buttons { | |
margin: auto; | |
width: 80%; | |
text-align: center; | |
} | |
#authorize-div { | |
margin: 5% auto; | |
width: 80%; | |
text-align: center; | |
} | |
#slider { | |
width: 60%; | |
margin: 2% auto 4%; | |
} | |
#output { | |
margin: 5% auto; | |
width: 80%; | |
text-align: center; | |
} | |
#output span { | |
font-size: 1.2em; | |
font-weight: bold; | |
} | |
.hidden { | |
display: none; | |
} | |
/*! jQuery UI - v1.11.4 - 2015-04-03 | |
* http://jqueryui.com | |
* not whole lib - just the styles used by the slider | |
*/ | |
.ui-corner-all, | |
.ui-corner-top, | |
.ui-corner-left, | |
.ui-corner-tl { | |
border-top-left-radius: 4px; | |
} | |
.ui-corner-all, | |
.ui-corner-top, | |
.ui-corner-right, | |
.ui-corner-tr { | |
border-top-right-radius: 4px; | |
} | |
.ui-corner-all, | |
.ui-corner-bottom, | |
.ui-corner-left, | |
.ui-corner-bl { | |
border-bottom-left-radius: 4px; | |
} | |
.ui-corner-all, | |
.ui-corner-bottom, | |
.ui-corner-right, | |
.ui-corner-br { | |
border-bottom-right-radius: 4px; | |
} | |
.ui-widget-content { | |
border: 1px solid #aaaaaa; | |
background: #ffffff; | |
color: #222222; | |
} | |
.ui-widget { | |
font-family: Verdana,Arial,sans-serif; | |
font-size: 1.1em; | |
} | |
.ui-slider-horizontal { | |
height: .8em; | |
} | |
.ui-slider-horizontal .ui-slider-handle { | |
top: -.3em; | |
margin-left: -.6em; | |
} | |
.ui-slider { | |
position: relative; | |
text-align: left; | |
} | |
.ui-slider .ui-slider-handle { | |
position: absolute; | |
z-index: 2; | |
width: 1.2em; | |
height: 1.2em; | |
cursor: default; | |
-ms-touch-action: none; | |
touch-action: none; | |
} | |
.ui-state-default, | |
.ui-widget-content .ui-state-default, | |
.ui-widget-header .ui-state-default { | |
border: 1px solid #d3d3d3; | |
background: #e6e6e6; | |
font-weight: normal; | |
color: #555555; | |
} | |
.ui-state-active, | |
.ui-widget-content .ui-state-active, | |
.ui-widget-header .ui-state-active { | |
border: 1px solid #aaaaaa; | |
background: #ffffff; | |
font-weight: normal; | |
color: #212121; | |
} | |
</style> | |
<body> | |
<h1>DIMMER CONTROL</h1> | |
<div id='slider'></div> | |
<br> | |
<div class='buttons'> | |
<button id='on' data-state='1'>ON</button> | |
<button id='off' data-state='0'>OFF</button> | |
<button id='stop' data-state='stop'>STOP</button> | |
</div> | |
<!-- ImpBase --> | |
<script> | |
var source = document.URL + '/data/impbase.js'; | |
var script = document.createElement('script'); | |
script.src = source; | |
document.getElementsByTagName('head')[0].appendChild(script); | |
</script> | |
<!-- jQuery --> | |
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'></script> | |
<script src='https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js'></script> | |
<!-- JavaScript File --> | |
<script> | |
window.onload = function() { | |
var agentID = document.URL; | |
var IB = new ImpBase(); | |
var sliderState = 0; | |
var testListener, ledListener | |
getState(initialzeSlider); | |
function getState(callback) { | |
IB.child('led/state').once('value', function(snapshot) { | |
console.log(snapshot.val()); | |
callback(snapshot.val()); | |
}, function(error) { | |
console.log('Error:', error); | |
}) | |
} | |
function sendState(state) { | |
console.log('in send state') | |
if(state != sliderState) { | |
console.log('Sending '+ sliderState +' to IB at ' + new Date()); | |
IB.child('/led/state').set(sliderState); | |
} | |
} | |
function initialzeSlider(state) { | |
$('#slider').slider({'value' : state*100}); | |
openListeners(); | |
} | |
function openListeners() { | |
$('#slider').on('slidechange', function(e, ui) { | |
sliderState = ui.value / 100; | |
getState(sendState); | |
}); | |
$('button').on('click', translateButtonClick); | |
ledListener = IB.child('/led').on('value', handleIBChange); | |
// testListener = IB.child('/test').on('value', function(s){ console.log('in js test listener'); console.log(s.val()); }) | |
} | |
function translateButtonClick(e){ | |
var state = e.currentTarget.dataset.state; | |
if(state == 'stop') { | |
// IB.child('/test').set('pushing') | |
// IB.once('value', function(s) { console.log(s.child('/test')); console.log(s); }) | |
} else { | |
setSlider(state); | |
} | |
} | |
function setSlider(state) { | |
$('#slider').slider('option', 'value', state*100); | |
} | |
function handleIBChange(snapshot) { | |
console.log('IB change registered. Snapshot value: ' + JSON.stringify(snapshot.val()) + ' Time Received: ' + new Date()); | |
if(snapshot.val() && 'state' in snapshot.val() && snapshot.val().state != sliderState ) { | |
setSlider(snapshot.val().state); | |
} | |
} | |
} | |
</script> | |
</body> | |
</html> | |
" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment