From 4476e6dcd0126ee8eb9759238feb5fe1009fa689 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 6 Dec 2023 14:22:04 +0000 Subject: [PATCH] [hue] Fix edge cases for broken lights (#15999) Signed-off-by: Andrew Fiddian-Green --- .../hue/internal/api/dto/clip2/OnState.java | 3 +- .../hue/internal/api/dto/clip2/Resource.java | 15 ++- .../clip2/OnStateDimmingEdgeCaseTest.java | 119 ++++++++++++++++++ 3 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/OnStateDimmingEdgeCaseTest.java diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/OnState.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/OnState.java index d0ba4056fad..7907d5dd714 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/OnState.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/OnState.java @@ -38,7 +38,8 @@ public class OnState { throw new DTOPresentButEmptyException("'on' DTO is present but empty"); } - public void setOn(boolean on) { + public OnState setOn(boolean on) { this.on = on; + return this; } } diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Resource.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Resource.java index c41ce35f94e..74676dda213 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Resource.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/api/dto/clip2/Resource.java @@ -205,10 +205,16 @@ public class Resource { Dimming dimming = this.dimming; if (Objects.nonNull(dimming)) { try { - // if off the brightness is 0, otherwise it is dimming value + // if off the brightness is 0, otherwise it is the larger of dimming value or minimum dimming level OnState on = this.on; - double brightness = Objects.nonNull(on) && !on.isOn() ? 0f - : Math.max(0f, Math.min(100f, dimming.getBrightness())); + double brightness; + if (Objects.nonNull(on) && !on.isOn()) { + brightness = 0f; + } else { + Double minimumDimmingLevel = dimming.getMinimumDimmingLevel(); + brightness = Math.max(Objects.nonNull(minimumDimmingLevel) ? minimumDimmingLevel + : Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL, Math.min(100f, dimming.getBrightness())); + } return new PercentType(new BigDecimal(brightness, PERCENT_MATH_CONTEXT)); } catch (DTOPresentButEmptyException e) { return UnDefType.UNDEF; // indicates the DTO is present but its inner fields are missing @@ -874,8 +880,9 @@ public class Resource { return this; } - public void setOnState(@Nullable OnState on) { + public Resource setOnState(@Nullable OnState on) { this.on = on; + return this; } public Resource setRecallAction(SceneRecallAction recallAction) { diff --git a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/OnStateDimmingEdgeCaseTest.java b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/OnStateDimmingEdgeCaseTest.java new file mode 100644 index 00000000000..e5d5bd4af6c --- /dev/null +++ b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/clip2/OnStateDimmingEdgeCaseTest.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.hue.internal.clip2; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.Test; +import org.openhab.binding.hue.internal.api.dto.clip2.Dimming; +import org.openhab.binding.hue.internal.api.dto.clip2.OnState; +import org.openhab.binding.hue.internal.api.dto.clip2.Resource; +import org.openhab.binding.hue.internal.api.dto.clip2.enums.ResourceType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.UnDefType; + +/** + * JUnit test for edge cases of OnState and Dimming event and cache resources. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +class OnStateDimmingEdgeCaseTest { + + @Test + void getBrightnessStateWhenDimmingMissingReturnNull() { + assertThat(createLightResource(true, null).getBrightnessState(), is(equalTo(UnDefType.NULL))); + } + + @Test + void getBrightnessStateWhenOnAndDimming75ReturnBrightness75() { + assertThat(createLightResource(true, 75.0).getBrightnessState(), is(equalTo(new PercentType(75)))); + } + + @Test + void getBrightnessStateWhenOnAndDimming125ReturnBrightness100() { + assertThat(createLightResource(true, 125.0).getBrightnessState(), is(equalTo(new PercentType(100)))); + } + + @Test + void getBrightnessStateWhenOffAndDimming100ReturnBrightness0() { + assertThat(createLightResource(false, 100.0).getBrightnessState(), is(equalTo(new PercentType(0)))); + } + + @Test + void getBrightnessStateWhenOnStateMissingAndDimming0ReturnMinimumBrightness0() { + assertThat(createLightResource(null, 0.0).getBrightnessState(), + is(equalTo(new PercentType(new BigDecimal(Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL))))); + } + + @Test + void getBrightnessStateWhenOnStateMissingAndDimming100ReturnBrightness100() { + assertThat(createLightResource(null, 100.0).getBrightnessState(), is(equalTo(new PercentType(100)))); + } + + @Test + void getBrightnessStateWhenOnStateMissingAndDimmingMinus1ReturnMinimumBrightness() { + assertThat(createLightResource(null, -1.0).getBrightnessState(), + is(equalTo(new PercentType(new BigDecimal(Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL))))); + } + + @Test + void getBrightnessStateWhenOnAndDimmingMinus1ReturnMinimumBrightness() { + assertThat(createLightResource(true, -1.0).getBrightnessState(), + is(equalTo(new PercentType(new BigDecimal(Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL))))); + } + + @Test + void getBrightnessStateWhenOnAndDimming0ReturnMinimumBrightness() { + assertThat(createLightResource(true, 0.0).getBrightnessState(), + is(equalTo(new PercentType(new BigDecimal(Dimming.DEFAULT_MINIMUM_DIMMIMG_LEVEL))))); + } + + @Test + void getBrightnessStateWhenOnAndDimming0ReturnCustomMinimumBrightness() { + assertThat(createLightResource(true, 0.0, 2.0).getBrightnessState(), is(equalTo(new PercentType(2)))); + } + + private Resource createLightResource(@Nullable Boolean on, @Nullable Double brightness) { + return createLightResource(on, brightness, null); + } + + private Resource createLightResource(@Nullable Boolean on, @Nullable Double brightness, + @Nullable Double minimumDimmingLevel) { + Resource resource = new Resource(ResourceType.LIGHT); + + if (on != null) { + OnState onState = new OnState(); + onState.setOn(on); + resource.setOnState(onState); + } + + if (brightness != null) { + Dimming dimming = new Dimming(); + dimming.setBrightness(brightness); + + if (minimumDimmingLevel != null) { + dimming.setMinimumDimmingLevel(minimumDimmingLevel); + } + + resource.setDimming(dimming); + } + + return resource; + } +}