11const net = require ( 'net' ) ;
22const dgram = require ( 'dgram' ) ;
3- const MessageParser = require ( 'tuyapi/lib/message-parser' ) ;
4- const Cipher = require ( 'tuyapi/lib/cipher' ) ;
3+ const { MessageParser, CommandType} = require ( 'tuyapi/lib/message-parser' ) ;
54const debug = require ( 'debug' ) ( 'TuyaStub' ) ;
65
76/**
@@ -24,7 +23,7 @@ class TuyaStub {
2423 this . id = options . id ;
2524 this . key = options . key ;
2625
27- this . cipher = new Cipher ( { key : this . key , version : 3.1 } ) ;
26+ this . parser = new MessageParser ( { key : this . key , version : 3.1 } ) ;
2827 }
2928
3029 /**
@@ -74,7 +73,7 @@ class TuyaStub {
7473 options . interval = options . interval ? options . interval : 5 ;
7574
7675 // Encode broadcast
77- const message = MessageParser . encode ( { data : { devId : this . id , gwId : this . id , ip : 'localhost' } , commandByte : 10 } ) ;
76+ const message = this . parser . encode ( { data : { devId : this . id , gwId : this . id , ip : 'localhost' } , commandByte : CommandType . DP_QUERY } ) ;
7877
7978 // Create and bind socket
8079 this . broadcastSocket = dgram . createSocket ( { type : 'udp4' , reuseAddr : true } ) ;
@@ -100,76 +99,89 @@ class TuyaStub {
10099 * @param {Buffer } data to handle
101100 */
102101 _handleRequest ( data ) {
103- const parsedData = MessageParser . parse ( data ) ;
102+ debug ( 'Incoming packet(s):' ) ;
103+ debug ( data . toString ( 'hex' ) ) ;
104104
105- debug ( 'Parsed request:' ) ;
106- debug ( parsedData ) ;
107-
108- if ( parsedData . commandByte === 10 ) { // GET request
109- // Check device ID
110- if ( parsedData . data . devId !== this . id ) {
111- throw new Error ( 'devId of request does not match' ) ;
112- }
113-
114- const response = {
115- data : {
116- devId : this . id ,
117- gwId : this . id ,
118- dps : this . state
119- } ,
120-
121- commandByte : 10
122- } ;
123-
124- // Write response
125- this . socket . write ( MessageParser . encode ( response ) ) ;
126- } else if ( parsedData . commandByte === 7 ) { // SET request
127- // Decrypt data
128- const decryptedData = this . cipher . decrypt ( parsedData . data ) ;
129-
130- debug ( 'Decrypted data:' ) ;
131- debug ( decryptedData ) ;
132-
133- // Check device ID
134- if ( decryptedData . devId !== this . id ) {
135- throw new Error ( 'devId of request does not match' ) ;
136- }
137-
138- // Check timestamp
139- const now = Math . floor ( Date . now ( ) / 1000 ) ; // Seconds since epoch
105+ const parsedPackets = this . parser . parse ( data ) ;
140106
141- // Timestamp difference must be no more than 10 seconds
142- if ( Math . abs ( now - decryptedData . t ) > 10 ) {
143- throw new Error ( 'Bad timestamp.' ) ;
107+ debug ( 'Parsed request:' ) ;
108+ debug ( parsedPackets ) ;
109+
110+ parsedPackets . forEach ( packet => {
111+ if ( packet . commandByte === CommandType . DP_QUERY ) { // GET request
112+ // Check device ID
113+ if ( packet . payload . devId !== this . id ) {
114+ throw new Error ( 'devId of request does not match' ) ;
115+ }
116+
117+ const response = {
118+ data : {
119+ devId : this . id ,
120+ gwId : this . id ,
121+ dps : this . state
122+ } ,
123+ commandByte : 10 ,
124+ sequenceN : packet . sequenceN
125+ } ;
126+
127+ // Write response
128+ this . socket . write ( this . parser . encode ( response ) ) ;
129+ } else if ( packet . commandByte === CommandType . CONTROL ) { // SET request
130+ // Decrypt data
131+ const decryptedData = packet . payload ;
132+
133+ debug ( 'Decrypted data:' ) ;
134+ debug ( decryptedData ) ;
135+
136+ // Check device ID
137+ if ( decryptedData . devId !== this . id ) {
138+ throw new Error ( 'devId of request does not match' ) ;
139+ }
140+
141+ // Check timestamp
142+ const now = Math . floor ( Date . now ( ) / 1000 ) ; // Seconds since epoch
143+
144+ // Timestamp difference must be no more than 10 seconds
145+ if ( Math . abs ( now - decryptedData . t ) > 10 ) {
146+ throw new Error ( 'Bad timestamp.' ) ;
147+ }
148+
149+ // Set properties
150+ Object . keys ( decryptedData . dps ) . forEach ( property => {
151+ this . setProperty ( property , decryptedData . dps [ property ] ) ;
152+ } ) ;
153+
154+ // Responses for status updates have two parts
155+ const confirmChangeResponse = {
156+ data : { } ,
157+ commandByte : 7 ,
158+ sequenceN : packet . sequenceN
159+ } ;
160+
161+ this . socket . write ( this . parser . encode ( confirmChangeResponse ) ) ;
162+
163+ const statusUpdateResponse = {
164+ data : {
165+ devId : this . id ,
166+ gwId : this . id ,
167+ dps : this . state
168+ } ,
169+ commandByte : 8
170+ } ;
171+
172+ this . socket . write ( this . parser . encode ( statusUpdateResponse ) ) ;
173+ } else if ( packet . commandByte === CommandType . HEART_BEAT ) { // Heartbeat packet
174+ // Send response pong
175+ debug ( 'Sending pong...' ) ;
176+ const buffer = this . parser . encode ( {
177+ data : Buffer . allocUnsafe ( 0 ) ,
178+ commandByte : CommandType . HEART_BEAT ,
179+ sequenceN : packet . sequenceN
180+ } ) ;
181+
182+ this . socket . write ( buffer ) ;
144183 }
145-
146- // Set properties
147- Object . keys ( decryptedData . dps ) . forEach ( property => {
148- this . setProperty ( property , decryptedData . dps [ property ] ) ;
149- } ) ;
150-
151- // Write response
152- const response = {
153- data : {
154- devId : this . id ,
155- gwId : this . id ,
156- dps : this . state
157- } ,
158-
159- commandByte : 10
160- } ;
161-
162- this . socket . write ( MessageParser . encode ( response ) ) ;
163- } else if ( parsedData . commandByte === 9 ) { // Heartbeat packet
164- // Send response pong
165- debug ( 'Sending pong...' ) ;
166- const buffer = MessageParser . encode ( {
167- data : Buffer . allocUnsafe ( 0 ) ,
168- commandByte : 9 // 0x09
169- } ) ;
170-
171- this . socket . write ( buffer ) ;
172- }
184+ } ) ;
173185 }
174186
175187 /**
@@ -190,10 +202,10 @@ class TuyaStub {
190202 dps : this . state
191203 } ,
192204
193- commandByte : 10
205+ commandByte : CommandType . CONTROL
194206 } ;
195207
196- this . socket . write ( MessageParser . encode ( response ) ) ;
208+ this . socket . write ( this . parser . encode ( response ) ) ;
197209 }
198210
199211 return this . state [ property ] ;
0 commit comments