Skip to content

Commit d955c00

Browse files
committed
Refactor late flow tests to improve clarity and accuracy in power signing logic
1 parent 38582ad commit d955c00

File tree

1 file changed

+56
-182
lines changed

1 file changed

+56
-182
lines changed

tests/test_tuya_power.py

Lines changed: 56 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -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)])
396396
async 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
)
456478
async 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

Comments
 (0)