@@ -8,10 +8,11 @@ if (typeof(URL) === 'undefined') {
88 }
99}
1010
11- const request = require ( 'request-promise-native' )
1211const util = require ( 'util' )
13- const uuid4 = require ( 'uuid4' ) ;
14- const md5 = require ( 'md5' ) ;
12+ const uuid4 = require ( 'uuid4' )
13+ const md5 = require ( 'md5' )
14+ const term = require ( 'terminal-kit' ) . terminal
15+ const axios = require ( 'axios' )
1516
1617const cleanServerUrl = ( server ) => {
1718 server = / m q t t s ? : \/ \/ / . test ( server ) ? server : 'mqtt://' + server // add protocol
@@ -22,54 +23,190 @@ const cleanServerUrl = (server) => {
2223
2324const serverRegex = / ( ( ( [ 0 - 9 ] | [ 1 - 9 ] [ 0 - 9 ] | 1 [ 0 - 9 ] { 2 } | 2 [ 0 - 4 ] [ 0 - 9 ] | 2 5 [ 0 - 5 ] ) \. ) { 3 } ( [ 0 - 9 ] | [ 1 - 9 ] [ 0 - 9 ] | 1 [ 0 - 9 ] { 2 } | 2 [ 0 - 4 ] [ 0 - 9 ] | 2 5 [ 0 - 5 ] ) | ( ( [ a - z A - Z 0 - 9 ] | [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 \- ] * [ a - z A - Z 0 - 9 ] ) \. ) * ( [ A - Z a - z 0 - 9 ] | [ A - Z a - z 0 - 9 ] [ A - Z a - z 0 - 9 \- ] * [ A - Z a - z 0 - 9 ] ) ) : (?: 6 5 5 3 [ 0 - 5 ] | 6 5 5 [ 0 - 2 ] [ 0 - 9 ] | 6 5 [ 0 - 4 ] [ 0 - 9 ] { 2 } | 6 [ 0 - 4 ] [ 0 - 9 ] { 3 } | [ 1 - 5 ] [ 0 - 9 ] { 4 } | [ 1 - 9 ] [ 0 - 9 ] { 1 , 3 } | [ 0 - 9 ] ) $ /
2425
26+ const base64Encode = str => Buffer . from ( str ) . toString ( 'base64' )
27+
28+ const tableOptions = {
29+ hasBorder : true ,
30+ borderChars : 'light' ,
31+ contentHasMarkup : true ,
32+ fit : true ,
33+ width : 95 ,
34+ firstColumnTextAttr : { color : 'yellow' }
35+ }
36+
37+ function logRequest ( request ) {
38+ let url = new URL ( request . url ) ;
39+ console . log ( `> ${ request . method . toUpperCase ( ) } ${ url . path } ` )
40+ console . log ( `> Host: ${ url . host } ` )
41+
42+ let headers = { }
43+ headers = Object . assign ( headers , request . headers . common ) ;
44+ headers = Object . assign ( headers , request . headers [ request . method ] ) ;
45+ headers = Object . assign ( headers , Object . fromEntries (
46+ Object . entries ( request . headers ) . filter (
47+ ( [ header ] ) => ! [ 'common' , 'delete' , 'get' , 'head' , 'post' , 'put' , 'patch' ] . includes ( header )
48+ )
49+ ) ) ;
50+ for ( let [ header , value ] of Object . entries ( headers ) ) {
51+ console . log ( `> ${ header } : ${ value } ` )
52+ }
53+
54+ console . log ( '>' )
55+ console . log ( util . inspect ( request . data , { showHidden : false , depth : null } ) )
56+ }
57+
58+ function logResponse ( response ) {
59+ console . log ( `< ${ response . status } ${ response . statusText } ` )
60+ for ( const [ header , value ] of Object . entries ( response . headers ) ) {
61+ console . log ( `< ${ header } : ${ value } ` )
62+ }
63+ console . log ( '<' )
64+ console . log ( util . inspect ( response . data , { showHidden : false , depth : null } ) )
65+ }
66+
67+ function handleRequestError ( error , verbose )
68+ {
69+ if ( verbose ) {
70+ if ( error . response ) {
71+ logResponse ( error . response )
72+ } else if ( error . request ) {
73+ logRequest ( error . request )
74+ } else {
75+ console . error ( 'Error' , error . message ) ;
76+ }
77+ } else {
78+ console . error ( 'Error' , 'Unable to connect to device' ) ;
79+ }
80+ }
2581
2682module . exports = class API {
27- constructor ( host , key , userId ) {
83+ constructor ( host , key , userId , verbose = false ) {
2884 this . host = host
2985 this . key = key
3086 this . userId = userId
87+
88+ axios . interceptors . request . use ( request => {
89+ if ( verbose ) {
90+ logRequest ( request )
91+ }
92+ return request
93+ } )
94+
95+ axios . interceptors . response . use ( response => {
96+ if ( verbose ) {
97+ logResponse ( response )
98+ }
99+ return response
100+ } )
31101 }
32102
33- signPacket ( payload ) {
34- let messageId = md5 ( uuid4 ( ) )
35- let timestamp = Math . floor ( Date . now ( ) / 1000 )
36- let signature = md5 ( messageId + this . key + timestamp )
103+ signPacket ( packet ) {
104+ const messageId = md5 ( uuid4 ( ) )
105+ const timestamp = Math . floor ( Date . now ( ) / 1000 )
106+ const signature = md5 ( messageId + this . key + timestamp )
37107
38- payload . header . messageId = messageId
39- payload . header . timestamp = timestamp
40- payload . header . sign = signature
108+ packet . header . messageId = messageId
109+ packet . header . timestamp = timestamp
110+ packet . header . sign = signature
41111
42- return payload
112+ return packet
43113 }
44114
45- deviceInformation ( ) {
46- let payload = {
115+ async deviceInformation ( ) {
116+ const packet = this . signPacket ( {
47117 'header' : {
48118 'method' : 'GET' ,
49119 'namespace' : 'Appliance.System.All'
50120 } ,
51121 'payload' : { }
52- }
53-
54- payload = this . signPacket ( payload )
55-
56- console . log ( 'sending payload' , util . inspect ( payload , { showHidden : false , depth : null } ) )
122+ } )
57123
58- return request . post ( {
59- url : `http://${ this . host } /config` ,
60- headers : {
61- 'Content-Type' : 'application/json'
62- } ,
63- json : payload
64- } ) . catch ( err => console . error ( err ) )
124+ try {
125+ const response = await axios . post (
126+ `http://${ this . host } /config` ,
127+ packet ,
128+ {
129+ headers : {
130+ 'Content-Type' : 'application/json'
131+ } ,
132+ }
133+ )
134+
135+ const data = response . data ;
136+
137+ if ( 'error' in data . payload ) {
138+ let { code, message} = data . payload . error ;
139+
140+ switch ( code ) {
141+ case 5001 :
142+ console . error ( 'Incorrect shared key provided.' )
143+ break ;
144+ }
145+
146+ return
147+ }
148+
149+ const system = data . payload . all . system
150+ const digest = data . payload . all . digest
151+ const hw = system . hardware
152+ const fw = system . firmware
153+
154+ let rows = [
155+ [ 'Device' , `${ hw . type } ${ hw . subType } ${ hw . chipType } (hardware:${ hw . version } firmware:${ fw . version } )` ] ,
156+ [ 'UUID' , hw . uuid ] ,
157+ [ 'Mac address' , hw . macAddress ] ,
158+ [ 'WIFI' , `${ fw . innerIp } (${ fw . wifiMac } )` ] ,
159+ ] ;
160+
161+ if ( fw . server ) {
162+ rows . push (
163+ [ 'MQTT broker' , `${ fw . server } :${ fw . port } ` ] ,
164+ [ 'Status' , `${ system . online . status == 0 ? '^ROffline' : '^GOnline' } ` ]
165+ )
166+ } else {
167+ rows . push (
168+ [ 'Status' , `^BConfiguration` ]
169+ )
170+ }
171+
172+ rows . push (
173+ [ 'Credentials' , `User: ^C${ fw . userId } \nPassword: ^C${ this . calculateDevicePassword ( hw . macAddress , fw . userId ) } ` ]
174+ )
175+
176+ if ( digest . togglex ) {
177+ let row = [ 'Switch state' ]
178+ let col = [ ]
179+ for ( let sw of digest . togglex ) {
180+ let ch = [ ] ;
181+ if ( digest . togglex . length > 1 ) {
182+ ch . push ( `Channel ${ sw . channel } :` ) ;
183+ }
184+ ch . push ( sw . onoff == 0 ? '^ROff' : '^GOn' )
185+
186+ const switchUpdateDate = new Date ( ) ;
187+ switchUpdateDate . setTime ( sw . lmTime * 1000 ) // put time into ms not seconds
188+ ch . push ( `(${ new Intl . DateTimeFormat ( process . env . LC_TIME , { dateStyle : 'full' , timeStyle : 'long' } ) . format ( switchUpdateDate ) } )` )
189+
190+ col . push ( ch . join ( '^ ' ) )
191+ }
192+ row . push ( col . join ( "\n" ) ) ;
193+ rows . push ( row )
194+ }
195+
196+ term . table (
197+ rows ,
198+ tableOptions
199+ )
200+ } catch ( error ) {
201+ handleRequestError ( error )
202+ }
65203 }
66204
67- configureMqttServers ( mqtt ) {
68- let servers = mqtt /*.filter((server) => serverRegex.test(server))*/
69- servers = mqtt . map ( ( server ) => {
205+ async configureMqttServers ( mqtt ) {
206+ const servers = mqtt . map ( ( server ) => {
70207 server = cleanServerUrl ( server )
71208
72- let url = new URL ( server )
209+ const url = new URL ( server )
73210 return {
74211 host : url . hostname ,
75212 port : url . port
@@ -80,18 +217,24 @@ module.exports = class API {
80217 if ( servers . length == 1 ) {
81218 servers . push ( servers [ 0 ] ) ;
82219 }
220+
221+ term . table (
222+ [
223+ [ 'Primary MQTT broker' , `${ servers [ 0 ] . host } :${ servers [ 0 ] . port } ` ] ,
224+ [ 'Secondary MQTT broker' , `${ servers [ 1 ] . host } :${ servers [ 1 ] . port } ` ]
225+ ] ,
226+ tableOptions
227+ )
83228
84- console . log ( 'Setting MQTT servers' , servers )
85-
86- let payload = {
229+ const packet = this . signPacket ( {
87230 'header' : {
88231 'method' : 'SET' ,
89232 'namespace' : 'Appliance.Config.Key'
90233 } ,
91234 'payload' : {
92235 'key' : {
93236 'gateway' : ( ( servers ) => {
94- let gateway = servers [ 0 ]
237+ const gateway = servers [ 0 ]
95238
96239 if ( servers . length > 1 ) {
97240 gateway . secondHost = servers [ 1 ] . host
@@ -104,45 +247,64 @@ module.exports = class API {
104247 'userId' : this . userId
105248 }
106249 }
107- }
108-
109- payload = this . signPacket ( payload )
110-
111- console . log ( 'sending payload' , util . inspect ( payload , { showHidden : false , depth : null } ) )
250+ } )
112251
113- return request . post ( {
114- url : `http://${ this . host } /config` ,
115- headers : {
116- 'Content-Type' : 'application/json'
117- } ,
118- json : payload
119- } ) . catch ( err => console . error ( err ) )
252+ try {
253+ const response = await axios . post (
254+ `http://${ this . host } /config` ,
255+ packet ,
256+ {
257+ headers : {
258+ 'Content-Type' : 'application/json'
259+ } ,
260+ }
261+ )
262+ } catch ( error ) {
263+ handleRequestError ( error )
264+ }
120265 }
121266
122- configureWifiCredentials ( credentials ) {
123- let payload = {
267+ async configureWifiCredentials ( credentials ) {
268+ const ssid = base64Encode ( credentials . ssid )
269+ const password = base64Encode ( credentials . password )
270+
271+ const packet = this . signPacket ( {
124272 'header' : {
125273 'method' : 'SET' ,
126274 'namespace' : 'Appliance.Config.Wifi'
127275 } ,
128276 'payload' : {
129277 'wifi' : {
130- "ssid" : Buffer . from ( credentials . ssid ) . toString ( 'base64' ) ,
131- "password" : Buffer . from ( credentials . password ) . toString ( 'base64' )
278+ "ssid" : ssid ,
279+ "password" : password
132280 }
133281 }
134- }
135-
136- payload = this . signPacket ( payload )
137-
138- console . log ( 'sending payload' , util . inspect ( payload , { showHidden : false , depth : null } ) )
282+ } )
283+
284+ term . table (
285+ [
286+ [ 'Encoded WIFI SSID' , `${ ssid } ` ] ,
287+ [ 'Encoded WIFI password' , `${ password } ` ]
288+ ] ,
289+ tableOptions
290+ )
139291
140- return request . post ( {
141- url : `http://${ this . host } /config` ,
142- headers : {
143- 'Content-Type' : 'application/json'
144- } ,
145- json : payload
146- } ) . catch ( err => console . error ( err ) )
292+ try {
293+ const response = await axios . post (
294+ `http://${ this . host } /config` ,
295+ packet ,
296+ {
297+ headers : {
298+ 'Content-Type' : 'application/json'
299+ } ,
300+ }
301+ )
302+ } catch ( error ) {
303+ handleRequestError ( error )
304+ }
305+ }
306+
307+ calculateDevicePassword ( macAddress , user = null ) {
308+ return `${ user } _${ md5 ( macAddress + this . key ) } `
147309 }
148310}
0 commit comments