@@ -269,6 +269,229 @@ describe("lib/lumi", () => {
269269 expect ( lateAttr107Payload ) . toStrictEqual ( { } ) ;
270270 } ) ;
271271
272+ it . each ( [
273+ { invert_cover : false , terminalReadbackPosition : 100 , adjacentReadbackPosition : 99 , expectedPayload : { position : 100 , state : "OPEN" } } ,
274+ { invert_cover : false , terminalReadbackPosition : 100 , adjacentReadbackPosition : 98 , expectedPayload : { position : 100 , state : "OPEN" } } ,
275+ { invert_cover : false , terminalReadbackPosition : 0 , adjacentReadbackPosition : 1 , expectedPayload : { position : 0 , state : "CLOSE" } } ,
276+ { invert_cover : false , terminalReadbackPosition : 0 , adjacentReadbackPosition : 2 , expectedPayload : { position : 0 , state : "CLOSE" } } ,
277+ { invert_cover : true , terminalReadbackPosition : 0 , adjacentReadbackPosition : 1 , expectedPayload : { position : 100 , state : "CLOSE" } } ,
278+ { invert_cover : true , terminalReadbackPosition : 0 , adjacentReadbackPosition : 2 , expectedPayload : { position : 100 , state : "CLOSE" } } ,
279+ { invert_cover : true , terminalReadbackPosition : 100 , adjacentReadbackPosition : 99 , expectedPayload : { position : 0 , state : "OPEN" } } ,
280+ { invert_cover : true , terminalReadbackPosition : 100 , adjacentReadbackPosition : 98 , expectedPayload : { position : 0 , state : "OPEN" } } ,
281+ ] ) ( "normalizes adjacent terminal readback while stopped when invert_cover=$invert_cover" , async ( {
282+ invert_cover,
283+ terminalReadbackPosition,
284+ adjacentReadbackPosition,
285+ expectedPayload,
286+ } ) => {
287+ const { device, msg} = createCurtainMessage ( ) ;
288+
289+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1057 : 2 } ) ;
290+
291+ fromZigbee . lumi_curtain_position_tilt . convert (
292+ znclbl01lmDefinition ,
293+ {
294+ data : { currentPositionLiftPercentage : terminalReadbackPosition } ,
295+ endpoint : device . endpoints [ 0 ] ,
296+ } as Fz . Message < "closuresWindowCovering" , undefined , "readResponse" > ,
297+ null ,
298+ { invert_cover} ,
299+ { } as Fz . Meta ,
300+ ) ;
301+
302+ const adjacentReadbackPayload = fromZigbee . lumi_curtain_position_tilt . convert (
303+ znclbl01lmDefinition ,
304+ {
305+ data : { currentPositionLiftPercentage : adjacentReadbackPosition } ,
306+ endpoint : device . endpoints [ 0 ] ,
307+ } as Fz . Message < "closuresWindowCovering" , undefined , "readResponse" > ,
308+ null ,
309+ { invert_cover} ,
310+ { } as Fz . Meta ,
311+ ) ;
312+
313+ expect ( adjacentReadbackPayload ) . toStrictEqual ( expectedPayload ) ;
314+ } ) ;
315+
316+ it . each ( [
317+ { invert_cover : false , adjacentReadbackPosition : 99 , expectedPayload : { position : 99 , state : "OPEN" } } ,
318+ { invert_cover : false , adjacentReadbackPosition : 98 , expectedPayload : { position : 98 , state : "OPEN" } } ,
319+ { invert_cover : false , adjacentReadbackPosition : 1 , expectedPayload : { position : 1 , state : "OPEN" } } ,
320+ { invert_cover : false , adjacentReadbackPosition : 2 , expectedPayload : { position : 2 , state : "OPEN" } } ,
321+ { invert_cover : true , adjacentReadbackPosition : 1 , expectedPayload : { position : 99 , state : "CLOSE" } } ,
322+ { invert_cover : true , adjacentReadbackPosition : 2 , expectedPayload : { position : 98 , state : "CLOSE" } } ,
323+ { invert_cover : true , adjacentReadbackPosition : 99 , expectedPayload : { position : 1 , state : "CLOSE" } } ,
324+ { invert_cover : true , adjacentReadbackPosition : 98 , expectedPayload : { position : 2 , state : "CLOSE" } } ,
325+ ] ) ( "keeps adjacent readback unchanged without a previous terminal endpoint when invert_cover=$invert_cover" , async ( {
326+ invert_cover,
327+ adjacentReadbackPosition,
328+ expectedPayload,
329+ } ) => {
330+ const { device, msg} = createCurtainMessage ( ) ;
331+
332+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1057 : 2 } ) ;
333+
334+ const adjacentReadbackPayload = fromZigbee . lumi_curtain_position_tilt . convert (
335+ znclbl01lmDefinition ,
336+ {
337+ data : { currentPositionLiftPercentage : adjacentReadbackPosition } ,
338+ endpoint : device . endpoints [ 0 ] ,
339+ } as Fz . Message < "closuresWindowCovering" , undefined , "readResponse" > ,
340+ null ,
341+ { invert_cover} ,
342+ { } as Fz . Meta ,
343+ ) ;
344+
345+ expect ( adjacentReadbackPayload ) . toStrictEqual ( expectedPayload ) ;
346+ } ) ;
347+
348+ it . each ( [
349+ { invert_cover : false , terminalTargetPosition : 100 , adjacentTargetPosition : 99 , expectedTargetPosition : 100 } ,
350+ { invert_cover : false , terminalTargetPosition : 100 , adjacentTargetPosition : 98 , expectedTargetPosition : 100 } ,
351+ { invert_cover : false , terminalTargetPosition : 0 , adjacentTargetPosition : 1 , expectedTargetPosition : 0 } ,
352+ { invert_cover : false , terminalTargetPosition : 0 , adjacentTargetPosition : 2 , expectedTargetPosition : 0 } ,
353+ { invert_cover : true , terminalTargetPosition : 0 , adjacentTargetPosition : 1 , expectedTargetPosition : 100 } ,
354+ { invert_cover : true , terminalTargetPosition : 0 , adjacentTargetPosition : 2 , expectedTargetPosition : 100 } ,
355+ { invert_cover : true , terminalTargetPosition : 100 , adjacentTargetPosition : 99 , expectedTargetPosition : 0 } ,
356+ { invert_cover : true , terminalTargetPosition : 100 , adjacentTargetPosition : 98 , expectedTargetPosition : 0 } ,
357+ ] ) ( "normalizes adjacent target_position while stopped when invert_cover=$invert_cover" , async ( {
358+ invert_cover,
359+ terminalTargetPosition,
360+ adjacentTargetPosition,
361+ expectedTargetPosition,
362+ } ) => {
363+ const { msg} = createCurtainMessage ( ) ;
364+
365+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1057 : 2 } ) ;
366+
367+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1055 : terminalTargetPosition } ) ;
368+
369+ const adjacentTargetPayload = await numericAttributes2Payload (
370+ msg ,
371+ { } as Fz . Meta ,
372+ znclbl01lmDefinition ,
373+ { invert_cover} ,
374+ { 1055 : adjacentTargetPosition } ,
375+ ) ;
376+
377+ expect ( adjacentTargetPayload ) . toStrictEqual ( { target_position : expectedTargetPosition } ) ;
378+ } ) ;
379+
380+ it . each ( [
381+ {
382+ invert_cover : false ,
383+ terminalTargetPosition : 100 ,
384+ runningStateValue : 1 ,
385+ adjacentPosition : 98 ,
386+ expectedPayload : { position : 100 , state : "OPEN" } ,
387+ } ,
388+ {
389+ invert_cover : false ,
390+ terminalTargetPosition : 0 ,
391+ runningStateValue : 0 ,
392+ adjacentPosition : 2 ,
393+ expectedPayload : { position : 0 , state : "CLOSE" } ,
394+ } ,
395+ {
396+ invert_cover : true ,
397+ terminalTargetPosition : 0 ,
398+ runningStateValue : 0 ,
399+ adjacentPosition : 2 ,
400+ expectedPayload : { position : 100 , state : "CLOSE" } ,
401+ } ,
402+ {
403+ invert_cover : true ,
404+ terminalTargetPosition : 100 ,
405+ runningStateValue : 1 ,
406+ adjacentPosition : 98 ,
407+ expectedPayload : { position : 0 , state : "OPEN" } ,
408+ } ,
409+ ] ) ( "keeps terminal target through movement for stopped readback when invert_cover=$invert_cover" , async ( {
410+ invert_cover,
411+ terminalTargetPosition,
412+ runningStateValue,
413+ adjacentPosition,
414+ expectedPayload,
415+ } ) => {
416+ const { device, msg} = createCurtainMessage ( ) ;
417+
418+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1055 : terminalTargetPosition } ) ;
419+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1057 : runningStateValue } ) ;
420+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1057 : 2 } ) ;
421+
422+ const adjacentReadbackPayload = fromZigbee . lumi_curtain_position_tilt . convert (
423+ znclbl01lmDefinition ,
424+ {
425+ data : { currentPositionLiftPercentage : adjacentPosition } ,
426+ endpoint : device . endpoints [ 0 ] ,
427+ } as Fz . Message < "closuresWindowCovering" , undefined , "readResponse" > ,
428+ null ,
429+ { invert_cover} ,
430+ { } as Fz . Meta ,
431+ ) ;
432+
433+ expect ( adjacentReadbackPayload ) . toStrictEqual ( expectedPayload ) ;
434+ } ) ;
435+
436+ it . each ( [
437+ { invert_cover : false , terminalReadbackPosition : 0 , requestedPosition : 1 , reportedPosition : 1 , expectedState : "OPEN" } ,
438+ { invert_cover : false , terminalReadbackPosition : 0 , requestedPosition : 2 , reportedPosition : 2 , expectedState : "OPEN" } ,
439+ { invert_cover : false , terminalReadbackPosition : 100 , requestedPosition : 98 , reportedPosition : 98 , expectedState : "OPEN" } ,
440+ { invert_cover : false , terminalReadbackPosition : 100 , requestedPosition : 99 , reportedPosition : 99 , expectedState : "OPEN" } ,
441+ { invert_cover : false , terminalReadbackPosition : 100 , requestedPosition : 1 , reportedPosition : 1 , expectedState : "OPEN" } ,
442+ { invert_cover : false , terminalReadbackPosition : 100 , requestedPosition : 2 , reportedPosition : 2 , expectedState : "OPEN" } ,
443+ { invert_cover : true , terminalReadbackPosition : 100 , requestedPosition : 1 , reportedPosition : 99 , expectedState : "CLOSE" } ,
444+ { invert_cover : true , terminalReadbackPosition : 100 , requestedPosition : 2 , reportedPosition : 98 , expectedState : "CLOSE" } ,
445+ { invert_cover : true , terminalReadbackPosition : 0 , requestedPosition : 98 , reportedPosition : 2 , expectedState : "CLOSE" } ,
446+ { invert_cover : true , terminalReadbackPosition : 0 , requestedPosition : 99 , reportedPosition : 1 , expectedState : "CLOSE" } ,
447+ { invert_cover : true , terminalReadbackPosition : 0 , requestedPosition : 1 , reportedPosition : 99 , expectedState : "CLOSE" } ,
448+ { invert_cover : true , terminalReadbackPosition : 0 , requestedPosition : 2 , reportedPosition : 98 , expectedState : "CLOSE" } ,
449+ ] ) ( "keeps explicit non-terminal near-end target $requestedPosition when invert_cover=$invert_cover" , async ( {
450+ invert_cover,
451+ terminalReadbackPosition,
452+ requestedPosition,
453+ reportedPosition,
454+ expectedState,
455+ } ) => {
456+ const { device, msg} = createCurtainMessage ( ) ;
457+ const meta = {
458+ device,
459+ mapped : znclbl01lmDefinition ,
460+ options : { invert_cover} ,
461+ } as Tz . Meta ;
462+
463+ await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1057 : 2 } ) ;
464+
465+ fromZigbee . lumi_curtain_position_tilt . convert (
466+ znclbl01lmDefinition ,
467+ {
468+ data : { currentPositionLiftPercentage : terminalReadbackPosition } ,
469+ endpoint : device . endpoints [ 0 ] ,
470+ } as Fz . Message < "closuresWindowCovering" , undefined , "readResponse" > ,
471+ null ,
472+ { invert_cover} ,
473+ { } as Fz . Meta ,
474+ ) ;
475+
476+ await toZigbee . lumi_curtain_position_state . convertSet ( device . endpoints [ 0 ] , "position" , requestedPosition , meta ) ;
477+
478+ const targetPayload = await numericAttributes2Payload ( msg , { } as Fz . Meta , znclbl01lmDefinition , { invert_cover} , { 1055 : reportedPosition } ) ;
479+
480+ const readbackPayload = fromZigbee . lumi_curtain_position_tilt . convert (
481+ znclbl01lmDefinition ,
482+ {
483+ data : { currentPositionLiftPercentage : reportedPosition } ,
484+ endpoint : device . endpoints [ 0 ] ,
485+ } as Fz . Message < "closuresWindowCovering" , undefined , "readResponse" > ,
486+ null ,
487+ { invert_cover} ,
488+ { } as Fz . Meta ,
489+ ) ;
490+
491+ expect ( targetPayload ) . toStrictEqual ( { target_position : requestedPosition } ) ;
492+ expect ( readbackPayload ) . toStrictEqual ( { position : requestedPosition , state : expectedState } ) ;
493+ } ) ;
494+
272495 it . each ( [
273496 { invert_cover : false , runningStateValue : 1 , resumedAttr107Position : 99 , expectedPosition : 99 , expectedState : "OPEN" } ,
274497 { invert_cover : true , runningStateValue : 0 , resumedAttr107Position : 1 , expectedPosition : 99 , expectedState : "CLOSE" } ,
0 commit comments