Skip to content

Commit 477a0fb

Browse files
committed
Fix multiple state updates
Fixes #15700 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
1 parent 1c8ce7c commit 477a0fb

File tree

8 files changed

+426
-58
lines changed

8 files changed

+426
-58
lines changed

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

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,45 @@ public Resource(@Nullable ResourceType resourceType) {
121121
}
122122
}
123123

124+
/**
125+
* Create an empty clone of this object.
126+
* The clone will carry the same id, resource type and status.
127+
*
128+
* @return cloned object
129+
*/
130+
public Resource cloneEmpty() {
131+
Resource clonedResource = new Resource(getType());
132+
clonedResource.id = id;
133+
clonedResource.hasSparseData = true;
134+
135+
JsonElement status = this.status;
136+
clonedResource.status = status == null ? null : status.deepCopy();
137+
138+
return clonedResource;
139+
}
140+
141+
/**
142+
* Check if resource contains any state information.
143+
*
144+
* @return true is resource doesn't contain any state information
145+
*/
146+
public boolean isEmpty() {
147+
return metadata == null && on == null && dimming == null && colorTemperature == null && color == null
148+
&& alert == null && effects == null && timedEffects == null && actions == null && recall == null
149+
&& enabled == null && light == null && button == null && temperature == null && motion == null
150+
&& powerState == null && relativeRotary == null && contactReport == null && tamperReports == null
151+
&& status == null;
152+
}
153+
154+
/**
155+
* Check if resource contains any HSB component (on, dimming or color).
156+
*
157+
* @return true if resource has any HSB component
158+
*/
159+
public boolean hasHSBComponent() {
160+
return on != null || dimming != null || color != null;
161+
}
162+
124163
public @Nullable List<ActionEntry> getActions() {
125164
return actions;
126165
}
@@ -777,7 +816,7 @@ public Resource setColorTemperature(ColorTemperature colorTemperature) {
777816
return this;
778817
}
779818

780-
public Resource setColorXy(ColorXy color) {
819+
public Resource setColorXy(@Nullable ColorXy color) {
781820
this.color = color;
782821
return this;
783822
}
@@ -787,7 +826,7 @@ public Resource setContactReport(ContactReport contactReport) {
787826
return this;
788827
}
789828

790-
public Resource setDimming(Dimming dimming) {
829+
public Resource setDimming(@Nullable Dimming dimming) {
791830
this.dimming = dimming;
792831
return this;
793832
}
@@ -844,7 +883,7 @@ public Resource setOnOff(Command command) {
844883
return this;
845884
}
846885

847-
public void setOnState(OnState on) {
886+
public void setOnState(@Nullable OnState on) {
848887
this.on = on;
849888
}
850889

@@ -889,6 +928,11 @@ public Resource setType(ResourceType resourceType) {
889928
return this;
890929
}
891930

931+
public Resource setStatus(JsonElement status) {
932+
this.status = status.deepCopy();
933+
return this;
934+
}
935+
892936
@Override
893937
public String toString() {
894938
String id = this.id;

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@
1414

1515
import java.math.BigDecimal;
1616
import java.time.Duration;
17+
import java.util.Collection;
18+
import java.util.HashMap;
1719
import java.util.List;
20+
import java.util.Map;
1821
import java.util.Objects;
22+
import java.util.Set;
1923

2024
import javax.measure.Unit;
2125

@@ -33,6 +37,7 @@
3337
import org.openhab.binding.hue.internal.api.dto.clip2.TimedEffects;
3438
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ActionType;
3539
import org.openhab.binding.hue.internal.api.dto.clip2.enums.EffectType;
40+
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ResourceType;
3641
import org.openhab.core.library.types.DecimalType;
3742
import org.openhab.core.library.types.HSBType;
3843
import org.openhab.core.library.types.PercentType;
@@ -52,6 +57,8 @@
5257
@NonNullByDefault
5358
public class Setters {
5459

60+
private static final Set<ResourceType> LIGHT_TYPES = Set.of(ResourceType.LIGHT, ResourceType.GROUPED_LIGHT);
61+
5562
/**
5663
* Setter for Alert field:
5764
* Use the given command value to set the target resource DTO value based on the attributes of the source resource
@@ -341,7 +348,39 @@ public static Resource setResource(Resource target, Resource source) {
341348
targetTimedEffects.setDuration(duration);
342349
}
343350
}
344-
345351
return target;
346352
}
353+
354+
public static Collection<Resource> mergeLightResources(Collection<Resource> resources) {
355+
Map<String, Resource> extractedResources = new HashMap<>();
356+
for (Resource resource : resources) {
357+
if (!resource.hasFullState() && LIGHT_TYPES.contains(resource.getType()) && resource.hasHSBComponent()) {
358+
String id = resource.getId();
359+
Resource extractedResource = extractedResources.get(id);
360+
if (extractedResource == null) {
361+
extractedResource = resource.cloneEmpty();
362+
extractedResources.put(id, extractedResource);
363+
}
364+
OnState onState = resource.getOnState();
365+
if (onState != null) {
366+
extractedResource.setOnState(onState);
367+
resource.setOnState(null);
368+
}
369+
Dimming dimming = resource.getDimming();
370+
if (dimming != null) {
371+
extractedResource.setDimming(dimming);
372+
resource.setDimming(null);
373+
}
374+
ColorXy colorXy = resource.getColorXy();
375+
if (colorXy != null) {
376+
extractedResource.setColorXy(colorXy);
377+
resource.setColorXy(null);
378+
}
379+
}
380+
}
381+
resources.removeIf(r -> r.isEmpty());
382+
resources.addAll(extractedResources.values());
383+
384+
return resources;
385+
}
347386
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2BridgeHandler.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.openhab.binding.hue.internal.api.dto.clip2.Resources;
3838
import org.openhab.binding.hue.internal.api.dto.clip2.enums.Archetype;
3939
import org.openhab.binding.hue.internal.api.dto.clip2.enums.ResourceType;
40+
import org.openhab.binding.hue.internal.api.dto.clip2.helper.Setters;
4041
import org.openhab.binding.hue.internal.config.Clip2BridgeConfig;
4142
import org.openhab.binding.hue.internal.connection.Clip2Bridge;
4243
import org.openhab.binding.hue.internal.connection.HueTlsTrustManagerProvider;
@@ -528,13 +529,15 @@ public void onResourcesEvent(List<Resource> resources) {
528529
}
529530

530531
private void onResourcesEventTask(List<Resource> resources) {
531-
logger.debug("onResourcesEventTask() resource count {}", resources.size());
532+
int numberOfResources = resources.size();
533+
logger.debug("onResourcesEventTask() resource count {}", numberOfResources);
534+
Setters.mergeLightResources(resources);
535+
if (numberOfResources != resources.size()) {
536+
logger.debug("onResourcesEventTask() merged to {} resources", resources.size());
537+
}
532538
getThing().getThings().forEach(thing -> {
533-
ThingHandler handler = thing.getHandler();
534-
if (handler instanceof Clip2ThingHandler) {
535-
resources.forEach(resource -> {
536-
((Clip2ThingHandler) handler).onResource(resource);
537-
});
539+
if (thing.getHandler() instanceof Clip2ThingHandler clip2ThingHandler) {
540+
resources.forEach(resource -> clip2ThingHandler.onResource(resource));
538541
}
539542
});
540543
}

bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/Clip2ThingHandler.java

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -635,46 +635,53 @@ public void initialize() {
635635
* @param resource a Resource object containing the new state.
636636
*/
637637
public void onResource(Resource resource) {
638-
if (!disposing) {
639-
boolean resourceConsumed = false;
640-
String incomingResourceId = resource.getId();
641-
if (resourceId.equals(incomingResourceId)) {
642-
if (resource.hasFullState()) {
643-
thisResource = resource;
644-
if (!updatePropertiesDone) {
645-
updateProperties(resource);
646-
resourceConsumed = updatePropertiesDone;
647-
}
648-
}
649-
if (!updateDependenciesDone) {
650-
resourceConsumed = true;
651-
cancelTask(updateDependenciesTask, false);
652-
updateDependenciesTask = scheduler.submit(() -> updateDependencies());
653-
}
654-
} else if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) {
655-
Resource cachedScene = sceneContributorsCache.get(incomingResourceId);
656-
if (Objects.nonNull(cachedScene)) {
657-
Setters.setResource(resource, cachedScene);
658-
resourceConsumed = updateChannels(resource);
659-
sceneContributorsCache.put(incomingResourceId, resource);
660-
}
661-
} else {
662-
Resource cachedService = serviceContributorsCache.get(incomingResourceId);
663-
if (Objects.nonNull(cachedService)) {
664-
Setters.setResource(resource, cachedService);
665-
resourceConsumed = updateChannels(resource);
666-
serviceContributorsCache.put(incomingResourceId, resource);
667-
if (ResourceType.LIGHT == resource.getType() && !updateLightPropertiesDone) {
668-
updateLightProperties(resource);
669-
}
638+
if (disposing) {
639+
return;
640+
}
641+
boolean resourceConsumed = false;
642+
if (resourceId.equals(resource.getId())) {
643+
if (resource.hasFullState()) {
644+
thisResource = resource;
645+
if (!updatePropertiesDone) {
646+
updateProperties(resource);
647+
resourceConsumed = updatePropertiesDone;
670648
}
671649
}
672-
if (resourceConsumed) {
673-
logger.debug("{} -> onResource() consumed resource {}", resourceId, resource);
650+
if (!updateDependenciesDone) {
651+
resourceConsumed = true;
652+
cancelTask(updateDependenciesTask, false);
653+
updateDependenciesTask = scheduler.submit(() -> updateDependencies());
674654
}
655+
} else {
656+
Resource cachedResource = getResourceFromCache(resource);
657+
if (cachedResource != null) {
658+
Setters.setResource(resource, cachedResource);
659+
resourceConsumed = updateChannels(resource);
660+
putResourceToCache(resource);
661+
if (ResourceType.LIGHT == resource.getType() && !updateLightPropertiesDone) {
662+
updateLightProperties(resource);
663+
}
664+
}
665+
}
666+
if (resourceConsumed) {
667+
logger.debug("{} -> onResource() consumed resource {}", resourceId, resource);
668+
}
669+
}
670+
671+
private void putResourceToCache(Resource resource) {
672+
if (SUPPORTED_SCENE_TYPES.contains(resource.getType())) {
673+
sceneContributorsCache.put(resource.getId(), resource);
674+
} else {
675+
serviceContributorsCache.put(resource.getId(), resource);
675676
}
676677
}
677678

679+
private @Nullable Resource getResourceFromCache(Resource resource) {
680+
return SUPPORTED_SCENE_TYPES.contains(resource.getType()) //
681+
? sceneContributorsCache.get(resource.getId())
682+
: serviceContributorsCache.get(resource.getId());
683+
}
684+
678685
/**
679686
* Update the thing internal state depending on a full list of resources sent from the bridge. If the resourceType
680687
* is SCENE then call updateScenes(), otherwise if the resource refers to this thing, consume it via onResource() as

bundles/org.openhab.binding.hue/src/main/resources/OH-INF/thing/Clip2Thing.xml

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@
1313
<description>A Hue API v2 device with channels depending on its actual capabilities.</description>
1414

1515
<channels>
16-
<channel id="color" typeId="system.color"/>
17-
<channel id="color-temperature" typeId="system.color-temperature"/>
18-
<channel id="brightness" typeId="system.brightness"/>
19-
<channel id="switch" typeId="system.power"/>
16+
<channel id="color" typeId="system.color">
17+
<autoUpdatePolicy>veto</autoUpdatePolicy>
18+
</channel>
19+
<channel id="color-temperature" typeId="system.color-temperature">
20+
<autoUpdatePolicy>veto</autoUpdatePolicy>
21+
</channel>
22+
<channel id="brightness" typeId="system.brightness">
23+
<autoUpdatePolicy>veto</autoUpdatePolicy>
24+
</channel>
25+
<channel id="switch" typeId="system.power">
26+
<autoUpdatePolicy>veto</autoUpdatePolicy>
27+
</channel>
2028
<channel id="alert" typeId="alert-v2">
2129
<description>Activate the alert for the light.</description>
2230
</channel>
@@ -85,20 +93,16 @@
8593
<channel id="battery-low" typeId="system.low-battery"/>
8694
<channel id="last-updated" typeId="last-updated-v2"/>
8795
<channel id="dynamics" typeId="dynamics"/>
88-
<channel id="color-temperature-abs" typeId="system.color-temperature-abs"/>
89-
<channel id="color-xy-only" typeId="advanced-color">
90-
<description>Set the color xy parameter of the light without changing other state parameters.</description>
91-
</channel>
92-
<channel id="dimming-only" typeId="advanced-brightness">
93-
<description>Set the dimming parameter of the light without changing other state parameters.</description>
94-
</channel>
95-
<channel id="on-off-only" typeId="advanced-power">
96-
<description>Set the on/off parameter of the light without changing other state parameters.</description>
96+
<channel id="color-temperature-abs" typeId="system.color-temperature-abs">
97+
<autoUpdatePolicy>veto</autoUpdatePolicy>
9798
</channel>
99+
<channel id="color-xy-only" typeId="advanced-color"/>
100+
<channel id="dimming-only" typeId="advanced-brightness"/>
101+
<channel id="on-off-only" typeId="advanced-power"/>
98102
</channels>
99103

100104
<properties>
101-
<property name="thingTypeVersion">1</property>
105+
<property name="thingTypeVersion">2</property>
102106
</properties>
103107

104108
<representation-property>resourceId</representation-property>

bundles/org.openhab.binding.hue/src/main/resources/OH-INF/thing/channels.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,20 +240,26 @@
240240
<channel-type id="advanced-color" advanced="true">
241241
<item-type>Color</item-type>
242242
<label>Color XY Only</label>
243+
<description>Set the color xy parameter of the light without changing other state parameters.</description>
243244
<category>ColorLight</category>
245+
<autoUpdatePolicy>veto</autoUpdatePolicy>
244246
</channel-type>
245247

246248
<channel-type id="advanced-brightness" advanced="true">
247249
<item-type>Dimmer</item-type>
248250
<label>Dimming Only</label>
251+
<description>Set the dimming parameter of the light without changing other state parameters.</description>
249252
<category>Light</category>
250253
<state pattern="%.1f %%"/>
254+
<autoUpdatePolicy>veto</autoUpdatePolicy>
251255
</channel-type>
252256

253257
<channel-type id="advanced-power" advanced="true">
254258
<item-type>Switch</item-type>
255259
<label>On/Off Only</label>
260+
<description>Set the on/off parameter of the light without changing other state parameters.</description>
256261
<category>Switch</category>
262+
<autoUpdatePolicy>veto</autoUpdatePolicy>
257263
</channel-type>
258264

259265
<channel-type id="security-contact">

bundles/org.openhab.binding.hue/src/main/resources/OH-INF/update/instructions.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@
3333
</add-channel>
3434
</instruction-set>
3535

36+
<instruction-set targetVersion="2">
37+
<update-channel id="brightness">
38+
<type>system:brightness</type>
39+
</update-channel>
40+
<update-channel id="color">
41+
<type>system:color</type>
42+
</update-channel>
43+
<update-channel id="color-temperature">
44+
<type>system:color-temperature</type>
45+
</update-channel>
46+
<update-channel id="color-temperature-abs">
47+
<type>system:color-temperature-abs</type>
48+
</update-channel>
49+
<update-channel id="switch">
50+
<type>system:power</type>
51+
</update-channel>
52+
</instruction-set>
53+
3654
</thing-type>
3755

3856
</update:update-descriptions>

0 commit comments

Comments
 (0)