Skip to content

Commit 3c72f69

Browse files
committed
Add CCT component support for Pro RGBWW PM rgbcct mode; harden @nullable DTO annotations
- Add cct:0/cct:1 config and status fields (Shelly2ApiJsonDTO) - Add CCT.GetStatus/CCT.Set RPC constants - Select CCT.GetStatus for cctx2 profile in getLightStatus() - Include cct configs in fillRgbwSettings() white-mode light array - Apply updateLightModeStatus() for cct0/cct1 in fillDeviceStatus() - Add @nullable throughout Shelly2GetConfigLight and Shelly2RGBWStatus - Fix value.id unboxing via getInteger() in update helpers - Improve test assertions: assertNotNull + separate assertThat Signed-off-by: Markus Michels <markus7017@gmail.com>
1 parent 2a3b197 commit 3c72f69

5 files changed

Lines changed: 105 additions & 72 deletions

File tree

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyDevices.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ public class ShellyDevices {
638638
Map.entry("shellydimmerg3", THING_TYPE_SHELLYPLUSDIMMER),
639639
Map.entry("shellyprodm2pm", THING_TYPE_SHELLYPRODM2PM),
640640
Map.entry("shellyplusrgbwpm", THING_TYPE_SHELLYPLUSRGBWPM),
641-
Map.entry("shellyprorgbwwpm", THING_TYPE_SHELLYPRORGBWWPM), // TODO: verify firmware id field
641+
Map.entry("shellyprorgbwwpm", THING_TYPE_SHELLYPRORGBWWPM),
642642
Map.entry("shellyplusstrip", THING_TYPE_SHELLYPLUSSTRIP),
643643
Map.entry("shellyblugw", THING_TYPE_SHELLYPLUSBLUGW), //
644644
Map.entry("shellyblugwg3", THING_TYPE_SHELLYPLUSBLUGW), //

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiClient.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,8 @@ protected boolean fillDeviceStatus(ShellySettingsStatus status, Shelly2DeviceSta
563563
updated |= updateLightModeStatus(2, status, result.light2, channelUpdate);
564564
updated |= updateLightModeStatus(3, status, result.light3, channelUpdate);
565565
updated |= updateLightModeStatus(4, status, result.light4, channelUpdate);
566+
updated |= updateLightModeStatus(0, status, result.cct0, channelUpdate);
567+
updated |= updateLightModeStatus(1, status, result.cct1, channelUpdate);
566568
if (channelUpdate) {
567569
updated |= ShellyComponents.updateMeters(getThing(), status);
568570
}
@@ -1083,16 +1085,21 @@ protected void fillRgbwSettings(ShellyDeviceProfile profile, Shelly2GetConfigRes
10831085
}
10841086

10851087
ArrayList<@Nullable ShellySettingsRgbwLight> lights = new ArrayList<>();
1086-
if (dc.rgbw0 != null) {
1088+
Shelly2GetConfigLight rgbw0 = dc.rgbw0;
1089+
Shelly2GetConfigLight rgb0 = dc.rgb0;
1090+
if (rgbw0 != null) {
10871091
profile.inColor = true;
1088-
lights.add(createRgbwLightSetting(dc.rgbw0));
1089-
} else if (dc.rgb0 != null) {
1092+
lights.add(createRgbwLightSetting(rgbw0));
1093+
} else if (rgb0 != null) {
10901094
profile.inColor = true;
1091-
lights.add(createRgbwLightSetting(dc.rgb0));
1095+
lights.add(createRgbwLightSetting(rgb0));
10921096
} else {
10931097
profile.inColor = false;
1094-
Shelly2GetConfigLight[] lightConfigs = { dc.light0, dc.light1, dc.light2, dc.light3, dc.light4 };
1095-
for (Shelly2GetConfigLight lc : lightConfigs) {
1098+
@Nullable
1099+
Shelly2GetConfigLight[] lightConfigs = { dc.light0, dc.light1, dc.light2, dc.light3, dc.light4, dc.cct0,
1100+
dc.cct1 };
1101+
for (@Nullable
1102+
Shelly2GetConfigLight lc : lightConfigs) {
10961103
if (lc != null) {
10971104
lights.add(createRgbwLightSetting(lc));
10981105
}
@@ -1121,16 +1128,17 @@ private boolean updateDimmerStatus(int id, ShellySettingsStatus status, @Nullabl
11211128
if (value.id == null) { // fw 1.6.1
11221129
value.id = id;
11231130
}
1131+
int dimmerId = getInteger(value.id);
11241132

1125-
ShellyShortLightStatus ds = status.dimmers.get(value.id);
1133+
ShellyShortLightStatus ds = status.dimmers.get(dimmerId);
11261134
Double brightness = value.brightness;
11271135
if (brightness != null) {
11281136
ds.brightness = brightness.intValue();
11291137
}
11301138
ds.ison = value.output;
11311139
ds.hasTimer = value.timerStartedAt != null;
11321140
ds.timerDuration = getDuration(value.timerStartedAt, value.timerDuration);
1133-
status.dimmers.set(value.id, ds);
1141+
status.dimmers.set(dimmerId, ds);
11341142
return channelUpdate ? ShellyComponents.updateDimmers(getThing(), status) : false;
11351143
}
11361144

@@ -1143,8 +1151,9 @@ private boolean updateRGBWStatus(int id, ShellySettingsStatus status, @Nullable
11431151
if (value.id == null) {
11441152
value.id = id;
11451153
}
1154+
int rgbwId = getInteger(value.id);
11461155

1147-
ShellySettingsLight ds = status.lights.get(value.id);
1156+
ShellySettingsLight ds = status.lights.get(rgbwId);
11481157
ds.brightness = Objects.requireNonNullElse(value.brightness, ds.brightness).intValue();
11491158
if (value.rgb != null) {
11501159
ds.red = value.rgb[0];
@@ -1154,7 +1163,7 @@ private boolean updateRGBWStatus(int id, ShellySettingsStatus status, @Nullable
11541163
ds.white = Objects.requireNonNullElse(value.white, ds.white);
11551164
ds.ison = value.output;
11561165

1157-
status.lights.set(value.id, ds);
1166+
status.lights.set(rgbwId, ds);
11581167
return channelUpdate ? ShellyComponents.updateRGBW(getThing(), status) : false;
11591168
}
11601169

@@ -1167,11 +1176,12 @@ private boolean updateLightModeStatus(int id, ShellySettingsStatus status, @Null
11671176
if (value.id == null) {
11681177
value.id = id;
11691178
}
1179+
int lightId = getInteger(value.id);
11701180
List<@Nullable ShellySettingsLight> lights = status.lights;
1171-
if (lights == null || value.id >= lights.size()) {
1181+
if (lights == null || lightId >= lights.size()) {
11721182
return false;
11731183
}
1174-
ShellySettingsLight ds = lights.get(value.id);
1184+
ShellySettingsLight ds = lights.get(lightId);
11751185
if (ds == null) {
11761186
return false;
11771187
}
@@ -1180,7 +1190,7 @@ private boolean updateLightModeStatus(int id, ShellySettingsStatus status, @Null
11801190
ds.brightness = brightness.intValue();
11811191
}
11821192
ds.ison = value.output;
1183-
lights.set(value.id, ds);
1193+
lights.set(lightId, ds);
11841194
return false; // channel updates deferred to getLightStatus() polling cycle
11851195
}
11861196

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiJsonDTO.java

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public class Shelly2ApiJsonDTO {
6262
public static final String SHELLYRPC_METHOD_RGB_SET = "RGB.Set";
6363
public static final String SHELLYRPC_METHOD_RGBW_STATUS = "RGBW.GetStatus";
6464
public static final String SHELLYRPC_METHOD_RGBW_SET = "RGBW.Set";
65+
public static final String SHELLYRPC_METHOD_CCT_STATUS = "CCT.GetStatus";
66+
public static final String SHELLYRPC_METHOD_CCT_SET = "CCT.Set";
6567
public static final String SHELLYRPC_METHOD_LED_SETCONFIG = "WD_UI.SetConfig";
6668
public static final String SHELLYRPC_METHOD_WIFIGETCONG = "Wifi.GetConfig";
6769
public static final String SHELLYRPC_METHOD_WIFISETCONG = "Wifi.SetConfig";
@@ -426,55 +428,55 @@ public static class Shelly2ConfigSmoke {
426428

427429
public static class Shelly2GetConfigLight {
428430
public static class Shelly2GetConfigLightDefault {
429-
public Integer brightness;
431+
public @Nullable Integer brightness;
430432
}
431433

432434
public static class Shelly2GetConfigLightNightMode {
433-
public boolean enable;
434-
public Integer brightness;
435-
public Double[] rgb;
436-
public Double white;
435+
public @Nullable Boolean enable;
436+
public @Nullable Integer brightness;
437+
public @Nullable Double[] rgb;
438+
public @Nullable Double white;
437439
// active_between[]
438440
}
439441

440442
public static class Shelly2ConfigLightPresets {
441443
public static class Shelly2ConfigLightButtonPreset {
442-
Double brightness;
443-
public Double[] rgb;
444+
public @Nullable Double brightness;
445+
public @Nullable Double[] rgb;
444446
}
445447

446448
@SerializedName("button_doublepush")
447-
public Shelly2ConfigLightButtonPreset buttonDoublePush;
449+
public @Nullable Shelly2ConfigLightButtonPreset buttonDoublePush;
448450
}
449451

450-
public Integer id;
451-
public String name;
452+
public @Nullable Integer id;
453+
public @Nullable String name;
452454
@SerializedName("in_mode")
453-
public String inMode;
455+
public @Nullable String inMode;
454456
@SerializedName("initial_state")
455-
public String initialState;
457+
public @Nullable String initialState;
456458
@SerializedName("auto_on")
457-
public Boolean autoOn;
459+
public @Nullable Boolean autoOn;
458460
@SerializedName("auto_off")
459-
public Boolean autoOff;
461+
public @Nullable Boolean autoOff;
460462
@SerializedName("auto_on_delay")
461-
public Double autoOnDelay;
463+
public @Nullable Double autoOnDelay;
462464
@SerializedName("auto_off_delay")
463-
public Double autoOffDelay;
465+
public @Nullable Double autoOffDelay;
464466
@SerializedName("transition_duration")
465-
public Double transitionDuration;
467+
public @Nullable Double transitionDuration;
466468
@SerializedName("min_brightness_on_toggle")
467-
public Double minBrightnessOnToggle;
469+
public @Nullable Double minBrightnessOnToggle;
468470
@SerializedName("button_fade_rate")
469-
public Integer buttonFadeRate;
471+
public @Nullable Integer buttonFadeRate;
470472
@SerializedName("button_presets")
471-
public Shelly2ConfigLightPresets buttonPresets;
473+
public @Nullable Shelly2ConfigLightPresets buttonPresets;
472474
@SerializedName("default")
473-
public Shelly2GetConfigLightDefault defaultCfg;
475+
public @Nullable Shelly2GetConfigLightDefault defaultCfg;
474476
@SerializedName("night_mode")
475-
public Shelly2GetConfigLightNightMode nightMode;
477+
public @Nullable Shelly2GetConfigLightNightMode nightMode;
476478
@SerializedName("range_map")
477-
public Double[] rangeMap;
479+
public @Nullable Double[] rangeMap;
478480
}
479481

480482
public class Shelly2DeviceConfigLed {
@@ -554,19 +556,23 @@ public class Shelly2DevConfigMqtt {
554556
public Shelly2DevConfigCover cover0;
555557

556558
@SerializedName("light:0")
557-
public Shelly2GetConfigLight light0;
559+
public @Nullable Shelly2GetConfigLight light0;
558560
@SerializedName("light:1")
559-
public Shelly2GetConfigLight light1;
561+
public @Nullable Shelly2GetConfigLight light1;
560562
@SerializedName("light:2")
561-
public Shelly2GetConfigLight light2;
563+
public @Nullable Shelly2GetConfigLight light2;
562564
@SerializedName("light:3")
563-
public Shelly2GetConfigLight light3;
565+
public @Nullable Shelly2GetConfigLight light3;
564566
@SerializedName("light:4")
565-
public Shelly2GetConfigLight light4;
567+
public @Nullable Shelly2GetConfigLight light4;
566568
@SerializedName("rgb:0")
567-
public Shelly2GetConfigLight rgb0;
569+
public @Nullable Shelly2GetConfigLight rgb0;
568570
@SerializedName("rgbw:0")
569-
public Shelly2GetConfigLight rgbw0;
571+
public @Nullable Shelly2GetConfigLight rgbw0;
572+
@SerializedName("cct:0")
573+
public @Nullable Shelly2GetConfigLight cct0;
574+
@SerializedName("cct:1")
575+
public @Nullable Shelly2GetConfigLight cct1;
570576

571577
@SerializedName("smoke:0")
572578
public Shelly2ConfigSmoke smoke0;
@@ -642,8 +648,8 @@ public class Shelly2InputStatus {
642648
}
643649

644650
public static class Shelly2DeviceStatusLight {
645-
public Integer id;
646-
public String source;
651+
public @Nullable Integer id;
652+
public @Nullable String source;
647653
public @Nullable Boolean output;
648654
public @Nullable Double[] rgb;
649655
public @Nullable Double brightness;
@@ -658,6 +664,7 @@ public static class Shelly2DeviceStatusLight {
658664
@SerializedName("timer_duration")
659665
public @Nullable Double timerDuration;
660666
public @Nullable String[] flags;
667+
public @Nullable Integer ct; // color temperature in Kelvin (CCT component)
661668
}
662669

663670
public static class Shelly2DeviceStatusResult {
@@ -801,17 +808,17 @@ public class Shelly2DeviceStatusSmoke {
801808
}
802809

803810
public static class Shelly2RGBWStatus {
804-
public Integer id;
805-
public String source;
806-
public Boolean output;
807-
public Integer[] rgb;
808-
public Double brightness;
809-
public Integer white;
810-
public Shelly2DeviceStatusTemp temperature;
811-
public Shelly2Energy aenergy;
812-
public Double apower;
813-
public Double voltage;
814-
public Double current;
811+
public @Nullable Integer id;
812+
public @Nullable String source;
813+
public @Nullable Boolean output;
814+
public @Nullable Integer[] rgb;
815+
public @Nullable Double brightness;
816+
public @Nullable Integer white;
817+
public @Nullable Shelly2DeviceStatusTemp temperature;
818+
public @Nullable Shelly2Energy aenergy;
819+
public @Nullable Double apower;
820+
public @Nullable Double voltage;
821+
public @Nullable Double current;
815822
}
816823

817824
public Shelly2DeviceStatusBle ble;
@@ -832,7 +839,7 @@ public static class Shelly2RGBWStatus {
832839
public Shelly2InputStatus input100; // Digital Input from Add-On
833840

834841
@SerializedName("rgbw:0")
835-
public Shelly2RGBWStatus rgbw0;
842+
public @Nullable Shelly2RGBWStatus rgbw0;
836843

837844
@SerializedName("switch:0")
838845
public Shelly2RelayStatus switch0;
@@ -872,17 +879,21 @@ public static class Shelly2RGBWStatus {
872879
public Shelly2CoverStatus cover0;
873880

874881
@SerializedName("light:0")
875-
public Shelly2DeviceStatusLight light0;
882+
public @Nullable Shelly2DeviceStatusLight light0;
876883
@SerializedName("light:1")
877-
public Shelly2DeviceStatusLight light1;
884+
public @Nullable Shelly2DeviceStatusLight light1;
878885
@SerializedName("light:2")
879-
public Shelly2DeviceStatusLight light2;
886+
public @Nullable Shelly2DeviceStatusLight light2;
880887
@SerializedName("light:3")
881-
public Shelly2DeviceStatusLight light3;
888+
public @Nullable Shelly2DeviceStatusLight light3;
882889
@SerializedName("light:4")
883-
public Shelly2DeviceStatusLight light4;
890+
public @Nullable Shelly2DeviceStatusLight light4;
884891
@SerializedName("rgb:0")
885-
public Shelly2RGBWStatus rgb0;
892+
public @Nullable Shelly2RGBWStatus rgb0;
893+
@SerializedName("cct:0")
894+
public @Nullable Shelly2DeviceStatusLight cct0;
895+
@SerializedName("cct:1")
896+
public @Nullable Shelly2DeviceStatusLight cct1;
886897

887898
@SerializedName("temperature:0")
888899
public Shelly2DeviceStatusTempId temperature0;

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiRpc.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -797,18 +797,21 @@ public ShellyStatusLight getLightStatus() throws ShellyApiException {
797797
lightChannel.blue = ls.rgb[2];
798798
}
799799
lightChannel.white = ls.white;
800-
if (ls.brightness != null) {
801-
lightChannel.brightness = ls.brightness.intValue();
800+
Double rgbBrightness = ls.brightness;
801+
if (rgbBrightness != null) {
802+
lightChannel.brightness = rgbBrightness.intValue();
802803
}
803804
lightChannel.ison = ls.output;
804805
status.lights.add(lightChannel);
805806
status.ison = ls.output;
806807
} else {
808+
String rawProfile = getString(profile.device.profile);
809+
String lightMethod = SHELLY2_PROFILE_CCTX2.equals(rawProfile) ? SHELLYRPC_METHOD_CCT_STATUS
810+
: SHELLYRPC_METHOD_LIGHT_STATUS;
807811
List<@Nullable ShellySettingsRgbwLight> settingLights = profile.settings.lights;
808812
int numLights = settingLights != null ? settingLights.size() : 1;
809813
for (int i = 0; i < numLights; i++) {
810-
Shelly2DeviceStatusLight ls = apiRequest(
811-
new Shelly2RpcRequest().withMethod(SHELLYRPC_METHOD_LIGHT_STATUS).withId(i),
814+
Shelly2DeviceStatusLight ls = apiRequest(new Shelly2RpcRequest().withMethod(lightMethod).withId(i),
812815
Shelly2DeviceStatusLight.class);
813816
ShellyStatusLightChannel lightChannel = new ShellyStatusLightChannel();
814817
lightChannel.ison = ls.output;

bundles/org.openhab.binding.shelly/src/test/java/org/openhab/binding/shelly/internal/api2/Shelly2GetDeviceProfileTest.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.eclipse.jdt.annotation.NonNullByDefault;
2424
import org.eclipse.jdt.annotation.Nullable;
2525
import org.eclipse.jetty.client.HttpClient;
26+
import org.junit.jupiter.api.Assertions;
2627
import org.junit.jupiter.api.Test;
2728
import org.junit.jupiter.params.ParameterizedTest;
2829
import org.junit.jupiter.params.provider.CsvSource;
@@ -338,7 +339,9 @@ void plusRgbwPm_rgbwProfile_isRGBW2TrueAndInColorTrue() throws ShellyApiExceptio
338339
ShellyDeviceProfile profile = client.getDeviceProfile(THING_TYPE_SHELLYPLUSRGBWPM, deviceInfo());
339340
assertThat(profile.isRGBW2, is(true));
340341
assertThat(profile.inColor, is(true));
341-
assertThat(profile.settings.lights != null && profile.settings.lights.size() == 1, is(true));
342+
var lights1 = profile.settings.lights;
343+
Assertions.assertNotNull(lights1);
344+
assertThat(lights1.size(), is(1));
342345
}
343346

344347
@Test
@@ -350,7 +353,9 @@ void plusRgbwPm_rgbProfile_isRGBW2TrueAndInColorTrue() throws ShellyApiException
350353
ShellyDeviceProfile profile = client.getDeviceProfile(THING_TYPE_SHELLYPLUSRGBWPM, deviceInfo());
351354
assertThat(profile.isRGBW2, is(true));
352355
assertThat(profile.inColor, is(true));
353-
assertThat(profile.settings.lights != null && profile.settings.lights.size() == 1, is(true));
356+
var lights2 = profile.settings.lights;
357+
Assertions.assertNotNull(lights2);
358+
assertThat(lights2.size(), is(1));
354359
}
355360

356361
@Test
@@ -362,7 +367,9 @@ void plusRgbwPm_lightProfile_isRGBW2TrueAndInColorFalseAndFourChannels() throws
362367
ShellyDeviceProfile profile = client.getDeviceProfile(THING_TYPE_SHELLYPLUSRGBWPM, deviceInfo());
363368
assertThat(profile.isRGBW2, is(true));
364369
assertThat(profile.inColor, is(false));
365-
assertThat(profile.settings.lights != null && profile.settings.lights.size() == 4, is(true));
370+
var lights4 = profile.settings.lights;
371+
Assertions.assertNotNull(lights4);
372+
assertThat(lights4.size(), is(4));
366373
}
367374

368375
@Test
@@ -386,6 +393,8 @@ void proRgbwwPm_lightProfile_fiveChannels() throws ShellyApiException {
386393
ShellyDeviceProfile profile = client.getDeviceProfile(THING_TYPE_SHELLYPRORGBWWPM, deviceInfo());
387394
assertThat(profile.isRGBW2, is(true));
388395
assertThat(profile.inColor, is(false));
389-
assertThat(profile.settings.lights != null && profile.settings.lights.size() == 5, is(true));
396+
var lights5 = profile.settings.lights;
397+
Assertions.assertNotNull(lights5);
398+
assertThat(lights5.size(), is(5));
390399
}
391400
}

0 commit comments

Comments
 (0)