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 QuantityType<Temperature> (but it can also handle DecimalType).
84 * @param source another resource containing the MirekSchema.
86 * @return the target resource.
88 public static Resource setColorTemperatureAbsolute(Resource target, Command command, @Nullable Resource source) {
89 QuantityType<?> mirek;
90 if (command instanceof QuantityType<?>) {
91 QuantityType<?> quantity = (QuantityType<?>) command;
92 Unit<?> unit = quantity.getUnit();
93 if (Units.KELVIN.equals(unit)) {
94 mirek = quantity.toInvertibleUnit(Units.MIRED);
95 } else if (Units.MIRED.equals(unit)) {
98 QuantityType<?> kelvin = quantity.toInvertibleUnit(Units.KELVIN);
99 mirek = Objects.nonNull(kelvin) ? kelvin.toInvertibleUnit(Units.MIRED) : null;
101 } else if (command instanceof DecimalType) {
102 mirek = QuantityType.valueOf(((DecimalType) command).doubleValue(), Units.KELVIN)
103 .toInvertibleUnit(Units.MIRED);
107 if (Objects.nonNull(mirek)) {
108 MirekSchema schema = target.getMirekSchema();
109 schema = Objects.nonNull(schema) ? schema : Objects.nonNull(source) ? source.getMirekSchema() : null;
110 schema = Objects.nonNull(schema) ? schema : MirekSchema.DEFAULT_SCHEMA;
111 ColorTemperature colorTemperature = target.getColorTemperature();
112 colorTemperature = Objects.nonNull(colorTemperature) ? colorTemperature : new ColorTemperature();
113 double min = schema.getMirekMinimum();
114 double max = schema.getMirekMaximum();
115 double val = Math.max(min, Math.min(max, mirek.doubleValue()));
116 target.setColorTemperature(colorTemperature.setMirek(val));
122 * Setter for Color Temperature field:
123 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
126 * @param target the target resource.
127 * @param command the new state command should be a PercentType.
128 * @param source another resource containing the MirekSchema.
130 * @return the target resource.
132 public static Resource setColorTemperaturePercent(Resource target, Command command, @Nullable Resource source) {
133 if (command instanceof PercentType) {
134 MirekSchema schema = target.getMirekSchema();
135 schema = Objects.nonNull(schema) ? schema : Objects.nonNull(source) ? source.getMirekSchema() : null;
136 schema = Objects.nonNull(schema) ? schema : MirekSchema.DEFAULT_SCHEMA;
137 ColorTemperature colorTemperature = target.getColorTemperature();
138 colorTemperature = Objects.nonNull(colorTemperature) ? colorTemperature : new ColorTemperature();
139 double min = schema.getMirekMinimum();
140 double max = schema.getMirekMaximum();
141 double val = min + ((max - min) * ((PercentType) command).doubleValue() / 100f);
142 target.setColorTemperature(colorTemperature.setMirek(val));
148 * Setter for Color Xy field:
149 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
150 * (if any). Use the HS parts of the HSB value to set the value of the 'ColorXy' JSON element, and ignore the 'B'
153 * @param target the target resource.
154 * @param command the new state command should be an HSBType with the new color XY value.
155 * @param source another resource containing the color Gamut.
157 * @return the target resource.
159 public static Resource setColorXy(Resource target, Command command, @Nullable Resource source) {
160 if (command instanceof HSBType) {
161 Gamut gamut = target.getGamut();
162 gamut = Objects.nonNull(gamut) ? gamut : Objects.nonNull(source) ? source.getGamut() : null;
163 gamut = Objects.nonNull(gamut) ? gamut : ColorUtil.DEFAULT_GAMUT;
164 HSBType hsb = (HSBType) command;
165 ColorXy color = target.getColorXy();
166 target.setColorXy((Objects.nonNull(color) ? color : new ColorXy()).setXY(ColorUtil.hsbToXY(hsb, gamut)));
172 * Setter for Dimming field:
173 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
176 * @param target the target resource.
177 * @param command the new state command should be a PercentType with the new dimming parameter.
178 * @param source another resource containing the minimum dimming level.
180 * @return the target resource.
182 public static Resource setDimming(Resource target, Command command, @Nullable Resource source) {
183 if (command instanceof PercentType) {
184 Double min = target.getMinimumDimmingLevel();
185 min = Objects.nonNull(min) ? min : Objects.nonNull(source) ? source.getMinimumDimmingLevel() : null;
186 min = Objects.nonNull(min) ? min : Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL;
187 PercentType brightness = (PercentType) command;
188 if (brightness.doubleValue() < min.doubleValue()) {
189 brightness = new PercentType(new BigDecimal(min, Resource.PERCENT_MATH_CONTEXT));
191 Dimming dimming = target.getDimming();
192 dimming = Objects.nonNull(dimming) ? dimming : new Dimming();
193 dimming.setBrightness(brightness.doubleValue());
194 target.setDimming(dimming);
200 * Setter for Effect field:
201 * Use the given command value to set the target resource DTO value based on the attributes of the source resource
204 * @param target the target resource.
205 * @param command the new state command should be a StringType.
206 * @param source another resource containing the allowed effect action values.
208 * @return the target resource.
210 public static Resource setEffect(Resource target, Command command, @Nullable Resource source) {
211 if ((command instanceof StringType) && Objects.nonNull(source)) {
212 Effects otherEffects = source.getEffects();
213 if (Objects.nonNull(otherEffects)) {
214 EffectType effectType = EffectType.of(((StringType) command).toString());
215 if (otherEffects.allows(effectType)) {
216 target.setEffects(new Effects().setEffect(effectType));
224 * Setter to copy persisted fields from the source Resource into the target Resource. If the field in the target is
225 * null and the same field in the source is not null, then the value from the source is copied to the target. This
226 * method allows 'hasSparseData' resources to expand themselves to include necessary fields taken over from a
227 * previously cached full data resource.
229 * @param target the target resource.
230 * @param source another resource containing the values to be taken over.
232 * @return the target resource.
234 public static Resource setResource(Resource target, Resource source) {
236 OnState targetOnOff = target.getOnState();
237 OnState sourceOnOff = source.getOnState();
238 if (Objects.isNull(targetOnOff) && Objects.nonNull(sourceOnOff)) {
239 target.setOnState(sourceOnOff);
242 Dimming targetDimming = target.getDimming();
243 Dimming sourceDimming = source.getDimming();
244 if (Objects.isNull(targetDimming) && Objects.nonNull(sourceDimming)) {
245 target.setDimming(sourceDimming);
246 targetDimming = target.getDimming();
248 // minimum dimming level
249 Double targetMinDimmingLevel = Objects.nonNull(targetDimming) ? targetDimming.getMinimumDimmingLevel() : null;
250 Double sourceMinDimmingLevel = Objects.nonNull(sourceDimming) ? sourceDimming.getMinimumDimmingLevel() : null;
251 if (Objects.isNull(targetMinDimmingLevel) && Objects.nonNull(sourceMinDimmingLevel)) {
252 targetDimming = Objects.nonNull(targetDimming) ? targetDimming : new Dimming();
253 targetDimming.setMinimumDimmingLevel(sourceMinDimmingLevel);
256 ColorXy targetColor = target.getColorXy();
257 ColorXy sourceColor = source.getColorXy();
258 if (Objects.isNull(targetColor) && Objects.nonNull(sourceColor)) {
259 target.setColorXy(sourceColor);
260 targetColor = target.getColorXy();
263 Gamut targetGamut = Objects.nonNull(targetColor) ? targetColor.getGamut() : null;
264 Gamut sourceGamut = Objects.nonNull(sourceColor) ? sourceColor.getGamut() : null;
265 if (Objects.isNull(targetGamut) && Objects.nonNull(sourceGamut)) {
266 targetColor = Objects.nonNull(targetColor) ? targetColor : new ColorXy();
267 targetColor.setGamut(sourceGamut);
270 ColorTemperature targetColorTemp = target.getColorTemperature();
271 ColorTemperature sourceColorTemp = source.getColorTemperature();
272 if (Objects.isNull(targetColorTemp) && Objects.nonNull(sourceColorTemp)) {
273 target.setColorTemperature(sourceColorTemp);
274 targetColorTemp = target.getColorTemperature();
277 MirekSchema targetMirekSchema = Objects.nonNull(targetColorTemp) ? targetColorTemp.getMirekSchema() : null;
278 MirekSchema sourceMirekSchema = Objects.nonNull(sourceColorTemp) ? sourceColorTemp.getMirekSchema() : null;
279 if (Objects.isNull(targetMirekSchema) && Objects.nonNull(sourceMirekSchema)) {
280 targetColorTemp = Objects.nonNull(targetColorTemp) ? targetColorTemp : new ColorTemperature();
281 targetColorTemp.setMirekSchema(sourceMirekSchema);
284 MetaData targetMetaData = target.getMetaData();
285 MetaData sourceMetaData = source.getMetaData();
286 if (Objects.isNull(targetMetaData) && Objects.nonNull(sourceMetaData)) {
287 target.setMetadata(sourceMetaData);
290 Alerts targetAlerts = target.getAlerts();
291 Alerts sourceAlerts = source.getAlerts();
292 if (Objects.isNull(targetAlerts) && Objects.nonNull(sourceAlerts)) {
293 target.setAlerts(sourceAlerts);
296 Effects targetEffects = target.getEffects();
297 Effects sourceEffects = source.getEffects();
298 if (Objects.isNull(targetEffects) && Objects.nonNull(sourceEffects)) {
299 targetEffects = sourceEffects;
300 target.setEffects(sourceEffects);
301 targetEffects = target.getEffects();
304 List<String> targetStatusValues = Objects.nonNull(targetEffects) ? targetEffects.getStatusValues() : null;
305 List<String> sourceStatusValues = Objects.nonNull(sourceEffects) ? sourceEffects.getStatusValues() : null;
306 if (Objects.isNull(targetStatusValues) && Objects.nonNull(sourceStatusValues)) {
307 targetEffects = Objects.nonNull(targetEffects) ? targetEffects : new Effects();
308 targetEffects.setStatusValues(sourceStatusValues);