2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.hue.internal.dto.clip2.helper;
15 import java.math.BigDecimal;
16 import java.util.List;
17 import java.util.Objects;
19 import javax.measure.Unit;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.hue.internal.dto.clip2.Alerts;
24 import org.openhab.binding.hue.internal.dto.clip2.ColorTemperature;
25 import org.openhab.binding.hue.internal.dto.clip2.ColorXy;
26 import org.openhab.binding.hue.internal.dto.clip2.Dimming;
27 import org.openhab.binding.hue.internal.dto.clip2.Effects;
28 import org.openhab.binding.hue.internal.dto.clip2.MetaData;
29 import org.openhab.binding.hue.internal.dto.clip2.MirekSchema;
30 import org.openhab.binding.hue.internal.dto.clip2.OnState;
31 import org.openhab.binding.hue.internal.dto.clip2.Resource;
32 import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
33 import org.openhab.binding.hue.internal.dto.clip2.enums.EffectType;
34 import org.openhab.core.library.types.DecimalType;
35 import org.openhab.core.library.types.HSBType;
36 import org.openhab.core.library.types.PercentType;
37 import org.openhab.core.library.types.QuantityType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.library.unit.Units;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.util.ColorUtil;
42 import org.openhab.core.util.ColorUtil.Gamut;
45 * Advanced setter methods for fields in the Resource class for special cases where setting the new value in the target
46 * resource depends on logic using the values of other fields in a another source Resource.
48 * @author Andrew Fiddian-Green - Initial contribution
51 public class Setters {
54 * Setter for Alert field:
55 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
58 * @param target the target resource.
59 * @param command the new state command should be a StringType.
60 * @param source another resource containing the allowed alert action values.
62 * @return the target resource.
64 public static Resource setAlert(Resource target, Command command, @Nullable Resource source) {
65 if ((command instanceof StringType) && Objects.nonNull(source)) {
66 Alerts otherAlert = source.getAlerts();
67 if (Objects.nonNull(otherAlert)) {
68 ActionType actionType = ActionType.of(((StringType) command).toString());
69 if (otherAlert.getActionValues().contains(actionType)) {
70 target.setAlerts(new Alerts().setAction(actionType));
78 * Setter for Color Temperature field:
79 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
82 * @param target the target resource.
83 * @param command the new state command should be a {@code QuantityType<Temperature>} (but it can also handle
84 * {@code DecimalType}).
85 * @param source another resource containing the MirekSchema.
87 * @return the target resource.
89 public static Resource setColorTemperatureAbsolute(Resource target, Command command, @Nullable Resource source) {
90 QuantityType<?> mirek;
91 if (command instanceof QuantityType<?>) {
92 QuantityType<?> quantity = (QuantityType<?>) command;
93 Unit<?> unit = quantity.getUnit();
94 if (Units.KELVIN.equals(unit)) {
95 mirek = quantity.toInvertibleUnit(Units.MIRED);
96 } else if (Units.MIRED.equals(unit)) {
99 QuantityType<?> kelvin = quantity.toInvertibleUnit(Units.KELVIN);
100 mirek = Objects.nonNull(kelvin) ? kelvin.toInvertibleUnit(Units.MIRED) : null;
102 } else if (command instanceof DecimalType) {
103 mirek = QuantityType.valueOf(((DecimalType) command).doubleValue(), Units.KELVIN)
104 .toInvertibleUnit(Units.MIRED);
108 if (Objects.nonNull(mirek)) {
109 MirekSchema schema = target.getMirekSchema();
110 schema = Objects.nonNull(schema) ? schema : Objects.nonNull(source) ? source.getMirekSchema() : null;
111 schema = Objects.nonNull(schema) ? schema : MirekSchema.DEFAULT_SCHEMA;
112 ColorTemperature colorTemperature = target.getColorTemperature();
113 colorTemperature = Objects.nonNull(colorTemperature) ? colorTemperature : new ColorTemperature();
114 double min = schema.getMirekMinimum();
115 double max = schema.getMirekMaximum();
116 double val = Math.max(min, Math.min(max, mirek.doubleValue()));
117 target.setColorTemperature(colorTemperature.setMirek(val));
123 * Setter for Color Temperature field:
124 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
127 * @param target the target resource.
128 * @param command the new state command should be a PercentType.
129 * @param source another resource containing the MirekSchema.
131 * @return the target resource.
133 public static Resource setColorTemperaturePercent(Resource target, Command command, @Nullable Resource source) {
134 if (command instanceof PercentType) {
135 MirekSchema schema = target.getMirekSchema();
136 schema = Objects.nonNull(schema) ? schema : Objects.nonNull(source) ? source.getMirekSchema() : null;
137 schema = Objects.nonNull(schema) ? schema : MirekSchema.DEFAULT_SCHEMA;
138 ColorTemperature colorTemperature = target.getColorTemperature();
139 colorTemperature = Objects.nonNull(colorTemperature) ? colorTemperature : new ColorTemperature();
140 double min = schema.getMirekMinimum();
141 double max = schema.getMirekMaximum();
142 double val = min + ((max - min) * ((PercentType) command).doubleValue() / 100f);
143 target.setColorTemperature(colorTemperature.setMirek(val));
149 * Setter for Color Xy field:
150 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
151 * (if any). Use the HS parts of the HSB value to set the value of the 'ColorXy' JSON element, and ignore the 'B'
154 * @param target the target resource.
155 * @param command the new state command should be an HSBType with the new color XY value.
156 * @param source another resource containing the color Gamut.
158 * @return the target resource.
160 public static Resource setColorXy(Resource target, Command command, @Nullable Resource source) {
161 if (command instanceof HSBType) {
162 Gamut gamut = target.getGamut();
163 gamut = Objects.nonNull(gamut) ? gamut : Objects.nonNull(source) ? source.getGamut() : null;
164 gamut = Objects.nonNull(gamut) ? gamut : ColorUtil.DEFAULT_GAMUT;
165 HSBType hsb = (HSBType) command;
166 ColorXy color = target.getColorXy();
167 target.setColorXy((Objects.nonNull(color) ? color : new ColorXy()).setXY(ColorUtil.hsbToXY(hsb, gamut)));
173 * Setter for Dimming field:
174 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
177 * @param target the target resource.
178 * @param command the new state command should be a PercentType with the new dimming parameter.
179 * @param source another resource containing the minimum dimming level.
181 * @return the target resource.
183 public static Resource setDimming(Resource target, Command command, @Nullable Resource source) {
184 if (command instanceof PercentType) {
185 Double min = target.getMinimumDimmingLevel();
186 min = Objects.nonNull(min) ? min : Objects.nonNull(source) ? source.getMinimumDimmingLevel() : null;
187 min = Objects.nonNull(min) ? min : Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL;
188 PercentType brightness = (PercentType) command;
189 if (brightness.doubleValue() < min.doubleValue()) {
190 brightness = new PercentType(new BigDecimal(min, Resource.PERCENT_MATH_CONTEXT));
192 Dimming dimming = target.getDimming();
193 dimming = Objects.nonNull(dimming) ? dimming : new Dimming();
194 dimming.setBrightness(brightness.doubleValue());
195 target.setDimming(dimming);
201 * Setter for Effect field:
202 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
205 * @param target the target resource.
206 * @param command the new state command should be a StringType.
207 * @param source another resource containing the allowed effect action values.
209 * @return the target resource.
211 public static Resource setEffect(Resource target, Command command, @Nullable Resource source) {
212 if ((command instanceof StringType) && Objects.nonNull(source)) {
213 Effects otherEffects = source.getEffects();
214 if (Objects.nonNull(otherEffects)) {
215 EffectType effectType = EffectType.of(((StringType) command).toString());
216 if (otherEffects.allows(effectType)) {
217 target.setEffects(new Effects().setEffect(effectType));
225 * Setter to copy persisted fields from the source Resource into the target Resource. If the field in the target is
226 * null and the same field in the source is not null, then the value from the source is copied to the target. This
227 * method allows 'hasSparseData' resources to expand themselves to include necessary fields taken over from a
228 * previously cached full data resource.
230 * @param target the target resource.
231 * @param source another resource containing the values to be taken over.
233 * @return the target resource.
235 public static Resource setResource(Resource target, Resource source) {
237 OnState targetOnOff = target.getOnState();
238 OnState sourceOnOff = source.getOnState();
239 if (Objects.isNull(targetOnOff) && Objects.nonNull(sourceOnOff)) {
240 target.setOnState(sourceOnOff);
243 Dimming targetDimming = target.getDimming();
244 Dimming sourceDimming = source.getDimming();
245 if (Objects.isNull(targetDimming) && Objects.nonNull(sourceDimming)) {
246 target.setDimming(sourceDimming);
247 targetDimming = target.getDimming();
249 // minimum dimming level
250 Double targetMinDimmingLevel = Objects.nonNull(targetDimming) ? targetDimming.getMinimumDimmingLevel() : null;
251 Double sourceMinDimmingLevel = Objects.nonNull(sourceDimming) ? sourceDimming.getMinimumDimmingLevel() : null;
252 if (Objects.isNull(targetMinDimmingLevel) && Objects.nonNull(sourceMinDimmingLevel)) {
253 targetDimming = Objects.nonNull(targetDimming) ? targetDimming : new Dimming();
254 targetDimming.setMinimumDimmingLevel(sourceMinDimmingLevel);
257 ColorXy targetColor = target.getColorXy();
258 ColorXy sourceColor = source.getColorXy();
259 if (Objects.isNull(targetColor) && Objects.nonNull(sourceColor)) {
260 target.setColorXy(sourceColor);
261 targetColor = target.getColorXy();
264 Gamut targetGamut = Objects.nonNull(targetColor) ? targetColor.getGamut() : null;
265 Gamut sourceGamut = Objects.nonNull(sourceColor) ? sourceColor.getGamut() : null;
266 if (Objects.isNull(targetGamut) && Objects.nonNull(sourceGamut)) {
267 targetColor = Objects.nonNull(targetColor) ? targetColor : new ColorXy();
268 targetColor.setGamut(sourceGamut);
271 ColorTemperature targetColorTemp = target.getColorTemperature();
272 ColorTemperature sourceColorTemp = source.getColorTemperature();
273 if (Objects.isNull(targetColorTemp) && Objects.nonNull(sourceColorTemp)) {
274 target.setColorTemperature(sourceColorTemp);
275 targetColorTemp = target.getColorTemperature();
278 MirekSchema targetMirekSchema = Objects.nonNull(targetColorTemp) ? targetColorTemp.getMirekSchema() : null;
279 MirekSchema sourceMirekSchema = Objects.nonNull(sourceColorTemp) ? sourceColorTemp.getMirekSchema() : null;
280 if (Objects.isNull(targetMirekSchema) && Objects.nonNull(sourceMirekSchema)) {
281 targetColorTemp = Objects.nonNull(targetColorTemp) ? targetColorTemp : new ColorTemperature();
282 targetColorTemp.setMirekSchema(sourceMirekSchema);
285 MetaData targetMetaData = target.getMetaData();
286 MetaData sourceMetaData = source.getMetaData();
287 if (Objects.isNull(targetMetaData) && Objects.nonNull(sourceMetaData)) {
288 target.setMetadata(sourceMetaData);
291 Alerts targetAlerts = target.getAlerts();
292 Alerts sourceAlerts = source.getAlerts();
293 if (Objects.isNull(targetAlerts) && Objects.nonNull(sourceAlerts)) {
294 target.setAlerts(sourceAlerts);
297 Effects targetEffects = target.getEffects();
298 Effects sourceEffects = source.getEffects();
299 if (Objects.isNull(targetEffects) && Objects.nonNull(sourceEffects)) {
300 targetEffects = sourceEffects;
301 target.setEffects(sourceEffects);
302 targetEffects = target.getEffects();
305 List<String> targetStatusValues = Objects.nonNull(targetEffects) ? targetEffects.getStatusValues() : null;
306 List<String> sourceStatusValues = Objects.nonNull(sourceEffects) ? sourceEffects.getStatusValues() : null;
307 if (Objects.isNull(targetStatusValues) && Objects.nonNull(sourceStatusValues)) {
308 targetEffects = Objects.nonNull(targetEffects) ? targetEffects : new Effects();
309 targetEffects.setStatusValues(sourceStatusValues);