Skip to content

Commit 46ddc22

Browse files
puddlyTheJulianJES
andauthored
Verify that no quirks do suspicious moves or copy/pastes of clusters (#4259)
* Verify that no quirks do suspicious moves or copy/pastes of clusters * Track removed clusters where one exists of the opposite type * Fix false positives for replacement cluster classes * Add ignore list for test * Remove `IkeaSymfoniskGen2v1` quirk from ignore list --------- Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
1 parent eabc285 commit 46ddc22

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

tests/test_quirks.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,155 @@ def check_for_duplicate_cluster_ids(clusters) -> None:
837837
check_for_duplicate_cluster_ids(ep_data.get(OUTPUT_CLUSTERS, []))
838838

839839

840+
@pytest.mark.parametrize(
841+
"quirk",
842+
[
843+
quirk_cls
844+
for quirk_cls in ALL_QUIRK_CLASSES
845+
if quirk_cls
846+
not in (
847+
# -- Tuya devices --
848+
# remove duplicated OnOff from input cluster (Tuya remotes):
849+
zhaquirks.tuya.ts004f.TuyaSmartRemote004F,
850+
zhaquirks.tuya.ts004f.TuyaSmartRemote004FROK,
851+
zhaquirks.tuya.ts004f.TuyaSmartRemote004FDMS,
852+
zhaquirks.tuya.ts004f.TuyaSmartRemote004FSK,
853+
zhaquirks.tuya.ts004f.TuyaSmartRemote004FSK_v2,
854+
# swap OnOff from input to output cluster (Tuya remotes):
855+
zhaquirks.tuya.ts0041.TuyaSmartRemote0041TOPlusA,
856+
zhaquirks.tuya.ts0042.TuyaSmartRemote0042TOPlusA,
857+
zhaquirks.tuya.ts0043.TuyaSmartRemote0043TOPlusB,
858+
zhaquirks.tuya.ts0044.TuyaSmartRemote0044TOPlusB,
859+
zhaquirks.tuya.ts0046.TuyaSmartRemote0046,
860+
# swap TuyaZBExternalSwitchTypeCluster input to output cluster (Tuya plug):
861+
zhaquirks.tuya.ts011f_plug.Plug_v6,
862+
#
863+
# -- Xiaomi/Aqara devices --
864+
# swap OnOff from input to output cluster (binary sensor):
865+
zhaquirks.xiaomi.aqara.magnet_aq2.MagnetAQ2,
866+
# swap OnOff from input to output cluster (Aqara remotes):
867+
zhaquirks.xiaomi.aqara.sensor_switch_aq3.SwitchAQ3,
868+
zhaquirks.xiaomi.aqara.switch_aq2.SwitchAQ2,
869+
# remove MultistateInput output cluster (Xiaomi cube):
870+
zhaquirks.xiaomi.aqara.cube.Cube,
871+
zhaquirks.xiaomi.aqara.cube_aqgl01.CubeAQGL01,
872+
# also add OTA input cluster (Aqara cube):
873+
zhaquirks.xiaomi.aqara.cube_aqgl01.CubeCAGL02,
874+
# remove custom Xiaomi output cluster (E1 curtain driver):
875+
zhaquirks.xiaomi.aqara.driver_curtain_e1.DriverE1,
876+
# remove random AnalogInput input cluster (Aqara remote + temp sensor):
877+
zhaquirks.xiaomi.aqara.remote_b186acn01.RemoteB186ACN01,
878+
zhaquirks.xiaomi.aqara.remote_b286acn01.RemoteB286ACN01,
879+
zhaquirks.xiaomi.mija.sensor_ht.Weather,
880+
# remove Time input cluster (Aqara switch):
881+
zhaquirks.xiaomi.aqara.switch_t1.SwitchT1Alt2,
882+
zhaquirks.xiaomi.aqara.switch_t1.SwitchT1,
883+
# remove OnOff output cluster (Aqara switch):
884+
zhaquirks.xiaomi.aqara.switch_t1.SwitchT1Alt3,
885+
# remove OTA input cluster (Aqara remote + motion sensor):
886+
zhaquirks.xiaomi.mija.motion.Motion,
887+
zhaquirks.xiaomi.mija.sensor_switch.MijaButton,
888+
# remove a bunch of incorrect output clusters (LUMI/Keen temp sensor):
889+
zhaquirks.keenhome.weather.TemperatureHumidtyPressureSensor,
890+
# this just exposed all ZCL clusters, remove a lot (Aqara light):
891+
zhaquirks.xiaomi.aqara.light_aqcn2.LightAqcn02,
892+
# DoorLock cluster that's actually a MultistateInput cluster
893+
# removed as output cluster (Aqara vibration sensor):
894+
zhaquirks.xiaomi.aqara.vibration_aq1.VibrationAQ1,
895+
#
896+
# -- IKEA devices --
897+
# swap PM25 cluster from output to input cluster (IKEA Starkvind):
898+
zhaquirks.ikea.starkvind.IkeaSTARKVIND,
899+
zhaquirks.ikea.starkvind.IkeaSTARKVIND_v2,
900+
# removes Group input cluster (IKEA remote):
901+
zhaquirks.ikea.twobtnremote.IkeaRodretRemote2BtnNew,
902+
# remove WindowCovering input cluster (IKEA remote):
903+
zhaquirks.ikea.twobtnremote.IkeaTradfriRemote2BtnZLL,
904+
#
905+
# -- other devices --
906+
# adds DoorLock cluster to output clusters (Yale door locks):
907+
zhaquirks.yale.realliving.YRD210PBDB220TSLL,
908+
zhaquirks.yale.realliving.YRD220240TSDB,
909+
# remove LevelControl input cluster (Adurolight remote):
910+
zhaquirks.aduro.adurolightncc.AdurolightNCC,
911+
# add a bunch of output clusters (Zhongxing motion sensor):
912+
zhaquirks.zhongxing.motion.SN10ZW,
913+
# remove Tuya clusters from input and output clusters (ZLinky):
914+
zhaquirks.lixee.zlinky.ZLinkyTICFWV14,
915+
zhaquirks.lixee.zlinky.ZLinkyTICFWV15,
916+
)
917+
],
918+
)
919+
def test_suspicious_cluster_moves(quirk: CustomDevice) -> None:
920+
"""Verify that no quirks do suspicious moves or copy/pastes of clusters."""
921+
for ep_id, ep_data in quirk.replacement[ENDPOINTS].items():
922+
# Originals
923+
orig_in_clusters = set(
924+
quirk.signature.get(ENDPOINTS, {}).get(ep_id, {}).get(INPUT_CLUSTERS, [])
925+
)
926+
orig_out_clusters = set(
927+
quirk.signature.get(ENDPOINTS, {}).get(ep_id, {}).get(OUTPUT_CLUSTERS, [])
928+
)
929+
930+
# New
931+
new_in_clusters = {
932+
cluster if isinstance(cluster, int) else cluster.cluster_id
933+
for cluster in ep_data.get(INPUT_CLUSTERS, [])
934+
}
935+
new_out_clusters = {
936+
cluster if isinstance(cluster, int) else cluster.cluster_id
937+
for cluster in ep_data.get(OUTPUT_CLUSTERS, [])
938+
}
939+
940+
added_in_clusters = set(new_in_clusters) - set(orig_in_clusters)
941+
added_out_clusters = set(new_out_clusters) - set(orig_out_clusters)
942+
943+
removed_in_clusters = set(orig_in_clusters) - set(new_in_clusters)
944+
removed_out_clusters = set(orig_out_clusters) - set(new_out_clusters)
945+
946+
# Moved clusters
947+
in_clusters_moved_to_out = added_out_clusters & removed_in_clusters
948+
out_clusters_moved_to_in = added_in_clusters & removed_out_clusters
949+
950+
if in_clusters_moved_to_out:
951+
pytest.fail(
952+
f"Quirk {quirk!r} moved input to output cluster on EP {ep_id}: {in_clusters_moved_to_out!r}"
953+
)
954+
955+
if out_clusters_moved_to_in:
956+
pytest.fail(
957+
f"Quirk {quirk!r} moved output to input cluster on EP {ep_id}: {out_clusters_moved_to_in!r}"
958+
)
959+
960+
# Mirrored clusters
961+
out_mirrored_to_in = added_in_clusters & orig_out_clusters
962+
in_mirrored_to_out = added_out_clusters & orig_in_clusters
963+
964+
if out_mirrored_to_in:
965+
pytest.fail(
966+
f"Quirk {quirk!r} mirrored output to input cluster on EP {ep_id}: {out_mirrored_to_in!r}"
967+
)
968+
969+
if in_mirrored_to_out:
970+
pytest.fail(
971+
f"Quirk {quirk!r} mirrored input to output cluster on EP {ep_id}: {in_mirrored_to_out!r}"
972+
)
973+
974+
# Removed clusters where one exists of the opposite type
975+
removed_duplicate_in_clusters = removed_in_clusters & orig_out_clusters
976+
removed_duplicate_out_clusters = removed_out_clusters & orig_in_clusters
977+
978+
if removed_duplicate_in_clusters:
979+
pytest.fail(
980+
f"Quirk {quirk!r} removed input cluster that has output cluster with same ID on EP {ep_id}: {removed_duplicate_in_clusters!r}"
981+
)
982+
983+
if removed_duplicate_out_clusters:
984+
pytest.fail(
985+
f"Quirk {quirk!r} removed output cluster that has input cluster with same ID on EP {ep_id}: {removed_duplicate_out_clusters!r}"
986+
)
987+
988+
840989
async def test_local_data_cluster(device_mock) -> None:
841990
"""Ensure reading attributes from a LocalDataCluster works as expected."""
842991
registry = DeviceRegistry()

0 commit comments

Comments
 (0)