Skip to content

Commit e3ccac9

Browse files
authored
[hue] Improve support for third party lights (#21062)
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
1 parent 1db22d3 commit e3ccac9

27 files changed

Lines changed: 1217 additions & 398 deletions

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBindingConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Set;
1919

2020
import org.eclipse.jdt.annotation.NonNullByDefault;
21+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ResourceType;
2122
import org.openhab.core.thing.ThingTypeUID;
2223
import org.openhab.core.thing.type.ChannelTypeUID;
2324

@@ -225,4 +226,6 @@ public class HueBindingConstants {
225226

226227
public static final String CHANNEL_GROUP_AUTOMATION = "automation";
227228
public static final ChannelTypeUID CHANNEL_TYPE_AUTOMATION = new ChannelTypeUID(BINDING_ID, "automation-enable");
229+
230+
public static final Set<ResourceType> LIGHT_TYPES = Set.of(ResourceType.LIGHT, ResourceType.GROUPED_LIGHT);
228231
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/ColorTemperature.java

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import org.eclipse.jdt.annotation.NonNullByDefault;
1818
import org.eclipse.jdt.annotation.Nullable;
19-
import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
2019
import org.openhab.core.library.types.QuantityType;
2120
import org.openhab.core.library.unit.Units;
2221

@@ -30,20 +29,20 @@
3029
@NonNullByDefault
3130
public class ColorTemperature {
3231
private @Nullable Long mirek;
32+
private @Nullable @SerializedName("mirek_valid") Boolean mirekValid;
3333
private @Nullable @SerializedName("mirek_schema") MirekSchema mirekSchema;
3434

3535
/**
3636
* Get the color temperature as a QuantityType value.
3737
*
3838
* @return a QuantityType value
39-
* @throws DTOPresentButEmptyException to indicate that the DTO is present but empty.
4039
*/
41-
public @Nullable QuantityType<?> getAbsolute() throws DTOPresentButEmptyException {
40+
public @Nullable QuantityType<?> getAbsolute() {
4241
Long mirek = this.mirek;
4342
if (Objects.nonNull(mirek)) {
4443
return QuantityType.valueOf(mirek, Units.MIRED).toInvertibleUnit(Units.KELVIN);
4544
}
46-
throw new DTOPresentButEmptyException("'color_temperature' DTO is present but empty");
45+
return null;
4746
}
4847

4948
public @Nullable Long getMirek() {
@@ -54,33 +53,23 @@ public class ColorTemperature {
5453
return mirekSchema;
5554
}
5655

57-
/**
58-
* Get the color temperature as a percentage based on the MirekSchema. Note: this method is only to be used on
59-
* cached state DTOs which already have a defined mirek schema.
60-
*
61-
* @return the percentage of the mirekSchema range.
62-
* @throws DTOPresentButEmptyException to indicate that the DTO is present but empty.
63-
*/
64-
public @Nullable Double getPercent() throws DTOPresentButEmptyException {
65-
Long mirek = this.mirek;
66-
if (Objects.nonNull(mirek)) {
67-
MirekSchema mirekSchema = this.mirekSchema;
68-
mirekSchema = Objects.nonNull(mirekSchema) ? mirekSchema : MirekSchema.DEFAULT_SCHEMA;
69-
double min = mirekSchema.getMirekMinimum();
70-
double max = mirekSchema.getMirekMaximum();
71-
double percent = 100f * (mirek.doubleValue() - min) / (max - min);
72-
return Math.max(0, Math.min(100, percent));
73-
}
74-
throw new DTOPresentButEmptyException("'mirek_schema' DTO is present but empty");
56+
public boolean isMirekValid() {
57+
Boolean mirekValid = this.mirekValid;
58+
return mirekValid != null ? mirekValid && (mirek != null) : false;
7559
}
7660

77-
public ColorTemperature setMirek(double mirek) {
78-
this.mirek = Math.round(mirek);
61+
public ColorTemperature setMirek(@Nullable Long mirek) {
62+
this.mirek = mirek;
7963
return this;
8064
}
8165

8266
public ColorTemperature setMirekSchema(@Nullable MirekSchema mirekSchema) {
8367
this.mirekSchema = mirekSchema;
8468
return this;
8569
}
70+
71+
@Override
72+
public boolean equals(@Nullable Object obj) {
73+
return (obj instanceof ColorTemperature c && mirek instanceof Long m) ? m.equals(c.getMirek()) : false;
74+
}
8675
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.api.dto.clip2;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ActionDeltaType;
18+
import org.openhab.core.library.types.IncreaseDecreaseType;
19+
20+
import com.google.gson.annotations.SerializedName;
21+
22+
/**
23+
* DTO for color temperature delta of a light.
24+
*
25+
* @author Andrew Fiddian-Green - Initial contribution
26+
*/
27+
@NonNullByDefault
28+
public class ColorTemperatureDelta {
29+
private @Nullable String action;
30+
private @SerializedName("mirek_delta") int mirekDelta;
31+
32+
public @Nullable String getAction() {
33+
return action;
34+
}
35+
36+
public int getMirekDelta() {
37+
return mirekDelta;
38+
}
39+
40+
public ColorTemperatureDelta setAction(IncreaseDecreaseType action) {
41+
this.action = ActionDeltaType.of(action).name().toLowerCase();
42+
return this;
43+
}
44+
45+
public ColorTemperatureDelta setDelta(int mirekDelta) {
46+
this.mirekDelta = mirekDelta;
47+
return this;
48+
}
49+
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/ColorXy.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import org.eclipse.jdt.annotation.NonNullByDefault;
1818
import org.eclipse.jdt.annotation.Nullable;
19-
import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
2019
import org.openhab.core.util.ColorUtil.Gamut;
2120

2221
/**
@@ -38,15 +37,8 @@ public class ColorXy {
3837
return this.gamut;
3938
}
4039

41-
/**
42-
* @throws DTOPresentButEmptyException to indicate that the DTO is present but empty.
43-
*/
44-
public double[] getXY() throws DTOPresentButEmptyException {
45-
PairXy pairXy = this.xy;
46-
if (Objects.nonNull(pairXy)) {
47-
return pairXy.getXY();
48-
}
49-
throw new DTOPresentButEmptyException("'color' DTO is present but empty");
40+
public @Nullable PairXy getXY() {
41+
return xy;
5042
}
5143

5244
public ColorXy setGamut(@Nullable Gamut gamut) {
@@ -61,4 +53,13 @@ public ColorXy setXY(double[] xyValues) {
6153
this.xy = pairXy;
6254
return this;
6355
}
56+
57+
public void setXY(@Nullable PairXy xy) {
58+
this.xy = xy;
59+
}
60+
61+
@Override
62+
public boolean equals(@Nullable Object obj) {
63+
return (this == obj) || ((xy instanceof PairXy p && obj instanceof ColorXy c) ? p.equals(c.getXY()) : false);
64+
}
6465
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Dimming.java

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import org.eclipse.jdt.annotation.NonNullByDefault;
1818
import org.eclipse.jdt.annotation.Nullable;
19-
import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
2019

2120
import com.google.gson.annotations.SerializedName;
2221

@@ -30,33 +29,15 @@ public class Dimming {
3029
private @Nullable Double brightness;
3130
private @Nullable @SerializedName("min_dim_level") Double minimumDimmingLevel;
3231

33-
public static final double DEFAULT_MINIMUM_DIMMIMG_LEVEL = 0.5f;
34-
35-
/**
36-
* @throws DTOPresentButEmptyException to indicate that the DTO is present but empty.
37-
*/
38-
public double getBrightness() throws DTOPresentButEmptyException {
39-
Double brightness = this.brightness;
40-
if (Objects.nonNull(brightness)) {
41-
return brightness;
42-
}
43-
throw new DTOPresentButEmptyException("'dimming' DTO is present but empty");
44-
}
45-
46-
public @Nullable Double getMinimumDimmingLevel() {
47-
return minimumDimmingLevel;
32+
public @Nullable Double getBrightness() {
33+
return brightness;
4834
}
4935

5036
public Dimming setBrightness(double brightness) {
5137
this.brightness = brightness;
5238
return this;
5339
}
5440

55-
public Dimming setMinimumDimmingLevel(Double minimumDimmingLevel) {
56-
this.minimumDimmingLevel = minimumDimmingLevel;
57-
return this;
58-
}
59-
6041
public @Nullable String toPropertyValue() {
6142
Double minimumDimmingLevel = this.minimumDimmingLevel;
6243
if (Objects.nonNull(minimumDimmingLevel)) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.hue.internal.api.dto.clip2;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ActionDeltaType;
18+
import org.openhab.core.library.types.IncreaseDecreaseType;
19+
20+
import com.google.gson.annotations.SerializedName;
21+
22+
/**
23+
* DTO for dimming delta of a light.
24+
*
25+
* @author Andrew Fiddian-Green - Initial contribution
26+
*/
27+
@NonNullByDefault
28+
public class DimmingDelta {
29+
private @Nullable String action;
30+
private @Nullable @SerializedName("brightness_delta") Double brightnessDelta;
31+
32+
public @Nullable String getAction() {
33+
return action;
34+
}
35+
36+
public @Nullable Double getBrightnessDelta() {
37+
return brightnessDelta;
38+
}
39+
40+
public DimmingDelta setAction(IncreaseDecreaseType action) {
41+
this.action = ActionDeltaType.of(action).name().toLowerCase();
42+
return this;
43+
}
44+
45+
public DimmingDelta setDelta(Double delta) {
46+
this.brightnessDelta = delta;
47+
return this;
48+
}
49+
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Error.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
package org.openhab.binding.hue.internal.api.dto.clip2;
1414

1515
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ErrorType;
17+
18+
import com.google.gson.annotations.SerializedName;
1619

1720
/**
1821
* DTO for CLIP 2 communication errors.
@@ -21,9 +24,14 @@
2124
*/
2225
@NonNullByDefault
2326
public class Error {
27+
private @SerializedName("error_code") @NonNullByDefault({}) String errorCode;
2428
private @NonNullByDefault({}) String description;
2529

2630
public String getDescription() {
2731
return description;
2832
}
33+
34+
public ErrorType getErrorType() {
35+
return ErrorType.of(errorCode);
36+
}
2937
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/MetaData.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public Archetype getArchetype() {
3535
return Archetype.of(archetype);
3636
}
3737

38+
public String getArchetypeAsString() {
39+
return getArchetype().toString();
40+
}
41+
3842
public @Nullable String getName() {
3943
return name;
4044
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/MirekSchema.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@
2727
*/
2828
@NonNullByDefault
2929
public class MirekSchema {
30-
private static final int MIN = 153;
31-
private static final int MAX = 500;
30+
private static final int MIN = 153; // ~6500K
31+
private static final int MAX = 500; // ~2000K
32+
33+
private static final int MIN_ALLOWED = 50; // ~20000K
34+
private static final int MAX_ALLOWED = 1000; // ~1000K
3235

3336
public static final MirekSchema DEFAULT_SCHEMA = new MirekSchema();
3437

@@ -49,6 +52,16 @@ private String toKelvin(int mirek) {
4952
}
5053

5154
public String toPropertyValue() {
52-
return String.format("%s .. %s", toKelvin(mirekMinimum), toKelvin(mirekMaximum));
55+
return invalid() //
56+
? "%dMk .. %dMk (INVALID)".formatted(mirekMinimum, mirekMaximum)
57+
: "%s .. %s".formatted(toKelvin(mirekMinimum), toKelvin(mirekMaximum));
58+
}
59+
60+
public static int toMirek(double kelvinValue) {
61+
return (int) Math.round(1000000.0 / kelvinValue);
62+
}
63+
64+
public boolean invalid() {
65+
return mirekMinimum < MIN_ALLOWED || mirekMaximum > MAX_ALLOWED || (mirekMaximum - mirekMinimum < 70);
5366
}
5467
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/OnState.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@
1212
*/
1313
package org.openhab.binding.hue.internal.api.dto.clip2;
1414

15-
import java.util.Objects;
16-
1715
import org.eclipse.jdt.annotation.NonNullByDefault;
1816
import org.eclipse.jdt.annotation.Nullable;
19-
import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
2017

2118
/**
2219
* DTO for 'on' state of a light.
@@ -27,15 +24,8 @@
2724
public class OnState {
2825
private @Nullable Boolean on;
2926

30-
/**
31-
* @throws DTOPresentButEmptyException to indicate that the DTO is present but empty.
32-
*/
33-
public boolean isOn() throws DTOPresentButEmptyException {
34-
Boolean on = this.on;
35-
if (Objects.nonNull(on)) {
36-
return on;
37-
}
38-
throw new DTOPresentButEmptyException("'on' DTO is present but empty");
27+
public @Nullable Boolean getOn() {
28+
return on;
3929
}
4030

4131
public OnState setOn(boolean on) {

0 commit comments

Comments
 (0)