diff --git a/dev/Makefile b/dev/Makefile index c20d55f..e1e0248 100644 --- a/dev/Makefile +++ b/dev/Makefile @@ -24,3 +24,7 @@ $(dev): $(files) cat > $@ $^ lib: $(min) + +copy: + cp $(dev) ./examples/calendar-based-lamp/js/ + cp $(dev) ./examples/stuffed-web/js/ diff --git a/dev/app.js b/dev/app.js index 9145286..5ee9f03 100644 --- a/dev/app.js +++ b/dev/app.js @@ -1,3 +1,6 @@ window.addEventListener("load", function() { CSSDeviceManager.start(); + document.getElementById("adc").addEventListener("change", function(e) { + console.log(e.target.value); + }); }); diff --git a/dev/css-devices.js b/dev/css-devices.js new file mode 100644 index 0000000..05cbd3f --- /dev/null +++ b/dev/css-devices.js @@ -0,0 +1,369 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +var CSSDeviceManager = { + device_number: 0, + device_map:{}, + style_observer_config: {attributes: true}, + start: function() { + var self = this; + self.style_observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.attributeName == "style" || mutation.attributeName == "class") { + var cssDeviceElement = mutation.target; + deviceNumber = cssDeviceElement.dataset.deviceNumber; + var cssDevice = self.device_map[deviceNumber]; + self.update(cssDevice, cssDeviceElement); + } + }); + }); + + var childObserver = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + var children = mutation.addedNodes; + for (var i = 0, n = children.length; i < n; i++) { + var child = children[i]; + var config = child.dataset.cssDevice; + if (config) { + self.createCSSDevice(config, child); + } + } + }); + }); + childObserver.observe(document.body, {childList: true}); + + var cssDeviceElements = document.querySelectorAll("[data-css-device]"); + for (var i = 0, n = cssDeviceElements.length; i < n; i++) { + var cssDeviceElement = cssDeviceElements[i]; + var cssDevice = self.createCSSDevice(cssDeviceElement.dataset.cssDevice, cssDeviceElement); + } + + }, + createCSSDevice: function(configString, cssDeviceElement) { + var configAsJSONString = + "{" + + configString.replace(/\s/g, "") + .replace(/([,:])?([^,;:]*)([,;])/g, "$1\"$2\"$3") + .replace(/\"(-?[.\d]+)\"/g, "$1") + .replace(/:(([^,:]+,)+[^;]+);/g, ":[$1];") + .replace(/;$/g, "") + .replace(/;/g, ",") + .replace(/(([-]|\w)+):/g, "\"$1\":") //attribute + + "}"; + var config = JSON.parse(configAsJSONString); + var self = this; + var portType = config["port-type"]; + var portNumber = config["port-number"]; + PortManager.getPort(portType, portNumber).then( + function(port) { + var manager = null; + switch (config.type) { + case "multi-color-led": { + manager = MultiColorLEDManager; + break; + } + case "servo": { + manager = ServoManager; + break; + } + } + if (manager) { + manager.createCSSDevice(config, port).then( + function(cssDevice) { + self.update(cssDevice, cssDeviceElement); + var deviceNumber = self.device_number++; + cssDeviceElement.dataset.deviceNumber = deviceNumber; + self.device_map[deviceNumber] = cssDevice; + self.style_observer.observe(cssDeviceElement, self.style_observer_config); + }, + function(error) { + throw new Error(error); + } + ); + } + }, + function(error) { + console.error(error); + } + ) + }, + + update: function(cssDevice, cssDeviceElement) { + var style = window.getComputedStyle(cssDeviceElement, null); + cssDevice.update(style); + } +} +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +var MultiColorLEDManager = { + createCSSDevice: function(config, port) { + return new Promise(function(resolve, reject) { + PCA9685Manager.getPCA9685(port, config.address).then( + function(device) { + var led = new MultiColorLED(config, device); + resolve(led); + }, + function(error) { + reject(error); + } + ); + }); + } +} +var MultiColorLED = function(config, device) { + this.config = config; + this.device = device; +} + +MultiColorLED.prototype = { + update: function(style) { + var color = style.color; + var match = /rgba?\((\d+),\s(\d+),\s(\d+)\)/.exec(color); + var r = parseInt(match[1]); + var g = parseInt(match[2]); + var b = parseInt(match[3]); + var pins = this.config["pwm-pin"]; + var minPulse = this.config["min-pulse"]; + var maxPulse = this.config["max-pulse"]; + var pulseRange = maxPulse - minPulse; + r = minPulse + r / 255 * pulseRange; + g = minPulse + g / 255 * pulseRange; + b = minPulse + b / 255 * pulseRange; + var self = this; + self.device.pwm(pins[0], r) + .then(function(){ + self.device.pwm(pins[1], g) + .then(function(){ + self.device.pwm(pins[2], b); + }); + }); + } +} +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +var ServoManager = { + createCSSDevice: function(config, port) { + return new Promise(function(resolve, reject) { + PCA9685Manager.getPCA9685(port, config.address).then( + function(device) { + var servo = new Servo(config, device); + resolve(servo); + }, + function(error) { + reject(error); + } + ); + }); + } +} +var Servo = function(config, device) { + this.config = config; + this.device = device; +} + +Servo.prototype = { + TO_DEGREE: 360 / (2 * Math.PI), + update: function(style) { + var transform = style.transform; + //matrix(0, -1, 1, 0, 0, 0) + var result = /matrix\(([^,]+),\s([^,]+),\s([^,]+),\s([^,]+).*/.exec(transform); + var m1 = parseFloat(result[1]); + //var m2 = parseFloat(result[2]); + //var m3 = parseFloat(result[3]); + //var m4 = parseFloat(result[4]); + var radian1 = Math.acos(m1); + //var radian2 = Math.asin(m2); + //var radian3 = -Math.asin(m3); + //var radian4 = Math.acos(m4); + var degree1 = Math.round(radian1 * this.TO_DEGREE); + //var degree2 = Math.round(radian2 * this.TO_DEGREE); + //var degree3 = Math.round(radian3 * this.TO_DEGREE); + //var degree4 = Math.round(radian4 * this.TO_DEGREE); + degree1 = 180 - degree1; + var angle = degree1; + var pin = this.config["pwm-pin"]; + var minPulse = this.config["min-pulse"]; + var maxPulse = this.config["max-pulse"]; + var pulseRange = maxPulse - minPulse; + var angleRange = this.config["angle-range"]; + var pulse = minPulse + angle / angleRange * pulseRange; + this.device.pwm(pin, pulse); + } +} +'use strict'; + +navigator.requestI2CAccess = function() { + return new Promise(function(resolve, reject) { + if (navigator.mozI2c) { + var i2cAccess = new I2CAccess() + resolve(i2cAccess); + } else { + reject({'message':'mozI2c not supported'}); + } + }); +} + +function I2CAccess() { +} + +I2CAccess.prototype = { + open: function(portNumber) { + navigator.mozI2c.open(portNumber); + + return new I2CPort(portNumber); + } +}; + +function I2CPort(portNumber) { + this.init(portNumber); +} + +I2CPort.prototype = { + init: function(portNumber) { + this.portNumber = portNumber; + }, + + setDeviceAddress: function(deviceAddress) { + this.deviceAddress = deviceAddress; + navigator.mozI2c.setDeviceAddress(this.portNumber, this.deviceAddress); + }, + + read8: function(command, isOctet) { + return new Promise(function(resolve, reject) { + resolve(navigator.mozI2c.read(this.portNumber, command, true)); + }.bind(this)); + }, + + read16: function(command, isOctet) { + return new Promise(function(resolve, reject) { + resolve(navigator.mozI2c.read(this.portNumber, command, false)); + }.bind(this)); + }, + + write8: function(command, value) { + return new Promise(function(resolve, reject) { + navigator.mozI2c.write(this.portNumber, command, value, true); + resolve(value); + }.bind(this)); + }, + + write16: function(command, value) { + return new Promise(function(resolve, reject) { + navigator.mozI2c.write(this.portNumber, command, value, false); + resolve(value); + }.bind(this)); + } +}; + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +var PortManager = { + port_map: {}, + getPort: function(portType, portNumber) { + var self = this; + return new Promise(function(resolve, reject) { + var portName = portType + portNumber; + var port = self.port_map[portName]; + if (port) { + resolve(port); + } else { + navigator.requestI2CAccess().then( + function(i2c) { + var port = i2c.open(portNumber); + self.port_map[portName] = port; + resolve(port); + } + ); + } + }); + } +} +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +var Utility = { + sleep: function(ms, generator) { + setTimeout(function(){ + try { + generator.next(); + } catch (e) { + if (! (e instanceof StopIteration)) throw e; + } + }, ms); + } +} +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +var PCA9685Manager = { + device_map: {}, + getPCA9685:function (port, address) { + var self = this; + return new Promise(function(resolve, reject) { + var addressMap = self.device_map[port]; + if (addressMap) { + var device = addressMap[address]; + if (device) { + resolve(device); + return; + } + } else { + self.device_map[port] = {}; + } + port.setDeviceAddress(address); + var thread = (function* (){ + port.write8(0x00, 0x00); + yield Utility.sleep(10, thread); + port.write8(0x01, 0x04); + yield Utility.sleep(10, thread); + port.write8(0x00, 0x10); + yield Utility.sleep(10, thread); + port.write8(0xfe, 0x64); + yield Utility.sleep(10, thread); + port.write8(0x00, 0x00); + yield Utility.sleep(10, thread); + port.write8(0x06, 0x00); + yield Utility.sleep(10, thread); + port.write8(0x07, 0x00); + yield Utility.sleep(300, thread); + var device = new PCA9685(port, address); + self.device_map[port][address] = device; + resolve(device); + })(); + thread.next(); + }); + } +} + +var PCA9685 = function(port, address) { + this.port = port; + this.address = address; +} + +PCA9685.prototype = { + tick_sec: 1 / 61 / 4096, + pwm:function(pin, pulse) { + var self = this; + var port = this.port; + var address = this.address; + var portStart = 8; + var portInterval = 4; + var ticks = Math.round(pulse / this.tick_sec); + var tickH = ((ticks >> 8) & 0x0f); + var tickL = (ticks & 0xff); + return new Promise(function(resolve, reject) { + var thread = (function*() { + port.setDeviceAddress(address); + var pwmPort = Math.round(portStart + pin * portInterval); + port.write8(pwmPort + 1, tickH); + yield Utility.sleep(1, thread); + port.write8(pwmPort, tickL); + resolve(); + })(); + thread.next(); + }); + } +} diff --git a/dev/css-devices/ADC.js b/dev/css-devices/ADC.js new file mode 100644 index 0000000..3556686 --- /dev/null +++ b/dev/css-devices/ADC.js @@ -0,0 +1,48 @@ +var ADCManager = { + createCSSDevice: function(config, port) { + return new Promise(function(resolve, reject) { + MCP3425Manager.getMCP3425(port, config.address).then( + function(device) { + var adc = new ADC(config, device); + resolve(adc); + }, + function(error) { + reject(error); + } + ); + }); + } +} +var ADC = function(config, device) { + this.config = config; + this.device = device; +} + +ADC.prototype = { + isSensor: true, + previous_value: 0, + setListener: function(listener) { + if (!this.listener) { + this.start(); + } + this.listener = listener; + }, + start: function() { + var self = this; + setTimeout(function() { + self.update(); + }, 10); + }, + update: function() { + var self = this; + this.device.read().then(function(value) { + if (self.previous_value != value) { + self.listener(value, self); + self.previous_value = value; + } + setTimeout(function() { + self.update(); + }, 500); + }); + } +} diff --git a/dev/css-devices/CSSDeviceManager.js b/dev/css-devices/CSSDeviceManager.js index 1cdcb22..b41284c 100644 --- a/dev/css-devices/CSSDeviceManager.js +++ b/dev/css-devices/CSSDeviceManager.js @@ -1,6 +1,3 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ var CSSDeviceManager = { device_number: 0, device_map:{}, @@ -66,15 +63,24 @@ var CSSDeviceManager = { manager = ServoManager; break; } + case "adc": { + manager = ADCManager; + break; + } } if (manager) { manager.createCSSDevice(config, port).then( function(cssDevice) { - self.update(cssDevice, cssDeviceElement); var deviceNumber = self.device_number++; cssDeviceElement.dataset.deviceNumber = deviceNumber; - self.device_map[deviceNumber] = cssDevice; - self.style_observer.observe(cssDeviceElement, self.style_observer_config); + cssDevice.deviceNumber = deviceNumber; + if (cssDevice.isSensor) { + cssDevice.setListener(self.listen); + } else { + self.update(cssDevice, cssDeviceElement); + self.device_map[deviceNumber] = cssDevice; + self.style_observer.observe(cssDeviceElement, self.style_observer_config); + } }, function(error) { throw new Error(error); @@ -91,5 +97,14 @@ var CSSDeviceManager = { update: function(cssDevice, cssDeviceElement) { var style = window.getComputedStyle(cssDeviceElement, null); cssDevice.update(style); + }, + + listen: function(value, cssDevice) { + var selector = "[data-device-number=\""+cssDevice.deviceNumber+"\"]"; + var cssDeviceElement = document.querySelector(selector); + cssDeviceElement.value = value; + var event = document.createEvent("HTMLEvents"); + event.initEvent("change", true, false); + cssDeviceElement.dispatchEvent(event); } } diff --git a/dev/css-devices/MultiColorLED.js b/dev/css-devices/MultiColorLED.js index c3fe8d1..81ac568 100644 --- a/dev/css-devices/MultiColorLED.js +++ b/dev/css-devices/MultiColorLED.js @@ -1,6 +1,3 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ var MultiColorLEDManager = { createCSSDevice: function(config, port) { return new Promise(function(resolve, reject) { diff --git a/dev/css-devices/Servo.js b/dev/css-devices/Servo.js index 42183cd..8368c95 100644 --- a/dev/css-devices/Servo.js +++ b/dev/css-devices/Servo.js @@ -1,6 +1,3 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ var ServoManager = { createCSSDevice: function(config, port) { return new Promise(function(resolve, reject) { diff --git a/dev/css-devices/devices/MCP3425.js b/dev/css-devices/devices/MCP3425.js new file mode 100644 index 0000000..ad1ce62 --- /dev/null +++ b/dev/css-devices/devices/MCP3425.js @@ -0,0 +1,52 @@ +var MCP3425Manager = { + device_map: {}, + getMCP3425:function (port, address) { + var self = this; + return new Promise(function(resolve, reject) { + var addressMap = self.device_map[port]; + if (addressMap) { + var device = addressMap[address]; + if (device) { + resolve(device); + return; + } + } else { + self.device_map[port] = {}; + } + var thread = (function* (){ + var device = new MCP3425(port, address); + self.device_map[port][address] = device; + resolve(device); + })(); + thread.next(); + }); + } +} + +var MCP3425 = function(port, address) { + this.port = port; + this.address = address; +} + +MCP3425.prototype = { + read:function() { + var self = this; + var port = this.port; + var address = this.address; + return new Promise(function(resolve, reject) { + var thread = (function*() { + port.setDeviceAddress(address); + port.write8(0b10001000); + yield Utility.sleep(200, thread); + Promise.all([ + port.read8(0x00, true) + ]).then(function(v){ + resolve(v[0]); + },function(){ + reject(); + }); + })(); + thread.next(); + }); + } +} diff --git a/dev/css-devices/devices/PCA9685.js b/dev/css-devices/devices/PCA9685.js index 133d13a..9013f7e 100644 --- a/dev/css-devices/devices/PCA9685.js +++ b/dev/css-devices/devices/PCA9685.js @@ -1,6 +1,3 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ var PCA9685Manager = { device_map: {}, getPCA9685:function (port, address) { diff --git a/dev/css-devices/devices/PortManager.js b/dev/css-devices/devices/PortManager.js index 07632d4..3b4e62b 100644 --- a/dev/css-devices/devices/PortManager.js +++ b/dev/css-devices/devices/PortManager.js @@ -1,6 +1,3 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ var PortManager = { port_map: {}, getPort: function(portType, portNumber) { diff --git a/dev/css-devices/devices/Utility.js b/dev/css-devices/devices/Utility.js index a73dd75..e4cde5c 100644 --- a/dev/css-devices/devices/Utility.js +++ b/dev/css-devices/devices/Utility.js @@ -1,6 +1,3 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ var Utility = { sleep: function(ms, generator) { setTimeout(function(){ diff --git a/dev/index.html b/dev/index.html index 7111b25..3516eb0 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,34 +1,47 @@ - WoT Beginners Toolkit + + + + + + + + + + - - - - - - - - + + + ADC: + diff --git a/examples/calendar-based-lamp/index.html b/examples/calendar-based-lamp/index.html index 40158a1..397f177 100755 --- a/examples/calendar-based-lamp/index.html +++ b/examples/calendar-based-lamp/index.html @@ -6,12 +6,13 @@ Calendar Based Lamp - WoT Beginners Toolkit Sample diff --git a/examples/calendar-based-lamp/js/app.js b/examples/calendar-based-lamp/js/app.js index 22a13b8..567b8da 100644 --- a/examples/calendar-based-lamp/js/app.js +++ b/examples/calendar-based-lamp/js/app.js @@ -62,10 +62,13 @@ var Main = { getData: function(id) { return new Promise(function(resolve, reject) { - var url = "http://www.google.com/calendar/feeds/" + id + "/public/basic?alt=json&orderby=starttime&max-results=15&singleevents=true&sortorder=ascending&futureevents=true"; + var time = (new Date()).getTime(); + var url = "http://www.google.com/calendar/feeds/" + id + "/public/basic?alt=json&orderby=starttime&max-results=15&singleevents=true&sortorder=ascending&futureevents=true&nocash="+time; var xhr = new XMLHttpRequest({mozSystem: true}); xhr.open("GET", url, true); + console.log(url); xhr.onload = function (e) { + console.log(xhr.responseText); if (xhr.readyState === 4) { if (xhr.status === 200) { resolve(JSON.parse(xhr.responseText)); diff --git a/examples/stuffed-web/index.html b/examples/stuffed-web/index.html index 694dbac..233d489 100755 --- a/examples/stuffed-web/index.html +++ b/examples/stuffed-web/index.html @@ -5,23 +5,30 @@ Stuffed Web - WoT Beginners Toolkit Sample @@ -33,9 +40,9 @@
- -
wottools
-
:) :( :) #stuffed_web
+ +
+
RIGHT Hand
diff --git a/examples/stuffed-web/js/app.js b/examples/stuffed-web/js/app.js index 95ad6da..6de6b8d 100644 --- a/examples/stuffed-web/js/app.js +++ b/examples/stuffed-web/js/app.js @@ -52,7 +52,7 @@ var Main = { Main.startActuation(xhr.response).then( function() { Main.log("END ANIMATION"); - setTimeout(Main.update, 15*1000); + setTimeout(Main.update, 5000); }, function(e) { console.error(e); @@ -70,7 +70,6 @@ var Main = { }, startActuation: function(json) { var latestTweet = json.statuses[0]; - console.log(latestTweet); var content = latestTweet.text.replace(/\s#stuffed_web/g, ""); document.getElementById("current-tweet").style.backgroundImage = "url(" + latestTweet.user.profile_background_image_url + ")"; document.getElementById("current-tweet-user-icon").src = latestTweet.user.profile_image_url; @@ -91,7 +90,7 @@ var Main = { } else if (word == ">:)") { return Main.angry(); } else { - return Main.wait(1000 * word.length); + return Main.wait(10 * word.length); } }); });