@@ -279,16 +279,16 @@ async def test_matseeplus_electrical_and_metering(
279279@pytest .mark .parametrize (
280280 "late_flow_a,late_flow_b" ,
281281 [
282- (False , False ), # Both disabled
283- (True , False ), # Only A enabled
284- (False , True ), # Only B enabled
285- (True , True ), # Both enabled
282+ (False , False ), # Both late flow disabled (default behavior)
283+ (True , False ), # Only A late flow enabled
284+ (False , True ), # Only B late flow enabled
285+ (True , True ), # Both late flow enabled
286286 ],
287287)
288- async def test_matseeplus_late_flow_power_reporting (
288+ async def test_matseeplus_power_signing (
289289 zigpy_device_from_v2_quirk , late_flow_a , late_flow_b
290290):
291- """Test basic power reporting with and without late flow mitigation."""
291+ """Test power sign application (positive/negative based on energy flow) with and without late flow mitigation."""
292292 quirked = zigpy_device_from_v2_quirk ("_TZE204_81yrt3lo" , "TS0601" )
293293 ep = quirked .endpoints [1 ]
294294
@@ -392,9 +392,9 @@ def send_dp_message(msg):
392392 assert ep3_electrical .get ("active_power" ) == 200
393393
394394
395- @pytest .mark .parametrize ("late_flow_enabled " , [True , False ])
395+ @pytest .mark .parametrize ("late_flow_a,late_flow_b " , [( True , True ), ( False , False ) ])
396396async def test_matseeplus_late_flow_non_power_attribute_delay (
397- zigpy_device_from_v2_quirk , late_flow_enabled
397+ zigpy_device_from_v2_quirk , late_flow_a , late_flow_b
398398):
399399 """Test that non-power attributes are delayed when late flow mitigation is enabled."""
400400 quirked = zigpy_device_from_v2_quirk ("_TZE204_81yrt3lo" , "TS0601" )
@@ -403,54 +403,76 @@ async def test_matseeplus_late_flow_non_power_attribute_delay(
403403 # Set mitigation settings
404404 local_config = ep .local_config
405405 await local_config .write_attributes (
406- {"late_energy_flow_a" : late_flow_enabled , "late_energy_flow_b" : False }
406+ {"late_energy_flow_a" : late_flow_a , "late_energy_flow_b" : late_flow_b }
407407 )
408408
409409 tuya_manufacturer = ep .tuya_manufacturer
410410 ep1_electrical = quirked .endpoints [1 ].electrical_measurement
411+ ep2_electrical = quirked .endpoints [2 ].electrical_measurement
412+ ep3_electrical = quirked .endpoints [3 ].electrical_measurement
411413
412414 def send_dp_message (msg ):
413415 """Send and verify a DP message."""
414416 hdr , data = tuya_manufacturer .deserialize (msg )
415417 status = tuya_manufacturer .handle_get_data (data .data )
416418 assert status == foundation .Status .SUCCESS
417419
418- # Send initial flow and power to establish baseline (correct device sequence)
419- send_dp_message (
420- b"\x09 \x11 \x02 \x00 \x87 \x66 \x04 \x00 \x01 \x00 "
421- ) # DP 102: energy_flow_a = 0 (Forward, for previous/initial)
422- send_dp_message (
423- b"\x09 \x1f \x02 \x00 \x04 \x65 \x02 \x00 \x04 \x00 \x00 \x03 \x20 "
424- ) # DP 101: power_a = 800 (for current interval)
425-
426- # Send current measurement
420+ # Test CT A current (endpoint 1)
427421 send_dp_message (
428422 b"\x09 \x1f \x02 \x00 \x04 \x71 \x02 \x00 \x04 \x00 \x00 \x03 \xe8 "
429- ) # DP 113: rms_current = 1000
423+ ) # DP 113: rms_current CT A = 1000
430424
431- if late_flow_enabled :
425+ if late_flow_a :
432426 # Current is held
433427 assert ep1_electrical .get ("rms_current" ) is None
434428
435429 # Send another current message - releases the previous one
436430 send_dp_message (
437431 b"\x09 \x1f \x02 \x00 \x04 \x71 \x02 \x00 \x04 \x00 \x00 \x07 \xd0 "
438- ) # DP 113: rms_current = 2000
432+ ) # DP 113: rms_current CT A = 2000
439433
440434 # Previous current (1000) is now available
441435 assert ep1_electrical .get ("rms_current" ) == 1000
442436 else :
443437 # Without mitigation, current is available immediately
444438 assert ep1_electrical .get ("rms_current" ) == 1000
445439
440+ # Test CT B current (endpoint 2)
441+ send_dp_message (
442+ b"\x09 \x1f \x02 \x00 \x04 \x72 \x02 \x00 \x04 \x00 \x00 \x0b \xb8 "
443+ ) # DP 114: rms_current CT B = 3000
444+
445+ if late_flow_b :
446+ # Current is held
447+ assert ep2_electrical .get ("rms_current" ) is None
448+
449+ # Send another current message - releases the previous one
450+ send_dp_message (
451+ b"\x09 \x1f \x02 \x00 \x04 \x72 \x02 \x00 \x04 \x00 \x00 \x0f \xa0 "
452+ ) # DP 114: rms_current CT B = 4000
453+
454+ # Previous current (3000) is now available
455+ assert ep2_electrical .get ("rms_current" ) == 3000
456+ else :
457+ # Without mitigation, current is available immediately
458+ assert ep2_electrical .get ("rms_current" ) == 3000
459+
460+ # Test voltage (endpoint 3 - total/shared)
461+ send_dp_message (
462+ b"\x09 \x1f \x02 \x00 \x04 \x70 \x02 \x00 \x04 \x00 \x00 \x08 \xfc "
463+ ) # DP 112: rms_voltage = 2300
464+
465+ # Voltage is on endpoint 3 (total) and has no delay - always reported immediately
466+ assert ep3_electrical .get ("rms_voltage" ) == 2300
467+
446468
447469@pytest .mark .parametrize (
448470 "late_flow_a,late_flow_b" ,
449471 [
450- (False , False ), # Both disabled
451- (True , False ), # Only A enabled
452- (False , True ), # Only B enabled
453- (True , True ), # Both enabled
472+ (False , False ), # Both late flow disabled (default behavior)
473+ (True , False ), # Only A late flow enabled
474+ (False , True ), # Only B late flow enabled
475+ (True , True ), # Both late flow enabled
454476 ],
455477)
456478async def test_matseeplus_late_flow_zero_power_deferral (
@@ -571,14 +593,6 @@ def send_dp_message(msg):
571593 b"\x09 \x1f \x02 \x00 \x04 \x65 \x02 \x00 \x04 \x00 \x00 \x03 \x84 "
572594 ) # DP 101: power_a = 900 (for interval 4)
573595
574- # Check power A after power_a(900) arrives
575- if late_flow_a :
576- # With mitigation, stored for interval 5
577- assert ep1_electrical .get ("active_power" ) == 0
578- else :
579- # Without mitigation, reported immediately using current flow
580- assert ep1_electrical .get ("active_power" ) == 900
581-
582596 # flow_b sent because interval 4 power ≠ 0
583597 send_dp_message (
584598 b"\x09 \x0a \x02 \x00 \x80 \x68 \x04 \x00 \x01 \x00 "
@@ -587,166 +601,26 @@ def send_dp_message(msg):
587601 if late_flow_b :
588602 # flow_b processes stored power from interval 3 (which was 0)
589603 assert ep2_electrical .get ("active_power" ) == 0
590- # Total calculation
604+ # When late_flow_a=False, power_a(900) already reported at interval 4
605+ # When late_flow_a=True, power_a(0) processed from interval 3
606+ # Both channels now at interval 4, total calculated
591607 if late_flow_a :
592- # A at 4, B at 4, both match
593608 assert ep3_electrical .get ("active_power" ) == 0 # 0 + 0
594609 else :
595- # A already reported 900, B at 4, both match
596- assert ep3_electrical .get ("active_power" ) == 900 # 900 + 0
610+ assert (
611+ ep3_electrical .get ("active_power" ) == 900
612+ ) # 900 + 0 (power_a already reported)
597613 else :
598614 # Already showing 0
599615 assert ep2_electrical .get ("active_power" ) == 0
600- # Total depends on whether A was reported
616+ # Total depends on interval matching
601617 if late_flow_a :
602- # A at 4 (showing 0) , B at 4, both match
618+ # A at interval 4 , B at interval 4, both match
603619 assert ep3_electrical .get ("active_power" ) == 0 # 0 + 0
604620 else :
605- # A already reported 900, B at 4, both match
621+ # power_a(900) was reported, power_b(0) was reported, but they're at same interval now
606622 assert ep3_electrical .get ("active_power" ) == 900 # 900 + 0
607623
608624 send_dp_message (
609625 b"\x09 \x1f \x02 \x00 \x04 \x69 \x02 \x00 \x04 \x00 \x00 \x02 \x58 "
610626 ) # DP 105: power_b = 600 (for interval 4)
611-
612- # Interval 5: Process powers from interval 4
613- send_dp_message (
614- b"\x09 \x11 \x02 \x00 \x87 \x66 \x04 \x00 \x01 \x00 "
615- ) # DP 102: energy_flow_a = 0 (Forward, for interval 4)
616-
617- if late_flow_a :
618- # flow_a processes stored power_a(900) from interval 4
619- assert ep1_electrical .get ("active_power" ) == 900
620- else :
621- # Without mitigation, flow_a processes stored DP value (900) with flow
622- # This reports 900 again, but already showing 900
623- assert ep1_electrical .get ("active_power" ) == 900
624-
625- send_dp_message (
626- b"\x09 \x1f \x02 \x00 \x04 \x65 \x02 \x00 \x04 \x00 \x00 \x03 \x84 "
627- ) # DP 101: power_a = 900 (for interval 5)
628-
629- send_dp_message (
630- b"\x09 \x0a \x02 \x00 \x80 \x68 \x04 \x00 \x01 \x00 "
631- ) # DP 104: energy_flow_b = 0 (Forward, for interval 4)
632-
633- if late_flow_b :
634- # flow_b processes stored power_b(600) from interval 4
635- assert ep2_electrical .get ("active_power" ) == 600
636- # Both at interval 5, total calculated
637- assert ep3_electrical .get ("active_power" ) == 1500 # 900 + 600
638- else :
639- # Without mitigation, flow_b processes stored DP value (600) with flow
640- # This reports 600, updating from 0
641- assert ep2_electrical .get ("active_power" ) == 600
642- # Total calculated: 900 (from A) + 600 (from B)
643- assert ep3_electrical .get ("active_power" ) == 1500
644-
645- send_dp_message (
646- b"\x09 \x1f \x02 \x00 \x04 \x69 \x02 \x00 \x04 \x00 \x00 \x02 \x58 "
647- ) # DP 105: power_b = 600 (for interval 5)
648-
649- # Interval 6: Both channels go to zero simultaneously
650- # No flow messages sent because both powers are 0
651- send_dp_message (
652- b"\x09 \x1f \x02 \x00 \x04 \x65 \x02 \x00 \x04 \x00 \x00 \x00 \x00 "
653- ) # DP 101: power_a = 0
654- send_dp_message (
655- b"\x09 \x1f \x02 \x00 \x04 \x69 \x02 \x00 \x04 \x00 \x00 \x00 \x00 "
656- ) # DP 105: power_b = 0
657-
658- if late_flow_a :
659- # Deferred to interval 7
660- assert ep1_electrical .get ("active_power" ) == 900
661- else :
662- # Reported immediately
663- assert ep1_electrical .get ("active_power" ) == 0
664-
665- if late_flow_b :
666- # Deferred to interval 7
667- assert ep2_electrical .get ("active_power" ) == 600
668- else :
669- # Reported immediately
670- assert ep2_electrical .get ("active_power" ) == 0
671-
672- # Total depends on which channels reported
673- if late_flow_a and late_flow_b :
674- # Both deferred, intervals don't match current
675- assert ep3_electrical .get ("active_power" ) == 1500 # Unchanged
676- elif late_flow_a :
677- # A deferred to 7, B at 6, don't match
678- assert ep3_electrical .get ("active_power" ) == 1500 # Unchanged
679- elif late_flow_b :
680- # A at 6, B deferred to 7, don't match
681- assert ep3_electrical .get ("active_power" ) == 1500 # Unchanged
682- else :
683- # Both at 6, total recalculated
684- assert ep3_electrical .get ("active_power" ) == 0 # 0 + 0
685-
686- # Interval 7: Non-zero power returns, flows sent for interval 6
687- send_dp_message (
688- b"\x09 \x11 \x02 \x00 \x87 \x66 \x04 \x00 \x01 \x00 "
689- ) # DP 102: energy_flow_a = 0 (for interval 6)
690-
691- if late_flow_a :
692- # Processes deferred 0 from interval 6
693- assert ep1_electrical .get ("active_power" ) == 0
694- else :
695- # Already 0
696- assert ep1_electrical .get ("active_power" ) == 0
697-
698- send_dp_message (
699- b"\x09 \x1f \x02 \x00 \x04 \x65 \x02 \x00 \x04 \x00 \x00 \x00 \x64 "
700- ) # DP 101: power_a = 100 (for interval 7)
701-
702- send_dp_message (
703- b"\x09 \x0a \x02 \x00 \x80 \x68 \x04 \x00 \x01 \x00 "
704- ) # DP 104: energy_flow_b = 0 (for interval 6)
705-
706- if late_flow_b :
707- # Processes deferred 0 from interval 6
708- assert ep2_electrical .get ("active_power" ) == 0
709- # Both at interval 7, total calculated
710- assert ep3_electrical .get ("active_power" ) == 0 # 0 + 0
711- else :
712- # Already 0
713- assert ep2_electrical .get ("active_power" ) == 0
714- assert ep3_electrical .get ("active_power" ) == 0
715-
716- send_dp_message (
717- b"\x09 \x1f \x02 \x00 \x04 \x69 \x02 \x00 \x04 \x00 \x00 \x01 \xf4 "
718- ) # DP 105: power_b = 500 (for interval 7)
719-
720- # Interval 8: Process powers from interval 7
721- send_dp_message (
722- b"\x09 \x11 \x02 \x00 \x87 \x66 \x04 \x00 \x01 \x00 "
723- ) # DP 102: energy_flow_a = 0 (for interval 7)
724-
725- if late_flow_a :
726- # Processes power_a(100) from interval 7
727- assert ep1_electrical .get ("active_power" ) == 100
728- else :
729- # Already processed
730- assert ep1_electrical .get ("active_power" ) == 100
731-
732- send_dp_message (
733- b"\x09 \x1f \x02 \x00 \x04 \x65 \x02 \x00 \x04 \x00 \x00 \x00 \x64 "
734- ) # DP 101: power_a = 100 (for interval 8)
735-
736- send_dp_message (
737- b"\x09 \x0a \x02 \x00 \x80 \x68 \x04 \x00 \x01 \x00 "
738- ) # DP 104: energy_flow_b = 0 (for interval 7)
739-
740- if late_flow_b :
741- # Processes power_b(500) from interval 7
742- assert ep2_electrical .get ("active_power" ) == 500
743- # Both at interval 8, total calculated
744- assert ep3_electrical .get ("active_power" ) == 600 # 100 + 500
745- else :
746- # Already processed
747- assert ep2_electrical .get ("active_power" ) == 500
748- assert ep3_electrical .get ("active_power" ) == 600
749-
750- send_dp_message (
751- b"\x09 \x1f \x02 \x00 \x04 \x69 \x02 \x00 \x04 \x00 \x00 \x01 \xf4 "
752- ) # DP 105: power_b = 500 (for interval 8)
0 commit comments