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;
15 import java.math.BigDecimal;
16 import java.math.MathContext;
17 import java.math.RoundingMode;
18 import java.time.Duration;
19 import java.time.Instant;
20 import java.time.ZoneId;
21 import java.time.ZonedDateTime;
22 import java.util.List;
24 import java.util.Objects;
25 import java.util.Optional;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.hue.internal.dto.clip2.enums.ActionType;
30 import org.openhab.binding.hue.internal.dto.clip2.enums.ButtonEventType;
31 import org.openhab.binding.hue.internal.dto.clip2.enums.EffectType;
32 import org.openhab.binding.hue.internal.dto.clip2.enums.ResourceType;
33 import org.openhab.binding.hue.internal.dto.clip2.enums.SceneRecallAction;
34 import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneRecallAction;
35 import org.openhab.binding.hue.internal.dto.clip2.enums.SmartSceneState;
36 import org.openhab.binding.hue.internal.dto.clip2.enums.ZigbeeStatus;
37 import org.openhab.binding.hue.internal.exceptions.DTOPresentButEmptyException;
38 import org.openhab.core.library.types.DateTimeType;
39 import org.openhab.core.library.types.DecimalType;
40 import org.openhab.core.library.types.HSBType;
41 import org.openhab.core.library.types.OnOffType;
42 import org.openhab.core.library.types.PercentType;
43 import org.openhab.core.library.types.QuantityType;
44 import org.openhab.core.library.types.StringType;
45 import org.openhab.core.library.unit.SIUnits;
46 import org.openhab.core.library.unit.Units;
47 import org.openhab.core.types.Command;
48 import org.openhab.core.types.State;
49 import org.openhab.core.types.UnDefType;
50 import org.openhab.core.util.ColorUtil;
51 import org.openhab.core.util.ColorUtil.Gamut;
53 import com.google.gson.JsonElement;
54 import com.google.gson.JsonObject;
55 import com.google.gson.annotations.SerializedName;
58 * Complete Resource information DTO for CLIP 2.
60 * Note: all fields are @Nullable because some cases do not (must not) use them.
62 * @author Andrew Fiddian-Green - Initial contribution
65 public class Resource {
67 public static final double PERCENT_DELTA = 30f;
68 public static final MathContext PERCENT_MATH_CONTEXT = new MathContext(4, RoundingMode.HALF_UP);
71 * The SSE event mechanism sends resources in a sparse (skeleton) format that only includes state fields whose
72 * values have changed. A sparse resource does not contain the full state of the resource. And the absence of any
73 * field from such a resource does not indicate that the field value is UNDEF, but rather that the value is the same
74 * as what it was previously set to by the last non-sparse resource.
76 private transient boolean hasSparseData;
78 private @Nullable String type;
79 private @Nullable String id;
80 private @Nullable @SerializedName("bridge_id") String bridgeId;
81 private @Nullable @SerializedName("id_v1") String idV1;
82 private @Nullable ResourceReference owner;
83 private @Nullable MetaData metadata;
84 private @Nullable @SerializedName("product_data") ProductData productData;
85 private @Nullable List<ResourceReference> services;
86 private @Nullable OnState on;
87 private @Nullable Dimming dimming;
88 private @Nullable @SerializedName("color_temperature") ColorTemperature colorTemperature;
89 private @Nullable ColorXy color;
90 private @Nullable Alerts alert;
91 private @Nullable Effects effects;
92 private @Nullable @SerializedName("timed_effects") TimedEffects timedEffects;
93 private @Nullable ResourceReference group;
94 private @Nullable List<ActionEntry> actions;
95 private @Nullable Recall recall;
96 private @Nullable Boolean enabled;
97 private @Nullable LightLevel light;
98 private @Nullable Button button;
99 private @Nullable Temperature temperature;
100 private @Nullable Motion motion;
101 private @Nullable @SerializedName("power_state") Power powerState;
102 private @Nullable @SerializedName("relative_rotary") RelativeRotary relativeRotary;
103 private @Nullable List<ResourceReference> children;
104 private @Nullable JsonElement status;
105 private @Nullable @SuppressWarnings("unused") Dynamics dynamics;
106 private @Nullable String state;
111 * @param resourceType
113 public Resource(@Nullable ResourceType resourceType) {
114 if (Objects.nonNull(resourceType)) {
115 setType(resourceType);
119 public @Nullable List<ActionEntry> getActions() {
123 public @Nullable Alerts getAlerts() {
127 public State getAlertState() {
128 Alerts alerts = this.alert;
129 if (Objects.nonNull(alerts)) {
130 if (!alerts.getActionValues().isEmpty()) {
131 ActionType alertType = alerts.getAction();
132 if (Objects.nonNull(alertType)) {
133 return new StringType(alertType.name());
135 return new StringType(ActionType.NO_ACTION.name());
138 return UnDefType.NULL;
141 public String getArchetype() {
142 MetaData metaData = getMetaData();
143 if (Objects.nonNull(metaData)) {
144 return metaData.getArchetype().toString();
146 return getType().toString();
149 public State getBatteryLevelState() {
150 Power powerState = this.powerState;
151 return Objects.nonNull(powerState) ? powerState.getBatteryLevelState() : UnDefType.NULL;
154 public State getBatteryLowState() {
155 Power powerState = this.powerState;
156 return Objects.nonNull(powerState) ? powerState.getBatteryLowState() : UnDefType.NULL;
159 public @Nullable String getBridgeId() {
160 String bridgeId = this.bridgeId;
161 return Objects.isNull(bridgeId) || bridgeId.isBlank() ? null : bridgeId;
165 * Get the brightness as a PercentType. If off the brightness is 0, otherwise use dimming value.
167 * @return a PercentType with the dimming state, or UNDEF, or NULL
169 public State getBrightnessState() {
170 Dimming dimming = this.dimming;
171 if (Objects.nonNull(dimming)) {
173 // if off the brightness is 0, otherwise it is dimming value
174 OnState on = this.on;
175 double brightness = Objects.nonNull(on) && !on.isOn() ? 0f
176 : Math.max(0f, Math.min(100f, dimming.getBrightness()));
177 return new PercentType(new BigDecimal(brightness, PERCENT_MATH_CONTEXT));
178 } catch (DTOPresentButEmptyException e) {
179 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
182 return UnDefType.NULL;
185 public @Nullable Button getButton() {
190 * Get the state corresponding to a button's last event value multiplied by the controlId found for it in the given
191 * controlIds map. States are decimal values formatted like '1002' where the first digit is the button's controlId
192 * and the last digit is the ordinal value of the button's last event.
194 * @param controlIds the map of control ids to be referenced.
197 public State getButtonEventState(Map<String, Integer> controlIds) {
198 Button button = this.button;
199 if (button == null) {
200 return UnDefType.NULL;
202 ButtonEventType event;
203 ButtonReport buttonReport = button.getButtonReport();
204 if (buttonReport == null) {
205 event = button.getLastEvent();
207 event = buttonReport.getLastEvent();
210 return UnDefType.NULL;
212 return new DecimalType((controlIds.getOrDefault(getId(), 0).intValue() * 1000) + event.ordinal());
215 public State getButtonLastUpdatedState(ZoneId zoneId) {
216 Button button = this.button;
217 if (button == null) {
218 return UnDefType.NULL;
220 ButtonReport buttonReport = button.getButtonReport();
221 if (buttonReport == null) {
222 return UnDefType.UNDEF;
224 Instant lastChanged = buttonReport.getLastChanged();
225 if (Instant.EPOCH.equals(lastChanged)) {
226 return UnDefType.UNDEF;
228 return new DateTimeType(ZonedDateTime.ofInstant(lastChanged, zoneId));
231 public List<ResourceReference> getChildren() {
232 List<ResourceReference> children = this.children;
233 return Objects.nonNull(children) ? children : List.of();
237 * Get the color as an HSBType. This returns an HSB that is based on an amalgamation of the color xy, dimming, and
238 * on/off JSON elements. It takes its 'H' and 'S' parts from the 'ColorXy' JSON element, and its 'B' part from the
239 * on/off resp. dimming JSON elements. If off the B part is 0, otherwise it is the dimming element value. Note: this
240 * method is only to be used on cached state DTOs which already have a defined color gamut.
242 * @return an HSBType containing the current color and brightness level, or UNDEF or NULL.
244 public State getColorState() {
245 ColorXy color = this.color;
246 if (Objects.nonNull(color)) {
248 Gamut gamut = color.getGamut();
249 gamut = Objects.nonNull(gamut) ? gamut : ColorUtil.DEFAULT_GAMUT;
250 HSBType hsb = ColorUtil.xyToHsb(color.getXY(), gamut);
251 OnState on = this.on;
252 Dimming dimming = this.dimming;
253 double brightness = Objects.nonNull(on) && !on.isOn() ? 0
254 : Objects.nonNull(dimming) ? Math.max(0, Math.min(100, dimming.getBrightness())) : 50;
255 return new HSBType(hsb.getHue(), hsb.getSaturation(),
256 new PercentType(new BigDecimal(brightness, PERCENT_MATH_CONTEXT)));
257 } catch (DTOPresentButEmptyException e) {
258 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
261 return UnDefType.NULL;
264 public @Nullable ColorTemperature getColorTemperature() {
265 return colorTemperature;
268 public State getColorTemperatureAbsoluteState() {
269 ColorTemperature colorTemp = colorTemperature;
270 if (Objects.nonNull(colorTemp)) {
272 QuantityType<?> colorTemperature = colorTemp.getAbsolute();
273 if (Objects.nonNull(colorTemperature)) {
274 return colorTemperature;
276 } catch (DTOPresentButEmptyException e) {
277 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
280 return UnDefType.NULL;
284 * Get the colour temperature in percent. Note: this method is only to be used on cached state DTOs which already
285 * have a defined mirek schema.
287 * @return a PercentType with the colour temperature percentage.
289 public State getColorTemperaturePercentState() {
290 ColorTemperature colorTemperature = this.colorTemperature;
291 if (Objects.nonNull(colorTemperature)) {
293 Double percent = colorTemperature.getPercent();
294 if (Objects.nonNull(percent)) {
295 return new PercentType(new BigDecimal(percent, PERCENT_MATH_CONTEXT));
297 } catch (DTOPresentButEmptyException e) {
298 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
301 return UnDefType.NULL;
304 public @Nullable ColorXy getColorXy() {
309 * Return an HSB where the HS part is derived from the color xy JSON element (only), so the B part is 100%
311 * @return an HSBType.
313 public State getColorXyState() {
314 ColorXy color = this.color;
315 if (Objects.nonNull(color)) {
317 Gamut gamut = color.getGamut();
318 gamut = Objects.nonNull(gamut) ? gamut : ColorUtil.DEFAULT_GAMUT;
319 HSBType hsb = ColorUtil.xyToHsb(color.getXY(), gamut);
320 return new HSBType(hsb.getHue(), hsb.getSaturation(), PercentType.HUNDRED);
321 } catch (DTOPresentButEmptyException e) {
322 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
325 return UnDefType.NULL;
328 public int getControlId() {
329 MetaData metadata = this.metadata;
330 return Objects.nonNull(metadata) ? metadata.getControlId() : 0;
333 public @Nullable Dimming getDimming() {
338 * Return a PercentType which is derived from the dimming JSON element (only).
340 * @return a PercentType.
342 public State getDimmingState() {
343 Dimming dimming = this.dimming;
344 if (Objects.nonNull(dimming)) {
346 double dimmingValue = Math.max(0f, Math.min(100f, dimming.getBrightness()));
347 return new PercentType(new BigDecimal(dimmingValue, PERCENT_MATH_CONTEXT));
348 } catch (DTOPresentButEmptyException e) {
349 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
352 return UnDefType.NULL;
355 public @Nullable Effects getFixedEffects() {
360 * Get the amalgamated effect state. The result may be either from an 'effects' field or from a 'timedEffects'
361 * field. If both fields are missing it returns UnDefType.NULL, otherwise if either field is present and has an
362 * active value (other than EffectType.NO_EFFECT) it returns a StringType of the name of the respective active
363 * effect; and if none of the above apply, it returns a StringType of 'NO_EFFECT'.
365 * @return either a StringType value or UnDefType.NULL
367 public State getEffectState() {
368 Effects effects = this.effects;
369 TimedEffects timedEffects = this.timedEffects;
370 if (Objects.isNull(effects) && Objects.isNull(timedEffects)) {
371 return UnDefType.NULL;
373 EffectType effect = Objects.nonNull(effects) ? effects.getStatus() : null;
374 if (Objects.nonNull(effect) && effect != EffectType.NO_EFFECT) {
375 return new StringType(effect.name());
377 EffectType timedEffect = Objects.nonNull(timedEffects) ? timedEffects.getStatus() : null;
378 if (Objects.nonNull(timedEffect) && timedEffect != EffectType.NO_EFFECT) {
379 return new StringType(timedEffect.name());
381 return new StringType(EffectType.NO_EFFECT.name());
384 public @Nullable Boolean getEnabled() {
388 public State getEnabledState() {
389 Boolean enabled = this.enabled;
390 return Objects.nonNull(enabled) ? OnOffType.from(enabled.booleanValue()) : UnDefType.NULL;
393 public @Nullable Gamut getGamut() {
394 ColorXy color = this.color;
395 return Objects.nonNull(color) ? color.getGamut() : null;
398 public @Nullable ResourceReference getGroup() {
402 public String getId() {
404 return Objects.nonNull(id) ? id : "";
407 public String getIdV1() {
408 String idV1 = this.idV1;
409 return Objects.nonNull(idV1) ? idV1 : "";
412 public @Nullable LightLevel getLightLevel() {
416 public State getLightLevelState() {
417 LightLevel lightLevel = this.light;
418 if (lightLevel == null) {
419 return UnDefType.NULL;
421 LightLevelReport lightLevelReport = lightLevel.getLightLevelReport();
422 if (lightLevelReport == null) {
423 return lightLevel.getLightLevelState();
425 return new QuantityType<>(Math.pow(10f, (double) lightLevelReport.getLightLevel() / 10000f) - 1f, Units.LUX);
428 public State getLightLevelLastUpdatedState(ZoneId zoneId) {
429 LightLevel lightLevel = this.light;
430 if (lightLevel == null) {
431 return UnDefType.NULL;
433 LightLevelReport lightLevelReport = lightLevel.getLightLevelReport();
434 if (lightLevelReport == null) {
435 return UnDefType.UNDEF;
437 Instant lastChanged = lightLevelReport.getLastChanged();
438 if (Instant.EPOCH.equals(lastChanged)) {
439 return UnDefType.UNDEF;
441 return new DateTimeType(ZonedDateTime.ofInstant(lastChanged, zoneId));
444 public @Nullable MetaData getMetaData() {
448 public @Nullable Double getMinimumDimmingLevel() {
449 Dimming dimming = this.dimming;
450 return Objects.nonNull(dimming) ? dimming.getMinimumDimmingLevel() : null;
453 public @Nullable MirekSchema getMirekSchema() {
454 ColorTemperature colorTemp = this.colorTemperature;
455 return Objects.nonNull(colorTemp) ? colorTemp.getMirekSchema() : null;
458 public @Nullable Motion getMotion() {
462 public State getMotionState() {
463 Motion motion = this.motion;
464 if (motion == null) {
465 return UnDefType.NULL;
467 MotionReport motionReport = motion.getMotionReport();
468 if (motionReport == null) {
469 return motion.getMotionState();
471 return OnOffType.from(motionReport.isMotion());
474 public State getMotionLastUpdatedState(ZoneId zoneId) {
475 Motion motion = this.motion;
476 if (motion == null) {
477 return UnDefType.NULL;
479 MotionReport motionReport = motion.getMotionReport();
480 if (motionReport == null) {
481 return UnDefType.UNDEF;
483 Instant lastChanged = motionReport.getLastChanged();
484 if (Instant.EPOCH.equals(lastChanged)) {
485 return UnDefType.UNDEF;
487 return new DateTimeType(ZonedDateTime.ofInstant(lastChanged, zoneId));
490 public State getMotionValidState() {
491 Motion motion = this.motion;
492 return Objects.nonNull(motion) ? motion.getMotionValidState() : UnDefType.NULL;
495 public String getName() {
496 MetaData metaData = getMetaData();
497 if (Objects.nonNull(metaData)) {
498 String name = metaData.getName();
499 if (Objects.nonNull(name)) {
503 return getType().toString();
507 * Return the state of the On/Off element (only).
509 public State getOnOffState() {
511 OnState on = this.on;
512 return Objects.nonNull(on) ? OnOffType.from(on.isOn()) : UnDefType.NULL;
513 } catch (DTOPresentButEmptyException e) {
514 return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing
518 public @Nullable OnState getOnState() {
522 public @Nullable ResourceReference getOwner() {
526 public @Nullable Power getPowerState() {
530 public @Nullable ProductData getProductData() {
534 public String getProductName() {
535 ProductData productData = getProductData();
536 if (Objects.nonNull(productData)) {
537 return productData.getProductName();
539 return getType().toString();
542 public @Nullable Recall getRecall() {
546 public @Nullable RelativeRotary getRelativeRotary() {
547 return relativeRotary;
550 public State getRotaryStepsState() {
551 RelativeRotary relativeRotary = this.relativeRotary;
552 if (relativeRotary == null) {
553 return UnDefType.NULL;
555 RotaryReport rotaryReport = relativeRotary.getRotaryReport();
556 if (rotaryReport == null) {
557 return relativeRotary.getStepsState();
559 Rotation rotation = rotaryReport.getRotation();
560 if (rotation == null) {
561 return UnDefType.NULL;
563 return rotation.getStepsState();
566 public State getRotaryStepsLastUpdatedState(ZoneId zoneId) {
567 RelativeRotary relativeRotary = this.relativeRotary;
568 if (relativeRotary == null) {
569 return UnDefType.NULL;
571 RotaryReport rotaryReport = relativeRotary.getRotaryReport();
572 if (rotaryReport == null) {
573 return UnDefType.UNDEF;
575 Instant lastChanged = rotaryReport.getLastChanged();
576 if (Instant.EPOCH.equals(lastChanged)) {
577 return UnDefType.UNDEF;
579 return new DateTimeType(ZonedDateTime.ofInstant(lastChanged, zoneId));
583 * Check if the scene resource contains a 'status.active' element. If such an element is present, returns a Boolean
584 * Optional whose value depends on the value of that element, or an empty Optional if it is not.
586 * @return true, false, or empty.
588 public Optional<Boolean> getSceneActive() {
589 if (ResourceType.SCENE == getType()) {
590 JsonElement status = this.status;
591 if (Objects.nonNull(status) && status.isJsonObject()) {
592 JsonElement active = ((JsonObject) status).get("active");
593 if (Objects.nonNull(active) && active.isJsonPrimitive()) {
594 return Optional.of(!"inactive".equalsIgnoreCase(active.getAsString()));
598 return Optional.empty();
602 * If the getSceneActive() optional result is empty return 'UnDefType.NULL'. Otherwise if the optional result is
603 * present and 'true' (i.e. the scene is active) return the scene name. Or finally (the optional result is present
604 * and 'false') return 'UnDefType.UNDEF'.
606 * @return either 'UnDefType.NULL', a StringType containing the (active) scene name, or 'UnDefType.UNDEF'.
608 public State getSceneState() {
609 return getSceneActive().map(a -> a ? new StringType(getName()) : UnDefType.UNDEF).orElse(UnDefType.NULL);
613 * Check if the smart scene resource contains a 'state' element. If such an element is present, returns a Boolean
614 * Optional whose value depends on the value of that element, or an empty Optional if it is not.
616 * @return true, false, or empty.
618 public Optional<Boolean> getSmartSceneActive() {
619 if (ResourceType.SMART_SCENE == getType()) {
620 String state = this.state;
621 if (Objects.nonNull(state)) {
622 return Optional.of(SmartSceneState.ACTIVE == SmartSceneState.of(state));
625 return Optional.empty();
629 * If the getSmartSceneActive() optional result is empty return 'UnDefType.NULL'. Otherwise if the optional result
630 * is present and 'true' (i.e. the scene is active) return the smart scene name. Or finally (the optional result is
631 * present and 'false') return 'UnDefType.UNDEF'.
633 * @return either 'UnDefType.NULL', a StringType containing the (active) scene name, or 'UnDefType.UNDEF'.
635 public State getSmartSceneState() {
636 return getSmartSceneActive().map(a -> a ? new StringType(getName()) : UnDefType.UNDEF).orElse(UnDefType.NULL);
639 public List<ResourceReference> getServiceReferences() {
640 List<ResourceReference> services = this.services;
641 return Objects.nonNull(services) ? services : List.of();
644 public JsonObject getStatus() {
645 JsonElement status = this.status;
646 if (Objects.nonNull(status) && status.isJsonObject()) {
647 return status.getAsJsonObject();
649 return new JsonObject();
652 public @Nullable Temperature getTemperature() {
656 public State getTemperatureState() {
657 Temperature temperature = this.temperature;
658 if (temperature == null) {
659 return UnDefType.NULL;
661 TemperatureReport temperatureReport = temperature.getTemperatureReport();
662 if (temperatureReport == null) {
663 return temperature.getTemperatureState();
665 return new QuantityType<>(temperatureReport.getTemperature(), SIUnits.CELSIUS);
668 public State getTemperatureLastUpdatedState(ZoneId zoneId) {
669 Temperature temperature = this.temperature;
670 if (temperature == null) {
671 return UnDefType.NULL;
673 TemperatureReport temperatureReport = temperature.getTemperatureReport();
674 if (temperatureReport == null) {
675 return UnDefType.UNDEF;
677 Instant lastChanged = temperatureReport.getLastChanged();
678 if (Instant.EPOCH.equals(lastChanged)) {
679 return UnDefType.UNDEF;
681 return new DateTimeType(ZonedDateTime.ofInstant(lastChanged, zoneId));
684 public State getTemperatureValidState() {
685 Temperature temperature = this.temperature;
686 return Objects.nonNull(temperature) ? temperature.getTemperatureValidState() : UnDefType.NULL;
689 public @Nullable TimedEffects getTimedEffects() {
693 public ResourceType getType() {
694 return ResourceType.of(type);
697 public State getZigbeeState() {
698 ZigbeeStatus zigbeeStatus = getZigbeeStatus();
699 return Objects.nonNull(zigbeeStatus) ? new StringType(zigbeeStatus.toString()) : UnDefType.NULL;
702 public @Nullable ZigbeeStatus getZigbeeStatus() {
703 JsonElement status = this.status;
704 if (Objects.nonNull(status) && status.isJsonPrimitive()) {
705 return ZigbeeStatus.of(status.getAsString());
710 public boolean hasFullState() {
711 return !hasSparseData;
715 * Mark that the resource has sparse data.
717 * @return this instance.
719 public Resource markAsSparse() {
720 hasSparseData = true;
724 public Resource setAlerts(Alerts alert) {
729 public Resource setColorTemperature(ColorTemperature colorTemperature) {
730 this.colorTemperature = colorTemperature;
734 public Resource setColorXy(ColorXy color) {
739 public Resource setDimming(Dimming dimming) {
740 this.dimming = dimming;
744 public Resource setDynamicsDuration(Duration duration) {
745 dynamics = new Dynamics().setDuration(duration);
749 public Resource setFixedEffects(Effects effect) {
750 this.effects = effect;
754 public Resource setEnabled(Command command) {
755 if (command instanceof OnOffType) {
756 this.enabled = ((OnOffType) command) == OnOffType.ON;
761 public Resource setId(String id) {
766 public Resource setMetadata(MetaData metadata) {
767 this.metadata = metadata;
771 public Resource setMirekSchema(@Nullable MirekSchema schema) {
772 ColorTemperature colorTemperature = this.colorTemperature;
773 if (Objects.nonNull(colorTemperature)) {
774 colorTemperature.setMirekSchema(schema);
780 * Set the on/off JSON element (only).
782 * @param command an OnOffTypee command value.
783 * @return this resource instance.
785 public Resource setOnOff(Command command) {
786 if (command instanceof OnOffType) {
787 OnOffType onOff = (OnOffType) command;
788 OnState on = this.on;
789 on = Objects.nonNull(on) ? on : new OnState();
790 on.setOn(OnOffType.ON.equals(onOff));
796 public void setOnState(OnState on) {
800 public Resource setRecallAction(SceneRecallAction recallAction) {
801 Recall recall = this.recall;
802 this.recall = ((Objects.nonNull(recall) ? recall : new Recall())).setAction(recallAction);
806 public Resource setRecallAction(SmartSceneRecallAction recallAction) {
807 Recall recall = this.recall;
808 this.recall = ((Objects.nonNull(recall) ? recall : new Recall())).setAction(recallAction);
812 public Resource setRecallDuration(Duration recallDuration) {
813 Recall recall = this.recall;
814 this.recall = ((Objects.nonNull(recall) ? recall : new Recall())).setDuration(recallDuration);
818 public Resource setTimedEffects(TimedEffects timedEffects) {
819 this.timedEffects = timedEffects;
823 public Resource setTimedEffectsDuration(Duration dynamicsDuration) {
824 TimedEffects timedEffects = this.timedEffects;
825 if (Objects.nonNull(timedEffects)) {
826 timedEffects.setDuration(dynamicsDuration);
831 public Resource setType(ResourceType resourceType) {
832 this.type = resourceType.name().toLowerCase();
837 public String toString() {
839 return String.format("id:%s, type:%s", Objects.nonNull(id) ? id : "?" + " ".repeat(35),
840 getType().name().toLowerCase());