mirror of
				https://github.com/karl0ss/orvibo-b25-server-kex.git
				synced 2025-10-31 06:34:16 +00:00 
			
		
		
		
	Inital commit of Orvibo socket server code
This commit is contained in:
		
							parent
							
								
									196493e7e2
								
							
						
					
					
						commit
						4ad59b2a46
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | .idea/ | ||||||
|  | node_modules | ||||||
							
								
								
									
										64
									
								
								Example.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								Example.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | const Orvibo = require('./Orvibo'); | ||||||
|  | const http = require('http'); | ||||||
|  | const url = require('url'); | ||||||
|  | 
 | ||||||
|  | const httpPort = 3000; | ||||||
|  | 
 | ||||||
|  | let orvbio = new Orvibo(); | ||||||
|  | 
 | ||||||
|  | // When a socket first connects and initiates the handshake it will emit the connected event with the uid of the socket;
 | ||||||
|  | orvbio.on('plugConnected', ({uid, name}) => { | ||||||
|  |     console.log(`Connected ${uid} name = ${name}`); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // If the socket state is updated this event will fire
 | ||||||
|  | orvbio.on('plugStateUpdated', ({uid, state , name}) => { | ||||||
|  |     console.log(`Plug ${name} ${uid} updated state ${state}`); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // The plug sends a hearbeat to let the server know it's still alive
 | ||||||
|  | orvbio.on('gotHeartbeat', ({uid, state , name}) => { | ||||||
|  |     console.log(`Plug ${name} ${uid} sent heartbeat`); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // Called when the plug disconnects
 | ||||||
|  | orvbio.on('plugDisconnected', ({uid, name }) => { | ||||||
|  |     console.log(`Plug ${uid} - ${name} disconnected`); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // Called when the plug disconnects with an error ie it's been unplugged
 | ||||||
|  | orvbio.on('plugDisconnectedWithError', ({uid, name }) => { | ||||||
|  |     console.log(`Plug ${uid} - ${name} disconnected with error`); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Start the Orvibo socket server
 | ||||||
|  | orvbio.startServer(); | ||||||
|  | 
 | ||||||
|  | // Create a basic example HTTP server
 | ||||||
|  | // If there are no parameters it will return the uid, state, modelId and name of the socket
 | ||||||
|  | // You can then use the uid to toggle the state of the switch like
 | ||||||
|  | // http://localhost:3000?uid=5dcf7ff76e7a
 | ||||||
|  | 
 | ||||||
|  | const requestHandler = (request, response) => { | ||||||
|  |     response.writeHead(200, {'Content-Type': 'application/json'}); | ||||||
|  |     let q = url.parse(request.url, true).query; | ||||||
|  |     if (q.uid != null) { | ||||||
|  |         orvbio.toggleSocket(q.uid); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Get all currently connected sockets, their names and states
 | ||||||
|  |     let sockets = orvbio.getConnectedSocket(); | ||||||
|  | 
 | ||||||
|  |     response.end(JSON.stringify(sockets)); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const httpServer = http.createServer(requestHandler) | ||||||
|  | 
 | ||||||
|  | httpServer.listen(httpPort, (err) => { | ||||||
|  |     if (err) { | ||||||
|  |         return console.log('something bad happened', err) | ||||||
|  |     } | ||||||
|  |     console.log(`http server is listening on ${httpPort}`) | ||||||
|  | }); | ||||||
							
								
								
									
										226
									
								
								Orvibo.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								Orvibo.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,226 @@ | |||||||
|  | const net = require('net'); | ||||||
|  | const util = require('util'); | ||||||
|  | const Packet = require('./OrviboPacket'); | ||||||
|  | const PacketBuilder = require('./PacketBuilder'); | ||||||
|  | const Utils = require('./Utils'); | ||||||
|  | const Settings = require('./OrviboSettings'); | ||||||
|  | const EventEmitter = require('events').EventEmitter; | ||||||
|  | 
 | ||||||
|  | const ORVIBO_KEY = Settings.ORVIBO_KEY; | ||||||
|  | const LOG_PACKET = Settings.LOG_PACKET; | ||||||
|  | 
 | ||||||
|  | if (ORVIBO_KEY === '') { | ||||||
|  |     console.log('Please add Orvibo PK key to OrviboSettings.js file. See Readme'); | ||||||
|  |     process.exit(1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Orvibo = function() {}; | ||||||
|  | 
 | ||||||
|  | Object.assign(Orvibo.prototype, EventEmitter.prototype); | ||||||
|  | 
 | ||||||
|  | let HEARTBEAT = 32; | ||||||
|  | let HELLO = 0; | ||||||
|  | let HANDSHAKE = 6; | ||||||
|  | let STATE_UPDATE = 42; | ||||||
|  | let STATE_UPDATE_CONFIRM = 15; | ||||||
|  | let UNKNOWN_CMD = 'UNKNOWN_CMD'; | ||||||
|  | 
 | ||||||
|  | let port = 10001; | ||||||
|  | let bindHost = '0.0.0.0'; | ||||||
|  | 
 | ||||||
|  | console.log(`Starting server Orvibo socket server on port ${port}`); | ||||||
|  | 
 | ||||||
|  | let plugConnections = []; | ||||||
|  | let packetData = {}; | ||||||
|  | 
 | ||||||
|  | // Put your smart socket data here and it will be matched to the uid
 | ||||||
|  | let plugInfo = Settings.plugInfo; | ||||||
|  | 
 | ||||||
|  | let getNameForUid = (uid) => { | ||||||
|  |     let item = plugInfo.find(item => item.uid === uid); | ||||||
|  |     return item != null ? item.name : 'unknown'; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let getData = (id) => { | ||||||
|  |     return packetData[id]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let setData = (id, data) => { | ||||||
|  |     packetData[id] = data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let respondAndSetData = (data, socket, packetFunction) => { | ||||||
|  |     setData(socket.id, data); | ||||||
|  |     socket.write(packetFunction(data)); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let helloHandler = (plugPacket, socket) => { | ||||||
|  |     let pkData = { | ||||||
|  |         serial: plugPacket.getSerial(), | ||||||
|  |         encryptionKey: Utils.generateRandomTextValue(16), | ||||||
|  |         id: Utils.generateRandomHexValue(32), | ||||||
|  |         modelId: plugPacket.getModelId(), | ||||||
|  |     }; | ||||||
|  |     respondAndSetData(pkData, socket, PacketBuilder.helloPacket); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let handshakeHandler = function(plugPacket, socket, socketData) { | ||||||
|  |     let uid = plugPacket.getUid(); | ||||||
|  |     let pkData = Object.assign({}, socketData, { | ||||||
|  |         serial: plugPacket.getSerial(), | ||||||
|  |         uid, | ||||||
|  |         name: getNameForUid(uid) | ||||||
|  |     }); | ||||||
|  |     respondAndSetData(pkData, socket, PacketBuilder.handshakePacket); | ||||||
|  |     this.emit('plugConnected', {uid:pkData.uid, name: pkData.name}); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let heartbeatHandler = function(plugPacket, socket, socketData) { | ||||||
|  |     let pkData = Object.assign({}, socketData, { | ||||||
|  |         serial: plugPacket.getSerial(), | ||||||
|  |         uid: plugPacket.getUid() | ||||||
|  |     }); | ||||||
|  |     respondAndSetData(pkData, socket, PacketBuilder.heartbeatPacket); | ||||||
|  |     this.emit('gotHeartbeat', {uid:pkData.uid, name: pkData.name}); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let stateUpdateHandler = function(plugPacket, socket, socketData) { | ||||||
|  |     let pkData = Object.assign({}, socketData, { | ||||||
|  |         serial: plugPacket.getSerial(), | ||||||
|  |         uid: plugPacket.getUid(), | ||||||
|  |         state: plugPacket.getValue1() | ||||||
|  |     }); | ||||||
|  |     respondAndSetData(pkData, socket, PacketBuilder.comfirmStatePacket); | ||||||
|  |     this.emit('plugStateUpdated', {uid:pkData.uid, state: pkData.state, name: pkData.name}); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let stateConfirmHandler = function() { | ||||||
|  |     // Do nothing at this stage
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let unknownCmdHandler = function(plugPacket, socket, socketData) { | ||||||
|  |     let pkData = Object.assign({}, socketData, { | ||||||
|  |         serial: plugPacket.getSerial(), | ||||||
|  |         uid: plugPacket.getUid(), | ||||||
|  |     }); | ||||||
|  |     respondAndSetData(pkData, socket, PacketBuilder.defaultPacket); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Orvibo.prototype.handlers = function() { | ||||||
|  |     return { | ||||||
|  |         [HELLO]: helloHandler.bind(this), | ||||||
|  |         [HANDSHAKE]: handshakeHandler.bind(this), | ||||||
|  |         [HEARTBEAT]: heartbeatHandler.bind(this), | ||||||
|  |         [STATE_UPDATE]: stateUpdateHandler.bind(this), | ||||||
|  |         [STATE_UPDATE_CONFIRM] : stateConfirmHandler, | ||||||
|  |         [UNKNOWN_CMD]: unknownCmdHandler.bind(this) | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Orvibo.prototype.startServer = function() { | ||||||
|  | 
 | ||||||
|  |     let self = this; | ||||||
|  | 
 | ||||||
|  |     let handlers = this.handlers(); | ||||||
|  | 
 | ||||||
|  |     this.server = net.createServer(function(socket) { | ||||||
|  | 
 | ||||||
|  |         socket.id = Utils.generateRandomTextValue(16); | ||||||
|  |         socket.setKeepAlive(true, 10000); | ||||||
|  |         plugConnections.push(socket); | ||||||
|  | 
 | ||||||
|  |         socket.on('data', function (data) { | ||||||
|  | 
 | ||||||
|  |             let socketData = getData(socket.id); | ||||||
|  |             let plugPacket = new Packet(data); | ||||||
|  | 
 | ||||||
|  |             if (!plugPacket.validCRC()) { | ||||||
|  |                 console.log('Got invalid CRC'); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (plugPacket.packetTypeText() === 'pk') { | ||||||
|  |                 plugPacket.processPacket(ORVIBO_KEY); | ||||||
|  |             } else { | ||||||
|  |                 plugPacket.processPacket(socketData.encryptionKey); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             LOG_PACKET && plugPacket.logPacket("Socket -> "); | ||||||
|  | 
 | ||||||
|  |             let handler = handlers[plugPacket.getCommand()]; | ||||||
|  |             if (handler != null) { | ||||||
|  |                 handler(plugPacket, socket, socketData); | ||||||
|  |             } else { | ||||||
|  |                 handlers[UNKNOWN_CMD](plugPacket, socket, socketData); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         socket.on('end', function () { | ||||||
|  |             let pkData = getData(socket.id); | ||||||
|  |             self.emit('plugDisconnected', {uid: pkData.uid, name: pkData.name}); | ||||||
|  |             delete packetData[socket.id]; | ||||||
|  |             plugConnections.splice(plugConnections.indexOf(socket), 1); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         socket.on('error', (err) => { | ||||||
|  |             console.log(err); | ||||||
|  |             console.log('error with socket ' + socket.id); | ||||||
|  |             self.emit('plugDisconnectedWithError', getData(socket.id)); | ||||||
|  |             delete packetData[socket.id]; | ||||||
|  |             plugConnections.splice(plugConnections.indexOf(socket), 1); | ||||||
|  | 
 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     this.server.listen(port, bindHost); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Orvibo.prototype.toggleSocket = function(uid) { | ||||||
|  | 
 | ||||||
|  |     let socketId = null; | ||||||
|  |     for (const key of Object.keys(packetData)) { | ||||||
|  |         if (packetData[key].uid === uid) { | ||||||
|  |             socketId = key; | ||||||
|  |             break | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (socketId === null) { | ||||||
|  |         console.log('Could not find socket ' + uid); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     let socket = plugConnections.find(s => s.id === socketId); | ||||||
|  |     if (socket != null) { | ||||||
|  |         let socketData = getData(socketId); | ||||||
|  |         let data = Object.assign({}, socketData,{ | ||||||
|  |             state: socketData.state === 1 ? 0 : 1, | ||||||
|  |             serial: Utils.generateRandomNumber(8), | ||||||
|  |             clientSessionId:  socketData.clientSessionId ? socketData.clientSessionId : Utils.generateRandomHexValue(32), | ||||||
|  |             deviceId: socketData.deviceId ? socketData.deviceId : Utils.generateRandomHexValue(32), | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         setData(socket.id, data); | ||||||
|  | 
 | ||||||
|  |         let packet = PacketBuilder.updatePacket(data); | ||||||
|  |         socket.write(packet); | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |         console.log('Can not find socket '); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Orvibo.prototype.getConnectedSocket = function() { | ||||||
|  |     let sockets = []; | ||||||
|  |     for (const key of Object.keys(packetData)) { | ||||||
|  |         let socketData = getData(key); | ||||||
|  |         sockets.push({ | ||||||
|  |             name: socketData.name, | ||||||
|  |             state: socketData.state, | ||||||
|  |             uid: socketData.uid, | ||||||
|  |             modelId: socketData.modelId | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     return sockets; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports = Orvibo; | ||||||
							
								
								
									
										71
									
								
								OrviboPacket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								OrviboPacket.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | const crc32 = require('buffer-crc32'); | ||||||
|  | const crypto = require('crypto'); | ||||||
|  | 
 | ||||||
|  | let pkt = function(packetBuffer) { | ||||||
|  |     this.magic = packetBuffer.slice(0,2); | ||||||
|  |     this.packetLength = packetBuffer.slice(2,4); | ||||||
|  |     this.packetType = packetBuffer.slice(4,6); | ||||||
|  |     this.crc32 = packetBuffer.slice(6,10); | ||||||
|  |     this.packetId = packetBuffer.slice(10,42); | ||||||
|  |     this.payload = packetBuffer.slice(42, packetBuffer.length); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.logPacket = function(type) { | ||||||
|  |     let output = { | ||||||
|  |         PacketId : this.packetIdText(), | ||||||
|  |         Payload : this.payloadJSON | ||||||
|  |     }; | ||||||
|  |     console.log(type, JSON.stringify(this.payloadJSON)); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.getCommand = function() { | ||||||
|  |     return this.payloadJSON.cmd; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.getSerial = function() { | ||||||
|  |     return this.payloadJSON.serial; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.getUid = function() { | ||||||
|  |     return this.payloadJSON.uid; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.getValue1 = function() { | ||||||
|  |     return this.payloadJSON.value1; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.getModelId = function() { | ||||||
|  |     return this.payloadJSON.modelId; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.processPacket = function(key) { | ||||||
|  |     this.payloadJSON = this.decodeJSON(key); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.getKeyValue = function() { | ||||||
|  |     return this.payloadJSON.key; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.validCRC = function() { | ||||||
|  |     return crc32(this.payload).toString('hex') === this.crc32.toString('hex'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.packetTypeText = function() { | ||||||
|  |     return this.packetType.toString('ascii'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.packetIdText = function() { | ||||||
|  |     return this.packetId.toString('ascii'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pkt.prototype.decodeJSON = function(key) { | ||||||
|  |     let decipher = crypto.createDecipheriv('aes-128-ecb', key, ''); | ||||||
|  |     decipher.setAutoPadding(true); | ||||||
|  |     let decrypted = decipher.update(this.payload.toString('hex'), 'hex', 'utf8'); | ||||||
|  |     decrypted += decipher.final('utf8'); | ||||||
|  |     // Sometimes there are bad chars on the end of the JSON so check here
 | ||||||
|  |     decrypted = decrypted.substring(0, decrypted.indexOf('}') + 1); | ||||||
|  |     return JSON.parse(decrypted); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports = pkt; | ||||||
							
								
								
									
										13
									
								
								OrviboSettings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								OrviboSettings.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | // Settings/Config store for orvibo socket server
 | ||||||
|  | 
 | ||||||
|  | module.exports = { | ||||||
|  |     LOG_PACKET: false, | ||||||
|  |     ORVIBO_KEY: '', // put your PK key here as plain text
 | ||||||
|  |     plugInfo : [ | ||||||
|  |         // Add uid and a name so you can easily identify the connected sockets
 | ||||||
|  |         { | ||||||
|  |             uid :'53dd7fe74de7', | ||||||
|  |             name: "Lamp in Kitchen" | ||||||
|  |         }, | ||||||
|  |     ], | ||||||
|  | } | ||||||
							
								
								
									
										182
									
								
								PacketBuilder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								PacketBuilder.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,182 @@ | |||||||
|  | const crc32 = require('buffer-crc32'); | ||||||
|  | const crypto = require('crypto'); | ||||||
|  | const Settings = require('./orviboSettings'); | ||||||
|  | 
 | ||||||
|  | const ORVIBO_KEY = Settings.ORVIBO_KEY; | ||||||
|  | 
 | ||||||
|  | let Packet = { | ||||||
|  |     encryptionKey : ORVIBO_KEY, | ||||||
|  |     magic: new Buffer("6864", 'hex'), | ||||||
|  |     build: function () { | ||||||
|  |         let packetId = new Buffer(this.id, 'ascii'); | ||||||
|  |         let payload = encodePayload(JSON.stringify(this.json), this.encryptionKey); | ||||||
|  |         let crc = crc32(payload); | ||||||
|  |         let length = getLength([this.magic, this.packetType, packetId, crc, payload], 2); // Extra 2 for the length field itself
 | ||||||
|  |         return Buffer.concat([this.magic, length, this.packetType, crc, packetId, payload]); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let PKPacket = Object.assign({}, Packet, { | ||||||
|  |     packetType: new Buffer('pk', 'ascii'), | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | let DKPacket = Object.assign({}, Packet, { | ||||||
|  |     packetType: new Buffer('dk', 'ascii'), | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | let helloPacket = function({ serial, encryptionKey, id }) { | ||||||
|  |     let json = { | ||||||
|  |         cmd: 0, | ||||||
|  |         status: 0, | ||||||
|  |         serial: serial, | ||||||
|  |         key: encryptionKey | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let pkt = Object.assign(Object.create(PKPacket), { | ||||||
|  |         json: json, | ||||||
|  |         id: id | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return pkt.build(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let handshakePacket = function({ serial, encryptionKey, id }) { | ||||||
|  | 
 | ||||||
|  |     let json = { | ||||||
|  |         cmd: 6, | ||||||
|  |         status:0, | ||||||
|  |         serial: serial | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let pkt = Object.assign(Object.create(DKPacket), { | ||||||
|  |         json: json, | ||||||
|  |         id: id, | ||||||
|  |         encryptionKey, encryptionKey | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return pkt.build(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let heartbeatPacket = function({serial, uid, encryptionKey, id}) { | ||||||
|  |     let json = { | ||||||
|  |         cmd: 32, | ||||||
|  |         status:0, | ||||||
|  |         serial: serial, | ||||||
|  |         uid: uid, | ||||||
|  |         utc: new Date().getTime() | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let pkt = Object.assign(Object.create(DKPacket), { | ||||||
|  |         json: json, | ||||||
|  |         id: id, | ||||||
|  |         encryptionKey, encryptionKey | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return pkt.build(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let comfirmStatePacket = function({serial, uid, state, encryptionKey, id}) { | ||||||
|  | 
 | ||||||
|  |     var json = { | ||||||
|  |         uid: uid, | ||||||
|  |         cmd: 42, | ||||||
|  |         statusType: 0, | ||||||
|  |         value3: 0, | ||||||
|  |         alarmType: 1, | ||||||
|  |         serial: serial, | ||||||
|  |         value4: 0, | ||||||
|  |         deviceId: 0, | ||||||
|  |         value1: state, | ||||||
|  |         value2: 0, | ||||||
|  |         updateTimeSec: new Date().getTime(), | ||||||
|  |         status: 0 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let pkt = Object.assign(Object.create(DKPacket), { | ||||||
|  |         json: json, | ||||||
|  |         id: id, | ||||||
|  |         encryptionKey, encryptionKey | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return pkt.build(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let defaultPacket = function({serial, uid, cmd, id, encryptionKey}) { | ||||||
|  | 
 | ||||||
|  |     let json = { | ||||||
|  |         uid: uid, | ||||||
|  |         cmd: cmd, | ||||||
|  |         serial: serial, | ||||||
|  |         status: 0 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let pkt = Object.assign(Object.create(DKPacket), { | ||||||
|  |         json: json, | ||||||
|  |         id: id, | ||||||
|  |         encryptionKey, encryptionKey | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return pkt.build(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let updatePacket = function({ uid, state, serial, id, clientSessionId , deviceId, encryptionKey}) { | ||||||
|  |     let json = { | ||||||
|  |         uid: uid, | ||||||
|  |         delayTime: 0, | ||||||
|  |         cmd: 15, | ||||||
|  |         order: state === 0 ? "on" : "off", | ||||||
|  |         userName: "iloveorvibo@orvibo.com", | ||||||
|  |         ver: "3.0.0", | ||||||
|  |         value3: 0, | ||||||
|  |         serial: serial, | ||||||
|  |         value4: 0, | ||||||
|  |         deviceId: deviceId, | ||||||
|  |         value1: state, | ||||||
|  |         value2: 0, | ||||||
|  |         clientSessionId: clientSessionId | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let pkt = Object.assign(Object.create(DKPacket), { | ||||||
|  |         json: json, | ||||||
|  |         id: id, | ||||||
|  |         encryptionKey, encryptionKey | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return pkt.build(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let encodePayload = function(json, key) { | ||||||
|  |     let cipher = crypto.createCipheriv('aes-128-ecb', key, ''); | ||||||
|  |     cipher.setAutoPadding(true); | ||||||
|  |     let crypted = cipher.update(json,'utf8','hex'); | ||||||
|  |     crypted += cipher.final('hex'); | ||||||
|  |     return new Buffer(crypted, 'hex'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let getLength = function(items, extra) { | ||||||
|  |     let length = extra || 0; | ||||||
|  |     for (let i = 0; i < items.length; i++) { | ||||||
|  |         length += items[i].length; | ||||||
|  |     } | ||||||
|  |     return getHexLengthPadded(length); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | let getHexLengthPadded = function(lengthDecimal) { | ||||||
|  |     let lengthHex = lengthDecimal.toString(16); | ||||||
|  |     let paddingLength = 4 - lengthHex.length; | ||||||
|  |     let padding = ''; | ||||||
|  |     for (let i = 0; i < paddingLength; i++) { | ||||||
|  |         padding +=0; | ||||||
|  |     } | ||||||
|  |     return new Buffer(padding + lengthHex, 'hex'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | module.exports.helloPacket = helloPacket; | ||||||
|  | module.exports.handshakePacket = handshakePacket; | ||||||
|  | module.exports.heartbeatPacket = heartbeatPacket; | ||||||
|  | module.exports.comfirmStatePacket = comfirmStatePacket; | ||||||
|  | module.exports.defaultPacket = defaultPacket; | ||||||
|  | module.exports.updatePacket = updatePacket; | ||||||
| @ -16,10 +16,7 @@ You will need to add the Orvibo PK key to decrypt and encrypt the initial packet | |||||||
| Once you have this key you will need to add it the ``OrviboSettings.js`` file. | Once you have this key you will need to add it the ``OrviboSettings.js`` file. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Because these new sockets don't use UDP packets to communicate like the older versions you will also need to redirect all traffic from the host name  | Because these new sockets don't use UDP packets to communicate like the older versions you will also need to redirect all traffic from the host name ``homemate.orvibo.com`` | ||||||
| ``` |  | ||||||
| homemate.orvibo.com |  | ||||||
| ``` |  | ||||||
| on TCP port 10001 the computer running the server. | on TCP port 10001 the computer running the server. | ||||||
| 
 | 
 | ||||||
| I used an Ubuntu machine running dnsmasq and set this server as my DNS server in my router but depending on your network you might have to do it differntly. | I used an Ubuntu machine running dnsmasq and set this server as my DNS server in my router but depending on your network you might have to do it differntly. | ||||||
| @ -37,7 +34,6 @@ to the official Orvibo server. | |||||||
| Clone the repo and then run | Clone the repo and then run | ||||||
| ``` | ``` | ||||||
| npm install  | npm install  | ||||||
| 
 |  | ||||||
| ``` | ``` | ||||||
| to install the dependencies (buffer-crc32) | to install the dependencies (buffer-crc32) | ||||||
| 
 | 
 | ||||||
| @ -50,7 +46,6 @@ To start the example http server run | |||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| npm start  | npm start  | ||||||
| 
 |  | ||||||
| ``` | ``` | ||||||
| This will start the Orvibo socket server create a basic example HTTP server used for interacting with sockets. | This will start the Orvibo socket server create a basic example HTTP server used for interacting with sockets. | ||||||
| Calling this http server with no parameters will return the uid, state, modelId and name of the socket. | Calling this http server with no parameters will return the uid, state, modelId and name of the socket. | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								Utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Utils.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | module.exports.generateRandomTextValue = function(length) { | ||||||
|  |     let chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; | ||||||
|  |     let result = ''; | ||||||
|  |     for (let i = length; i > 0; --i) { | ||||||
|  |         result += chars[Math.floor(Math.random() * chars.length)]; | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports.generateRandomHexValue = function(length) { | ||||||
|  |     let chars = '0123456789abcdef'; | ||||||
|  |     let result = ''; | ||||||
|  |     for (let i = length; i > 0; --i) { | ||||||
|  |         result += chars[Math.floor(Math.random() * chars.length)]; | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports.generateRandomNumber = function(length) { | ||||||
|  |     let numbers = '0123456789'; | ||||||
|  |     let result = ''; | ||||||
|  |     for (let i = length; i > 0; --i) { | ||||||
|  |         result += numbers[Math.floor(Math.random() * numbers.length)]; | ||||||
|  |     } | ||||||
|  |     return parseInt(result); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | { | ||||||
|  |   "name": "orvibo-b25-server", | ||||||
|  |   "version": "1.0.0", | ||||||
|  |   "lockfileVersion": 1, | ||||||
|  |   "requires": true, | ||||||
|  |   "dependencies": { | ||||||
|  |     "buffer-crc32": { | ||||||
|  |       "version": "0.2.13", | ||||||
|  |       "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", | ||||||
|  |       "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | { | ||||||
|  |   "name": "orvibo-b25-server", | ||||||
|  |   "version": "1.0.0", | ||||||
|  |   "description": "A server to control the Orvibo B25 range of smart sockets", | ||||||
|  |   "main": "Orvibo.js", | ||||||
|  |   "scripts": { | ||||||
|  |     "test": "echo \"Error: no test specified\" && exit 1", | ||||||
|  |     "start": "node Example.js" | ||||||
|  |   }, | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "git+https://github.com/sandysound/orvibo-b25-server.git" | ||||||
|  |   }, | ||||||
|  |   "keywords": [ | ||||||
|  |     "Orvibo", | ||||||
|  |     "B25", | ||||||
|  |     "Smart", | ||||||
|  |     "Socket" | ||||||
|  |   ], | ||||||
|  |   "author": "Sandy Milne", | ||||||
|  |   "license": "Apache-2.0", | ||||||
|  |   "bugs": { | ||||||
|  |     "url": "https://github.com/sandysound/orvibo-b25-server/issues" | ||||||
|  |   }, | ||||||
|  |   "homepage": "https://github.com/sandysound/orvibo-b25-server#readme", | ||||||
|  |   "dependencies": { | ||||||
|  |     "buffer-crc32": "^0.2.13" | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sandy Milne
						Sandy Milne