From bc32926a39a26725673d2c7371869e59dfcda283 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 16 Dec 2025 13:06:17 -0600 Subject: [PATCH 01/15] add statelessStep capability support --- .../statelessColorTemperatureStep.yml | 14 ++++++++++ .../capabilities/statelessSwitchLevelStep.yml | 14 ++++++++++ .../light-color-level-1800K-6500K.yml | 4 +++ .../light-color-level-2000K-7000K.yml | 4 +++ .../light-color-level-2200K-6500K.yml | 4 +++ .../light-color-level-2700K-6500K.yml | 4 +++ .../profiles/light-color-level-fan.yml | 4 +++ ...-level-illuminance-motion-1000K-15000K.yml | 4 +++ .../light-color-level-illuminance-motion.yml | 4 +++ .../profiles/light-color-level.yml | 4 +++ .../profiles/light-level-2-button.yml | 2 ++ .../profiles/light-level-3-button.yml | 2 ++ .../profiles/light-level-4-button.yml | 2 ++ .../profiles/light-level-5-button.yml | 2 ++ .../profiles/light-level-6-button.yml | 2 ++ .../profiles/light-level-7-button.yml | 2 ++ .../profiles/light-level-8-button.yml | 2 ++ ...ight-level-ColorTemperature-1500-9000k.yml | 4 +++ .../profiles/light-level-button.yml | 2 ++ ...ght-level-colorTemperature-2200K-6500K.yml | 4 +++ ...ght-level-colorTemperature-2700K-6500K.yml | 4 +++ ...ght-level-colorTemperature-2710k-6500k.yml | 4 +++ .../profiles/light-level-colorTemperature.yml | 4 +++ .../light-level-energy-powerConsumption.yml | 2 ++ .../profiles/light-level-motion.yml | 2 ++ ...ht-level-power-energy-powerConsumption.yml | 2 ++ .../profiles/light-level-power.yml | 2 ++ .../matter-switch/profiles/light-level.yml | 2 ++ .../plug-level-energy-powerConsumption.yml | 2 ++ ...ug-level-power-energy-powerConsumption.yml | 2 ++ .../profiles/plug-level-power.yml | 2 ++ .../matter-switch/profiles/plug-level.yml | 2 ++ .../profiles/switch-color-level.yml | 4 +++ .../switch-level-colorTemperature.yml | 4 +++ .../matter-switch/profiles/switch-level.yml | 2 ++ .../SmartThings/matter-switch/src/init.lua | 6 +++++ .../switch_handlers/capability_handlers.lua | 26 +++++++++++++++++++ 37 files changed, 156 insertions(+) create mode 100644 drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml create mode 100644 drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml diff --git a/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml b/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml new file mode 100644 index 0000000000..978293198a --- /dev/null +++ b/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml @@ -0,0 +1,14 @@ +name: Stateless Color Temperature Step +status: proposed +commands: + stepColorTemperatureByPercent: + arguments: + - name: stepSize + schema: + type: integer + minimum: -100 + maximum: 100 + optional: false + name: stepColorTemperatureByPercent +id: statelessColorTemperatureStep +version: 1 diff --git a/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml b/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml new file mode 100644 index 0000000000..e4f1418277 --- /dev/null +++ b/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml @@ -0,0 +1,14 @@ +name: Stateless Switch Level Step +status: proposed +commands: + stepLevel: + arguments: + - name: stepSize + schema: + type: integer + minimum: -100 + maximum: 100 + optional: false + name: stepLevel +id: statelessSwitchLevelStep +version: 1 diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml index 58c8e0fca6..d46df21c5c 100755 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml index 4772e22f66..557fc6120a 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml index 4977423135..92caf967a1 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml index dbef511bbb..cfc20776be 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml index 2fabc23bd7..e50af5addd 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml @@ -6,12 +6,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml index dd48e4e81a..87206f7c25 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml @@ -7,8 +7,12 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml index 999e64a047..821e866b4e 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml @@ -6,8 +6,12 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level.yml index 572686ffdd..94d4176446 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level.yml @@ -6,12 +6,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml index 7c8b60ef56..7726032e2a 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml index 59600efd72..03fbd89c58 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml index b49b7f2254..6156f9caa2 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml index ee55a6a394..1b07b8c23c 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml index 805c97763e..a745fda2b8 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml index 5cd1666a5f..0e9e620c3e 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml index 4636359e92..aa62c77ba6 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml b/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml index f19e55ec9c..8760877585 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-button.yml index 9fc53f642b..6c215a946d 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-button.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml index 79d6556485..56414b519f 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml index 75b2bd488c..f4601c8148 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml index b49179036f..9223bfa042 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml @@ -7,12 +7,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml index 4d130281d7..571033c33b 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml @@ -6,12 +6,16 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" range: [1, 100] - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml b/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml index 03963ccbd2..0eb608818f 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml b/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml index bdea457c21..222efe927f 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml b/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml index f6c45ed1f7..83398b009c 100755 --- a/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-power.yml b/drivers/SmartThings/matter-switch/profiles/light-level-power.yml index 23625ada16..e6701c5030 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-power.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-power.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/light-level.yml b/drivers/SmartThings/matter-switch/profiles/light-level.yml index e266f497c9..e63693813b 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 config: values: - key: "level.value" diff --git a/drivers/SmartThings/matter-switch/profiles/plug-level-energy-powerConsumption.yml b/drivers/SmartThings/matter-switch/profiles/plug-level-energy-powerConsumption.yml index 86bd861bce..4ae29d2320 100644 --- a/drivers/SmartThings/matter-switch/profiles/plug-level-energy-powerConsumption.yml +++ b/drivers/SmartThings/matter-switch/profiles/plug-level-energy-powerConsumption.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: energyMeter version: 1 - id: powerConsumptionReport diff --git a/drivers/SmartThings/matter-switch/profiles/plug-level-power-energy-powerConsumption.yml b/drivers/SmartThings/matter-switch/profiles/plug-level-power-energy-powerConsumption.yml index 17e7d6b7a0..477fb8b769 100644 --- a/drivers/SmartThings/matter-switch/profiles/plug-level-power-energy-powerConsumption.yml +++ b/drivers/SmartThings/matter-switch/profiles/plug-level-power-energy-powerConsumption.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: powerMeter version: 1 - id: energyMeter diff --git a/drivers/SmartThings/matter-switch/profiles/plug-level-power.yml b/drivers/SmartThings/matter-switch/profiles/plug-level-power.yml index d175930a92..8bb1c56361 100644 --- a/drivers/SmartThings/matter-switch/profiles/plug-level-power.yml +++ b/drivers/SmartThings/matter-switch/profiles/plug-level-power.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: powerMeter version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/plug-level.yml b/drivers/SmartThings/matter-switch/profiles/plug-level.yml index 0d888b843f..90fa2ece47 100644 --- a/drivers/SmartThings/matter-switch/profiles/plug-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/plug-level.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml b/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml index f1f9e78438..e5754e8ad6 100644 --- a/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml @@ -6,8 +6,12 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml b/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml index 42e3ef6257..b5f844ccf7 100644 --- a/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml +++ b/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml @@ -6,8 +6,12 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: colorTemperature version: 1 + - id: statelessColorTemperatureStep + version: 1 config: values: - key: "colorTemperature.value" diff --git a/drivers/SmartThings/matter-switch/profiles/switch-level.yml b/drivers/SmartThings/matter-switch/profiles/switch-level.yml index 8f3b9f5e5c..827fcdd898 100644 --- a/drivers/SmartThings/matter-switch/profiles/switch-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/switch-level.yml @@ -6,6 +6,8 @@ components: version: 1 - id: switchLevel version: 1 + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index 6655f81e56..1068d833dd 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -283,6 +283,12 @@ local matter_driver_template = { [capabilities.level.ID] = { [capabilities.level.commands.setLevel.NAME] = capability_handlers.handle_set_level }, + [capabilities.statelessColorTemperatureStep.ID] = { + [capabilities.statelessColorTemperatureStep.commands.stepColorTemperatureByPercent.NAME] = capability_handlers.handle_step_color_temperature_by_percent, + }, + [capabilities.statelessSwitchLevelStep.ID] = { + [capabilities.statelessSwitchLevelStep.commands.stepLevel.NAME] = capability_handlers.handle_step_level, + }, [capabilities.switch.ID] = { [capabilities.switch.commands.off.NAME] = capability_handlers.handle_switch_off, [capabilities.switch.commands.on.NAME] = capability_handlers.handle_switch_on, diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index f96686b73a..355911a00b 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -47,6 +47,17 @@ function CapabilityHandlers.handle_switch_set_level(driver, device, cmd) end +-- [[ STATELESS SWITCH LEVEL STEP CAPABILITY COMMANDS ]] -- + +function CapabilityHandlers.handle_step_level(driver, device, cmd) + local step_size = math.floor((cmd.args and cmd.args.stepSize or 0)/100.0 * 254) + if step_size == 0 then return end + local endpoint_id = device:component_to_endpoint(cmd.component) + local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN + device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) +end + + -- [[ COLOR CONTROL CAPABILITY COMMANDS ]] -- function CapabilityHandlers.handle_set_color(driver, device, cmd) @@ -109,6 +120,21 @@ function CapabilityHandlers.handle_set_color_temperature(driver, device, cmd) end +-- [[ STATELESS COLOR TEMPERATURE STEP CAPABILITY COMMANDS ]] -- + +function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, device, cmd) + local step_percent_change = cmd.args and cmd.args.stepSize or 0 + if step_percent_change == 0 then return end + local endpoint_id = device:component_to_endpoint(cmd.component) + -- before the Matter 1.3 lua libs update (HUB FW 55), there was no ColorControl StepModeEnum type defined + local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum.UP or 1) + local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or 2200 -- default min mireds + local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or 6500 -- default max mireds + local color_change_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100)) + device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, color_change_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) +end + + -- [[ VALVE CAPABILITY COMMANDS ]] -- function CapabilityHandlers.handle_valve_open(driver, device, cmd) From 72c909e8dda2380b406ed2184c7df35c440c59ae Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Mon, 29 Dec 2025 11:34:26 -0600 Subject: [PATCH 02/15] add test cases, update rounding logic slightly --- .../statelessColorTemperatureStep.yml | 1 + .../capabilities/statelessSwitchLevelStep.yml | 1 + .../switch_handlers/capability_handlers.lua | 4 +- .../src/test/test_stateless_step.lua | 148 ++++++++++++++++++ 4 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua diff --git a/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml b/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml index 978293198a..dee4dd2266 100644 --- a/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml +++ b/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml @@ -1,5 +1,6 @@ name: Stateless Color Temperature Step status: proposed +attributes: {} commands: stepColorTemperatureByPercent: arguments: diff --git a/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml b/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml index e4f1418277..a751ab6cd5 100644 --- a/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml +++ b/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml @@ -1,5 +1,6 @@ name: Stateless Switch Level Step status: proposed +attributes: {} commands: stepLevel: arguments: diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index 355911a00b..ca725631f2 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -130,8 +130,8 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum.UP or 1) local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or 2200 -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or 6500 -- default max mireds - local color_change_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100)) - device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, color_change_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) + local step_size_in_mireds = (max_mireds - min_mireds) * st_utils.round((math.abs(step_percent_change)/100)) + device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end diff --git a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua new file mode 100644 index 0000000000..c39e259697 --- /dev/null +++ b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua @@ -0,0 +1,148 @@ +-- Copyright © 2022 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local test = require "integration_test" +local capabilities = require "st.capabilities" +local t_utils = require "integration_test.utils" +local clusters = require "st.matter.clusters" + +local mock_device_color_temp = test.mock_device.build_test_matter_device({ + profile = t_utils.get_profile_definition("light-level-colorTemperature.yml"), + manufacturer_info = { + vendor_id = 0x0000, + product_id = 0x0000, + }, + endpoints = { + { + endpoint_id = 1, + clusters = { + {cluster_id = clusters.OnOff.ID, cluster_type = "SERVER"}, + {cluster_id = clusters.ColorControl.ID, cluster_type = "BOTH", feature_map = 30}, + {cluster_id = clusters.LevelControl.ID, cluster_type = "SERVER"} + }, + device_types = { + {device_type_id = 0x0100, device_type_revision = 1}, -- On/Off Light + {device_type_id = 0x010C, device_type_revision = 1} -- Color Temperature Light + } + } + } +}) + +local cluster_subscribe_list = { + clusters.OnOff.attributes.OnOff, + clusters.LevelControl.attributes.CurrentLevel, + clusters.LevelControl.attributes.MaxLevel, + clusters.LevelControl.attributes.MinLevel, + clusters.ColorControl.attributes.CurrentHue, + clusters.ColorControl.attributes.CurrentSaturation, + clusters.ColorControl.attributes.CurrentX, + clusters.ColorControl.attributes.CurrentY, + clusters.ColorControl.attributes.ColorMode, + clusters.ColorControl.attributes.ColorTemperatureMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, +} + +local function set_color_mode(device, endpoint, color_mode) + test.socket.matter:__queue_receive({ + device.id, + clusters.ColorControl.attributes.ColorMode:build_test_report_data( + device, endpoint, color_mode) + }) + local read_req + if color_mode == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then + read_req = clusters.ColorControl.attributes.CurrentHue:read() + read_req:merge(clusters.ColorControl.attributes.CurrentSaturation:read()) + else -- color_mode = clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY + read_req = clusters.ColorControl.attributes.CurrentX:read() + read_req:merge(clusters.ColorControl.attributes.CurrentY:read()) + end + test.socket.matter:__expect_send({device.id, read_req}) +end + +local function test_init() + local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_color_temp) + for i, cluster in ipairs(cluster_subscribe_list) do + if i > 1 then + subscribe_request:merge(cluster:subscribe(mock_device_color_temp)) + end + end + test.socket.matter:__expect_send({mock_device_color_temp.id, subscribe_request}) + test.mock_device.add_test_device(mock_device_color_temp) + set_color_mode(mock_device_color_temp, 1, clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION) +end +test.set_test_init_function(test_init) + +test.register_message_test( + "Color Temperature Step Command Test", + { + { + channel = "capability", + direction = "receive", + message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessColorTemperatureStep.stepColorTemperatureByPercent(25)) } + }, + { + channel = "matter", + direction = "send", + message = mock_device_color_temp:generate_test_message("matter", clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 1075, 10, 2200, 6500, 0, 0)) + }, + { + channel = "capability", + direction = "receive", + message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessColorTemperatureStep.stepColorTemperatureByPercent(-50)) } + }, + { + channel = "matter", + direction = "send", + message = mock_device_color_temp:generate_test_message("matter", clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 2150, 10, 2200, 6500, 0, 0)) + }, + { + channel = "capability", + direction = "receive", + message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessColorTemperatureStep.stepColorTemperatureByPercent(100)) } + }, + { + channel = "matter", + direction = "send", + message = mock_device_color_temp:generate_test_message("matter", clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 4300, 10, 2200, 6500, 0, 0)) + } + } +) + +test.register_message_test( + "Level Step Command Test", + { + { + channel = "capability", + direction = "receive", + message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessSwitchLevelStep.stepLevel(25)) } + }, + { + channel = "matter", + direction = "send", + message = mock_device_color_temp:generate_test_message("matter", clusters.LevelControl.server.commands.StepLevel(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 1075, 10, 254)) + }, + { + channel = "capability", + direction = "receive", + message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessSwitchLevelStep.stepLevel(-50)) } + }, + { + channel = "matter", + direction = "send", + message = mock_device_color_temp:generate_test_message("matter", clusters.LevelControl.server.commands.StepLevel(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 2150, 10, 254)) + }, + { + channel = "capability", + direction = "receive", + message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessSwitchLevelStep.stepLevel(100)) } + }, + { + channel = "matter", + direction = "send", + message = mock_device_color_temp:generate_test_message("matter", clusters.LevelControl.server.commands.StepLevel(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 4300, 10, 254)) + } + } +) + +test.run_registered_tests() From bb13f5fc1a8cf33d9e25648177603310985840d5 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 30 Dec 2025 13:00:21 -0600 Subject: [PATCH 03/15] add extra pre-1.3 checks for nil --- .../matter-switch/src/switch_handlers/capability_handlers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index ca725631f2..86879e764e 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -127,7 +127,7 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev if step_percent_change == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) -- before the Matter 1.3 lua libs update (HUB FW 55), there was no ColorControl StepModeEnum type defined - local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum.UP or 1) + local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.UP or 1) local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or 2200 -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or 6500 -- default max mireds local step_size_in_mireds = (max_mireds - min_mireds) * st_utils.round((math.abs(step_percent_change)/100)) From 663625d1fd3d73ad6f4f03b996f4c1a8db32c3a8 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 30 Dec 2025 13:04:58 -0600 Subject: [PATCH 04/15] remove embedded cap defs, fix default mireds --- .../statelessColorTemperatureStep.yml | 15 --------------- .../capabilities/statelessSwitchLevelStep.yml | 15 --------------- .../src/switch_handlers/capability_handlers.lua | 4 ++-- 3 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml delete mode 100644 drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml diff --git a/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml b/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml deleted file mode 100644 index dee4dd2266..0000000000 --- a/drivers/SmartThings/matter-switch/capabilities/statelessColorTemperatureStep.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Stateless Color Temperature Step -status: proposed -attributes: {} -commands: - stepColorTemperatureByPercent: - arguments: - - name: stepSize - schema: - type: integer - minimum: -100 - maximum: 100 - optional: false - name: stepColorTemperatureByPercent -id: statelessColorTemperatureStep -version: 1 diff --git a/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml b/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml deleted file mode 100644 index a751ab6cd5..0000000000 --- a/drivers/SmartThings/matter-switch/capabilities/statelessSwitchLevelStep.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Stateless Switch Level Step -status: proposed -attributes: {} -commands: - stepLevel: - arguments: - - name: stepSize - schema: - type: integer - minimum: -100 - maximum: 100 - optional: false - name: stepLevel -id: statelessSwitchLevelStep -version: 1 diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index 86879e764e..e53eeefaf7 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -128,8 +128,8 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local endpoint_id = device:component_to_endpoint(cmd.component) -- before the Matter 1.3 lua libs update (HUB FW 55), there was no ColorControl StepModeEnum type defined local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.UP or 1) - local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or 2200 -- default min mireds - local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or 6500 -- default max mireds + local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds + local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds local step_size_in_mireds = (max_mireds - min_mireds) * st_utils.round((math.abs(step_percent_change)/100)) device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end From 1e93f242fe8df7a383217b8b68e2239890993d3b Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Wed, 7 Jan 2026 13:12:36 -0600 Subject: [PATCH 05/15] fix up logic --- .../matter-switch/src/switch_handlers/capability_handlers.lua | 4 +++- drivers/SmartThings/matter-switch/src/switch_utils/fields.lua | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index e53eeefaf7..f583e3cb67 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -54,6 +54,7 @@ function CapabilityHandlers.handle_step_level(driver, device, cmd) if step_size == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN + print("Stepping level by ", math.abs(step_size)) device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end @@ -130,7 +131,8 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local step_mode = step_percent_change > 0 and (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.DOWN or 3) or (clusters.ColorControl.types.StepModeEnum and clusters.ColorControl.types.StepModeEnum.UP or 1) local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds - local step_size_in_mireds = (max_mireds - min_mireds) * st_utils.round((math.abs(step_percent_change)/100)) + local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) + print("Stepping color temperature by ", step_size_in_mireds, " mireds", " with max mireds of ", max_mireds, " and min mireds of ", min_mireds) device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index f670b154c8..91732f4f47 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -13,8 +13,8 @@ SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT = 1000000 -- These values are a "sanity check" to check that values we are getting are reasonable local COLOR_TEMPERATURE_KELVIN_MAX = 15000 local COLOR_TEMPERATURE_KELVIN_MIN = 1000 -SwitchFields.COLOR_TEMPERATURE_MIRED_MAX = SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN -SwitchFields.COLOR_TEMPERATURE_MIRED_MIN = SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MAX +SwitchFields.COLOR_TEMPERATURE_MIRED_MAX = math.floor(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN) +SwitchFields.COLOR_TEMPERATURE_MIRED_MIN = math.floor(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MAX) SwitchFields.SWITCH_LEVEL_LIGHTING_MIN = 1 SwitchFields.CURRENT_HUESAT_ATTR_MIN = 0 From cdd29df0fc142ccb359b6920f31e7c6fd0942bb5 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 30 Dec 2025 16:28:17 -0600 Subject: [PATCH 06/15] fix tests --- .../src/test/test_stateless_step.lua | 87 +++++++++++-------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua index c39e259697..d4b99f48a0 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua @@ -2,7 +2,6 @@ -- Licensed under the Apache License, Version 2.0 local test = require "integration_test" -local capabilities = require "st.capabilities" local t_utils = require "integration_test.utils" local clusters = require "st.matter.clusters" @@ -33,33 +32,11 @@ local cluster_subscribe_list = { clusters.LevelControl.attributes.CurrentLevel, clusters.LevelControl.attributes.MaxLevel, clusters.LevelControl.attributes.MinLevel, - clusters.ColorControl.attributes.CurrentHue, - clusters.ColorControl.attributes.CurrentSaturation, - clusters.ColorControl.attributes.CurrentX, - clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode, clusters.ColorControl.attributes.ColorTemperatureMireds, clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, } -local function set_color_mode(device, endpoint, color_mode) - test.socket.matter:__queue_receive({ - device.id, - clusters.ColorControl.attributes.ColorMode:build_test_report_data( - device, endpoint, color_mode) - }) - local read_req - if color_mode == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then - read_req = clusters.ColorControl.attributes.CurrentHue:read() - read_req:merge(clusters.ColorControl.attributes.CurrentSaturation:read()) - else -- color_mode = clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY - read_req = clusters.ColorControl.attributes.CurrentX:read() - read_req:merge(clusters.ColorControl.attributes.CurrentY:read()) - end - test.socket.matter:__expect_send({device.id, read_req}) -end - local function test_init() local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_color_temp) for i, cluster in ipairs(cluster_subscribe_list) do @@ -69,78 +46,116 @@ local function test_init() end test.socket.matter:__expect_send({mock_device_color_temp.id, subscribe_request}) test.mock_device.add_test_device(mock_device_color_temp) - set_color_mode(mock_device_color_temp, 1, clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION) end test.set_test_init_function(test_init) +local fields = require "switch_utils.fields" + test.register_message_test( "Color Temperature Step Command Test", { { channel = "capability", direction = "receive", - message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessColorTemperatureStep.stepColorTemperatureByPercent(25)) } + message = { + mock_device_color_temp.id, + { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { 20 } } + } }, { channel = "matter", direction = "send", - message = mock_device_color_temp:generate_test_message("matter", clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 1075, 10, 2200, 6500, 0, 0)) + message = { + mock_device_color_temp.id, + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 187, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + }, }, { channel = "capability", direction = "receive", - message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessColorTemperatureStep.stepColorTemperatureByPercent(-50)) } + message = { + mock_device_color_temp.id, + { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { 90 } } + } }, { channel = "matter", direction = "send", - message = mock_device_color_temp:generate_test_message("matter", clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 2150, 10, 2200, 6500, 0, 0)) + message = { + mock_device_color_temp.id, + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 841, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + }, }, { channel = "capability", direction = "receive", - message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessColorTemperatureStep.stepColorTemperatureByPercent(100)) } + message = { + mock_device_color_temp.id, + { capability = "statelessColorTemperatureStep", component = "main", command = "stepColorTemperatureByPercent", args = { -50 } } + } }, { channel = "matter", direction = "send", - message = mock_device_color_temp:generate_test_message("matter", clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 4300, 10, 2200, 6500, 0, 0)) + message = { + mock_device_color_temp.id, + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 467, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + }, } } ) + test.register_message_test( "Level Step Command Test", { { channel = "capability", direction = "receive", - message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessSwitchLevelStep.stepLevel(25)) } + message = { + mock_device_color_temp.id, + { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 25 } } + } }, { channel = "matter", direction = "send", - message = mock_device_color_temp:generate_test_message("matter", clusters.LevelControl.server.commands.StepLevel(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 1075, 10, 254)) + message = { + mock_device_color_temp.id, + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 63, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + }, }, { channel = "capability", direction = "receive", - message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessSwitchLevelStep.stepLevel(-50)) } + message = { + mock_device_color_temp.id, + { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { -50 } } + } }, { channel = "matter", direction = "send", - message = mock_device_color_temp:generate_test_message("matter", clusters.LevelControl.server.commands.StepLevel(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 2150, 10, 254)) + message = { + mock_device_color_temp.id, + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 127, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + }, }, { channel = "capability", direction = "receive", - message = { mock_device_color_temp:generate_test_message("main", capabilities.statelessSwitchLevelStep.stepLevel(100)) } + message = { + mock_device_color_temp.id, + { capability = "statelessSwitchLevelStep", component = "main", command = "stepLevel", args = { 100 } } + } }, { channel = "matter", direction = "send", - message = mock_device_color_temp:generate_test_message("matter", clusters.LevelControl.server.commands.StepLevel(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 4300, 10, 254)) + message = { + mock_device_color_temp.id, + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 254, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + }, } } ) From 840ad3bc8389aa76c9d5376cc1b9cd59a727cf44 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Wed, 7 Jan 2026 16:13:17 -0600 Subject: [PATCH 07/15] remove debug print statements --- .../matter-switch/src/switch_handlers/capability_handlers.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index f583e3cb67..c82bc5bfb6 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -54,7 +54,6 @@ function CapabilityHandlers.handle_step_level(driver, device, cmd) if step_size == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN - print("Stepping level by ", math.abs(step_size)) device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end @@ -132,7 +131,6 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) - print("Stepping color temperature by ", step_size_in_mireds, " mireds", " with max mireds of ", max_mireds, " and min mireds of ", min_mireds) device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end From dcb52f01551f960a8d130b97c379b742ba033c95 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Thu, 8 Jan 2026 15:16:43 -0600 Subject: [PATCH 08/15] move some cap defs after config, fix year, change floor to round --- .../profiles/light-color-level-1800K-6500K.yml | 8 ++++---- .../profiles/light-color-level-2000K-7000K.yml | 8 ++++---- .../profiles/light-color-level-2200K-6500K.yml | 8 ++++---- .../profiles/light-color-level-2700K-6500K.yml | 8 ++++---- .../matter-switch/profiles/light-color-level-fan.yml | 8 ++++---- .../light-color-level-illuminance-motion-1000K-15000K.yml | 4 ++-- .../profiles/light-color-level-illuminance-motion.yml | 4 ++-- .../matter-switch/profiles/light-color-level.yml | 8 ++++---- .../matter-switch/profiles/light-level-2-button.yml | 4 ++-- .../matter-switch/profiles/light-level-3-button.yml | 4 ++-- .../matter-switch/profiles/light-level-4-button.yml | 4 ++-- .../matter-switch/profiles/light-level-5-button.yml | 4 ++-- .../matter-switch/profiles/light-level-6-button.yml | 4 ++-- .../matter-switch/profiles/light-level-7-button.yml | 4 ++-- .../matter-switch/profiles/light-level-8-button.yml | 4 ++-- .../profiles/light-level-ColorTemperature-1500-9000k.yml | 8 ++++---- .../matter-switch/profiles/light-level-button.yml | 4 ++-- .../profiles/light-level-colorTemperature-2200K-6500K.yml | 8 ++++---- .../profiles/light-level-colorTemperature-2700K-6500K.yml | 8 ++++---- .../profiles/light-level-colorTemperature-2710k-6500k.yml | 8 ++++---- .../profiles/light-level-colorTemperature.yml | 8 ++++---- .../profiles/light-level-energy-powerConsumption.yml | 4 ++-- .../matter-switch/profiles/light-level-motion.yml | 4 ++-- .../light-level-power-energy-powerConsumption.yml | 4 ++-- .../matter-switch/profiles/light-level-power.yml | 4 ++-- .../SmartThings/matter-switch/profiles/light-level.yml | 4 ++-- .../matter-switch/profiles/switch-color-level.yml | 4 ++-- .../profiles/switch-level-colorTemperature.yml | 4 ++-- .../SmartThings/matter-switch/src/switch_utils/fields.lua | 6 ++++-- .../matter-switch/src/test/test_stateless_step.lua | 2 +- 30 files changed, 83 insertions(+), 81 deletions(-) diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml index d46df21c5c..a0f5196b12 100755 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: colorTemperature version: 1 - - id: statelessColorTemperatureStep - version: 1 config: values: - key: "colorTemperature.value" range: [ 1800, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml index 557fc6120a..7424d241e2 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-2000K-7000K.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2000, 7000 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml index 92caf967a1..a098cdd06f 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-2200K-6500K.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml index cfc20776be..1b469ae8e8 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-2700K-6500K.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2700, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml index e50af5addd..2f91bcb04e 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-fan.yml @@ -6,20 +6,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2700, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: fanMode diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml index 87206f7c25..fb7a4fca94 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion-1000K-15000K.yml @@ -11,12 +11,12 @@ components: version: 1 - id: colorTemperature version: 1 - - id: statelessColorTemperatureStep - version: 1 config: values: - key: "colorTemperature.value" range: [ 1000, 15000 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: motionSensor diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml index 821e866b4e..50f68f60fc 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-illuminance-motion.yml @@ -10,12 +10,12 @@ components: version: 1 - id: colorTemperature version: 1 - - id: statelessColorTemperatureStep - version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: motionSensor diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level.yml index 94d4176446..2f0f673bee 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-color-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level.yml @@ -6,20 +6,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml index 7726032e2a..ed76480116 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml index 03fbd89c58..9e3106d9de 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-3-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml index 6156f9caa2..43bcec5955 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-4-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml index 1b07b8c23c..2b98b9784d 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-5-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml index a745fda2b8..230b2ea341 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-6-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml index 0e9e620c3e..13b3ee9443 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-7-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml index aa62c77ba6..a2dc732257 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-8-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml b/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml index 8760877585..effe5f93b6 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-ColorTemperature-1500-9000k.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 1500, 9000 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-button.yml index 6c215a946d..8334e32feb 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-button.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml index 56414b519f..2541fddc09 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2200K-6500K.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml index f4601c8148..7c845e8238 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2700K-6500K.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2700, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml index 9223bfa042..5ccc67ab4c 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature-2710k-6500k.yml @@ -7,20 +7,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2710, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml index 571033c33b..5681a97e9e 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-colorTemperature.yml @@ -6,20 +6,20 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] - - id: colorTemperature + - id: statelessSwitchLevelStep version: 1 - - id: statelessColorTemperatureStep + - id: colorTemperature version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml b/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml index 0eb608818f..b2ddcc0a60 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-energy-powerConsumption.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: energyMeter version: 1 - id: powerConsumptionReport diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml b/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml index 222efe927f..54ce30abdf 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-motion.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: motionSensor version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml b/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml index 83398b009c..31c6d44ee1 100755 --- a/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-power-energy-powerConsumption.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: powerMeter version: 1 - id: energyMeter diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-power.yml b/drivers/SmartThings/matter-switch/profiles/light-level-power.yml index e6701c5030..86d61c0345 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-power.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-power.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: powerMeter version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/light-level.yml b/drivers/SmartThings/matter-switch/profiles/light-level.yml index e63693813b..3b286f8262 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level.yml @@ -6,12 +6,12 @@ components: version: 1 - id: switchLevel version: 1 - - id: statelessSwitchLevelStep - version: 1 config: values: - key: "level.value" range: [1, 100] + - id: statelessSwitchLevelStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml b/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml index e5754e8ad6..c684f632c9 100644 --- a/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml +++ b/drivers/SmartThings/matter-switch/profiles/switch-color-level.yml @@ -10,12 +10,12 @@ components: version: 1 - id: colorTemperature version: 1 - - id: statelessColorTemperatureStep - version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: colorControl version: 1 - id: firmwareUpdate diff --git a/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml b/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml index b5f844ccf7..9cba419d02 100644 --- a/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml +++ b/drivers/SmartThings/matter-switch/profiles/switch-level-colorTemperature.yml @@ -10,12 +10,12 @@ components: version: 1 - id: colorTemperature version: 1 - - id: statelessColorTemperatureStep - version: 1 config: values: - key: "colorTemperature.value" range: [ 2200, 6500 ] + - id: statelessColorTemperatureStep + version: 1 - id: firmwareUpdate version: 1 - id: refresh diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index 91732f4f47..969bbe2a84 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -1,6 +1,8 @@ -- Copyright © 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 +local st_utils = require "st.utils" + local SwitchFields = {} SwitchFields.MOST_RECENT_TEMP = "mostRecentTemp" @@ -13,8 +15,8 @@ SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT = 1000000 -- These values are a "sanity check" to check that values we are getting are reasonable local COLOR_TEMPERATURE_KELVIN_MAX = 15000 local COLOR_TEMPERATURE_KELVIN_MIN = 1000 -SwitchFields.COLOR_TEMPERATURE_MIRED_MAX = math.floor(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN) -SwitchFields.COLOR_TEMPERATURE_MIRED_MIN = math.floor(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MAX) +SwitchFields.COLOR_TEMPERATURE_MIRED_MAX = st_utils.round(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN) +SwitchFields.COLOR_TEMPERATURE_MIRED_MIN = st_utils.round(SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MAX) SwitchFields.SWITCH_LEVEL_LIGHTING_MIN = 1 SwitchFields.CURRENT_HUESAT_ATTR_MIN = 0 diff --git a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua index d4b99f48a0..a1b3d69d6b 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua @@ -1,4 +1,4 @@ --- Copyright © 2022 SmartThings, Inc. +-- Copyright 2026 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 local test = require "integration_test" From 0f0cb7d87f807ccd619368a9400358f11d381c32 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Thu, 8 Jan 2026 15:55:51 -0600 Subject: [PATCH 09/15] update handler to round from floor --- .../matter-switch/src/switch_handlers/capability_handlers.lua | 4 +++- .../matter-switch/src/test/test_stateless_step.lua | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index c82bc5bfb6..4e6628d560 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -50,10 +50,11 @@ end -- [[ STATELESS SWITCH LEVEL STEP CAPABILITY COMMANDS ]] -- function CapabilityHandlers.handle_step_level(driver, device, cmd) - local step_size = math.floor((cmd.args and cmd.args.stepSize or 0)/100.0 * 254) + local step_size = st_utils.round((cmd.args and cmd.args.stepSize or 0)/100.0 * 254) if step_size == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN + print("level:", step_size) device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end @@ -131,6 +132,7 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) + print("percent:", step_size_in_mireds) device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end diff --git a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua index a1b3d69d6b..69d33ed9dd 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua @@ -83,7 +83,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 841, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 840, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) }, }, { @@ -122,7 +122,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 63, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 64, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) }, }, { From 19dd39911b3a06c2569a44dd34f33288824d7cab Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Fri, 9 Jan 2026 13:21:32 -0600 Subject: [PATCH 10/15] remove print statements from testing --- .../matter-switch/src/switch_handlers/capability_handlers.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index 4e6628d560..dbce2dc841 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -54,7 +54,6 @@ function CapabilityHandlers.handle_step_level(driver, device, cmd) if step_size == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN - print("level:", step_size) device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end @@ -132,7 +131,6 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) - print("percent:", step_size_in_mireds) device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) end From 9b7146c700be76d56f1232794e8569eaee673203 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Tue, 20 Jan 2026 13:57:44 -0600 Subject: [PATCH 11/15] fix profile indentation --- .../SmartThings/matter-switch/profiles/light-level-2-button.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml b/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml index ed76480116..6a4fbcc9d0 100644 --- a/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml +++ b/drivers/SmartThings/matter-switch/profiles/light-level-2-button.yml @@ -11,7 +11,7 @@ components: - key: "level.value" range: [1, 100] - id: statelessSwitchLevelStep - version: 1 + version: 1 - id: firmwareUpdate version: 1 - id: refresh From 94a1e0e445da3792980ce44af8c3f6e22fe17814 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Mon, 29 Dec 2025 11:46:53 -0600 Subject: [PATCH 12/15] include ikea subdriver support for knob capability --- .../matter-switch/profiles/ikea-scroll.yml | 6 + .../src/sub_drivers/ikea_scroll/init.lua | 10 + .../scroll_handlers/event_handlers.lua | 39 ++ .../scroll_utils/device_configuration.lua | 10 +- .../ikea_scroll/scroll_utils/fields.lua | 30 +- .../ikea_scroll/scroll_utils/utils.lua | 18 +- .../matter-switch/src/switch_utils/utils.lua | 20 +- .../src/test/test_ikea_scroll.lua | 407 +++++++++++++++++- 8 files changed, 518 insertions(+), 22 deletions(-) create mode 100644 drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua diff --git a/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml b/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml index 4dc77d026d..cce4b68d2d 100644 --- a/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml +++ b/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml @@ -5,6 +5,8 @@ components: capabilities: - id: button version: 1 + - id: knob + version: 1 - id: battery version: 1 - id: firmwareUpdate @@ -18,6 +20,8 @@ components: capabilities: - id: button version: 1 + - id: knob + version: 1 categories: - name: RemoteController - id: group3 @@ -25,5 +29,7 @@ components: capabilities: - id: button version: 1 + - id: knob + version: 1 categories: - name: RemoteController diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua index 6e21a56ac0..83e324a19f 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua @@ -1,9 +1,11 @@ -- Copyright © 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 +local clusters = require "st.matter.clusters" local switch_utils = require "switch_utils.utils" local scroll_utils = require "sub_drivers.ikea_scroll.scroll_utils.utils" local scroll_cfg = require "sub_drivers.ikea_scroll.scroll_utils.device_configuration" +local event_handlers = require "sub_drivers.ikea_scroll.scroll_handlers.event_handlers" local IkeaScrollLifecycleHandlers = {} @@ -44,6 +46,14 @@ local ikea_scroll_handler = { infoChanged = IkeaScrollLifecycleHandlers.info_changed, init = IkeaScrollLifecycleHandlers.device_init, }, + matter_handlers = { + event = { + [clusters.Switch.ID] = { + [clusters.Switch.events.InitialPress.ID] = event_handlers.initial_press_handler, + [clusters.Switch.events.MultiPressOngoing.ID] = event_handlers.multi_press_ongoing_handler, + } + } + }, can_handle = require("sub_drivers.ikea_scroll.can_handle") } diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua new file mode 100644 index 0000000000..ae742c2216 --- /dev/null +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua @@ -0,0 +1,39 @@ +-- Copyright © 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local st_utils = require "st.utils" +local capabilities = require "st.capabilities" +local switch_utils = require "switch_utils.utils" +local generic_event_handlers = require "switch_handlers.event_handlers" +local scroll_fields = require "sub_drivers.ikea_scroll.scroll_utils.fields" + +local IkeaScrollEventHandlers = {} + +local function rotate_amount_event_helper(device, endpoint_id, num_presses_to_handle) + -- to cut down on checks, we can assume that if the endpoint is not in ENDPOINTS_UP_SCROLL, it is in ENDPOINTS_DOWN_SCROLL + local scroll_direction = switch_utils.tbl_contains(scroll_fields.ENDPOINTS_UP_SCROLL, endpoint_id) and 1 or -1 + local scroll_amount = st_utils.clamp_value(scroll_direction * scroll_fields.PER_SCROLL_EVENT_ROTATION * num_presses_to_handle, -100, 100) + device:emit_event_for_endpoint(endpoint_id, capabilities.knob.rotateAmount(scroll_amount, {state_change = true})) +end + +function IkeaScrollEventHandlers.initial_press_handler(driver, device, ib, response) + -- use the generic handler logic for the push endpoints. Else, use custom logic. + if switch_utils.tbl_contains(scroll_fields.ENDPOINTS_PUSH, ib.endpoint_id) then + generic_event_handlers.initial_press_handler(driver, device, ib, response) + else + device:set_field(scroll_fields.LATEST_NUMBER_OF_PRESSES_COUNTED, 1) + rotate_amount_event_helper(device, ib.endpoint_id, 1) + end +end + +-- Used by ENDPOINTS_UP_SCROLL and ENDPOINTS_DOWN_SCROLL, not ENDPOINTS_PUSH +function IkeaScrollEventHandlers.multi_press_ongoing_handler(driver, device, ib, response) + local cur_num_presses_counted = ib.data and ib.data.elements and ib.data.elements.current_number_of_presses_counted.value or 0 + local num_presses_to_handle = cur_num_presses_counted - (device:get_field(scroll_fields.LATEST_NUMBER_OF_PRESSES_COUNTED) or 0) + if num_presses_to_handle > 0 then + device:set_field(scroll_fields.LATEST_NUMBER_OF_PRESSES_COUNTED, cur_num_presses_counted) + rotate_amount_event_helper(device, ib.endpoint_id, num_presses_to_handle) + end +end + +return IkeaScrollEventHandlers diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/device_configuration.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/device_configuration.lua index cd2cee49ce..1d88a2f3a3 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/device_configuration.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/device_configuration.lua @@ -11,18 +11,20 @@ local IkeaScrollConfiguration = {} function IkeaScrollConfiguration.build_button_component_map(device) local component_map = { - main = scroll_fields.ENDPOINTS_PRESS[1], - group2 = scroll_fields.ENDPOINTS_PRESS[2], - group3 = scroll_fields.ENDPOINTS_PRESS[3], + main = {scroll_fields.ENDPOINTS_PUSH[1], scroll_fields.ENDPOINTS_UP_SCROLL[1], scroll_fields.ENDPOINTS_DOWN_SCROLL[1]}, + group2 = {scroll_fields.ENDPOINTS_PUSH[2], scroll_fields.ENDPOINTS_UP_SCROLL[2], scroll_fields.ENDPOINTS_DOWN_SCROLL[2]}, + group3 = {scroll_fields.ENDPOINTS_PUSH[3], scroll_fields.ENDPOINTS_UP_SCROLL[3], scroll_fields.ENDPOINTS_DOWN_SCROLL[3]}, } device:set_field(switch_fields.COMPONENT_TO_ENDPOINT_MAP, component_map, {persist = true}) end function IkeaScrollConfiguration.configure_buttons(device) - for _, ep in ipairs(scroll_fields.ENDPOINTS_PRESS) do + for _, ep in ipairs(scroll_fields.ENDPOINTS_PUSH) do device:send(clusters.Switch.attributes.MultiPressMax:read(device, ep)) switch_utils.set_field_for_endpoint(device, switch_fields.SUPPORTS_MULTI_PRESS, ep, true, {persist = true}) device:emit_event_for_endpoint(ep, capabilities.button.button.pushed({state_change = false})) + -- though unrelated to the knob capability, the push endpoints all map to components including a knob + device:emit_event_for_endpoint(ep, capabilities.knob.supportedAttributes({"rotateAmount"}, {visibility = {displayed = false}})) end end diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua index fff0a1cce4..f0aeb77c5a 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua @@ -1,6 +1,7 @@ -- Copyright © 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 +local st_utils = require "st.utils" local clusters = require "st.matter.clusters" local IkeaScrollFields = {} @@ -8,14 +9,37 @@ local IkeaScrollFields = {} -- PowerSource supported on Root Node IkeaScrollFields.ENDPOINT_POWER_SOURCE = 0 --- Switch Endpoints used for basic press functionality -IkeaScrollFields.ENDPOINTS_PRESS = {3, 6, 9} +-- Generic Switch Endpoints used for basic push functionality +IkeaScrollFields.ENDPOINTS_PUSH = {3, 6, 9} --- Required Events for the ENDPOINTS_PRESS. +-- Generic Switch Endpoints used for Up Scroll functionality +IkeaScrollFields.ENDPOINTS_UP_SCROLL = {1, 4, 7} + +-- Generic Switch Endpoints used for Down Scroll functionality +IkeaScrollFields.ENDPOINTS_DOWN_SCROLL = {2, 5, 8} + +-- Maximum number of presses at a time +IkeaScrollFields.MAX_SCROLL_PRESSES = 18 + +-- Amount to rotate per scroll event +IkeaScrollFields.PER_SCROLL_EVENT_ROTATION = st_utils.round(1 / IkeaScrollFields.MAX_SCROLL_PRESSES * 100) + +-- Field to track the latest number of presses counted during a single scroll event sequence +IkeaScrollFields.LATEST_NUMBER_OF_PRESSES_COUNTED = "__latest_number_of_presses_counted" + +-- Required Events for the ENDPOINTS_PUSH. IkeaScrollFields.switch_press_subscribed_events = { clusters.Switch.events.InitialPress.ID, clusters.Switch.events.MultiPressComplete.ID, clusters.Switch.events.LongPress.ID, } +-- Required Events for the ENDPOINTS_UP_SCROLL and ENDPOINTS_DOWN_SCROLL. Adds a +-- MultiPressOngoing subscription to handle step functionality in real-time, and +-- removes MultiPressComplete subscription due to the real-time handling +IkeaScrollFields.switch_scroll_subscribed_events = { + clusters.Switch.events.InitialPress.ID, + clusters.Switch.events.MultiPressOngoing.ID, +} + return IkeaScrollFields diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/utils.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/utils.lua index 67ba2acba5..9a9f95228b 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/utils.lua @@ -7,12 +7,24 @@ local scroll_fields = require "sub_drivers.ikea_scroll.scroll_utils.fields" local IkeaScrollUtils = {} --- override subscribe function to prevent subscribing to additional events from the main driver +-- override subscribe function in the main driver function IkeaScrollUtils.subscribe(device) local subscribe_request = im.InteractionRequest(im.InteractionRequest.RequestType.SUBSCRIBE, {}) - for _, ep_press in ipairs(scroll_fields.ENDPOINTS_PRESS) do + for _, ep_push in ipairs(scroll_fields.ENDPOINTS_PUSH) do for _, switch_event in ipairs(scroll_fields.switch_press_subscribed_events) do - local ib = im.InteractionInfoBlock(ep_press, clusters.Switch.ID, nil, switch_event) + local ib = im.InteractionInfoBlock(ep_push, clusters.Switch.ID, nil, switch_event) + subscribe_request:with_info_block(ib) + end + end + for _, ep_up in ipairs(scroll_fields.ENDPOINTS_UP_SCROLL) do + for _, switch_event in ipairs(scroll_fields.switch_scroll_subscribed_events) do + local ib = im.InteractionInfoBlock(ep_up, clusters.Switch.ID, nil, switch_event) + subscribe_request:with_info_block(ib) + end + end + for _, ep_down in ipairs(scroll_fields.ENDPOINTS_DOWN_SCROLL) do + for _, switch_event in ipairs(scroll_fields.switch_scroll_subscribed_events) do + local ib = im.InteractionInfoBlock(ep_down, clusters.Switch.ID, nil, switch_event) subscribe_request:with_info_block(ib) end end diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua index b258688234..91eb4a8cc2 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua @@ -201,7 +201,8 @@ end --- An extension of the library function endpoint_to_component, used to support a mapping scheme --- that optionally includes cluster and attribute ids so that multiple components can be mapped ---- to a single endpoint. +--- to a single endpoint. This extension also handles the case that multiple endpoints map to the +--- same component --- --- @param device any a Matter device object --- @param ep_info number|table either an ep_id or a table { endpoint_id, optional(cluster_id), optional(attribute_id) } @@ -214,10 +215,19 @@ function utils.endpoint_to_component(device, ep_info) for component, map_info in pairs(device:get_field(fields.COMPONENT_TO_ENDPOINT_MAP) or {}) do if type(map_info) == "number" and map_info == ep_info.endpoint_id then return component - elseif type(map_info) == "table" and map_info.endpoint_id == ep_info.endpoint_id - and (not map_info.cluster_id or (map_info.cluster_id == ep_info.cluster_id - and (not map_info.attribute_ids or utils.tbl_contains(map_info.attribute_ids, ep_info.attribute_id)))) then - return component + elseif type(map_info) == "table" then + if type(map_info.endpoint_id) == "number" then + map_info = {map_info} + end + for _, ep_map_info in ipairs(map_info) do + if type(ep_map_info) == "number" and ep_map_info == ep_info.endpoint_id then + return component + elseif type(ep_map_info) == "table" and ep_map_info.endpoint_id == ep_info.endpoint_id + and (not ep_map_info.cluster_id or (ep_map_info.cluster_id == ep_info.cluster_id + and (not ep_map_info.attribute_ids or utils.tbl_contains(ep_map_info.attribute_ids, ep_info.attribute_id)))) then + return component + end + end end end return "main" diff --git a/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua b/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua index c560386e37..126b30d7bc 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua @@ -122,18 +122,28 @@ local mock_ikea_scroll = test.mock_device.build_test_matter_device({ } }) -local ENDPOINTS_PRESS = { 3, 6, 9 } +local ENDPOINTS_PUSH = { 3, 6, 9 } +local ENDPOINTS_SCROLL = {1, 2, 4, 5, 7, 8} -- the ikea scroll subdriver has overriden subscribe behavior local function ikea_scroll_subscribe() - local CLUSTER_SUBSCRIBE_LIST ={ - clusters.Switch.events.InitialPress, + local CLUSTER_SUBSCRIBE_LIST_PUSH ={ + clusters.Switch.events.InitialPress, clusters.Switch.server.events.LongPress, clusters.Switch.server.events.MultiPressComplete, } - local subscribe_request = CLUSTER_SUBSCRIBE_LIST[1]:subscribe(mock_ikea_scroll, ENDPOINTS_PRESS[1]) - for _, ep_press in ipairs(ENDPOINTS_PRESS) do - for _, event in ipairs(CLUSTER_SUBSCRIBE_LIST) do + local CLUSTER_SUBSCRIBE_LIST_SCROLL = { + clusters.Switch.events.InitialPress, + clusters.Switch.server.events.MultiPressOngoing, + } + local subscribe_request = CLUSTER_SUBSCRIBE_LIST_PUSH[1]:subscribe(mock_ikea_scroll, ENDPOINTS_PUSH[1]) + for _, ep_press in ipairs(ENDPOINTS_PUSH) do + for _, event in ipairs(CLUSTER_SUBSCRIBE_LIST_PUSH) do + subscribe_request:merge(event:subscribe(mock_ikea_scroll, ep_press)) + end + end + for _, ep_press in ipairs(ENDPOINTS_SCROLL) do + for _, event in ipairs(CLUSTER_SUBSCRIBE_LIST_SCROLL) do subscribe_request:merge(event:subscribe(mock_ikea_scroll, ep_press)) end end @@ -145,10 +155,13 @@ local function expect_configure_buttons() local button_attr = capabilities.button.button test.socket.matter:__expect_send({mock_ikea_scroll.id, clusters.Switch.attributes.MultiPressMax:read(mock_ikea_scroll, 3)}) test.socket.capability:__expect_send(mock_ikea_scroll:generate_test_message("main", button_attr.pushed({state_change = false}))) + test.socket.capability:__expect_send(mock_ikea_scroll:generate_test_message("main", capabilities.knob.supportedAttributes({"rotateAmount"}, {visibility = {displayed = false}}))) test.socket.matter:__expect_send({mock_ikea_scroll.id, clusters.Switch.attributes.MultiPressMax:read(mock_ikea_scroll, 6)}) test.socket.capability:__expect_send(mock_ikea_scroll:generate_test_message("group2", button_attr.pushed({state_change = false}))) + test.socket.capability:__expect_send(mock_ikea_scroll:generate_test_message("group2", capabilities.knob.supportedAttributes({"rotateAmount"}, {visibility = {displayed = false}}))) test.socket.matter:__expect_send({mock_ikea_scroll.id, clusters.Switch.attributes.MultiPressMax:read(mock_ikea_scroll, 9)}) test.socket.capability:__expect_send(mock_ikea_scroll:generate_test_message("group3", button_attr.pushed({state_change = false}))) + test.socket.capability:__expect_send(mock_ikea_scroll:generate_test_message("group3", capabilities.knob.supportedAttributes({"rotateAmount"}, {visibility = {displayed = false}}))) end local function test_init() @@ -221,4 +234,384 @@ test.register_message_test( } ) -test.run_registered_tests() \ No newline at end of file +test.register_message_test( + "Ikea Scroll Positive rotateAmount events on main are emitted correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {current_number_of_presses_counted = 5, new_position = 5} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(18, {state_change = true})) + } + } +) + +test.register_message_test( + "Ikea Scroll Negative rotateAmount events on main are emitted correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(-6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(-6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 5, new_position = 5} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(-18, {state_change = true})) + } + } +) + +test.register_message_test( + "Ikea Scroll Positive rotateAmount events on group2 are emitted correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[3], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.knob.rotateAmount(6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[3], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.knob.rotateAmount(6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[3], {current_number_of_presses_counted = 5, new_position = 5} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.knob.rotateAmount(18, {state_change = true})) + } + } +) + +test.register_message_test( + "Ikea Scroll Negative rotateAmount events on group2 are emitted correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[4], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.knob.rotateAmount(-6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[4], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.knob.rotateAmount(-6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[4], {current_number_of_presses_counted = 5, new_position = 5} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.knob.rotateAmount(-18, {state_change = true})) + } + } +) + +test.register_message_test( + "Ikea Scroll Positive rotateAmount events on group3 are emitted correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[5], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group3", + capabilities.knob.rotateAmount(6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[5], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group3", + capabilities.knob.rotateAmount(6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[5], {current_number_of_presses_counted = 5, new_position = 5} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group3", + capabilities.knob.rotateAmount(18, {state_change = true})) + } + } +) + +test.register_message_test( + "Ikea Scroll Negative rotateAmount events on group3 are emitted correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[6], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group3", + capabilities.knob.rotateAmount(-6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[6], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group3", + capabilities.knob.rotateAmount(-6, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[6], {current_number_of_presses_counted = 5, new_position = 5} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group3", + capabilities.knob.rotateAmount(-18, {state_change = true})) + } + } +) + +test.register_message_test( + "Ikea Scroll Long Press Push events on main are handled correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_PUSH[1], {new_position = 1} + ) + }, + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.LongPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_PUSH[1], {new_position = 1} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.button.button.held({state_change = true})) + }, + } +) + +test.register_message_test( + "Ikea Scroll MultiPressComplete Push events on group2 are handled correctly", { + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.InitialPress:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_PUSH[2], {new_position = 1} + ) + }, + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressComplete:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_PUSH[2], {total_number_of_presses_counted = 1, previous_position = 0} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("group2", + capabilities.button.button.pushed({state_change = true})) + }, + } +) + +test.run_registered_tests() From a76a2650006def01aa40f66e0f4a9317fdc5d135 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Mon, 26 Jan 2026 14:01:28 -0600 Subject: [PATCH 13/15] update optionsOverride bits for stateless capabilities --- .../switch_handlers/capability_handlers.lua | 14 +++++++------- .../matter-switch/src/switch_utils/fields.lua | 8 +++++--- .../src/test/test_light_illuminance_motion.lua | 10 +++++----- .../src/test/test_matter_light_fan.lua | 4 ++-- .../test_matter_multi_button_switch_mcd.lua | 4 ++-- .../src/test/test_matter_switch.lua | 18 +++++++++--------- .../test_multi_switch_parent_child_lights.lua | 6 +++--- .../test_multi_switch_parent_child_plugs.lua | 6 +++--- .../src/test/test_stateless_step.lua | 12 ++++++------ 9 files changed, 42 insertions(+), 40 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index dbce2dc841..ccbdb1515b 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -54,7 +54,7 @@ function CapabilityHandlers.handle_step_level(driver, device, cmd) if step_size == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN - device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) + device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF)) end @@ -67,10 +67,10 @@ function CapabilityHandlers.handle_set_color(driver, device, cmd) if switch_utils.tbl_contains(huesat_endpoints, endpoint_id) then local hue = switch_utils.convert_huesat_st_to_matter(cmd.args.color.hue) local sat = switch_utils.convert_huesat_st_to_matter(cmd.args.color.saturation) - req = clusters.ColorControl.server.commands.MoveToHueAndSaturation(device, endpoint_id, hue, sat, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + req = clusters.ColorControl.server.commands.MoveToHueAndSaturation(device, endpoint_id, hue, sat, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.HANDLE_COMMAND_IF_OFF) else local x, y, _ = st_utils.safe_hsv_to_xy(cmd.args.color.hue, cmd.args.color.saturation) - req = clusters.ColorControl.server.commands.MoveToColor(device, endpoint_id, x, y, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + req = clusters.ColorControl.server.commands.MoveToColor(device, endpoint_id, x, y, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.HANDLE_COMMAND_IF_OFF) end device:send(req) end @@ -80,7 +80,7 @@ function CapabilityHandlers.handle_set_hue(driver, device, cmd) local huesat_endpoints = device:get_endpoints(clusters.ColorControl.ID, {feature_bitmap = clusters.ColorControl.FeatureMap.HUE_AND_SATURATION}) if switch_utils.tbl_contains(huesat_endpoints, endpoint_id) then local hue = switch_utils.convert_huesat_st_to_matter(cmd.args.hue) - local req = clusters.ColorControl.server.commands.MoveToHue(device, endpoint_id, hue, 0, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + local req = clusters.ColorControl.server.commands.MoveToHue(device, endpoint_id, hue, 0, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.HANDLE_COMMAND_IF_OFF) device:send(req) else device.log.warn("Device does not support huesat features on its color control cluster") @@ -92,7 +92,7 @@ function CapabilityHandlers.handle_set_saturation(driver, device, cmd) local huesat_endpoints = device:get_endpoints(clusters.ColorControl.ID, {feature_bitmap = clusters.ColorControl.FeatureMap.HUE_AND_SATURATION}) if switch_utils.tbl_contains(huesat_endpoints, endpoint_id) then local sat = switch_utils.convert_huesat_st_to_matter(cmd.args.saturation) - local req = clusters.ColorControl.server.commands.MoveToSaturation(device, endpoint_id, sat, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + local req = clusters.ColorControl.server.commands.MoveToSaturation(device, endpoint_id, sat, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.HANDLE_COMMAND_IF_OFF) device:send(req) else device.log.warn("Device does not support huesat features on its color control cluster") @@ -114,7 +114,7 @@ function CapabilityHandlers.handle_set_color_temperature(driver, device, cmd) elseif max_temp_kelvin ~= nil and temp_in_kelvin >= max_temp_kelvin then temp_in_mired = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) end - local req = clusters.ColorControl.server.commands.MoveToColorTemperature(device, endpoint_id, temp_in_mired, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + local req = clusters.ColorControl.server.commands.MoveToColorTemperature(device, endpoint_id, temp_in_mired, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.HANDLE_COMMAND_IF_OFF) device:set_field(fields.MOST_RECENT_TEMP, cmd.args.temperature, {persist = true}) device:send(req) end @@ -131,7 +131,7 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) - device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE)) + device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF)) end diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index 969bbe2a84..a86be9fe5e 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -175,9 +175,11 @@ SwitchFields.TEMP_MIN = "__temp_min" SwitchFields.TEMP_MAX = "__temp_max" SwitchFields.TRANSITION_TIME = 0 --1/10ths of a second --- When sent with a command, these options mask and override bitmaps cause the command --- to take effect when the switch/light is off. + +-- For Level/Color Control cluster commands, this field indicates which bits in the OptionsOverride field are valid. In this case, we specify that the ExecuteIfOff option (bit 1) may be overridden. SwitchFields.OPTIONS_MASK = 0x01 -SwitchFields.OPTIONS_OVERRIDE = 0x01 +-- the OptionsOverride field's first bit overrides the ExecuteIfOff option, defining whether the command should take effect when the device is off. +SwitchFields.HANDLE_COMMAND_IF_OFF = 0x01 +SwitchFields.IGNORE_COMMAND_IF_OFF = 0x00 return SwitchFields diff --git a/drivers/SmartThings/matter-switch/src/test/test_light_illuminance_motion.lua b/drivers/SmartThings/matter-switch/src/test/test_light_illuminance_motion.lua index 24987079fc..c5a108898d 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_light_illuminance_motion.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_light_illuminance_motion.lua @@ -9,7 +9,7 @@ local st_utils = require "st.utils" local clusters = require "st.matter.clusters" local TRANSITION_TIME = 0 local OPTIONS_MASK = 0x01 -local OPTIONS_OVERRIDE = 0x01 +local HANDLE_COMMAND_IF_OFF = 0x01 local mock_device = test.mock_device.build_test_matter_device({ profile = t_utils.get_profile_definition("light-color-level-illuminance-motion.yml"), @@ -332,7 +332,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToHueAndSaturation(mock_device, 1, hue, sat, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToHueAndSaturation(mock_device, 1, hue, sat, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -404,7 +404,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToHue(mock_device, 1, hue, 0, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToHue(mock_device, 1, hue, 0, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, } @@ -426,7 +426,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToSaturation(mock_device, 1, sat, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToSaturation(mock_device, 1, sat, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, } @@ -448,7 +448,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 556, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua index 676a376726..e1af3ad52b 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua @@ -9,7 +9,7 @@ local version = require "version" local TRANSITION_TIME = 0 local OPTIONS_MASK = 0x01 -local OPTIONS_OVERRIDE = 0x01 +local HANDLE_COMMAND_IF_OFF = 0x01 local mock_device_ep1 = 1 local mock_device_ep2 = 2 @@ -178,7 +178,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, mock_device_ep1, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, mock_device_ep1, 556, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua index d412c2b35e..db1ae04bfb 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua @@ -9,7 +9,7 @@ local clusters = require "st.matter.generated.zap_clusters" local TRANSITION_TIME = 0 local OPTIONS_MASK = 0x01 -local OPTIONS_OVERRIDE = 0x01 +local HANDLE_COMMAND_IF_OFF = 0x01 local button_attr = capabilities.button.button @@ -366,7 +366,7 @@ test.register_coroutine_test( }) test.socket.matter:__expect_send({ mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, mock_device_ep5, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, mock_device_ep5, 556, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) }) test.socket.matter:__queue_receive({ mock_device.id, diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua index 6a5ecd2cc4..0751c2139f 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua @@ -9,7 +9,7 @@ local st_utils = require "st.utils" local clusters = require "st.matter.clusters" local TRANSITION_TIME = 0 local OPTIONS_MASK = 0x01 -local OPTIONS_OVERRIDE = 0x01 +local HANDLE_COMMAND_IF_OFF = 0x01 local mock_device = test.mock_device.build_test_matter_device({ profile = t_utils.get_profile_definition("switch-color-level.yml"), @@ -440,7 +440,7 @@ test.register_coroutine_test( test.socket.matter:__expect_send( { mock_device_no_hue_sat.id, - clusters.ColorControl.server.commands.MoveToColor(mock_device_no_hue_sat, 1, 15182, 21547, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColor(mock_device_no_hue_sat, 1, 15182, 21547, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } ) test.socket.matter:__queue_receive( @@ -502,7 +502,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToHueAndSaturation(mock_device, 1, hue, sat, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToHueAndSaturation(mock_device, 1, hue, sat, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -584,7 +584,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToHueAndSaturation(mock_device, 1, hue, sat, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToHueAndSaturation(mock_device, 1, hue, sat, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -658,7 +658,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToHue(mock_device, 1, hue, 0, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToHue(mock_device, 1, hue, 0, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, } @@ -680,7 +680,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToSaturation(mock_device, 1, sat, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToSaturation(mock_device, 1, sat, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, } @@ -702,7 +702,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 556, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -974,7 +974,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 165, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 165, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -990,7 +990,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 365, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, 1, 365, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } } } diff --git a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua index c0ed244d51..bf56633ada 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua @@ -10,7 +10,7 @@ test.disable_startup_messages() local TRANSITION_TIME = 0 local OPTIONS_MASK = 0x01 -local OPTIONS_OVERRIDE = 0x01 +local HANDLE_COMMAND_IF_OFF = 0x01 local parent_ep = 10 local child1_ep = 20 @@ -496,7 +496,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, child2_ep, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, child2_ep, 556, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -571,7 +571,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColor(mock_device, child2_ep, 15182, 21547, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColor(mock_device, child2_ep, 15182, 21547, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { diff --git a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua index 09fa39c4f6..3efca0f614 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua @@ -10,7 +10,7 @@ test.disable_startup_messages() local TRANSITION_TIME = 0 local OPTIONS_MASK = 0x01 -local OPTIONS_OVERRIDE = 0x01 +local HANDLE_COMMAND_IF_OFF = 0x01 local parent_ep = 10 local child1_ep = 20 @@ -489,7 +489,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, child2_ep, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, child2_ep, 556, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { @@ -564,7 +564,7 @@ test.register_message_test( direction = "send", message = { mock_device.id, - clusters.ColorControl.server.commands.MoveToColor(mock_device, child2_ep, 15182, 21547, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.MoveToColor(mock_device, child2_ep, 15182, 21547, TRANSITION_TIME, OPTIONS_MASK, HANDLE_COMMAND_IF_OFF) } }, { diff --git a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua index 69d33ed9dd..973efeca69 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua @@ -67,7 +67,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 187, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 187, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -83,7 +83,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 840, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 840, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -99,7 +99,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 467, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 467, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, } } @@ -122,7 +122,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 64, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 64, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -138,7 +138,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 127, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 127, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -154,7 +154,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 254, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.OPTIONS_OVERRIDE) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 254, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, } } From d3b876178aa390a07a99653705eb19e77e30ebd2 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Thu, 29 Jan 2026 16:01:46 -0600 Subject: [PATCH 14/15] switch from InitialPress to MultiPressComplete as SOT for scroll --- .../src/sub_drivers/ikea_scroll/init.lua | 2 +- .../scroll_handlers/event_handlers.lua | 23 +-- .../ikea_scroll/scroll_utils/fields.lua | 5 +- .../src/test/test_ikea_scroll.lua | 156 +++++++++--------- 4 files changed, 98 insertions(+), 88 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua index 83e324a19f..5c2a009de1 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/init.lua @@ -49,8 +49,8 @@ local ikea_scroll_handler = { matter_handlers = { event = { [clusters.Switch.ID] = { - [clusters.Switch.events.InitialPress.ID] = event_handlers.initial_press_handler, [clusters.Switch.events.MultiPressOngoing.ID] = event_handlers.multi_press_ongoing_handler, + [clusters.Switch.events.MultiPressComplete.ID] = event_handlers.multi_press_complete_handler, } } }, diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua index ae742c2216..e97b0de2a5 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_handlers/event_handlers.lua @@ -16,16 +16,6 @@ local function rotate_amount_event_helper(device, endpoint_id, num_presses_to_ha device:emit_event_for_endpoint(endpoint_id, capabilities.knob.rotateAmount(scroll_amount, {state_change = true})) end -function IkeaScrollEventHandlers.initial_press_handler(driver, device, ib, response) - -- use the generic handler logic for the push endpoints. Else, use custom logic. - if switch_utils.tbl_contains(scroll_fields.ENDPOINTS_PUSH, ib.endpoint_id) then - generic_event_handlers.initial_press_handler(driver, device, ib, response) - else - device:set_field(scroll_fields.LATEST_NUMBER_OF_PRESSES_COUNTED, 1) - rotate_amount_event_helper(device, ib.endpoint_id, 1) - end -end - -- Used by ENDPOINTS_UP_SCROLL and ENDPOINTS_DOWN_SCROLL, not ENDPOINTS_PUSH function IkeaScrollEventHandlers.multi_press_ongoing_handler(driver, device, ib, response) local cur_num_presses_counted = ib.data and ib.data.elements and ib.data.elements.current_number_of_presses_counted.value or 0 @@ -36,4 +26,17 @@ function IkeaScrollEventHandlers.multi_press_ongoing_handler(driver, device, ib, end end +function IkeaScrollEventHandlers.multi_press_complete_handler(driver, device, ib, response) + if switch_utils.tbl_contains(scroll_fields.ENDPOINTS_PUSH, ib.endpoint_id) then + generic_event_handlers.multi_press_complete_handler(driver, device, ib, response) + elseif device:get_field(scroll_fields.LATEST_NUMBER_OF_PRESSES_COUNTED) == nil then + -- if LATEST_NUMBER_OF_PRESSES_COUNTED is nil, only InitialPress event(s) have been sent + -- since the previous MultiPressComplete. Therefore, handle a single press event. + rotate_amount_event_helper(device, ib.endpoint_id, 1) + else + -- reset the LATEST_NUMBER_OF_PRESSES_COUNTED to nil at the end of a MultiPress chain. + device:set_field(scroll_fields.LATEST_NUMBER_OF_PRESSES_COUNTED, nil) + end +end + return IkeaScrollEventHandlers diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua index f0aeb77c5a..58580328b1 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/ikea_scroll/scroll_utils/fields.lua @@ -35,11 +35,10 @@ IkeaScrollFields.switch_press_subscribed_events = { } -- Required Events for the ENDPOINTS_UP_SCROLL and ENDPOINTS_DOWN_SCROLL. Adds a --- MultiPressOngoing subscription to handle step functionality in real-time, and --- removes MultiPressComplete subscription due to the real-time handling +-- MultiPressOngoing subscription to handle step functionality in real-time IkeaScrollFields.switch_scroll_subscribed_events = { - clusters.Switch.events.InitialPress.ID, clusters.Switch.events.MultiPressOngoing.ID, + clusters.Switch.events.MultiPressComplete.ID, } return IkeaScrollFields diff --git a/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua b/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua index 126b30d7bc..1acd3eefe2 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_ikea_scroll.lua @@ -133,8 +133,8 @@ local function ikea_scroll_subscribe() clusters.Switch.server.events.MultiPressComplete, } local CLUSTER_SUBSCRIBE_LIST_SCROLL = { - clusters.Switch.events.InitialPress, clusters.Switch.server.events.MultiPressOngoing, + clusters.Switch.server.events.MultiPressComplete, } local subscribe_request = CLUSTER_SUBSCRIBE_LIST_PUSH[1]:subscribe(mock_ikea_scroll, ENDPOINTS_PUSH[1]) for _, ep_press in ipairs(ENDPOINTS_PUSH) do @@ -241,8 +241,24 @@ test.register_message_test( direction = "receive", message = { mock_ikea_scroll.id, - clusters.Switch.events.InitialPress:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[1], {new_position = 1} + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {current_number_of_presses_counted = 2, new_position = 2} + ) + }, + }, + { + channel = "capability", + direction = "send", + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(12, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {current_number_of_presses_counted = 5, new_position = 5} ) }, }, @@ -250,9 +266,19 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("main", - capabilities.knob.rotateAmount(6, {state_change = true})) + capabilities.knob.rotateAmount(18, {state_change = true})) }, { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressComplete:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {new_position = 5, total_number_of_presses_counted = 5, previous_position = 0} + ) + }, + }, + { channel = "matter", direction = "receive", message = { @@ -266,7 +292,7 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("main", - capabilities.knob.rotateAmount(6, {state_change = true})) + capabilities.knob.rotateAmount(12, {state_change = true})) }, { channel = "matter", @@ -283,6 +309,16 @@ test.register_message_test( direction = "send", message = mock_ikea_scroll:generate_test_message("main", capabilities.knob.rotateAmount(18, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressComplete:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[1], {new_position = 5, total_number_of_presses_counted = 5, previous_position = 0} + ) + }, } } ) @@ -294,8 +330,8 @@ test.register_message_test( direction = "receive", message = { mock_ikea_scroll.id, - clusters.Switch.events.InitialPress:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[2], {new_position = 1} + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 2, new_position = 2} ) }, }, @@ -303,7 +339,7 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("main", - capabilities.knob.rotateAmount(-6, {state_change = true})) + capabilities.knob.rotateAmount(-12, {state_change = true})) }, { channel = "matter", @@ -311,7 +347,7 @@ test.register_message_test( message = { mock_ikea_scroll.id, clusters.Switch.events.MultiPressOngoing:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 2, new_position = 2} + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 5, new_position = 5} ) }, }, @@ -319,7 +355,17 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("main", - capabilities.knob.rotateAmount(-6, {state_change = true})) + capabilities.knob.rotateAmount(-18, {state_change = true})) + }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressComplete:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {new_position = 5, total_number_of_presses_counted = 5, previous_position = 0} + ) + }, }, { channel = "matter", @@ -327,7 +373,7 @@ test.register_message_test( message = { mock_ikea_scroll.id, clusters.Switch.events.MultiPressOngoing:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 5, new_position = 5} + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 2, new_position = 2} ) }, }, @@ -335,29 +381,39 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("main", - capabilities.knob.rotateAmount(-18, {state_change = true})) - } - } -) - -test.register_message_test( - "Ikea Scroll Positive rotateAmount events on group2 are emitted correctly", { + capabilities.knob.rotateAmount(-12, {state_change = true})) + }, { channel = "matter", direction = "receive", message = { mock_ikea_scroll.id, - clusters.Switch.events.InitialPress:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[3], {new_position = 1} + clusters.Switch.events.MultiPressOngoing:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {current_number_of_presses_counted = 5, new_position = 5} ) }, }, { channel = "capability", direction = "send", - message = mock_ikea_scroll:generate_test_message("group2", - capabilities.knob.rotateAmount(6, {state_change = true})) + message = mock_ikea_scroll:generate_test_message("main", + capabilities.knob.rotateAmount(-18, {state_change = true})) }, + { + channel = "matter", + direction = "receive", + message = { + mock_ikea_scroll.id, + clusters.Switch.events.MultiPressComplete:build_test_event_report( + mock_ikea_scroll, ENDPOINTS_SCROLL[2], {new_position = 5, total_number_of_presses_counted = 5, previous_position = 0} + ) + }, + } + } +) + +test.register_message_test( + "Ikea Scroll Positive rotateAmount events on group2 are emitted correctly", { { channel = "matter", direction = "receive", @@ -372,7 +428,7 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("group2", - capabilities.knob.rotateAmount(6, {state_change = true})) + capabilities.knob.rotateAmount(12, {state_change = true})) }, { channel = "matter", @@ -395,22 +451,6 @@ test.register_message_test( test.register_message_test( "Ikea Scroll Negative rotateAmount events on group2 are emitted correctly", { - { - channel = "matter", - direction = "receive", - message = { - mock_ikea_scroll.id, - clusters.Switch.events.InitialPress:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[4], {new_position = 1} - ) - }, - }, - { - channel = "capability", - direction = "send", - message = mock_ikea_scroll:generate_test_message("group2", - capabilities.knob.rotateAmount(-6, {state_change = true})) - }, { channel = "matter", direction = "receive", @@ -425,7 +465,7 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("group2", - capabilities.knob.rotateAmount(-6, {state_change = true})) + capabilities.knob.rotateAmount(-12, {state_change = true})) }, { channel = "matter", @@ -448,22 +488,6 @@ test.register_message_test( test.register_message_test( "Ikea Scroll Positive rotateAmount events on group3 are emitted correctly", { - { - channel = "matter", - direction = "receive", - message = { - mock_ikea_scroll.id, - clusters.Switch.events.InitialPress:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[5], {new_position = 1} - ) - }, - }, - { - channel = "capability", - direction = "send", - message = mock_ikea_scroll:generate_test_message("group3", - capabilities.knob.rotateAmount(6, {state_change = true})) - }, { channel = "matter", direction = "receive", @@ -478,7 +502,7 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("group3", - capabilities.knob.rotateAmount(6, {state_change = true})) + capabilities.knob.rotateAmount(12, {state_change = true})) }, { channel = "matter", @@ -501,22 +525,6 @@ test.register_message_test( test.register_message_test( "Ikea Scroll Negative rotateAmount events on group3 are emitted correctly", { - { - channel = "matter", - direction = "receive", - message = { - mock_ikea_scroll.id, - clusters.Switch.events.InitialPress:build_test_event_report( - mock_ikea_scroll, ENDPOINTS_SCROLL[6], {new_position = 1} - ) - }, - }, - { - channel = "capability", - direction = "send", - message = mock_ikea_scroll:generate_test_message("group3", - capabilities.knob.rotateAmount(-6, {state_change = true})) - }, { channel = "matter", direction = "receive", @@ -531,7 +539,7 @@ test.register_message_test( channel = "capability", direction = "send", message = mock_ikea_scroll:generate_test_message("group3", - capabilities.knob.rotateAmount(-6, {state_change = true})) + capabilities.knob.rotateAmount(-12, {state_change = true})) }, { channel = "matter", From 85cae05b85bea44a0d6795bf0799295566c20024 Mon Sep 17 00:00:00 2001 From: Harrison Carter Date: Fri, 30 Jan 2026 10:16:06 -0600 Subject: [PATCH 15/15] increase default stateless step transition time --- .../src/switch_handlers/capability_handlers.lua | 4 ++-- .../matter-switch/src/switch_utils/fields.lua | 3 ++- .../matter-switch/src/test/test_stateless_step.lua | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua index ccbdb1515b..3c65e3e8c0 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/capability_handlers.lua @@ -54,7 +54,7 @@ function CapabilityHandlers.handle_step_level(driver, device, cmd) if step_size == 0 then return end local endpoint_id = device:component_to_endpoint(cmd.component) local step_mode = step_size > 0 and clusters.LevelControl.types.StepMode.UP or clusters.LevelControl.types.StepMode.DOWN - device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF)) + device:send(clusters.LevelControl.server.commands.Step(device, endpoint_id, step_mode, math.abs(step_size), fields.TRANSITION_TIME_FAST, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF)) end @@ -131,7 +131,7 @@ function CapabilityHandlers.handle_step_color_temperature_by_percent(driver, dev local min_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MIN, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MIN -- default min mireds local max_mireds = switch_utils.get_field_for_endpoint(device, fields.COLOR_TEMP_BOUND_RECEIVED_MIRED..fields.COLOR_TEMP_MAX, endpoint_id) or fields.COLOR_TEMPERATURE_MIRED_MAX -- default max mireds local step_size_in_mireds = st_utils.round((max_mireds - min_mireds) * (math.abs(step_percent_change)/100.0)) - device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF)) + device:send(clusters.ColorControl.server.commands.StepColorTemperature(device, endpoint_id, step_mode, step_size_in_mireds, fields.TRANSITION_TIME_FAST, min_mireds, max_mireds, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF)) end diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index a86be9fe5e..a72ea11150 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -174,7 +174,8 @@ SwitchFields.TEMP_BOUND_RECEIVED = "__temp_bound_received" SwitchFields.TEMP_MIN = "__temp_min" SwitchFields.TEMP_MAX = "__temp_max" -SwitchFields.TRANSITION_TIME = 0 --1/10ths of a second +SwitchFields.TRANSITION_TIME = 0 -- number of 10ths of a second +SwitchFields.TRANSITION_TIME_FAST = 3 -- 0.3 seconds -- For Level/Color Control cluster commands, this field indicates which bits in the OptionsOverride field are valid. In this case, we specify that the ExecuteIfOff option (bit 1) may be overridden. SwitchFields.OPTIONS_MASK = 0x01 diff --git a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua index 973efeca69..a8a8e83b4b 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_stateless_step.lua @@ -67,7 +67,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 187, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 187, fields.TRANSITION_TIME_FAST, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -83,7 +83,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 840, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.DOWN, 840, fields.TRANSITION_TIME_FAST, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -99,7 +99,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 467, fields.TRANSITION_TIME, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) + clusters.ColorControl.server.commands.StepColorTemperature(mock_device_color_temp, 1, clusters.ColorControl.types.StepModeEnum.UP, 467, fields.TRANSITION_TIME_FAST, fields.COLOR_TEMPERATURE_MIRED_MIN, fields.COLOR_TEMPERATURE_MIRED_MAX, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, } } @@ -122,7 +122,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 64, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 64, fields.TRANSITION_TIME_FAST, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -138,7 +138,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 127, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.DOWN, 127, fields.TRANSITION_TIME_FAST, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, }, { @@ -154,7 +154,7 @@ test.register_message_test( direction = "send", message = { mock_device_color_temp.id, - clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 254, fields.TRANSITION_TIME, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) + clusters.LevelControl.server.commands.Step(mock_device_color_temp, 1, clusters.LevelControl.types.StepModeEnum.UP, 254, fields.TRANSITION_TIME_FAST, fields.OPTIONS_MASK, fields.IGNORE_COMMAND_IF_OFF) }, } }