@@ -49,6 +49,10 @@ export class TplinkSmarthomePlatform implements DynamicPlatformPlugin {
4949
5050 private readonly categories : Map < Categories , string > = new Map ( ) ;
5151
52+ private readonly legacyClient : Client ;
53+
54+ private readonly klapDiscovery : KlapDiscovery | undefined ;
55+
5256 constructor (
5357 public readonly log : Logging ,
5458 config : PlatformConfig ,
@@ -90,6 +94,12 @@ export class TplinkSmarthomePlatform implements DynamicPlatformPlugin {
9094 this . categories . set ( Categories . OUTLET , "OUTLET" ) ;
9195 this . categories . set ( Categories . SWITCH , "SWITCH" ) ;
9296
97+ this . legacyClient = this . setupLegacyDiscovery ( ) ;
98+ this . klapDiscovery = this . setupKlapDiscovery ( ) ;
99+ this . setupLifecycleHandlers ( ) ;
100+ }
101+
102+ private setupLegacyDiscovery ( ) : Client {
93103 const tplinkApiLogger : Logging = Object . assign ( ( ) => { } , this . log , {
94104 prefix : `${ this . log . prefix || PLATFORM_NAME } .API` ,
95105 } ) ;
@@ -136,136 +146,145 @@ export class TplinkSmarthomePlatform implements DynamicPlatformPlugin {
136146 }
137147 } ) ;
138148
149+ return client ;
150+ }
151+
152+ private setupKlapDiscovery ( ) : KlapDiscovery | undefined {
139153 // KLAP/AES discovery for newer devices (port 80)
140154 // Only enabled when Kasa credentials are configured
141- let klapDiscovery : KlapDiscovery | undefined ;
142-
143- if ( this . config . kasaCredentials ) {
155+ if ( ! this . config . kasaCredentials ) {
144156 this . log . info (
145- "Kasa credentials configured, enabling KLAP/AES discovery for newer devices" ,
157+ "No Kasa credentials configured. Newer devices using KLAP/AES protocol will not be discovered. " +
158+ "Set kasaUsername and kasaPassword in config to enable." ,
146159 ) ;
160+ return undefined ;
161+ }
147162
148- klapDiscovery = new KlapDiscovery ( {
149- credentials : this . config . kasaCredentials ,
150- devices : this . config . discoveryOptions . devices ,
151- broadcast : this . config . discoveryOptions . broadcast ,
152- discoveryInterval : this . config . discoveryOptions . discoveryInterval ,
153- timeout : this . config . defaultSendOptions . timeout ,
154- } ) ;
163+ this . log . info (
164+ "Kasa credentials configured, enabling KLAP/AES discovery for newer devices" ,
165+ ) ;
155166
156- klapDiscovery . on ( "device-new" , ( device : KlapPlug | KlapBulb ) => {
157- this . log . info (
158- `[KLAP] Device First Online: ${ chalk . blue ( `[${ device . alias } ]` ) } %s [%s]` ,
159- device . deviceType ,
160- device . id ,
161- device . host ,
162- device . port ,
163- ) ;
164- this . foundDevice ( device ) ;
165- } ) ;
167+ const klapDiscovery = new KlapDiscovery ( {
168+ credentials : this . config . kasaCredentials ,
169+ devices : this . config . discoveryOptions . devices ,
170+ broadcast : this . config . discoveryOptions . broadcast ,
171+ discoveryInterval : this . config . discoveryOptions . discoveryInterval ,
172+ timeout : this . config . defaultSendOptions . timeout ,
173+ } ) ;
174+
175+ klapDiscovery . on ( "device-new" , ( device : KlapPlug | KlapBulb ) => {
176+ this . log . info (
177+ `[KLAP] Device First Online: ${ chalk . blue ( `[${ device . alias } ]` ) } %s [%s]` ,
178+ device . deviceType ,
179+ device . id ,
180+ device . host ,
181+ device . port ,
182+ ) ;
183+ this . foundDevice ( device ) ;
184+ } ) ;
166185
167- klapDiscovery . on ( "device-online" , ( device : KlapPlug | KlapBulb ) => {
186+ klapDiscovery . on ( "device-online" , ( device : KlapPlug | KlapBulb ) => {
187+ this . log . debug (
188+ `[KLAP] Device Online: ${ chalk . blue ( `[${ device . alias } ]` ) } %s [%s]` ,
189+ device . deviceType ,
190+ device . id ,
191+ device . host ,
192+ device . port ,
193+ ) ;
194+ this . foundDevice ( device ) ;
195+ } ) ;
196+
197+ klapDiscovery . on ( "device-offline" , ( device : KlapPlug | KlapBulb ) => {
198+ const deviceAccessory = this . homekitDevicesById . get ( device . id ) ;
199+ if ( deviceAccessory !== undefined ) {
168200 this . log . debug (
169- `[KLAP] Device Online: ${ chalk . blue ( `[${ device . alias } ]` ) } %s [%s]` ,
201+ `[KLAP] Device Offline: ${ chalk . blue ( `[${ device . alias } ]` ) } %s [%s]` ,
202+ deviceAccessory . homebridgeAccessory . displayName ,
170203 device . deviceType ,
171204 device . id ,
172205 device . host ,
173206 device . port ,
174207 ) ;
175- this . foundDevice ( device ) ;
176- } ) ;
208+ }
209+ } ) ;
177210
178- klapDiscovery . on ( "device-offline" , ( device : KlapPlug | KlapBulb ) => {
179- const deviceAccessory = this . homekitDevicesById . get ( device . id ) ;
180- if ( deviceAccessory !== undefined ) {
181- this . log . debug (
182- `[KLAP] Device Offline: ${ chalk . blue ( `[${ device . alias } ]` ) } %s [%s]` ,
183- deviceAccessory . homebridgeAccessory . displayName ,
184- device . deviceType ,
185- device . id ,
186- device . host ,
187- device . port ,
188- ) ;
189- }
190- } ) ;
211+ klapDiscovery . on ( "error" , ( err : Error ) => {
212+ this . log . error ( "[KLAP] Discovery error: %s" , err . message ) ;
213+ this . log . debug ( "[KLAP] %O" , err ) ;
214+ } ) ;
191215
192- klapDiscovery . on ( "error" , ( err : Error ) => {
193- this . log . error ( "[KLAP] Discovery error: %s" , err . message ) ;
194- this . log . debug ( "[KLAP] %O" , err ) ;
195- } ) ;
196- } else {
197- this . log . info (
198- "No Kasa credentials configured. Newer devices using KLAP/AES protocol will not be discovered. " +
199- "Set kasaUsername and kasaPassword in config to enable." ,
200- ) ;
201- }
216+ return klapDiscovery ;
217+ }
202218
219+ private setupLifecycleHandlers ( ) : void {
203220 this . api . on ( APIEvent . DID_FINISH_LAUNCHING , ( ) => {
204221 this . log . debug ( APIEvent . DID_FINISH_LAUNCHING ) ;
205222
206- client . startDiscovery ( {
223+ this . legacyClient . startDiscovery ( {
207224 ...this . config . discoveryOptions ,
208225 filterCallback : ( si : Sysinfo ) => {
209226 return si . deviceId != null && si . deviceId . length > 0 ;
210227 } ,
211228 } ) ;
212229
213230 // Start KLAP/AES discovery alongside legacy (if credentials configured)
214- if ( klapDiscovery ) {
215- klapDiscovery . start ( ) ;
231+ if ( this . klapDiscovery ) {
232+ this . klapDiscovery . start ( ) ;
216233 }
217234
218- const refreshEmeterForAccessories = async ( accessories : HomekitDevice [ ] ) => {
219- for ( const acc of accessories ) {
220- const device = acc . tplinkDevice ;
221- if ( device . supportsEmeter ) {
222- this . log . debug ( `getEmeterRealtime ${ chalk . blue ( `[${ device . alias } ]` ) } ` ) ;
223- await device . emeter . getRealtime ( ) . catch ( ( reason ) => {
224- this . log . error ( "[%s] %s" , device . alias , "emeter.getRealtime()" ) ;
225- this . log . error ( reason ) ;
226- } ) ;
227- }
228- }
229- } ;
230-
231- const refreshEmeter = async ( ) => {
232- this . log . debug ( `${ chalk . magenta ( "refreshEmeter()" ) } ` ) ;
233- if ( this . config . emeterPollingInterval <= 0 ) return ;
234-
235- try {
236- const deviceAccessories = this . deviceAccessoriesByHost ;
237- const promises : Promise < unknown > [ ] = [ ] ;
238-
239- for ( const accForHost of deviceAccessories . values ( ) ) {
240- promises . push ( refreshEmeterForAccessories ( accForHost ) ) ;
241- }
242- await Promise . all ( promises ) ;
243- } catch ( err ) {
244- this . log . error ( `Error in ${ chalk . magenta ( "refreshEmeter()" ) } :` ) ;
245- this . log . error ( String ( err ) ) ;
246- } finally {
247- this . log . debug (
248- `Scheduling next run of ${ chalk . magenta ( "refreshEmeter()" ) } in %d(ms)` ,
249- this . config . emeterPollingInterval ,
250- ) ;
251- setTimeout ( ( ) => {
252- refreshEmeter ( ) ;
253- } , this . config . emeterPollingInterval ) ;
254- }
255- } ;
256-
257- if ( this . config . emeterPollingInterval > 0 ) refreshEmeter ( ) ;
235+ if ( this . config . emeterPollingInterval > 0 ) this . refreshEmeter ( ) ;
258236 } ) ;
259237
260238 this . api . on ( "shutdown" , ( ) => {
261239 this . log . debug ( "shutdown" ) ;
262- client . stopDiscovery ( ) ;
263- if ( klapDiscovery ) {
264- klapDiscovery . stop ( ) ;
240+ this . legacyClient . stopDiscovery ( ) ;
241+ if ( this . klapDiscovery ) {
242+ this . klapDiscovery . stop ( ) ;
265243 }
266244 } ) ;
267245 }
268246
247+ private async refreshEmeter ( ) : Promise < void > {
248+ this . log . debug ( `${ chalk . magenta ( "refreshEmeter()" ) } ` ) ;
249+ if ( this . config . emeterPollingInterval <= 0 ) return ;
250+
251+ try {
252+ const deviceAccessories = this . deviceAccessoriesByHost ;
253+ const promises : Promise < unknown > [ ] = [ ] ;
254+
255+ for ( const accForHost of deviceAccessories . values ( ) ) {
256+ promises . push ( this . refreshEmeterForAccessories ( accForHost ) ) ;
257+ }
258+ await Promise . all ( promises ) ;
259+ } catch ( err ) {
260+ this . log . error ( `Error in ${ chalk . magenta ( "refreshEmeter()" ) } :` ) ;
261+ this . log . error ( String ( err ) ) ;
262+ } finally {
263+ this . log . debug (
264+ `Scheduling next run of ${ chalk . magenta ( "refreshEmeter()" ) } in %d(ms)` ,
265+ this . config . emeterPollingInterval ,
266+ ) ;
267+ setTimeout ( ( ) => {
268+ this . refreshEmeter ( ) ;
269+ } , this . config . emeterPollingInterval ) ;
270+ }
271+ }
272+
273+ private async refreshEmeterForAccessories (
274+ accessories : HomekitDevice [ ] ,
275+ ) : Promise < void > {
276+ for ( const acc of accessories ) {
277+ const device = acc . tplinkDevice ;
278+ if ( device . supportsEmeter ) {
279+ this . log . debug ( `getEmeterRealtime ${ chalk . blue ( `[${ device . alias } ]` ) } ` ) ;
280+ await device . emeter . getRealtime ( ) . catch ( ( reason ) => {
281+ this . log . error ( "[%s] %s" , device . alias , "emeter.getRealtime()" ) ;
282+ this . log . error ( reason ) ;
283+ } ) ;
284+ }
285+ }
286+ }
287+
269288 /**
270289 * Return string representation of Service/Characteristic for logging
271290 *
0 commit comments