--- /dev/null
+/node_modules/
--- /dev/null
+To Build and Run cloud dashboard
+
+1) Install Node.js if you don't have it
+
+ https://nodejs.org/en/download/
+
+2) Install packages that cloud dashboard depends on
+
+ $ npm install
+
+3) Run dashboard with command below, and it will be launched on your browser
+
+ $ npm start
\ No newline at end of file
--- /dev/null
+{
+ "name": "dashboard",
+ "version": "0.1.0",
+ "private": true,
+ "devDependencies": {
+ "react-scripts": "0.9.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "dependencies": {
+ "blob-to-buffer": "^1.2.6",
+ "material-ui": "^0.17.0",
+ "node-localstorage": "^1.3.0",
+ "react": "^15.4.2",
+ "react-dom": "^15.4.2",
+ "react-tap-event-plugin": "^2.0.1",
+ "websocket": "^1.0.24",
+ "hashmap": "^2.0.6",
+ "crypto": "0.0.3",
+ "express": "^4.14.1",
+ "json-buffer": "^2.0.11",
+ "system-sleep": "^1.3.0"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
--- /dev/null
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <!-- TODO set icon in header -->
+ <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+ <!--
+ Notice the use of %PUBLIC_URL% in the tag above.
+ It will be replaced with the URL of the `public` folder during the build.
+ Only files inside the `public` folder can be referenced from the HTML.
+
+ Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+ work correctly both with client-side routing and a non-root public URL.
+ Learn how to configure a non-root public URL by running `npm run build`.
+ -->
+ <title>Resource Manager</title>
+ </head>
+ <body>
+ <div id="appbar"></div>
+ <div id="menu" style="float: left; width: 15%; height: 91.9vh"></div>
+ <div id="body" style="float: right; width: 85%; height: 91.9vh"></div>
+ <div id="error"></div>
+ <!--
+ This HTML file is a template.
+ If you open it directly in the browser, you will see an empty page.
+
+ You can add webfonts, meta tags, or analytics to this file.
+ The build step will place the bundled scripts into the <body> tag.
+
+ To begin the development, run `npm start`.
+ To create a production bundle, use `npm run build`.
+ -->
+ </body>
+</html>
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+var EventEmitter = require('events').EventEmitter;
+var WS = require('websocket').w3cwebsocket;
+var convertBlob = require('blob-to-buffer');
+var HashMap = require('hashmap');
+var coapTokenMap = new HashMap();
+var localStorage = window.localStorage;
+if (typeof localStorage === "undefined" || localStorage === null) {
+ var LocalStorage = require('node-localstorage').LocalStorage;
+ localStorage = new LocalStorage('./client');
+}
+
+const coap = require('./components/CoapWebsocketCodec').coap;
+const parse = require('./components/CoapWebsocketCodec').parse;
+const path = require('./components/CoapWebsocketCodec').path;
+
+const CONNECTED = 'connected';
+const DISCONNECTED = 'disconnected';
+const SIGNUP = 'signup';
+const SIGNIN = 'signin';
+const SIGNOUT = 'signout';
+const ERROR = 'error';
+
+var Client = new function() {
+
+ var ws;
+ this.event = new EventEmitter();
+
+ // coap websocket client initialize.
+ this.init = function(address) {
+ console.log('client init: ' + address);
+
+ /* TODO websocket secured */
+ var serverURL = "ws://" + address + "/.well-known/coap";
+ this.ws = new WS(serverURL, 'coap');
+ this.ws.onopen = function() {
+ console.debug('Connected to server ' + address);
+ Client.event.emit(CONNECTED);
+ };
+ this.ws.onclose = function() {
+ console.debug('Disconnected from server ' + address);
+ Client.event.emit(DISCONNECTED);
+ };
+ this.ws.onerror = function() {
+ console.error('Error occurs');
+ Client.event.emit(ERROR, 'Error occurs while websocket connection');
+ Client.event.emit(DISCONNECTED);
+ };
+
+ // response callback.
+ this.ws.onmessage = function(event) {
+ console.debug('Message received -');
+
+ convertBlob(event.data, function (err, buffer) {
+ if (err) throw err;
+
+ var packet = parse(buffer);
+ console.debug(packet);
+ console.debug(packet.getPayloadObject);
+
+ var func = coapTokenMap.get(packet.getToken.toString());
+ func(packet);
+ if(packet.getSequenceNumber === -1){
+ coapTokenMap.remove(packet.getToken.toString());
+ }
+ });
+ };
+ }
+
+ // coap websocket client close.
+ this.close = function() {
+ console.log('client close');
+ this.ws.close();
+ Client.event.emit(DISCONNECTED);
+ }
+
+ // send sign-up request.
+ this.onSignUp = function(packet) {
+ if(packet.getCode === 68) {
+ Client.event.emit(SIGNUP, packet.getPayloadObject.uid,
+ packet.getPayloadObject.accesstoken);
+ } else {
+ Client.event.emit(ERROR, "SignUp Failed" + packet.getCode);
+ }
+ }
+ this.signUp = function(di, provider, authcode) {
+ console.log('client signUp');
+ var payload = {
+ di: di,
+ authprovider: provider,
+ authcode: authcode,
+ devicetype: "device"
+ };
+ this.ws.send(this.doRequest("POST", path.ACCOUNT_FULL_URI, null, payload, this.onSignUp));
+ }
+
+ // send sign-in request.
+ this.onSignIn = function(packet) {
+ if(packet.getCode === 68) {
+ Client.event.emit(SIGNIN, packet.getPayloadObject.uid,
+ packet.getPayloadObject.accesstoken);
+ } else {
+ Client.event.emit(ERROR, "SignIn Failed" + packet.getCode);
+ }
+ }
+ this.signIn = function(di, uid, accesstoken) {
+ console.log('client signIn');
+ var payload = {
+ di: di,
+ uid: uid,
+ accesstoken: accesstoken,
+ login: true
+ };
+ this.ws.send(this.doRequest("POST", path.ACCOUNT_SESSION_FULL_URI, null, payload, this.onSignIn));
+ }
+
+ // send sign-out request.
+ this.onSignOut = function(packet) {
+ if(packet.getCode === 68) {
+ Client.event.emit(SIGNOUT);
+ } else {
+ Client.event.emit(ERROR, "SignOut Failed" + packet.getCode);
+ }
+ }
+ this.signOut = function(di, accesstoken) {
+ console.log('client signOut');
+ var payload = {
+ di: di,
+ accesstoken: accesstoken,
+ login: false
+ };
+ this.ws.send(this.doRequest("POST", path.ACCOUNT_SESSION_FULL_URI, null, payload, this.onSignOut));
+ }
+
+ // send resource discovery request.
+ this.discoverResource = function(queries, response) {
+ console.log('client discoverResource ' + queries);
+ this.ws.send(this.doRequest("GET", path.WELL_KNOWN_FULL_URI, queries, null, response));
+ }
+
+ // send control message.
+ this.sendMessage = function(uri, method, payload, queries, response) {
+ console.log('client sendMessage');
+ this.ws.send(this.doRequest(method, uri, queries, payload, response));
+ }
+
+ this.doRequest = function(method, uri, query, payload, response)
+ {
+ var newCoaptoken = require('crypto').randomBytes(8);
+ coapTokenMap.set(newCoaptoken.toString(), response);
+ return coap.createTokenRequest(newCoaptoken, method, uri, query, payload);
+ }
+
+
+ // erase data in local storage
+ this.removeClientData = function(keyArray) {
+ for (var i = 0; i < keyArray.length; i++) {
+ localStorage.removeItem(keyArray[i]);
+ }
+ };
+
+ this.writeClientData = function(keyValArray) {
+ for (var i = 0; i < keyValArray.length; i++) {
+ localStorage.setItem(keyValArray[i][0], keyValArray[i][1]);
+ }
+ };
+
+ this.readClientData = function(key) {
+ return localStorage.getItem(key);
+ };
+
+
+ this.getResourceList = function(discoveryPayload) {
+ if (discoveryPayload === null) {
+ return [];
+ }
+
+ var resourceList = [];
+
+ console.debug("Discovered devices: " + discoveryPayload.length);
+ for (var i = 0; i < discoveryPayload.length; i++) {
+ for (var j = 0; j < discoveryPayload[i].links.length; j++) {
+ console.debug("[" + i + "] " + discoveryPayload[i].links[j].href);
+ resourceList.push({
+ n: discoveryPayload[i].n,
+ di: discoveryPayload[i].di,
+ uri: discoveryPayload[i].links[j].href,
+ rts: this.buildArrayString(discoveryPayload[i].links[j].rt),
+ ifs: this.buildArrayString(discoveryPayload[i].links[j].if) });
+ }
+ }
+
+ return resourceList;
+ }
+
+ this.buildArrayString = function(array) {
+ var result = '';
+ var seperates = ', ';
+
+ for (var i = 0; i < array.length; i++) {
+ // last data
+ if (i === array.length -1) seperates = '';
+ result += array[i] + seperates;
+ }
+
+ return result;
+ }
+}();
+
+module.exports = Client;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+const coap = require('../CoapWebsocketCodec').coap;
+const parse = require('../CoapWebsocketCodec').parse;
+const path = require('../CoapWebsocketCodec').path;
+var WebSocketClient = require('websocket').client;
+var client = new WebSocketClient();
+var sleep = require('system-sleep');
+
+var readline = require('readline');
+var r = readline.createInterface({
+ input : process.stdin,
+ output : process.stdout
+});
+
+var seqNum = 1;
+var temperature = 0.0;
+var observeNotify = true;
+var arg = new Array();
+var deviceId = "61646d69-6e44-6576-6963-234231423441";
+
+process.argv.forEach(function(val, index, array) {
+ arg[index] = val;
+});
+
+client.on('connectFailed', function(error) {
+ console.log('Connect Error: ' + error.toString());
+});
+
+client.on('connect', function(connection) {
+ console.log('WebSocket Client Connected');
+
+ connection.on('error', function(error) {
+ console.log("Connection Error: " + error.toString());
+ });
+ connection.on('close', function() {
+ console.log('echo-protocol Connection Closed');
+ });
+ connection.on('message', function(message) {
+ if (message.type != 'binary')
+ console.log("message type is not binary!!");
+
+ var packet = parse(message.binaryData);
+
+ // console.log();
+ console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
+ console.log('received Token : ' + packet.getToken);
+ console.log('received uriPath : ' + packet.getUriPath);
+ console.log('received UriQuery : ' + packet.getUriQuery);
+ console.log('received payload : ' + packet.getPayloadString);
+ console.log('received getSequenceNumber : ' + packet.getSequenceNumber);
+ console.log('received getMethod : ' + packet.getMethod);
+ console.log('received getCode : ' + packet.getCode);
+ console.log();
+
+ if (packet.getCode < 5) {
+ console.log("Received Request Message");
+ switch(packet.getMethod) {
+ case "GET":
+ console.log("request GET");
+
+ var response = coap.createResponse(packet, "CONTENT", {
+ temperature : 0.0,
+ units : "C"
+ });
+ connection.sendBytes(response);
+
+ break;
+ case "GET OBSERVE":
+ console.log("request GET OBSERVE");
+ while (temperature < 1.3) {
+ var response = coap.createNotifyResponse(packet, "CONTENT", {
+ temperature : temperature,
+ units : "C"
+ }, seqNum++);
+ connection.sendBytes(response);
+ sleep(100);
+ temperature += 0.1;
+ }
+ break;
+
+ case "GET OBSERVE CANCEL":
+ console.log("request GET OBSERVE CANCEL");
+ observeNotify = false;
+ var response = coap.createNotifyResponse(packet, "CONTENT", null);
+ connection.sendBytes(response);
+ break;
+ }
+ } else if (packet.getCode < 70) {
+
+ switch(packet.getUriPath) {
+
+ case path.ACCOUNT_FULL_URI:
+ console.log('STEP 2 : Sign in');
+ var payload = {
+ di : deviceId,
+ uid : packet.getPayloadObject.uid,
+ accesstoken : packet.getPayloadObject.accesstoken,
+ login : true
+ };
+ connection.sendBytes(signIn(payload));
+ break;
+
+ case path.ACCOUNT_SESSION_FULL_URI:
+ console.log('STEP 3 : publish Resource');
+ connection.sendBytes(publishResource('rt=oic.wk.rdpub', {
+ di : "61646d69-6e44-6576-6963-234231423441",
+ n : "Air Conditioner",
+ lt : 86400,
+ links : [{
+ href : "/oic/d",
+ rt : ["oic.wk.d", "oic.d.airconditioner"],
+ if : ["oic.if.baseline", "oic.if.r"],
+ ins : 0,
+ type : ["application/json"],
+ p : {
+ bm : 1
+ }
+ }, {
+ href : "/oic/p",
+ rt : ["oic.wk.p"],
+ if : ["oic.if.baseline", "oic.if.r"],
+ ins : 0,
+ type : ["application/json"],
+ p : {
+ bm : 1
+ }
+ }]
+ }));
+
+ connection.sendBytes(publishResource('rt=oic.wk.rdpub', {
+ di : "61646d69-6e44-6576-6963-234231423441",
+ n : "Air Conditioner",
+ lt : 86400,
+ links : [{
+ href : "/temperature",
+ rt : ["oic.r.temperature"],
+ if : ["oic.if.baseline"],
+ ins : 0,
+ type : ["application/json"],
+ p : {
+ bm : 3
+ }
+ }]
+ }));
+ break;
+ case path.RD_FULL_URI:
+ break;
+
+ case path.WELL_KNOWN_FULL_URI:
+ if (packet.getPayloadObject[0].links.length == 1) {
+ } else {
+ console.log('STEP 4 : Send Observe Presence');
+ connection.sendBytes(observePresence('di=' + packet.getPayloadObject[0].di));
+ console.log('STEP 5 : Send GET Observe Message');
+ connection.sendBytes(observeMessage(packet.getPayloadObject[0].links[2].href, 'if=' + packet.getPayloadObject[0].links[2].if[0] + ';rt=' + packet.getPayloadObject[0].links[2].rt[0], null));
+
+ }
+ break;
+
+ case path.DEVICE_PRESENCE_FULL_URI:
+ console.log('response for DEVICE_PRESENCE_FULL_URI');
+ break;
+ }
+
+ if (packet.getUriPath.indexOf(path.ROUTE_FULL_URI) > -1) {
+ console.log('temperature : ' + packet.getPayloadObject.temperature);
+ }
+ } else {
+ console.log("error : " + packet.getMethod);
+ }
+ });
+
+ function sendCoapPacketforSignUpIn() {
+ if (connection.connected) {
+ var count = process.argv.length;
+ switch(count) {
+ case 4:
+ console.log('STEP 1 : sign Up');
+ var payload = {
+ di : deviceId,
+ authprovider : "github",
+ authcode : arg[3],
+ devicetype : "device"
+ };
+ connection.sendBytes(signUp(payload));
+ break;
+ case 5:
+ console.log('STEP 2 : Sign in');
+ var payload = {
+ di : deviceId,
+ uid : arg[3],
+ accesstoken : arg[4],
+ login : true
+ };
+ connection.sendBytes(signIn(payload));
+ break;
+ default:
+ console.log("argument is not valid!!");
+ break;
+ }
+ }
+ }
+
+ sendCoapPacketforSignUpIn();
+});
+
+function signUp(payload) {
+ return postMessage(path.ACCOUNT_FULL_URI, null, payload);
+}
+
+function signIn(payload) {
+ return postMessage(path.ACCOUNT_SESSION_FULL_URI, null, payload);
+}
+
+function findResource(uriQury) {
+ return getMessage(path.WELL_KNOWN_FULL_URI, uriQury, null);
+}
+
+function publishResource(uriQury, payload) {
+ return postMessage(path.RD_FULL_URI, uriQury, payload);
+}
+
+function observePresence(uriQury) {
+ return observeMessage(path.DEVICE_PRESENCE_FULL_URI, uriQury, null);
+}
+
+function postMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("POST", uriPath, uriQury, payload);
+}
+
+function getMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("GET", uriPath, uriQury, payload);
+}
+
+function observeMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("GET OBSERVE", uriPath, uriQury, payload);
+};
+
+//client.connect('ws://52.78.151.180:8080/.well-known/coap', 'coap');
+client.connect('ws://' + arg[2] + '/.well-known/coap', 'coap');
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+const coap = require('../CoapWebsocketCodec').coap;
+const parse = require('../CoapWebsocketCodec').parse;
+const path = require('../CoapWebsocketCodec').path;
+var WebSocketClient = require('websocket').client;
+var client = new WebSocketClient();
+
+var readline = require('readline');
+var r = readline.createInterface({
+ input : process.stdin,
+ output : process.stdout
+});
+
+var arg = new Array();
+var deviceId = "ac59e41e-1111-bfd0-9292-20a95af72863";
+
+process.argv.forEach(function(val, index, array) {
+ arg[index] = val;
+});
+
+client.on('connectFailed', function(error) {
+ console.log('Connect Error: ' + error.toString());
+});
+
+client.on('connect', function(connection) {
+ console.log('WebSocket Client Connected');
+
+ connection.on('error', function(error) {
+ console.log("Connection Error: " + error.toString());
+ });
+ connection.on('close', function() {
+ console.log('echo-protocol Connection Closed');
+ });
+ connection.on('message', function(message) {
+ if (message.type != 'binary')
+ console.log("message type is not binary!!");
+
+ var packet = parse(message.binaryData);
+
+ // console.log();
+ // console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
+ // console.log('received Token : ' + packet.getToken);
+ // console.log('received uriPath : ' + packet.getUriPath);
+ // console.log('received UriQuery : ' + packet.getUriQuery);
+ console.log('received payload : ' + packet.getPayloadString);
+ // console.log('received getMethod : ' + packet.getMethod);
+ // console.log('received getCode : ' + packet.getCode);
+ console.log('received getSequenceNumber : ' + packet.getSequenceNumber);
+ // console.log();
+
+ if (packet.getCode < 70) {
+ switch(packet.getUriPath) {
+ case path.ACCOUNT_FULL_URI:
+ console.log('STEP 2 : Sign in');
+ var payload = {
+ di : deviceId,
+ uid : packet.getPayloadObject.uid,
+ accesstoken : packet.getPayloadObject.accesstoken,
+ login : true
+ };
+ connection.sendBytes(signIn(payload));
+ break;
+
+ case path.ACCOUNT_SESSION_FULL_URI:
+ console.log('STEP 3 : findResource rt');
+
+ connection.sendBytes(findResource(null));
+ //61646d69-6e44-6576-6963-234231423441 2aaad9a5-a034-62e7-9d39-efd7ec9203d9 ac59e41e-cf97-bfd0-9292-20a95af72863
+ case path.RD_FULL_URI:
+ break;
+
+ case path.WELL_KNOWN_FULL_URI:
+ if (packet.getPayloadObject[0].links.length == 1) {
+ } else {
+ console.log('STEP 4 : Send Observe Presence');
+ connection.sendBytes(observePresence('di=' + packet.getPayloadObject[0].di));
+
+ console.log('STEP 5-2 : Send GET Observe Message');
+ connection.sendBytes(observeMessage(packet.getPayloadObject[0].links[2].href, 'if=' + packet.getPayloadObject[0].links[2].if[0] + ';rt=' + packet.getPayloadObject[0].links[2].rt[0], null));
+
+ }
+ break;
+
+ case path.DEVICE_PRESENCE_FULL_URI:
+ console.log('response for DEVICE_PRESENCE_FULL_URI');
+ break;
+ }
+
+ if (packet.getUriPath.indexOf(path.ROUTE_FULL_URI) > -1) {
+ console.log('temperature : ' + packet.getPayloadObject.temperature);
+ if(packet.getPayloadObject.temperature == 1.2)
+ connection.sendBytes(observeCancelMessage(packet.getToken, packet.getUriPath, packet.getUriQuery, null));
+ }
+ } else {
+ console.log("error : " + packet.getMethod);
+ }
+ });
+
+ function sendCoapPacketforSignUpIn() {
+ if (connection.connected) {
+ var count = process.argv.length;
+ switch(count) {
+ case 4:
+ console.log('STEP 1 : sign Up');
+ var payload = {
+ di : deviceId,
+ authprovider : "github",
+ authcode : arg[3],
+ devicetype : "device"
+ };
+ connection.sendBytes(signUp(payload));
+ break;
+ case 5:
+ console.log('STEP 2 : Sign in');
+ var payload = {
+ di : deviceId,
+ uid : arg[3],
+ accesstoken : arg[4],
+ login : true
+ };
+ connection.sendBytes(signIn(payload));
+ break;
+ default:
+ console.log("argument is not valid!!");
+ break;
+ }
+ }
+ }
+
+ sendCoapPacketforSignUpIn();
+});
+
+function signUp(payload) {
+ return postMessage(path.ACCOUNT_FULL_URI, null, payload);
+}
+
+function signIn(payload) {
+ return postMessage(path.ACCOUNT_SESSION_FULL_URI, null, payload);
+}
+
+function findResource(uriQury) {
+ return getMessage(path.WELL_KNOWN_FULL_URI, uriQury, null);
+}
+
+function publishResource(uriQury, payload) {
+ return postMessage(path.RD_FULL_URI, uriQury, payload);
+}
+
+function observePresence(uriQury) {
+ return observeMessage(path.DEVICE_PRESENCE_FULL_URI, uriQury, null);
+}
+
+function postMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("POST", uriPath, uriQury, payload);
+}
+
+function getMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("GET", uriPath, uriQury, payload);
+}
+
+function observeMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("GET OBSERVE", uriPath, uriQury, payload);
+};
+
+function observeCancelMessage(token, uriPath, uriQury, payload) {
+ return coap.createTokenRequest(token, "GET OBSERVE CANCEL", uriPath, uriQury, payload);
+};
+
+//client.connect('ws://52.78.151.180:8080/.well-known/coap', 'coap');
+client.connect('ws://' + arg[2] + '/.well-known/coap', 'coap');
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+const coap = require('../CoapWebsocketCodec').coap;
+const parse = require('../CoapWebsocketCodec').parse;
+const path = require('../CoapWebsocketCodec').path;
+var WebSocketClient = require('websocket').client;
+var client = new WebSocketClient();
+
+var readline = require('readline');
+var r = readline.createInterface({
+ input : process.stdin,
+ output : process.stdout
+});
+
+var arg = new Array();
+var deviceId = "ac59e41e-4444-bfd0-9292-20a95af72863";
+
+process.argv.forEach(function(val, index, array) {
+ arg[index] = val;
+});
+
+client.on('connectFailed', function(error) {
+ console.log('Connect Error: ' + error.toString());
+});
+
+client.on('connect', function(connection) {
+ console.log('WebSocket Client Connected');
+
+ connection.on('error', function(error) {
+ console.log("Connection Error: " + error.toString());
+ });
+ connection.on('close', function() {
+ console.log('echo-protocol Connection Closed');
+ });
+ connection.on('message', function(message) {
+ if (message.type != 'binary')
+ console.log("message type is not binary!!");
+
+ var packet = parse(message.binaryData);
+
+ // console.log();
+ // console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
+ // console.log('received Token : ' + packet.getToken);
+ console.log('received uriPath : ' + packet.getUriPath);
+ // console.log('received UriQuery : ' + packet.getUriQuery);
+ console.log('received payload : ' + packet.getPayloadString);
+ // console.log('received getMethod : ' + packet.getMethod);
+ // console.log('received getCode : ' + packet.getCode);
+ console.log('received getSequenceNumber : ' + packet.getSequenceNumber);
+ // console.log();
+
+ if (packet.getCode < 70) {
+ switch(packet.getUriPath) {
+ case path.ACCOUNT_FULL_URI:
+ console.log('STEP 2 : Sign in');
+ var payload = {
+ di : deviceId,
+ uid : packet.getPayloadObject.uid,
+ accesstoken : packet.getPayloadObject.accesstoken,
+ login : true
+ };
+ connection.sendBytes(signIn(payload));
+ break;
+
+ case path.ACCOUNT_SESSION_FULL_URI:
+ console.log('STEP 3 : findResource rt');
+
+ connection.sendBytes(findResource(null));
+ //61646d69-6e44-6576-6963-234231423441 2aaad9a5-a034-62e7-9d39-efd7ec9203d9 ac59e41e-cf97-bfd0-9292-20a95af72863
+ case path.RD_FULL_URI:
+ break;
+
+ case path.WELL_KNOWN_FULL_URI:
+ if (packet.getPayloadObject[0].links.length == 1) {
+ } else {
+ console.log('STEP 4 : Send Observe Presence');
+ connection.sendBytes(observePresence('di=' + packet.getPayloadObject[0].di));
+
+ console.log('STEP 5-1 : Send GET Message');
+ connection.sendBytes(getMessage(packet.getPayloadObject[0].links[2].href, 'if=' + packet.getPayloadObject[0].links[2].if[2]));
+ //+ ';rt=' + packet.getPayloadObject[0].links[2].rt[0], null));
+
+ }
+ break;
+
+ case path.DEVICE_PRESENCE_FULL_URI:
+ console.log('response for DEVICE_PRESENCE_FULL_URI');
+ break;
+ }
+
+ if (packet.getUriPath.indexOf(path.ROUTE_FULL_URI) > -1) {
+ if (packet.getUriPath.indexOf('aircon') > -1) {
+ connection.sendBytes(observeMessage(packet.getPayloadObject[1].href, 'if=' + packet.getPayloadObject[1].if[0] + ';rt=' + packet.getPayloadObject[1].rt[0], null));
+ } else {
+ console.log("power value : " + packet.getPayloadObject.value);
+ }
+ }
+ } else {
+ console.log("error : " + packet.getMethod);
+ }
+ });
+
+ function sendCoapPacketforSignUpIn() {
+ if (connection.connected) {
+ var count = process.argv.length;
+ switch(count) {
+ case 4:
+ console.log('STEP 1 : sign Up');
+ var payload = {
+ di : deviceId,
+ authprovider : "github",
+ authcode : arg[3],
+ devicetype : "device"
+ };
+ connection.sendBytes(signUp(payload));
+ break;
+ case 5:
+ console.log('STEP 2 : Sign in');
+ var payload = {
+ di : deviceId,
+ uid : arg[3],
+ accesstoken : arg[4],
+ login : true
+ };
+ connection.sendBytes(signIn(payload));
+ break;
+ default:
+ console.log("argument is not valid!!");
+ break;
+ }
+ }
+ }
+
+ sendCoapPacketforSignUpIn();
+});
+
+function signUp(payload) {
+ return postMessage(path.ACCOUNT_FULL_URI, null, payload);
+}
+
+function signIn(payload) {
+ return postMessage(path.ACCOUNT_SESSION_FULL_URI, null, payload);
+}
+
+function findResource(uriQury) {
+ return getMessage(path.WELL_KNOWN_FULL_URI, uriQury, null);
+}
+
+function publishResource(uriQury, payload) {
+ return postMessage(path.RD_FULL_URI, uriQury, payload);
+}
+
+function observePresence(uriQury) {
+ return observeMessage(path.DEVICE_PRESENCE_FULL_URI, uriQury, null);
+}
+
+function postMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("POST", uriPath, uriQury, payload);
+}
+
+function getMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("GET", uriPath, uriQury, payload);
+}
+
+function observeMessage(uriPath, uriQury, payload) {
+ return coap.createRequest("GET OBSERVE", uriPath, uriQury, payload);
+};
+
+function observeCancelMessage(token, uriPath, uriQury, payload) {
+ return coap.createTokenRequest(token, "GET OBSERVE CANCEL", uriPath, uriQury, payload);
+};
+
+//client.connect('ws://52.78.151.180:8080/.well-known/coap', 'coap');
+client.connect('ws://' + arg[2] + '/.well-known/coap', 'coap');
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+var empty = new Buffer(0)
+
+// a global index for parsing the options and the payload
+// we can do this as the parsing is a sync operation
+,
+ index,
+ lengthfield
+
+// last five bits are 1
+// 31.toString(2) => '111111'
+,
+ lowerCodeMask = 31,
+ nextMsgId = Math.floor(Math.random() * 65535),
+ codes;
+
+codes = {
+ 'GET' : 1,
+ 'GET OBSERVE' : 1,
+ 'GET OBSERVE CANCEL' : 1,
+ 'POST' : 2,
+ 'PUT' : 3,
+ 'DELETE' : 4,
+
+ 'CREATED' : 65,
+ 'DELETED' : 66,
+ 'VALID' : 67,
+ 'CHANGED' : 68,
+ 'CONTENT' : 69,
+
+ 'BAD_REQUEST' : 128,
+ 'UNAUTHORIZED' : 129,
+ 'BAD_OPTION' : 130,
+ 'FORBIDDEN' : 131,
+ 'NOT_FOUND' : 132,
+ 'METHOD_NOT_ALLOWED' : 133,
+ 'NOT_ACCEPTABLE' : 134,
+
+ 'PRECONDITION_FAILED' : 140,
+ 'REQUEST_ENTITY_TOO_LARGE' : 141,
+ 'UNSUPPORTED_CONTENT_FORMAT' : 143,
+
+ 'INTERNAL_SERVER_ERROR' : 160,
+ 'NOT_IMPLEMENTED' : 161,
+ 'BAD_GATEWAY' : 162,
+ 'SERVICE_UNAVAILABLE' : 163,
+ 'GATEWAY_TIMEOUT' : 164,
+ 'PROXY_NOT_SUPPORTED' : 165,
+};
+
+module.exports.path = {
+ 'ACCOUNT_FULL_URI' : '/oic/account',
+ 'ACCOUNT_SEARCH_FULL_URI' : '/oic/account/search',
+ 'ACCOUNT_SESSION_FULL_URI' : '/oic/account/session',
+ 'ACCOUNT_TOKENREFRESH_FULL_URI' : '/oic/account/tokenrefresh',
+ 'KEEP_ALIVE_FULL_URI' : '/oic/ping',
+ 'ROUTE_FULL_URI' : '/oic/route',
+ 'RD_FULL_URI' : '/oic/rd',
+ 'WELL_KNOWN_FULL_URI' : '/oic/res',
+ 'DEVICE_PRESENCE_FULL_URI' : '/oic/prs',
+};
+
+module.exports.coap = {
+
+ createRequest : function(requestMethod, uriPath, uriQuery, payload) {
+ return createMessage(null, requestMethod, uriPath, uriQuery, payload, null);
+ },
+
+ createTokenRequest : function(token, requestMethod, uriPath, uriQuery, payload) {
+ return createMessage(token, requestMethod, uriPath, uriQuery, payload, null);
+ },
+
+ createResponse : function(request, responseMethod, payload) {
+ return createMessage(request.getToken, responseMethod, request.getUriPath, request.getUriQuery, payload, null);
+ },
+
+ createNotifyResponse : function(request, responseMethod, payload, seqNum) {
+ return createMessage(request.getToken, responseMethod, request.getUriPath, request.getUriQuery, payload, seqNum);
+ }
+};
+
+module.exports.parse = function parse(buffer) {
+
+ index = 1;
+ var result = {
+ getCode : getCode(buffer),
+ getMethod : getMethod(buffer),
+ getToken : getToken(buffer),
+ getUriPath : getUriPath(buffer),
+ getUriPathSegment : getUriPathSegment(buffer),
+ getUriQuery : getUriQuery(buffer),
+ getUriQuerySegment : getUriQuerySegment(buffer),
+ getPayloadString : getPayloadString(buffer),
+ getPayloadObject : getPayloadObject(buffer),
+ getSequenceNumber : getSequenceNumber(buffer),
+
+ };
+
+ return result;
+};
+
+function createMessage(token, requestMethod, uriPath, uriQuery, payload, seqNum) {
+ var tokenValue,
+ codeValue = (codes[requestMethod] > -1 ? codes[requestMethod] : toCode(requestMethod)),
+ optionsValue = new Array(),
+ payloadValue;
+ if (token != null) {
+ tokenValue = new Buffer(token);
+ } else {
+ tokenValue = require('crypto').randomBytes(8);
+ }
+
+ if (payload != null)
+ payloadValue = new Buffer(JSON.stringify(payload));
+ console.log();
+ console.log("Token : " + tokenValue);
+ console.log("Method : " + requestMethod);
+ console.log("Request uri : " + uriPath);
+ console.log("Request query : " + uriQuery);
+ console.log("Request payload : " + payloadValue);
+ console.log("Request seqNum : " + seqNum);
+
+ if (seqNum != null) {
+ var buf = Buffer.allocUnsafe(3);
+ buf.writeUInt8(seqNum & 0xFF, 2);
+ buf.writeUInt8((seqNum >> 8) & 0xFF, 1);
+ buf.writeUInt8((seqNum >> 16) & 0xFF, 0);
+ optionsValue.push({
+ name : 'Observe',
+ value : buf
+ });
+
+ } else {
+ if (requestMethod.indexOf('OBSERVE CANCEL') > -1) {
+ optionsValue.push({
+ name : 'Observe',
+ value : Buffer.alloc(1, 1)
+ });
+ } else if (requestMethod.indexOf('OBSERVE') > -1) {
+ optionsValue.push({
+ name : 'Observe',
+ value : Buffer.alloc(0)
+ });
+ }
+ }
+
+ if (uriPath != null) {
+ uriPath = setUriPath(uriPath);
+ while (uriPath.length != 0) {
+ var path = uriPath.shift();
+ optionsValue.push(path);
+ }
+ }
+
+ optionsValue.push({
+ name : 'Content-Format',
+ value : Buffer.alloc(1, 50)
+ });
+
+ if (uriQuery != null) {
+ uriQuery = setUriQuery(uriQuery);
+ while (uriQuery.length != 0) {
+ var query = uriQuery.shift();
+ optionsValue.push(query);
+ }
+ }
+
+ var packet = encoder({
+ token : tokenValue,
+ code : codeValue,
+ payload : payloadValue,
+ options : optionsValue
+ });
+
+ return packet;
+}
+
+function getkey(value) {
+ var flag = false;
+ var keyVal;
+ for (var key in codes) {
+ if (codes[key] == value) {
+ flag = true;
+ keyVal = key;
+ break;
+ }
+ }
+ if (flag) {
+ return keyVal;
+ } else {
+ return false;
+ }
+}
+
+function getMethod(packet) {
+ var value = toCode(decoder(packet).code);
+ if (value == 1) {
+
+ var observeOption = getSequenceNumber(packet).pop();
+ var observe = 0;
+ if(observeOption.length == 3) {
+ ;
+ } else {
+ observe += observeOption & 0xFF;
+ }
+
+ if (observe == -1)
+ ;
+ else if (observe == 1)
+ return 'GET OBSERVE CANCEL';
+ else
+ return 'GET OBSERVE';
+ }
+ var str = getkey(toCode(decoder(packet).code));
+ return str;
+}
+
+function getCode(packet) {
+ return toCode(decoder(packet).code);
+}
+
+function getToken(packet) {
+ return decoder(packet).token;
+}
+
+function getUriPath(packet) {
+ var uriPath = new String(),
+ value = getOptionValue(packet, 'Uri-Path');
+
+ while (value.length != 0) {
+ uriPath += ("/" + value.pop());
+ }
+
+ return uriPath;
+}
+
+function getUriPathSegment(packet) {
+ var uriPathSegment = new Array(),
+ value = getOptionValue(packet, 'Uri-Path');
+
+ while (value.length != 0) {
+ uriPathSegment.push(value.pop());
+ }
+ return uriPathSegment;
+}
+
+function getPayloadString(packet) {
+ var payload = decoder(packet).payload;
+ if (payload.length != 0)
+ return decoder(packet).payload;
+ return null;
+}
+
+function getPayloadObject(packet) {
+ var payload = decoder(packet).payload;
+ if (payload.length != 0)
+ return JSON.parse(decoder(packet).payload);
+ return null;
+}
+
+function getPayloadValue(packet, property) {
+ if (payload.length != 0) {
+ var payload = JSON.parse(decoder(packet).payload);
+ if (payload.property != null) {
+ return payload.property;
+ }
+ }
+ return null;
+}
+
+function setUriPath(path) {
+ var options = new Array();
+ var pathArray = path.split('/');
+ for (var i = 0; i < pathArray.length; i++) {
+ var value = {
+ name : 'Uri-Path',
+ value : new Buffer(pathArray[i])
+ };
+ if (pathArray[i].length != 0) {
+ options.push(value);
+ }
+ }
+ return options;
+}
+
+function setUriQuery(query) {
+ var options = new Array();
+ var queryArray = query.split(';');
+ for (var i = 0; i < queryArray.length; i++) {
+ var value = {
+ name : 'Uri-Query',
+ value : new Buffer(queryArray[i])
+ };
+ if (queryArray[i].length != 0 && queryArray[i] != null) {
+ options.push(value);
+ }
+ }
+ return options;
+}
+
+function getUriQuery(packet) {
+ var uriQuery = new String(),
+ value = getOptionValue(packet, 'Uri-Query');
+
+ if (value.length == 0 || value == null)
+ return null;
+
+ while (value.length != 0) {
+ uriQuery += value.pop();
+ if (value.length != 0)
+ uriQuery += ";";
+ }
+ return uriQuery;
+}
+
+function getSequenceNumber(packet) {
+ var value = getOptionValue(packet, 'Observe');
+
+ if (value.length == 0)
+ return -1;
+
+ return value;
+}
+
+function getUriQuerySegment(packet) {
+ var uriQuerySegment = new Array(),
+ value = getOptionValue(packet, 'Uri-Path');
+
+ if (value.length == 0)
+ return null;
+
+ while (value.length != 0) {
+ var valueArray = value.pop().toString().split('=');
+ uriQuerySegment.push(valueArray[1]);
+ }
+ return uriQuerySegment;
+}
+
+function getOptionValue(packet, optionName) {
+ var value = new Array(),
+ options = decoder(packet).options;
+ while (options.length != 0) {
+ var option = options.pop();
+ if (option.name == optionName) {
+ value.push(option.value);
+ }
+ }
+ return value;
+}
+
+function encoder(packet) {
+ var buffer,
+ byte,
+ pos = 0,
+ options,
+ i,
+ bufferLength,
+ length;
+
+ packet = fillGenDefaults(packet);
+ options = prepareOptions(packet);
+ length = calculateLength(packet, options);
+ bufferLength = calculateBuffer(packet, length);
+
+ console.log("length for Option and Payload : " + length);
+
+ if (bufferLength > 4294967295 + 65535 + 1 + 14)
+ throw new Error('Max packet size is ' + (4294967295 + 65535 + 1 + 14) + ': current is ' + bufferLength);
+
+ buffer = new Buffer(bufferLength);
+
+ byte = 0;
+ // The Length (Len) field MUST be set to zero
+ // because the WebSockets frame containsthe length.
+ // byte |= calulateExtendedLength(length) << 4;
+ // first four bits are Length
+ byte |= packet.token.length;
+ // last four bits are Token Length
+ buffer.writeUInt8(byte, pos++);
+
+ // if (length < 13) {
+ // ;
+ // } else if (length < 256 + 13 + 1) {
+ // buffer.writeUInt8(length - 13, pos++);
+ // } else if (length < 65535 + 269 + 1) {
+ // buffer.writeUInt16BE(length - 269, pos);
+ // pos += 2;
+ // } else if (length < 4294967295 + 65535 + 1) {
+ // buffer.writeUInt8(length - 65805, pos);
+ // pos += 2;
+ // } else {
+ // console.log('Length must be less than 4GB, current is ' + length);
+ // }
+
+ // code can be humized or not
+ if (codes[packet.code])
+ buffer.writeUInt8(codes[packet.code], pos++);
+ else
+ buffer.writeUInt8(toCode(packet.code), pos++);
+
+ // the token might be an empty buffer
+ packet.token.copy(buffer, pos);
+ pos += packet.token.length;
+
+ // write the options
+ for ( i = 0; i < options.length; i++) {
+ options[i].copy(buffer, pos);
+ pos += options[i].length;
+ }
+
+ if (packet.code !== '0.00' && packet.payload != '') {
+
+ // payload separator
+ buffer.writeUInt8(255, pos++);
+ packet.payload.copy(buffer, pos);
+ }
+
+ return buffer;
+}
+
+module.exports.generater = function generater(packet) {
+ var buffer,
+ byte,
+ pos = 0,
+ options,
+ i,
+ bufferLength,
+ length;
+
+ /* fillGenDefaults : add default values to generate packet */
+ packet = fillGenDefaults(packet);
+ options = prepareOptions(packet);
+ length = calculateLength(packet, options);
+ bufferLength = calculateBuffer(packet, length);
+
+ console.log("length for Option and Payload : " + length);
+
+ if (bufferLength > 4294967295 + 65535 + 1 + 14)
+ throw new Error('Max packet size is ' + (4294967295 + 65535 + 1 + 14) + ': current is ' + bufferLength);
+
+ buffer = new Buffer(bufferLength);
+
+ // first byte
+ byte = 0;
+ // byte |= calulateExtendedLength(length) << 4;
+ // first four bits are Length
+ byte |= packet.token.length;
+ // last four bits are Token Length
+ buffer.writeUInt8(byte, pos++);
+
+ // if (length < 13) {
+ // ;
+ // } else if (length < 256 + 13 + 1) {
+ // buffer.writeUInt8(length - 13, pos++);
+ // } else if (length < 65535 + 269 + 1) {
+ // buffer.writeUInt16BE(length - 269, pos);
+ // pos += 2;
+ // } else if (length < 4294967295 + 65535 + 1) {
+ // buffer.writeUInt8(length - 65805, pos);
+ // pos += 2;
+ // } else {
+ // console.log('Length must be less than 4GB, current is ' + length);
+ // }
+
+
+ // code can be humized or not
+ if (codes[packet.code])
+ buffer.writeUInt8(codes[packet.code], pos++);
+ else
+ buffer.writeUInt8(toCode(packet.code), pos++);
+
+ // the token might be an empty buffer
+ packet.token.copy(buffer, pos);
+ pos += packet.token.length;
+
+ // write the options
+ for ( i = 0; i < options.length; i++) {
+ options[i].copy(buffer, pos);
+ pos += options[i].length;
+ }
+
+ if (packet.code !== '0.00' && packet.payload != '') {
+
+ // payload separator
+ buffer.writeUInt8(255, pos++);
+ packet.payload.copy(buffer, pos);
+ }
+
+ return buffer;
+};
+
+// module.exports.decoder = function decoder(buffer) {
+function decoder(buffer) {
+ index = 1;
+ lengthfield = parseLength(buffer);
+
+ if (lengthfield >= 13) {
+ var extendedLength = parseExtendedLength(buffer, lengthfield);
+ }
+
+ var result = {
+ code : parseCode(buffer),
+ token : parseToken(buffer),
+ options : null,
+ payload : null,
+ };
+
+ if (result.code !== '0.00') {
+ result.options = parseOptions(buffer);
+ result.payload = buffer.slice(index + 1);
+ } else {
+ if (buffer.length != 4)
+ throw new Error('Empty messages must be empty');
+
+ result.options = [];
+ result.payload = empty;
+ }
+
+ return result;
+};
+
+function parseLength(buffer) {
+ var length = buffer.readUInt8(0) >> 4;
+
+ // if (length == 0)
+ // throw new Error('Message Length is 0');
+
+ // console.log('length : ' + length);
+
+ return length;
+}
+
+function parseExtendedLength(buffer, lengthfield) {
+ var extendedLength;
+
+ if (lengthfield == 13) {
+ extendedLength = buffer.slice(index, index++) + 13;
+ } else if (lengthfield == 14) {
+ extendedLength = buffer.slice(index, index + 2) + 269;
+ index += 2;
+ } else if (lengthfield == 15) {
+ extendedLength = buffer.slice(index, index + 4) + 65805;
+ index += 4;
+ } else
+ throw new Error('lengthfield is invalid : current lengthfield is ' + lengthfield);
+
+ // console.log('extendedLength : ' + extendedLength);
+
+ return extendedLength;
+}
+
+function parseCode(buffer) {
+ var byte = buffer.readUInt8(index),
+ code = '' + (byte >> 5) + '.';
+
+ index++;
+
+ byte = byte & lowerCodeMask;
+
+ if (byte < 10)
+ code += '0' + byte;
+ else
+ code += byte;
+
+ // console.log('code : ' + code);
+
+ return code;
+}
+
+function parseToken(buffer) {
+ var length = buffer.readUInt8(0) & 0x0F,
+ token;
+
+ if (length > 8) {
+ throw new Error('Token length not allowed');
+ }
+
+ token = buffer.slice(index, index + length);
+
+ index += length;
+
+ // console.log('token : ' + token);
+
+ return token;
+}
+
+var numMap = {
+ '1' : 'If-Match',
+ '3' : 'Uri-Host',
+ '4' : 'ETag',
+ '5' : 'If-None-Match',
+ '6' : 'Observe',
+ '7' : 'Uri-Port',
+ '8' : 'Location-Path',
+ '11' : 'Uri-Path',
+ '12' : 'Content-Format',
+ '14' : 'Max-Age',
+ '15' : 'Uri-Query',
+ '17' : 'Accept',
+ '20' : 'Location-Query',
+ '35' : 'Proxy-Uri',
+ '39' : 'Proxy-Scheme',
+ '60' : 'Size1'
+};
+
+var optionNumberToString = (function genOptionParser() {
+
+ var code = Object.keys(numMap).reduce(function(acc, key) {
+
+ acc += 'case ' + key + ':\n';
+ acc += ' return \'' + numMap[key] + '\'\n';
+
+ return acc;
+ }, 'switch(number) {\n');
+
+ code += 'default:\n';
+ code += 'return \'\' + number';
+ code += '}\n';
+
+ return new Function('number', code);
+})();
+
+function parseOptions(buffer) {
+
+ var byte,
+ number = 0,
+ delta,
+ length,
+ nextOption = true,
+ options = [],
+ option;
+
+ while (index < buffer.length) {
+ byte = buffer.readUInt8(index);
+
+ if (byte === 255 || index > buffer.length) {
+ break;
+ }
+
+ delta = byte >> 4;
+ length = byte & 15;
+
+ index += 1;
+
+ if (delta === 13) {
+ delta = buffer.readUInt8(index) + 13;
+ index += 1;
+ } else if (delta === 14) {
+ delta = buffer.readUInt16BE(index) + 269;
+ index += 2;
+ } else if (delta === 15) {
+ throw new Error('Wrong option delta');
+ }
+
+ if (length === 13) {
+ length = buffer.readUInt8(index) + 13;
+ index += 1;
+ } else if (length === 14) {
+ length = buffer.readUInt16BE(index) + 269;
+ index += 2;
+ } else if (length === 15) {
+ throw new Error('Wrong option length');
+ }
+
+ number += delta;
+
+ var name = optionNumberToString(number),
+ value = buffer.slice(index, index + length);
+
+ // console.log("name : " + name);
+ // console.log("value : " + value.toString());
+ // console.log();
+
+ options.push({
+ name : name,
+ value : value
+ });
+
+ index += length;
+ }
+
+ return options;
+}
+
+function toCode(code) {
+ var split = code.split && code.split('.'),
+ by = 0;
+
+ if (split && split.length === 2) {
+ by |= parseInt(split[0]) << 5;
+ by |= parseInt(split[1]);
+ } else {
+
+ if (!split)
+ code = parseInt(code);
+
+ by |= (code / 100) << 5;
+ by |= (code % 100);
+ }
+
+ return by;
+}
+
+function fillGenDefaults(packet) {
+ if (!packet)
+ packet = {};
+
+ if (!packet.payload)
+ packet.payload = empty;
+
+ if (!packet.token)
+ packet.token = empty;
+
+ if (packet.token.length > 8)
+ throw new Error('Token too long');
+
+ if (!packet.code)
+ packet.code = '0.01';
+
+ if (!packet.options)
+ packet.options = [];
+
+ return packet;
+}
+
+function calculateBuffer(packet, length) {
+ var extendedLengthValue = calulateExtendedLength(length),
+ extendLength;
+
+ if (extendedLengthValue < 13) {
+ extendLength = 0;
+ } else if (extendedLengthValue == 13) {
+ extendLength = 1;
+ } else if (extendedLengthValue == 14) {
+ extendLength = 2;
+ } else if (extendedLengthValue == 15) {
+ extendLength = 4;
+ } else {
+ console.log('Length must be less than 4GB, current is ' + length);
+ }
+
+ length += (2 + packet.token.length); //+ extendLength);
+
+ return length;
+}
+
+function calculateLength(packet, options) {
+
+ var length = packet.payload.length,
+ i,
+ lengthValue;
+
+ if (packet.payload != '')
+ length += 1;
+
+ for ( i = 0; i < options.length; i++) {
+ length += options[i].length;
+ }
+
+ return length;
+}
+
+function calulateExtendedLength(length) {
+ var lengthValue;
+ if (length < 13) {
+ lengthValue = length;
+ } else if (length < 256 + 13 + 1) {
+ lengthValue = 13;
+ } else if (length < 65535 + 269 + 1) {
+ lengthValue = 14;
+ } else if (length < 4294967295 + 65535 + 1) {
+ lengthValue = 15;
+ } else {
+ console.log('Length must be less than 4GB, current is ' + length);
+ }
+ return lengthValue;
+}
+
+var optionStringToNumber = (function genOptionParser() {
+
+ var code = Object.keys(numMap).reduce(function(acc, key) {
+
+ acc += 'case \'' + numMap[key] + '\':\n';
+ acc += ' return \'' + key + '\'\n';
+
+ return acc;
+ }, 'switch(string) {\n');
+
+ code += 'default:\n';
+ code += 'return parseInt(string)';
+ code += '}\n';
+
+ return new Function('string', code);
+})();
+
+var nameMap = Object.keys(numMap).reduce(function(acc, key) {
+ acc[numMap[key]] = key;
+ return acc;
+}, {});
+
+function optionSorter(a, b) {
+ a = a.name;
+ b = b.name;
+
+ a = parseInt(nameMap[a] || a);
+ b = parseInt(nameMap[b] || b);
+ console.log("a : " + a + ", b : " + b);
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+
+ return 0;
+}
+
+function prepareOptions(packet) {
+ var options = [],
+ total = 0,
+ delta,
+ buffer,
+ byte,
+ option,
+ i,
+ bufferSize,
+ pos,
+ value;
+
+
+ // packet.options.sort(optionSorter);
+
+ //console.log("path in prepareOptions : " + getUriPath(packet));
+
+ /* add delta, length and value to buffer for each option in order */
+ console.log();
+ console.log('packet.options.length : ' + packet.options.length);
+ console.log();
+
+ for ( i = 0; i < packet.options.length; i++) {
+ pos = 0;
+ option = packet.options[i].name;
+ delta = optionStringToNumber(option) - total;
+ value = packet.options[i].value;
+
+ // console.log('turn : ' + i);
+ // console.log('optionStringToNumber(option) : ' + optionStringToNumber(option));
+ // console.log('delta : ' + delta);
+ // console.log('value.length : ' + value.length);
+ // console.log('value : ' + value);
+
+ // max option length is 1 header, 2 ext numb, 2 ext length
+ buffer = new Buffer(value.length + 5);
+
+ byte = 0;
+
+ if (delta < 13) {
+ byte |= delta << 4;
+ } else if (delta < 255 + 13 + 1) {
+ byte |= 13 << 4;
+ } else if (delta < 65535 + 269 + 1) {
+ byte |= 14 << 4;
+ } else {
+ byte != 15 << 4;
+ console.log("Message Format Error");
+ }
+
+ if (value.length < 13) {
+ byte |= value.length;
+ } else if (value.length < 255 + 13 + 1) {
+ byte |= 13;
+ } else if (value.length < 65535 + 269 + 1) {
+ byte |= 14;
+ } else {
+ byte != 15 << 4;
+ console.log("Message Format Error");
+ }
+
+ // console.log("byte : " + byte);
+ buffer.writeUInt8(byte, pos++);
+
+ if (delta < 13) {
+ ;
+ } else if (delta < 255 + 13 + 1) {
+ buffer.writeUInt8(delta - 13, pos++);
+ } else if (delta < 65535 + 269 + 1) {
+ buffer.writeUInt16BE(delta - 269, pos);
+ pos += 2;
+ } else {
+ console.log("Message Format Error");
+ }
+
+ if (value.length < 13) {
+ ;
+ } else if (value.length < 269) {
+ buffer.writeUInt8(value.length - 13, pos++);
+ } else if (value.length < 65535 + 269 + 1) {
+ buffer.writeUInt16BE(value.length - 269, pos);
+ pos += 2;
+ } else {
+ console.log("Message Format Error");
+ }
+
+ value.copy(buffer, pos);
+ pos += value.length;
+ total += delta;
+ options.push(buffer.slice(0, pos));
+ // console.log('total : ' + total);
+ // console.log('pos : ' + pos);
+ // console.log('buffer : ' + buffer);
+ // console.log();
+ }
+
+ return options;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import TextField from 'material-ui/TextField';
+import Checkbox from 'material-ui/Checkbox';
+import RaisedButton from 'material-ui/RaisedButton';
+import Dialog from 'material-ui/Dialog';
+
+const Client = require('../Client');
+
+const style = {
+ dialog: {
+ width: '500px'
+ },
+
+ actions: {
+ textAlign: 'center',
+ },
+
+ actionCheckbox: {
+ marginLeft: '100px',
+ width: '200px'
+ },
+
+ error: {
+ textAlign: 'left',
+ }
+}
+
+const errorText = 'This field is required';
+
+class ConnectDialog extends React.Component {
+
+ handleChange = (event) => {
+ if (event.target.value.length !== 0) {
+ this.setState({ error : '' });
+ } else {
+ this.setState({ error : errorText });
+ }
+
+ this.setState({
+ address: event.target.value,
+ });
+ };
+
+ handleSubmit = () => {
+ if (this.state.address.length === 0) {
+ this.setState({ error : errorText });
+ return;
+ }
+
+ Client.writeClientData([['address', this.state.address]]);
+
+ console.log('input address: ' + this.state.address);
+ Client.init(this.state.address);
+ };
+
+ onAutoConnectChecked = (event, isInputChecked) => {
+ this.setState({ autoconnect: isInputChecked });
+
+ Client.writeClientData([['autoconnect', isInputChecked]]);
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ open: true,
+ autoconnect: props.autoconnect,
+ error: '',
+ address: props.address,
+ };
+
+ if(this.state.autoconnect === true) {
+ Client.init(this.state.address);
+ }
+ };
+
+ render() {
+ const actions = [
+ <TextField
+ floatingLabelText="Server address"
+ defaultValue={this.state.address}
+ hintText="127.0.0.1:80"
+ errorText={this.state.error}
+ errorStyle={style.error}
+ onChange={this.handleChange.bind(this)}
+ />, <br />, <br />,
+ <Checkbox
+ label="Auto Connect"
+ defaultChecked={this.state.autoconnect}
+ style={style.actionCheckbox}
+ onCheck={this.onAutoConnectChecked}
+ />, <br />, <br />,
+ <RaisedButton
+ label="Connect"
+ primary={true}
+ onTouchTap={this.handleSubmit.bind(this)}
+ />,
+ ];
+
+ return (
+ <MuiThemeProvider>
+ <div>
+ <Dialog
+ title="Connect to cloud"
+ titleStyle={{ fontWeight: 'bold' }}
+ actions={actions}
+ modal={true}
+ open={this.state.open}
+ contentStyle={style.dialog}
+ actionsContainerStyle={style.actions}>
+ </Dialog>
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+};
+
+export default ConnectDialog;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import Snackbar from 'material-ui/Snackbar';
+
+class ErrorMessage extends React.Component {
+
+ constructor(props, context) {
+ super(props, context);
+ var strMessage = 'ERROR, ' + props.message;
+ this.state = {
+ open: true,
+ message: strMessage
+ };
+ }
+
+ handleRequestClose = () => {
+ this.setState({
+ open: false,
+ });
+ };
+
+ render() {
+ return (
+ <MuiThemeProvider>
+ <Snackbar
+ open={this.state.open}
+ message={this.state.message}
+ autoHideDuration={3000}
+ onRequestClose={this.handleRequestClose}
+ />
+ </MuiThemeProvider>
+ );
+ }
+}
+
+export default ErrorMessage;
\ No newline at end of file
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import {List, ListItem} from 'material-ui/List';
+import RaisedButton from 'material-ui/RaisedButton';
+import TextField from 'material-ui/TextField';
+
+const Client = require('../Client');
+
+const style = {
+ basic: {
+ padding: 30
+ },
+
+ button: {
+ margin: 7,
+ },
+}
+
+class FirmwareHandler extends React.Component {
+ responseCallback = (packet) => {
+ console.debug("response received from " + this.state.uri);
+ var response = packet.getMethod + '\n';
+ if (packet.getPayloadObject !== null) {
+ response += packet.getPayloadString;
+ }
+ this.setState({ response: response });
+ }
+
+ handleFirmwareChanged = (event, newValue) => {
+ this.setState({ firmwarelink: newValue })
+ };
+
+ handleNewVersionChanged = (event, newValue) => {
+ this.setState({ newversion: newValue })
+ };
+
+ handleButtonClicked = (method) => {
+ var payload = '{"packageuri":"' + this.state.firmwarelink
+ + '", "newversion": "' + this.state.newversion + '"}';
+ Client.sendMessage(this.state.uri, method, JSON.parse(payload), null, this.responseCallback);
+ };
+
+ onFirmware = (packet) => {
+ console.debug("response received from " + this.state.uri);
+ var response = packet.getMethod + '\n';
+ if (packet.getPayloadObject !== null) {
+ response += packet.getPayloadString;
+ }
+ this.setState({ response: response });
+ }
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ uri: props.uri,
+ firmwarelink: null,
+ newversion: null,
+ currentversion: null,
+ response: '',
+ }
+
+ Client.sendMessage(this.state.uri, "GET", null, null, this.onFirmware);
+ }
+
+ render() {
+ return (
+ <div>
+ <TextField
+ floatingLabelText="Firmware Link"
+ fullWidth={true}
+ multiLine={true}
+ rows={1}
+ rowsMax={1}
+ floatingLabelFixed={true}
+ style={{ textAlign: 'left'}}
+ onChange={this.handleFirmwareChanged}
+ />
+ <TextField
+ floatingLabelText="New version"
+ fullWidth={true}
+ multiLine={true}
+ rows={1}
+ rowsMax={1}
+ floatingLabelFixed={true}
+ style={{ textAlign: 'left'}}
+ onChange={this.handleNewVersionChanged}
+ />
+ <RaisedButton backgroundColor="#BDBDBD" labelColor="#FFFFFF" style={style.button}
+ label="Register" onTouchTap={this.handleButtonClicked.bind(this, "POST")}
+ />
+ <TextField
+ floatingLabelText="response"
+ fullWidth={true}
+ multiLine={true}
+ rows={3}
+ rowsMax={3}
+ floatingLabelFixed={true}
+ style={{ textAlign: 'left'}}
+ value={this.state.response}
+ />
+ </div>
+ );
+ }
+};
+
+class FirmwareManagement extends React.Component {
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ devices: props.devices
+ };
+ };
+
+ makeListItem = (uri) => {
+ var handler =
+ <FirmwareHandler uri={uri} />;
+
+ return handler;
+ }
+
+ render() {
+ return (
+ <MuiThemeProvider>
+ <div>
+ <List>
+ <b>Devices</b>
+ {this.state.devices.map( (row, index) => {
+ if (row.size === 0) {
+ return (<p key='notfound' style={{ fontWeight: 'bold' }}> No Device found </p>);
+ }
+ return ( <ListItem key={index} primaryText={index + ' ' + row.n}
+ secondaryText={ <p> uri: {row.uri} <br /> di: {row.di} </p> }
+ secondaryTextLines={2}
+ nestedItems={[<ListItem key={index + '_item'} disabled={true}>
+ {this.makeListItem(row.uri)} </ListItem>]}
+ nestedListStyle={{ textAlign:'right' }} primaryTogglesNestedList={true}
+ innerDivStyle={{ fontWeight: 'bold' }}/>
+ );})
+ }
+ </List>
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+}
+
+export default FirmwareManagement;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import AppBar from 'material-ui/AppBar';
+import IconButton from 'material-ui/IconButton';
+import IconMenu from 'material-ui/IconMenu';
+import MenuItem from 'material-ui/MenuItem';
+import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
+import {orange700, white} from 'material-ui/styles/colors';
+
+const Client = require('../Client');
+
+const style = {
+ basic: {
+ backgroundColor: orange700
+ },
+
+ font: {
+ fontWeight: 'bold'
+ }
+}
+
+const handleSignout = () => {
+ console.debug("sign out clicked");
+ Client.signOut();
+};
+
+const handleDisconnect = () => {
+ console.debug("disconnect clicked");
+ Client.close();
+};
+
+const Menu = (props) => (
+ <IconMenu
+ iconButtonElement={
+ <IconButton><MoreVertIcon color={white}/></IconButton>
+ }
+ targetOrigin={{horizontal: 'right', vertical: 'top'}}
+ anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
+ >
+ <MenuItem primaryText="Help" />
+ {props.signin ? <MenuItem primaryText="Sign out" onTouchTap={handleSignout} />: null}
+ <MenuItem primaryText="Server setting" onTouchTap={handleDisconnect}/>
+ </IconMenu>
+);
+Menu.muiName = 'IconMenu';
+
+class MainAppBar extends React.Component {
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = {
+ signin: props.signin
+ }
+ };
+
+ render() {
+ return (
+ <MuiThemeProvider>
+ <div>
+ <AppBar
+ /* TODO set title */
+ title="Resource Manager"
+ titleStyle={style.font}
+ /* TODO add icon */
+ //iconElementLeft={ }
+ iconElementRight={ <Menu signin={this.state.signin} /> }
+ showMenuIconButton={false}
+ style={style.basic}
+ />
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+};
+
+export default MainAppBar;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import {List, ListItem} from 'material-ui/List';
+import Divider from 'material-ui/Divider';
+import {orange700, white} from 'material-ui/styles/colors';
+
+import ResourceList from './ResourceList';
+import ResourceListToolbar from './ResourceListToolbar';
+import FirmwareManagement from './FirmwareManagement';
+
+const Client = require('../Client');
+
+const ERROR = 'error';
+
+const style = {
+ list: {
+ backgroundColor: orange700,
+ height: '100%'
+ },
+
+ text: {
+ color: white,
+ }
+}
+
+function appendChildren(id, numChildren) {
+ var parent = document.getElementById(id);
+ while (parent.firstChild) {
+ parent.removeChild(parent.firstChild);
+ }
+
+ for (var i = 0; parent.childElementCount <= numChildren; i++) {
+ parent.appendChild(document.createElement('child' + i));
+ }
+};
+
+class MainMenu extends React.Component {
+
+ onDiscover = (packet) => {
+ if(packet.getCode !== 69)
+ {
+ Client.event.emit(ERROR, "Resource Discover Failed " + packet.getCode);
+ return;
+ }
+
+ var resources = Client.getResourceList(packet.getPayloadObject);
+
+ appendChildren('body', 2);
+ ReactDOM.render(
+ <ResourceListToolbar />,
+ document.getElementById('body').children[0]
+ );
+ ReactDOM.render(
+ <ResourceList data={resources} />,
+ document.getElementById('body').children[1]
+ );
+ };
+
+ onFirmwareResources = (packet) =>{
+ if(packet.getCode !== 69)
+ {
+ Client.event.emit(ERROR, "Firmware Discover Failed " + packet.getCode);
+ return;
+ }
+
+ var resources = Client.getResourceList(packet.getPayloadObject);
+
+ appendChildren('body', 1);
+ ReactDOM.render(
+ <FirmwareManagement devices={resources} />,
+ document.getElementById('body').children[0]
+ );
+ };
+
+ handleDiscovery = () => {
+ Client.discoverResource(null, this.onDiscover);
+ };
+
+ handleFirmware = () => {
+ Client.discoverResource("rt=x.samsung.firmware", this.onFirmwareResources);
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ signin: props.signin
+ }
+ };
+
+ render() {
+ return (
+ /* TODO add log page */
+ <MuiThemeProvider>
+ <List style={style.list}>
+ <ListItem primaryText='Resource list' style={style.text} disabled={!this.state.signin} onTouchTap={this.handleDiscovery} />
+ <Divider />
+ <ListItem primaryText='Firmware management' style={style.text} disabled={!this.state.signin} onTouchTap={this.handleFirmware} />
+ <Divider />
+ <ListItem primaryText='Log view' style={style.text} />
+ </List>
+ </MuiThemeProvider>
+ );
+ };
+};
+
+export default MainMenu;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import {List, ListItem} from 'material-ui/List';
+import Subheader from 'material-ui/Subheader';
+import RaisedButton from 'material-ui/RaisedButton';
+import TextField from 'material-ui/TextField';
+
+const Client = require('../Client');
+
+const style = {
+ basic: {
+ padding: 30
+ },
+
+ button: {
+ margin: 7,
+ },
+}
+
+/* TODO handle payload json syntax error */
+// Message handler & react component for each found resource.
+class MessageHandler extends React.Component {
+ responseCallback = (packet) => {
+ console.debug("response received from " + this.state.uri);
+ var response = packet.getMethod + '\n';
+ if (packet.getPayloadObject !== null) {
+ response += packet.getPayloadString;
+ }
+ this.setState({ response: response });
+ }
+
+ handleQueryChanged = (event, newValue) => {
+ this.setState({ queries: newValue })
+ };
+
+ handlePayloadChanged = (event, newValue) => {
+ this.setState({ payload: newValue })
+ };
+
+ handleButtonClicked = (method) => {
+ Client.sendMessage(this.state.uri, method, JSON.parse(this.state.payload), this.state.queries, this.responseCallback);
+ if(method === "GET OBSERVE"){
+ this.setState({observeState : "GET OBSERVE CANCEL"})
+ }
+ else if(method === "GET OBSERVE CANCEL"){
+ this.setState({observeState : "GET OBSERVE"})
+ }
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ uri: props.uri,
+ payload: null,
+ queries: null,
+ response: '',
+ observeState: "GET OBSERVE"
+ }
+ }
+
+ render() {
+ return (
+ <div>
+ <TextField
+ key={this.state.uri + 'queries'}
+ floatingLabelText="additional query"
+ fullWidth={true}
+ multiLine={true}
+ rows={1}
+ rowsMax={1}
+ floatingLabelFixed={true}
+ style={{ textAlign: 'left'}}
+ onChange={this.handleQueryChanged}
+ />
+ <TextField
+ key={this.state.uri + 'payload'}
+ floatingLabelText="payload"
+ fullWidth={true}
+ multiLine={true}
+ rows={5}
+ rowsMax={5}
+ floatingLabelFixed={true}
+ style={{ textAlign: 'left'}}
+ onChange={this.handlePayloadChanged}
+ />
+ <RaisedButton backgroundColor="#BDBDBD" labelColor="#FFFFFF" style={style.button}
+ label="GET" onTouchTap={this.handleButtonClicked.bind(this, "GET")}
+ />
+ <RaisedButton backgroundColor="#BDBDBD" labelColor="#FFFFFF" style={style.button}
+ label="PUT" onTouchTap={this.handleButtonClicked.bind(this, "PUT")}
+ />
+ <RaisedButton backgroundColor="#BDBDBD" labelColor="#FFFFFF" style={style.button}
+ label="POST" onTouchTap={this.handleButtonClicked.bind(this, "POST")}
+ />
+ <RaisedButton backgroundColor="#BDBDBD" labelColor="#FFFFFF" style={style.button}
+ label="DELETE" onTouchTap={this.handleButtonClicked.bind(this, "DELETE")}
+ />
+ <RaisedButton backgroundColor="#BDBDBD" labelColor="#FFFFFF" style={style.button}
+ label={this.state.observeState} onTouchTap={this.handleButtonClicked.bind(this, this.state.observeState)}
+ />
+ <TextField
+ floatingLabelText="response"
+ fullWidth={true}
+ multiLine={true}
+ rows={5}
+ rowsMax={5}
+ floatingLabelFixed={true}
+ style={{ textAlign: 'left'}}
+ value={this.state.response}
+ />
+ </div>
+ );
+ }
+};
+
+// List of found resources.
+class ResourceList extends React.Component {
+
+ state = {
+ inputRT: '',
+ inputDI: '',
+ };
+
+ handleRTTextChanged = (event, newValue) => {
+ this.setState({ inputRT: newValue });
+ }
+
+ handleDITextChanged = (event, newValue) => {
+ this.setState({ inputDI: newValue });
+ }
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ data: props.data,
+ inputRT: '',
+ inputDI: '',
+ };
+ };
+
+ makeListItem = (uri) => {
+ var handler =
+ <MessageHandler uri={uri} />;
+
+ return handler;
+ }
+
+ render() {
+ return (
+ <MuiThemeProvider>
+ <div>
+ <List style={style.basic}>
+ <Subheader style={{ fontWeight: 'bold' }} >Resources</Subheader>
+ {this.state.data.map( (row, index) => {
+ if (row.size === 0) {
+ return (<p key='notfound' style={{ fontWeight: 'bold' }}> No Resource found </p>);
+ }
+ return ( <ListItem primaryText={row.uri}
+ secondaryText={ <p> rt: {row.rts} <br /> if: {row.ifs} </p> }
+ secondaryTextLines={2}
+ nestedItems={[<ListItem key={index + '_item'} disabled={true}>
+ {this.makeListItem(row.uri)} </ListItem>]}
+ nestedListStyle={{ textAlign:'right' }} primaryTogglesNestedList={true}
+ innerDivStyle={{ fontWeight: 'bold' }}/>
+ );})
+ }
+ </List>
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+}
+
+export default ResourceList;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import RaisedButton from 'material-ui/RaisedButton';
+import TextField from 'material-ui/TextField';
+import {Toolbar, ToolbarGroup, ToolbarSeparator} from 'material-ui/Toolbar';
+import ResourceList from './ResourceList';
+
+const Client = require('../Client');
+
+const ERROR = 'error';
+
+class ResourceListToolbar extends React.Component {
+
+ state = {
+ inputRT: '',
+ inputDI: '',
+ };
+
+ handleRTTextChanged = (event, newValue) => {
+ this.setState({ inputRT: newValue });
+ }
+
+ handleDITextChanged = (event, newValue) => {
+ this.setState({ inputDI: newValue });
+ }
+
+ onDiscover = (packet) => {
+ if(packet.getCode !== 69)
+ {
+ Client.event.emit(ERROR, "Resource Discover Failed " + packet.getCode);
+ return;
+ }
+
+ var resources = Client.getResourceList(packet.getPayloadObject);
+
+ document.getElementById('body').children[1].innerHTML = "";
+ ReactDOM.render(
+ <ResourceList data={resources} />,
+ document.getElementById('body').children[1]
+ );
+ };
+
+ handleButtonClicked = () => {
+ var queries = '';
+ if (this.state.inputRT.length !== 0) {
+ queries += 'rt='+ this.state.inputRT + ';';
+ }
+ if (this.state.inputDI.length !== 0) {
+ queries += 'di='+ this.state.inputDI;
+ }
+
+ Client.discoverResource(queries, this.onDiscover);
+ }
+
+ render() {
+ return (
+ <MuiThemeProvider>
+ <div>
+ <Toolbar>
+ <ToolbarGroup>
+ <TextField
+ floatingLabelText="Resource type"
+ style={{ textAlign: 'left', margin: 7 }}
+ onChange={this.handleRTTextChanged}
+ value={this.state.inputRT}
+ />
+ <TextField
+ floatingLabelText="Device id"
+ style={{ textAlign: 'left', margin: 7 }}
+ onChange={this.handleDITextChanged}
+ value={this.state.inputDI}
+ />
+ <RaisedButton label="Discover" backgroundColor="#9CCC65" onTouchTap={this.handleButtonClicked} labelStyle={{ color: '#FFFFFF', fontWeight: 'bold' }}/>
+ </ToolbarGroup>
+ <ToolbarGroup lastChild={true}>
+ <ToolbarSeparator />
+ <RaisedButton label="Refresh" backgroundColor="#8BC34A" onTouchTap={this.handleButtonClicked} labelStyle={{ color: '#FFFFFF', fontWeight: 'bold' }}/>
+ </ToolbarGroup>
+ </Toolbar>
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+}
+
+export default ResourceListToolbar;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import RaisedButton from 'material-ui/RaisedButton';
+import TextField from 'material-ui/TextField';
+import Checkbox from 'material-ui/Checkbox';
+import {white} from 'material-ui/styles/colors';
+import Paper from 'material-ui/Paper';
+
+import SignupButton from './SignupButton';
+
+const Client = require('../Client');
+
+const style = {
+ basic: {
+ margin: 7
+ },
+
+ button: {
+ color: white
+ },
+
+ checkbox: {
+ marginLeft: '100px',
+ width: '200px'
+ },
+
+ error: {
+ textAlign: 'left',
+ },
+
+ font: {
+ fontWeight: 'bold'
+ },
+
+ paper: {
+ textAlign: 'center',
+ margin: 'auto',
+ position: 'absolute',
+ top: '20%',
+ left: '38%',
+ }
+};
+
+const errorText = 'This field is required';
+
+class SigninPage extends React.Component {
+
+ handleChange = (event) => {
+ var currentUidError = this.state.uidError;
+ var currentAccesstokenError = this.state.accesstokenError;
+
+ switch(event.target.id) {
+ case 'uid':
+ this.setState({ uid : event.target.value });
+ if (event.target.value.length !== 0) {
+ currentUidError = '';
+ } else {
+ currentUidError = errorText;
+ }
+ break;
+ case 'accesstoken':
+ this.setState({ accesstoken : event.target.value });
+ if (event.target.value.length !== 0) {
+ currentAccesstokenError = '';
+ } else {
+ currentAccesstokenError = errorText;
+ }
+ break;
+ default:
+ break;
+ }
+ this.setState({ uidError : currentUidError, accesstokenError : currentAccesstokenError })
+ };
+
+ handleSignin = () => {
+ if (this.state.uid.length === 0 || this.state.accesstoken.length === 0) {
+ return;
+ }
+
+ console.log("di: " + this.state.di);
+ console.log("uid: " + this.state.uid);
+ console.log("accesstoken: " + this.state.accesstoken);
+ Client.writeClientData([["uid", this.state.uid], ["accesstoken", this.state.accesstoken]]);
+ Client.signIn(this.state.di, this.state.uid, this.state.accesstoken);
+ };
+
+ onAutoSigninChecked = (event, isInputChecked) => {
+ this.setState({ autosignin: isInputChecked });
+
+ Client.writeClientData([['autosignin', isInputChecked]]);
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ autosignin: props.autosignin,
+ di: props.di,
+ uid: props.uid,
+ accesstoken: props.accesstoken,
+ uidError: '',
+ accesstokenError: '',
+ };
+
+ if(this.state.autosignin === true) {
+ Client.signIn(this.state.di, this.state.uid, this.state.accesstoken);
+ }
+ };
+
+ render() {
+ const actions = (
+ <div style={{ padding: '30px 50px 30px 50px' }}>
+ <p style={{ fontWeight: 'bold', fontSize: 25 }}> Sign In </p>
+ <TextField
+ floatingLabelText="Device Id"
+ disabled={true}
+ defaultValue={this.state.di}
+ style={{ width: '400px' }}
+ /> <br />
+ <TextField
+ id='uid'
+ floatingLabelText="User UUID"
+ defaultValue={this.state.uid}
+ style={{ width: '400px' }}
+ errorText={this.state.uidError}
+ errorStyle={style.error}
+ onChange={this.handleChange.bind(this)}
+ /> <br />
+ <TextField
+ id='accesstoken'
+ floatingLabelText="Access token"
+ defaultValue={this.state.accesstoken}
+ style={{ width: '400px' }}
+ errorText={this.state.accesstokenError}
+ errorStyle={style.error}
+ onChange={this.handleChange.bind(this)}
+ /> <br />
+ <Checkbox
+ label="Auto Sign-In"
+ defaultChecked={this.state.autosignin}
+ style={style.checkbox}
+ onCheck={this.onAutoSigninChecked}
+ /> <br /> <br />
+ <div style={{ display: 'flex' }}>
+ <SignupButton di={this.state.di} />
+ <RaisedButton
+ label="Sign-In"
+ style={style.basic}
+ onTouchTap={this.handleSignin.bind(this)}
+ />
+ </div>
+ </div>
+ );
+
+ return (
+ <MuiThemeProvider>
+ <div style={{ height: '100%' }}>
+ <Paper style={style.paper} zDepth={1}>
+ {actions}
+ </Paper>
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+};
+
+export default SigninPage;
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
+import RaisedButton from 'material-ui/RaisedButton';
+import SelectField from 'material-ui/SelectField';
+import MenuItem from 'material-ui/MenuItem';
+import TextField from 'material-ui/TextField';
+import FlatButton from 'material-ui/FlatButton';
+import Dialog from 'material-ui/Dialog';
+import {white} from 'material-ui/styles/colors';
+
+const Client = require('../Client');
+
+const style = {
+ basic: {
+ margin: 7
+ },
+
+ button: {
+ color: white
+ },
+
+ dialog: {
+ width: '500px'
+ },
+
+ dialogActions: {
+ textAlign: 'center'
+ },
+
+ selectfield: {
+ textAlign: 'left'
+ },
+
+ radiobutton: {
+ textAlign: 'center',
+ maxWidth: '250px',
+ marginBottom: '16px'
+ },
+
+ error: {
+ textAlign: 'left',
+ },
+
+ font: {
+ fontWeight: 'bold'
+ }
+};
+
+const errorText = 'This field is required';
+
+class SignupButton extends React.Component {
+
+ handleOpen = () => {
+ this.setState({ open: true });
+ };
+
+ handleClose = () => {
+ this.setState({ open: false });
+ };
+
+ handleAuthcodeButtonClicked = (value) => {
+ this.setState({ provider : value });
+ };
+
+ handleSelectfieldChange = (event, index, value) => {
+ this.setState({ provider : value });
+ };
+
+ handleTextChange = (event) => {
+ var currentCodeErr = this.state.codeError;
+
+ this.setState({ code : event.target.value });
+ if (event.target.value.length !== 0) {
+ currentCodeErr = '';
+ } else {
+ currentCodeErr = errorText;
+ }
+
+ this.setState({ codeError : currentCodeErr });
+ };
+
+ handleSubmit = () => {
+ if (this.state.code.length === 0) {
+ return;
+ }
+
+ Client.event.on('signup', this.signupCallback);
+ Client.signUp(this.state.di, this.state.provider, this.state.code);
+ };
+
+ signupCallback = (uid, accesstoken) => {
+ this.setState({
+ dialogMessage: 'User UUID: ' + uid + '\n Accesstoken: ' + accesstoken,
+ signup: true,
+ });
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ di: props.di,
+ open: false,
+ provider: 'github',
+ code: '',
+ codeError: errorText,
+ dialogMessage: '',
+ signup: false
+ };
+ };
+
+ render() {
+ var actionBeforeSignup = [
+ <RaisedButton label="Github" backgroundColor="#E0E0E0" style={style.basic} onTouchTap={this.handleAuthcodeButtonClicked.bind(this, "github")}
+ href="https://github.com/login?return_to=%2Flogin%2Foauth%2Fauthorize%3Fclient_id%3Dea9c18f540323b0213d0%26redirect_uri%3Dhttp%253A%252F%252Fwww.example.com%252Foauth_callback%252F"
+ target="_blank" />, <br />,
+ <SelectField
+ floatingLabelText="OAuth provider"
+ value={this.state.provider}
+ onChange={this.handleSelectfieldChange}
+ style={style.selectfield}
+ >
+ <MenuItem value={'github'} primaryText="Github" />
+ <MenuItem value={'samsung'} primaryText="Samsung" />
+ </SelectField>, <br />,
+ <TextField
+ floatingLabelText="Device Id"
+ disabled={true}
+ defaultValue={this.state.di}
+ />, <br />,
+ <TextField
+ floatingLabelText="Auth Code"
+ errorText={this.state.codeError}
+ errorStyle={style.error}
+ onChange={this.handleTextChange}
+ />, <br />, <br />,
+ <RaisedButton
+ label="Cancel"
+ style={style.basic}
+ onTouchTap={this.handleClose}
+ />,
+ <RaisedButton
+ label="Submit"
+ style={style.basic}
+ primary={true}
+ onTouchTap={this.handleSubmit}
+ />,
+ ];
+
+ var actionAfterSignup = [
+ <FlatButton
+ label="Close"
+ primary={true}
+ onTouchTap={this.handleClose}
+ />,
+ ];
+
+ return (
+ <MuiThemeProvider>
+ <div>
+ <RaisedButton label="Sign-up" backgroundColor="#9E9E9E" style={style.basic} labelStyle={style.button} onTouchTap={this.handleOpen}/>
+ <Dialog
+ title="Sign-up"
+ titleStyle={style.font}
+ actions={this.state.signup ? actionAfterSignup : actionBeforeSignup}
+ modal={true}
+ open={this.state.open}
+ contentStyle={style.dialog}
+ actionsContainerStyle={style.dialogActions}>
+ {this.state.dialogMessage}
+ </Dialog>
+ </div>
+ </MuiThemeProvider>
+ );
+ }
+};
+
+export default SignupButton;
--- /dev/null
+body {
+ margin: 0;
+ padding: 0;
+ font-family: sans-serif;
+}
--- /dev/null
+/*
+ * //******************************************************************
+ * //
+ * // Copyright 2017 Samsung Electronics All Rights Reserved.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ * //
+ * // Licensed under the Apache License, Version 2.0 (the "License");
+ * // you may not use this file except in compliance with the License.
+ * // You may obtain a copy of the License at
+ * //
+ * // http://www.apache.org/licenses/LICENSE-2.0
+ * //
+ * // Unless required by applicable law or agreed to in writing, software
+ * // distributed under the License is distributed on an "AS IS" BASIS,
+ * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * // See the License for the specific language governing permissions and
+ * // limitations under the License.
+ * //
+ * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ConnectDialog from './components/ConnectDialog';
+import MainAppBar from './components/MainAppBar';
+import MainMenu from './components/MainMenu';
+import SigninPage from './components/SigninPage';
+import ErrorMessage from './components/ErrorMessage';
+
+import './index.css';
+
+import injectTapEventPlugin from 'react-tap-event-plugin';
+injectTapEventPlugin();
+
+const Client = require('./Client');
+
+const CONNECTED = 'connected';
+const DISCONNECTED = 'disconnected';
+const SIGNUP = 'signup';
+const SIGNIN = 'signin';
+const SIGNOUT = 'signout';
+const ERROR = 'error';
+
+// #1 error cases: render error message.
+Client.event.on(ERROR, function(message) {
+ ReactDOM.unmountComponentAtNode(document.getElementById('error'));
+ ReactDOM.render(
+ <ErrorMessage message={message}/>,
+ document.getElementById('error')
+ );
+});
+
+// #2 websocket connected: default page
+Client.event.on(CONNECTED, function() {
+ renderDefaultPage(false);
+
+ ReactDOM.render(
+ <SigninPage di={getDeviceId()} uid={getUid()} accesstoken={getAccesstoken()} autosignin={getAutosignin()} />,
+ document.getElementById('body')
+ );
+});
+
+// #3 websocket disconnected: connect page
+Client.event.on(DISCONNECTED, function() {
+ Client.removeClientData(['autoconnect', 'autosignin']);
+
+ unmountDefaultPage();
+
+ ReactDOM.render(
+ <ConnectDialog address={getAddress()} autoconnect={getAutoconnect()} />,
+ document.getElementById('body')
+ );
+});
+
+// #4 sign-in: request for resource discovery
+Client.event.on(SIGNIN, function() {
+ unmountDefaultPage();
+
+ renderDefaultPage(true);
+
+ ReactDOM.unmountComponentAtNode(document.getElementById('body'));
+});
+
+// #5 sign-out: default page
+Client.event.on(SIGNOUT, function() {
+ Client.removeClientData(['accesstoken']);
+
+ unmountDefaultPage();
+ renderDefaultPage(false);
+
+ ReactDOM.render(
+ <SigninPage di={getDeviceId()} uid={getUid()} accesstoken={getAccesstoken()} autosignin={getAutosignin()} />,
+ document.getElementById('body')
+ );
+});
+
+ReactDOM.render(
+ <ConnectDialog address={getAddress()} autoconnect={getAutoconnect()} />,
+ document.getElementById('body')
+);
+
+function renderDefaultPage(signinStatue) {
+ ReactDOM.render(
+ <MainAppBar signin={signinStatue}/>,
+ document.getElementById('appbar')
+ );
+ ReactDOM.render(
+ <MainMenu signin={signinStatue}/>,
+ document.getElementById('menu')
+ );
+};
+
+function unmountDefaultPage() {
+ ReactDOM.unmountComponentAtNode(document.getElementById('appbar'));
+ ReactDOM.unmountComponentAtNode(document.getElementById('menu'));
+};
+
+/**
+ * Private API.
+ */
+ function generateUUID() {
+ var d = new Date().getTime();
+ var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = (d + Math.random()*16)%16 | 0;
+ d = Math.floor(d/16);
+ return (c ==='x' ? r : (r&0x3|0x8)).toString(16);
+ });
+ return uuid;
+ };
+
+// get device id from local storage if exists, or generate id.
+function getDeviceId() {
+ var deviceId = Client.readClientData('deviceId');
+
+ if (deviceId === undefined || deviceId === null) {
+ deviceId = generateUUID();
+ Client.writeClientData([['deviceId', deviceId]]);
+ }
+
+ return deviceId;
+};
+
+function getAddress() {
+ var address = Client.readClientData('address');
+
+ return address;
+};
+
+function getUid() {
+ var uid = Client.readClientData('uid');
+
+ return uid;
+};
+
+function getAccesstoken() {
+ var accesstoken = Client.readClientData('accesstoken');
+
+ return accesstoken;
+};
+
+function getAutoconnect() {
+ var autoconnect = (Client.readClientData('autoconnect') === 'true');
+
+ return autoconnect;
+};
+
+function getAutosignin() {
+ var autosignin = (Client.readClientData('autosignin') === 'true');
+
+ return autosignin;
+};
private int tokenLength = 0;
private int optionPayloadLength = 0;
private CoapMessage partialMsg = null;
+ private int websocketLength = -1;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
partialMsg.setToken(token);
}
+ if (websocketLength != -1) {
+ optionPayloadLength = websocketLength
+ - (bufferToRead + 1); // shimheader + code +
+ // tokenLength
+ }
+
if (optionPayloadLength > 0) {
int optionLen = parseOptions(partialMsg, in,
optionPayloadLength);
}
}
- public void decode(ByteBuf in, List<Object> out) throws Exception {
+ public void decode(ByteBuf in, List<Object> out, int length)
+ throws Exception {
+ websocketLength = length;
decode(null, in, out);
}
public class CoapEncoder extends MessageToByteEncoder<CoapMessage> {
+ private boolean isboolWebSocket = false;
+
@Override
protected void encode(ChannelHandlerContext ctx, CoapMessage msg,
ByteBuf out) throws Exception {
* encode options
*/
encodeOptions(optBuf, coapMessage);
+
long length = optBuf.readableBytes();
if (coapMessage.getPayloadSize() > 0) {
length += 1 + coapMessage.getPayloadSize();
}
+ if (isboolWebSocket) {
+ length = 0;
+ }
+
calcShimHeader(coapMessage, out, length);
out.writeByte(coapMessage.getCode());
}
}
- public void encode(CoapMessage msg, ByteBuf out) throws Exception {
+ public void encode(CoapMessage msg, ByteBuf out, boolean isWebsocket)
+ throws Exception {
+ isboolWebSocket = isWebsocket;
encode(null, msg, out);
}
*/
package org.iotivity.cloud.base.protocols.coap.websocket;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelDuplexHandler;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import io.netty.handler.codec.http.DefaultFullHttpResponse;
-import io.netty.handler.codec.http.HttpObjectAggregator;
-import io.netty.handler.codec.http.HttpServerCodec;
-import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.WebSocketFrame;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.iotivity.cloud.util.JSONUtil;
import org.iotivity.cloud.util.Log;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+
public class WebSocketFrameHandler extends ChannelDuplexHandler {
@Override
List<Object> messages = new ArrayList<>();
new CoapDecoder().decode(((BinaryWebSocketFrame) msg).content(),
- messages);
+ messages,
+ ((BinaryWebSocketFrame) msg).content().readableBytes());
for (Object message : messages) {
if (message instanceof CoapMessage) {
// convert content format to cbor if content format is json.
if (coapMessage.getPayloadSize() != 0
- && coapMessage.getContentFormat().equals(
- ContentFormat.APPLICATION_JSON)) {
+ && coapMessage.getContentFormat()
+ .equals(ContentFormat.APPLICATION_JSON)) {
byte[] payload = coapMessage.getPayload();
coapMessage.setPayload(convertJsonToCbor(payload));
- coapMessage
- .setContentFormat(ContentFormat.APPLICATION_CBOR);
+ coapMessage.setContentFormat(
+ ContentFormat.APPLICATION_CBOR);
}
ctx.fireChannelRead(coapMessage);
}
}
ByteBuf encodedBytes = Unpooled.buffer();
- new CoapEncoder().encode((CoapMessage) msg, encodedBytes);
+ new CoapEncoder().encode((CoapMessage) msg, encodedBytes, true);
WebSocketFrame frame = new BinaryWebSocketFrame(encodedBytes);
newMsg = frame;
} else {