Skip to content

Commit 2080c85

Browse files
authored
[various] Lamp handlers expose min/max Color Temperature in state description (openhab#17641)
* Lamp handlers expose min/max Colour Temperature in state description * add color temperature validit and range checks Signed-off-by: AndrewFG <[email protected]>
1 parent f4250b7 commit 2080c85

File tree

10 files changed

+260
-30
lines changed

10 files changed

+260
-30
lines changed

bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/channel-types.xml

+12
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,16 @@
241241
<description>Controls the rollershutter and states its opening level in percent</description>
242242
</channel-type>
243243

244+
<channel-type id="color-temperature-abs" advanced="true">
245+
<item-type>Number:Temperature</item-type>
246+
<label>Color Temperature</label>
247+
<description>Controls the color temperature of the light in Kelvin</description>
248+
<category>ColorLight</category>
249+
<tags>
250+
<tag>Control</tag>
251+
<tag>ColorTemperature</tag>
252+
</tags>
253+
<state min="2700" max="6500" pattern="%.0f K"/>
254+
</channel-type>
255+
244256
</thing:thing-descriptions>

bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/thing-types.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
<channels>
1717
<channel id="color" typeId="system.color"/>
1818
<channel id="color_temperature" typeId="system.color-temperature"/>
19-
<channel id="color_temperature_abs" typeId="system.color-temperature-abs"/>
19+
<channel id="color_temperature_abs" typeId="color-temperature-abs"/>
2020
</channels>
2121

2222
<properties>
23-
<property name="thingTypeVersion">1</property>
23+
<property name="thingTypeVersion">2</property>
2424
</properties>
2525

2626
<representation-property>ain</representation-property>
@@ -418,11 +418,11 @@
418418
<channels>
419419
<channel id="color" typeId="system.color"/>
420420
<channel id="color_temperature" typeId="system.color-temperature"/>
421-
<channel id="color_temperature_abs" typeId="system.color-temperature-abs"/>
421+
<channel id="color_temperature_abs" typeId="color-temperature-abs"/>
422422
</channels>
423423

424424
<properties>
425-
<property name="thingTypeVersion">1</property>
425+
<property name="thingTypeVersion">2</property>
426426
</properties>
427427

428428
<representation-property>ain</representation-property>

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

+10
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@
166166
<type>system:color-temperature-abs</type>
167167
</add-channel>
168168
</instruction-set>
169+
<instruction-set targetVersion="2">
170+
<update-channel id="color_temperature_abs">
171+
<type>avmfritz:color-temperature-abs</type>
172+
</update-channel>
173+
</instruction-set>
169174
</thing-type>
170175

171176
<thing-type uid="avmfritz:HAN_FUN_COLOR_BULB">
@@ -177,6 +182,11 @@
177182
<type>system:color-temperature-abs</type>
178183
</add-channel>
179184
</instruction-set>
185+
<instruction-set targetVersion="2">
186+
<update-channel id="color_temperature_abs">
187+
<type>avmfritz:color-temperature-abs</type>
188+
</update-channel>
189+
</instruction-set>
180190
</thing-type>
181191

182192
</update:update-descriptions>

bundles/org.openhab.binding.govee/src/main/resources/OH-INF/thing/thing-types.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616
<config-description-ref uri="thing-type:govee:govee-light"/>
1717
</thing-type>
1818

19-
<channel-type id="color-temperature-abs">
19+
20+
<channel-type id="color-temperature-abs" advanced="true">
2021
<item-type>Number:Temperature</item-type>
21-
<label>Absolute Color Temperature </label>
22+
<label>Color Temperature</label>
2223
<description>Controls the color temperature of the light in Kelvin</description>
23-
<category>Temperature</category>
24+
<category>ColorLight</category>
2425
<tags>
2526
<tag>Control</tag>
2627
<tag>ColorTemperature</tag>
2728
</tags>
2829
<state min="2000" max="9000" pattern="%.0f K"/>
2930
</channel-type>
3031

31-
3232
</thing:thing-descriptions>

bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/LifxHandlerFactory.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
import org.eclipse.jdt.annotation.NonNullByDefault;
1818
import org.eclipse.jdt.annotation.Nullable;
1919
import org.openhab.binding.lifx.internal.handler.LifxLightHandler;
20+
import org.openhab.binding.lifx.internal.handler.LifxStateDescriptionProvider;
2021
import org.openhab.core.thing.Thing;
2122
import org.openhab.core.thing.ThingTypeUID;
2223
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
2324
import org.openhab.core.thing.binding.ThingHandler;
2425
import org.openhab.core.thing.binding.ThingHandlerFactory;
2526
import org.osgi.service.component.ComponentContext;
27+
import org.osgi.service.component.annotations.Activate;
2628
import org.osgi.service.component.annotations.Component;
2729
import org.osgi.service.component.annotations.Reference;
2830

@@ -37,6 +39,12 @@
3739
public class LifxHandlerFactory extends BaseThingHandlerFactory {
3840

3941
private @NonNullByDefault({}) LifxChannelFactory channelFactory;
42+
private final LifxStateDescriptionProvider stateDescriptionProvider;
43+
44+
@Activate
45+
public LifxHandlerFactory(@Reference LifxStateDescriptionProvider stateDescriptionProvider) {
46+
this.stateDescriptionProvider = stateDescriptionProvider;
47+
}
4048

4149
@Override
4250
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
@@ -51,7 +59,7 @@ protected void activate(ComponentContext componentContext) {
5159
@Override
5260
protected @Nullable ThingHandler createHandler(Thing thing) {
5361
if (supportsThingType(thing.getThingTypeUID())) {
54-
return new LifxLightHandler(thing, channelFactory);
62+
return new LifxLightHandler(thing, channelFactory, stateDescriptionProvider);
5563
}
5664

5765
return null;

bundles/org.openhab.binding.lifx/src/main/java/org/openhab/binding/lifx/internal/handler/LifxLightHandler.java

+22-4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.openhab.binding.lifx.internal.LifxLightStateChanger;
4343
import org.openhab.binding.lifx.internal.LifxProduct;
4444
import org.openhab.binding.lifx.internal.LifxProduct.Features;
45+
import org.openhab.binding.lifx.internal.LifxProduct.TemperatureRange;
4546
import org.openhab.binding.lifx.internal.dto.Effect;
4647
import org.openhab.binding.lifx.internal.dto.GetHevCycleRequest;
4748
import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest;
@@ -74,6 +75,7 @@
7475
import org.openhab.core.types.Command;
7576
import org.openhab.core.types.RefreshType;
7677
import org.openhab.core.types.State;
78+
import org.openhab.core.types.UnDefType;
7779
import org.slf4j.Logger;
7880
import org.slf4j.LoggerFactory;
7981

@@ -97,6 +99,8 @@ public class LifxLightHandler extends BaseThingHandler {
9799
private static final Duration MAX_STATE_CHANGE_DURATION = Duration.ofSeconds(4);
98100

99101
private final LifxChannelFactory channelFactory;
102+
private final LifxStateDescriptionProvider stateDescriptionProvider;
103+
100104
private @NonNullByDefault({}) Features features;
101105

102106
private Duration hevCycleDuration = Duration.ZERO;
@@ -180,11 +184,23 @@ private void updateColorChannels(@Nullable PowerState powerState, HSBK[] colors)
180184
HSBK updateColor = nullSafeUpdateColor(powerState, color);
181185
HSBType hsb = updateColor.getHSB();
182186

187+
State colorTemperatureState = UnDefType.UNDEF;
188+
State colorTemperatureAbsoluteState = UnDefType.UNDEF;
189+
TemperatureRange temperatureRange = features.getTemperatureRange();
190+
if (temperatureRange.getRange() > 0) {
191+
stateDescriptionProvider.setMinMaxKelvin(new ChannelUID(thing.getUID(), CHANNEL_ABS_TEMPERATURE),
192+
temperatureRange.getMinimum(), temperatureRange.getMaximum());
193+
colorTemperatureState = kelvinToPercentType(updateColor.getKelvin(), temperatureRange);
194+
colorTemperatureAbsoluteState = QuantityType.valueOf(updateColor.getKelvin(), Units.KELVIN);
195+
} else {
196+
logger.warn("Thing {} invalid color temperature range {} .. {}", thing.getUID(),
197+
temperatureRange.getMinimum(), temperatureRange.getMaximum());
198+
}
199+
183200
updateStateIfChanged(CHANNEL_COLOR, hsb);
184201
updateStateIfChanged(CHANNEL_BRIGHTNESS, hsb.getBrightness());
185-
updateStateIfChanged(CHANNEL_TEMPERATURE,
186-
kelvinToPercentType(updateColor.getKelvin(), features.getTemperatureRange()));
187-
updateStateIfChanged(CHANNEL_ABS_TEMPERATURE, new QuantityType(updateColor.getKelvin(), Units.KELVIN));
202+
updateStateIfChanged(CHANNEL_TEMPERATURE, colorTemperatureState);
203+
updateStateIfChanged(CHANNEL_ABS_TEMPERATURE, colorTemperatureAbsoluteState);
188204

189205
updateZoneChannels(powerState, colors);
190206
}
@@ -249,9 +265,11 @@ private void updateZoneChannels(@Nullable PowerState powerState, HSBK[] colors)
249265
}
250266
}
251267

252-
public LifxLightHandler(Thing thing, LifxChannelFactory channelFactory) {
268+
public LifxLightHandler(Thing thing, LifxChannelFactory channelFactory,
269+
LifxStateDescriptionProvider stateDescriptionProvider) {
253270
super(thing);
254271
this.channelFactory = channelFactory;
272+
this.stateDescriptionProvider = stateDescriptionProvider;
255273
}
256274

257275
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Copyright (c) 2010-2024 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.lifx.internal.handler;
14+
15+
import java.math.BigDecimal;
16+
import java.util.Locale;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.concurrent.ConcurrentHashMap;
20+
21+
import org.eclipse.jdt.annotation.NonNullByDefault;
22+
import org.eclipse.jdt.annotation.Nullable;
23+
import org.openhab.core.events.EventPublisher;
24+
import org.openhab.core.thing.Channel;
25+
import org.openhab.core.thing.ChannelUID;
26+
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
27+
import org.openhab.core.thing.events.ThingEventFactory;
28+
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
29+
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
30+
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
31+
import org.openhab.core.types.StateDescription;
32+
import org.openhab.core.types.StateDescriptionFragment;
33+
import org.openhab.core.types.StateDescriptionFragmentBuilder;
34+
import org.osgi.service.component.annotations.Activate;
35+
import org.osgi.service.component.annotations.Component;
36+
import org.osgi.service.component.annotations.Reference;
37+
38+
/**
39+
* The {@link LifxStateDescriptionProvider} provides dynamic state description minimum and maximum vales of color
40+
* temperature channels whose capabilities are dynamically determined at runtime.
41+
*
42+
* @author Andrew Fiddian-Green - Initial contribution
43+
*
44+
*/
45+
@NonNullByDefault
46+
@Component(service = { DynamicStateDescriptionProvider.class, LifxStateDescriptionProvider.class })
47+
public class LifxStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
48+
49+
private final Map<ChannelUID, StateDescriptionFragment> stateDescriptionFragments = new ConcurrentHashMap<>();
50+
51+
@Activate
52+
public LifxStateDescriptionProvider(final @Reference EventPublisher eventPublisher,
53+
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
54+
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
55+
this.eventPublisher = eventPublisher;
56+
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
57+
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
58+
}
59+
60+
@Override
61+
public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
62+
@Nullable Locale locale) {
63+
StateDescriptionFragment stateDescriptionFragment = stateDescriptionFragments.get(channel.getUID());
64+
return stateDescriptionFragment != null ? stateDescriptionFragment.toStateDescription()
65+
: super.getStateDescription(channel, original, locale);
66+
}
67+
68+
/**
69+
* Set the state description minimum and maximum values and pattern in Kelvin for the given channel UID
70+
*/
71+
public void setMinMaxKelvin(ChannelUID channelUID, long minKelvin, long maxKelvin) {
72+
StateDescriptionFragment oldStateDescriptionFragment = stateDescriptionFragments.get(channelUID);
73+
StateDescriptionFragment newStateDescriptionFragment = StateDescriptionFragmentBuilder.create()
74+
.withMinimum(BigDecimal.valueOf(minKelvin)).withMaximum(BigDecimal.valueOf(maxKelvin))
75+
.withStep(BigDecimal.valueOf(100)).withPattern("%.0f K").build();
76+
if (!newStateDescriptionFragment.equals(oldStateDescriptionFragment)) {
77+
stateDescriptionFragments.put(channelUID, newStateDescriptionFragment);
78+
ItemChannelLinkRegistry itemChannelLinkRegistry = this.itemChannelLinkRegistry;
79+
postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID,
80+
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
81+
newStateDescriptionFragment, oldStateDescriptionFragment));
82+
}
83+
}
84+
}

bundles/org.openhab.binding.nanoleaf/src/main/java/org/openhab/binding/nanoleaf/internal/NanoleafHandlerFactory.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.eclipse.jdt.annotation.NonNullByDefault;
2121
import org.eclipse.jdt.annotation.Nullable;
22+
import org.openhab.binding.nanoleaf.internal.handler.NanoLeafStateDescriptionProvider;
2223
import org.openhab.binding.nanoleaf.internal.handler.NanoleafControllerHandler;
2324
import org.openhab.binding.nanoleaf.internal.handler.NanoleafPanelHandler;
2425
import org.openhab.core.io.net.http.HttpClientFactory;
@@ -51,10 +52,13 @@ public class NanoleafHandlerFactory extends BaseThingHandlerFactory {
5152

5253
private final Logger logger = LoggerFactory.getLogger(NanoleafHandlerFactory.class);
5354
private final HttpClientFactory httpClientFactory;
55+
private final NanoLeafStateDescriptionProvider nanoLeafStateDescriptionProvider;
5456

5557
@Activate
56-
public NanoleafHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
58+
public NanoleafHandlerFactory(@Reference HttpClientFactory httpClientFactory,
59+
@Reference NanoLeafStateDescriptionProvider nanoLeafStateDescriptionProvider) {
5760
this.httpClientFactory = httpClientFactory;
61+
this.nanoLeafStateDescriptionProvider = nanoLeafStateDescriptionProvider;
5862
}
5963

6064
@Override
@@ -67,7 +71,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
6771
protected ThingHandler createHandler(Thing thing) {
6872
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
6973
if (NanoleafBindingConstants.THING_TYPE_CONTROLLER.equals(thingTypeUID)) {
70-
NanoleafControllerHandler handler = new NanoleafControllerHandler((Bridge) thing, this.httpClientFactory);
74+
NanoleafControllerHandler handler = new NanoleafControllerHandler((Bridge) thing, this.httpClientFactory,
75+
this.nanoLeafStateDescriptionProvider);
7176
logger.debug("Nanoleaf controller handler created.");
7277
return handler;
7378
} else if (NanoleafBindingConstants.THING_TYPE_LIGHT_PANEL.equals(thingTypeUID)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Copyright (c) 2010-2024 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.nanoleaf.internal.handler;
14+
15+
import java.math.BigDecimal;
16+
import java.util.Locale;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.concurrent.ConcurrentHashMap;
20+
21+
import org.eclipse.jdt.annotation.NonNullByDefault;
22+
import org.eclipse.jdt.annotation.Nullable;
23+
import org.openhab.core.events.EventPublisher;
24+
import org.openhab.core.thing.Channel;
25+
import org.openhab.core.thing.ChannelUID;
26+
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
27+
import org.openhab.core.thing.events.ThingEventFactory;
28+
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
29+
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
30+
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
31+
import org.openhab.core.types.StateDescription;
32+
import org.openhab.core.types.StateDescriptionFragment;
33+
import org.openhab.core.types.StateDescriptionFragmentBuilder;
34+
import org.osgi.service.component.annotations.Activate;
35+
import org.osgi.service.component.annotations.Component;
36+
import org.osgi.service.component.annotations.Reference;
37+
38+
/**
39+
* The {@link NanoLeafStateDescriptionProvider} provides dynamic state description minimum and maximum vales of color
40+
* temperature channels whose capabilities are dynamically determined at runtime.
41+
*
42+
* @author Andrew Fiddian-Green - Initial contribution
43+
*
44+
*/
45+
@NonNullByDefault
46+
@Component(service = { DynamicStateDescriptionProvider.class, NanoLeafStateDescriptionProvider.class })
47+
public class NanoLeafStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
48+
49+
private final Map<ChannelUID, StateDescriptionFragment> stateDescriptionFragments = new ConcurrentHashMap<>();
50+
51+
@Activate
52+
public NanoLeafStateDescriptionProvider(final @Reference EventPublisher eventPublisher,
53+
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
54+
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
55+
this.eventPublisher = eventPublisher;
56+
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
57+
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
58+
}
59+
60+
@Override
61+
public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
62+
@Nullable Locale locale) {
63+
StateDescriptionFragment stateDescriptionFragment = stateDescriptionFragments.get(channel.getUID());
64+
return stateDescriptionFragment != null ? stateDescriptionFragment.toStateDescription()
65+
: super.getStateDescription(channel, original, locale);
66+
}
67+
68+
/**
69+
* Set the state description minimum and maximum values and pattern in Kelvin for the given channel UID
70+
*/
71+
public void setMinMaxKelvin(ChannelUID channelUID, long minKelvin, long maxKelvin) {
72+
StateDescriptionFragment oldStateDescriptionFragment = stateDescriptionFragments.get(channelUID);
73+
StateDescriptionFragment newStateDescriptionFragment = StateDescriptionFragmentBuilder.create()
74+
.withMinimum(BigDecimal.valueOf(minKelvin)).withMaximum(BigDecimal.valueOf(maxKelvin))
75+
.withStep(BigDecimal.valueOf(100)).withPattern("%.0f K").build();
76+
if (!newStateDescriptionFragment.equals(oldStateDescriptionFragment)) {
77+
stateDescriptionFragments.put(channelUID, newStateDescriptionFragment);
78+
ItemChannelLinkRegistry itemChannelLinkRegistry = this.itemChannelLinkRegistry;
79+
postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID,
80+
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
81+
newStateDescriptionFragment, oldStateDescriptionFragment));
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)