@@ -2,7 +2,7 @@ import { Buffer } from 'node:buffer'
22
33import { describe , expect , it } from 'vitest'
44
5- import { Advertising , ErrorUtils , LogLevel , ValidationUtils , WoAirPurifier , WoPlugMiniEU } from './device.js'
5+ import { Advertising , ErrorUtils , LogLevel , ValidationUtils , WoAirPurifier , WoPlugMiniEU , WoPlugMiniJP , WoPlugMiniUS } from './device.js'
66
77describe ( 'validationUtils' , ( ) => {
88 describe ( 'validatePercentage' , ( ) => {
@@ -290,6 +290,66 @@ describe('advertising', () => {
290290 expect ( result ?. sequence_number ) . toBe ( 1 )
291291 } )
292292
293+ it ( 'should parse Air Purifier service data from serviceData-only (no manufacturerData)' , ( ) => {
294+ // Air Purifier advertising with state data in serviceData (9 bytes), no manufacturerData
295+ const serviceData = Buffer . from ( [
296+ 0x2B , // '+' model byte
297+ 0x02 , // sequenceNumber
298+ 0x80 | 0x02 , // isOn=true, mode=2
299+ 0x04 , // isAqiValid=true, childLock=false
300+ 0x32 , // speed=50
301+ 0x02 , // aqiLevelRaw=1 (good)
302+ 0x00 , 0x0A , // workTime=10
303+ 0x00 , // errCode=0
304+ ] )
305+
306+ const emitLog = ( ) => { }
307+ const result = WoAirPurifier . parseServiceData ( serviceData , null , emitLog )
308+
309+ expect ( result ) . not . toBeNull ( )
310+ expect ( result ?. model ) . toBe ( '+' )
311+ expect ( result ?. modelName ) . toBe ( 'WoAirPurifier' )
312+ expect ( result ?. isOn ) . toBe ( true )
313+ expect ( result ?. mode ) . toBe ( 'auto' )
314+ expect ( result ?. speed ) . toBe ( 50 )
315+ expect ( result ?. aqi_level ) . toBe ( 'good' )
316+ expect ( result ?. isAqiValid ) . toBe ( true )
317+ expect ( result ?. child_lock ) . toBe ( false )
318+ expect ( result ?. filter_element_working_time ) . toBe ( 10 )
319+ expect ( result ?. err_code ) . toBe ( 0 )
320+ expect ( result ?. sequence_number ) . toBe ( 2 )
321+ } )
322+
323+ it ( 'should map Air Purifier mode=2 to auto, mode=3 to sleep, mode=4 to manual' , ( ) => {
324+ const makeManufData = ( modeByte : number ) => Buffer . from ( [
325+ 0x59 , 0x00 , 0x12 , 0x34 , 0x56 , 0x78 ,
326+ 0x01 , // sequenceNumber
327+ modeByte ,
328+ 0x00 , // isAqiValid=false, childLock=false
329+ 0x50 , // speed=80
330+ 0x00 , // aqiLevelRaw=0 (excellent)
331+ 0x00 , 0x00 , // workTime=0
332+ 0x00 , // errCode=0
333+ ] )
334+
335+ const emitLog = ( ) => { }
336+
337+ const resultAuto = WoAirPurifier . parseServiceData ( Buffer . from ( '+' ) , makeManufData ( 0x80 | 0x02 ) , emitLog )
338+ expect ( resultAuto ?. mode ) . toBe ( 'auto' )
339+
340+ const resultSleep = WoAirPurifier . parseServiceData ( Buffer . from ( '+' ) , makeManufData ( 0x80 | 0x03 ) , emitLog )
341+ expect ( resultSleep ?. mode ) . toBe ( 'sleep' )
342+
343+ const resultManual = WoAirPurifier . parseServiceData ( Buffer . from ( '+' ) , makeManufData ( 0x80 | 0x04 ) , emitLog )
344+ expect ( resultManual ?. mode ) . toBe ( 'manual' )
345+ } )
346+
347+ it ( 'should return null for Air Purifier when both buffers are insufficient' , ( ) => {
348+ const emitLog = ( ) => { }
349+ const result = WoAirPurifier . parseServiceData ( Buffer . from ( '+' ) , null , emitLog )
350+ expect ( result ) . toBeNull ( )
351+ } )
352+
293353 it ( 'should parse Plug Mini EU service data correctly' , async ( ) => {
294354 // bytes 0-8: header/MAC/sequence (unused by parser), bytes 9-13: state/flags/rssi/power
295355 const manufacturerData = Buffer . from ( [
@@ -382,5 +442,24 @@ describe('advertising', () => {
382442 expect ( result ?. serviceData . model ) . toBe ( 'l' )
383443 expect ( result ?. serviceData . modelName ) . toBe ( 'WoPlugMini' )
384444 } )
445+
446+ const plugMiniClasses = [
447+ { name : 'WoPlugMiniJP' , cls : WoPlugMiniJP } ,
448+ { name : 'WoPlugMiniEU' , cls : WoPlugMiniEU } ,
449+ { name : 'WoPlugMiniUS' , cls : WoPlugMiniUS } ,
450+ ] as const
451+
452+ for ( const { name, cls } of plugMiniClasses ) {
453+ it ( `should return null when ${ name } manufacturerData is undefined` , async ( ) => {
454+ const errors : string [ ] = [ ]
455+ const emitLog = ( _level : string , msg : string ) => { errors . push ( msg ) }
456+
457+ const result = await cls . parseServiceData ( undefined , emitLog )
458+
459+ expect ( result ) . toBeNull ( )
460+ expect ( errors . length ) . toBeGreaterThan ( 0 )
461+ expect ( errors [ 0 ] ) . toContain ( 'should be 14' )
462+ } )
463+ }
385464 } )
386465} )
0 commit comments