mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-27 19:50:36 +02:00
Fix precision-related issues (#20917)
Resolves #20912 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
+33
-5
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.io.hueemulation.internal;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -48,6 +50,9 @@ import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseState
|
||||
@NonNullByDefault
|
||||
public class StateUtils {
|
||||
|
||||
private static final BigDecimal BIG_DECIMAL_HUNDRED = BigDecimal.valueOf(100);
|
||||
private static final BigDecimal BIG_DECIMAL_HUE_BRI_MAX = BigDecimal.valueOf(HueStateBulb.MAX_BRI);
|
||||
|
||||
/**
|
||||
* Compute the hue state from a given item state and a device type.
|
||||
*
|
||||
@@ -65,7 +70,8 @@ public class StateUtils {
|
||||
if (itemState instanceof HSBType hsbState) {
|
||||
state = new HueStateColorBulb(hsbState);
|
||||
} else if (itemState instanceof PercentType percentState) {
|
||||
state = new HueStateColorBulb(percentState, percentState.intValue() > 0);
|
||||
state = new HueStateColorBulb(percentState,
|
||||
percentState.toBigDecimal().compareTo(BigDecimal.ZERO) > 0);
|
||||
} else if (itemState instanceof OnOffType onOffState) {
|
||||
state = new HueStateColorBulb(onOffState == OnOffType.ON);
|
||||
} else {
|
||||
@@ -76,9 +82,9 @@ public class StateUtils {
|
||||
case WhiteTemperatureType:
|
||||
if (itemState instanceof HSBType hsbState) {
|
||||
PercentType brightness = hsbState.getBrightness();
|
||||
state = new HueStateBulb(brightness, brightness.intValue() > 0);
|
||||
state = new HueStateBulb(brightness, brightness.toBigDecimal().compareTo(BigDecimal.ZERO) > 0);
|
||||
} else if (itemState instanceof PercentType percentState) {
|
||||
state = new HueStateBulb(percentState, percentState.intValue() > 0);
|
||||
state = new HueStateBulb(percentState, percentState.toBigDecimal().compareTo(BigDecimal.ZERO) > 0);
|
||||
} else if (itemState instanceof OnOffType onOffState) {
|
||||
state = new HueStateBulb(onOffState == OnOffType.ON);
|
||||
} else {
|
||||
@@ -170,7 +176,7 @@ public class StateUtils {
|
||||
if (newState.bri != null) {
|
||||
try {
|
||||
state.as(HueStateBulb.class).bri = newState.bri;
|
||||
command = new PercentType((int) (newState.bri * 100.0 / HueStateBulb.MAX_BRI + 0.5));
|
||||
command = percentTypeFromHueBrightness(newState.bri);
|
||||
successApplied.put("bri", newState.bri);
|
||||
} catch (ClassCastException e) {
|
||||
errorApplied.add("bri");
|
||||
@@ -183,7 +189,7 @@ public class StateUtils {
|
||||
if (newBri < 0 || newBri > HueStateBulb.MAX_BRI) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
command = new PercentType((int) (newBri * 100.0 / HueStateBulb.MAX_BRI + 0.5));
|
||||
command = percentTypeFromHueBrightness(newBri);
|
||||
successApplied.put("bri", newState.bri);
|
||||
} catch (ClassCastException e) {
|
||||
errorApplied.add("bri_inc");
|
||||
@@ -349,6 +355,28 @@ public class StateUtils {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Hue brightness value (0-254) to a {@link PercentType} with two decimal places.
|
||||
*
|
||||
* @param brightness the Hue brightness value
|
||||
* @return the corresponding PercentType
|
||||
*/
|
||||
public static PercentType percentTypeFromHueBrightness(int brightness) {
|
||||
return new PercentType(BigDecimal.valueOf(brightness).multiply(BIG_DECIMAL_HUNDRED)
|
||||
.divide(BIG_DECIMAL_HUE_BRI_MAX, 2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link PercentType} to a Hue brightness value (1-254).
|
||||
*
|
||||
* @param percentValue the PercentType value
|
||||
* @return the corresponding Hue brightness value
|
||||
*/
|
||||
public static int hueBrightnessFromPercentType(PercentType percentValue) {
|
||||
return Math.max(1, percentValue.toBigDecimal().multiply(BIG_DECIMAL_HUE_BRI_MAX)
|
||||
.divide(BIG_DECIMAL_HUNDRED, 0, RoundingMode.HALF_UP).intValue());
|
||||
}
|
||||
|
||||
public static @Nullable DeviceType determineTargetType(ConfigStore cs, Item element) {
|
||||
String category = element.getCategory();
|
||||
String type = element.getType();
|
||||
|
||||
+9
-8
@@ -12,7 +12,9 @@
|
||||
*/
|
||||
package org.openhab.io.hueemulation.internal.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.io.hueemulation.internal.StateUtils;
|
||||
|
||||
/**
|
||||
* Hue API state object
|
||||
@@ -21,9 +23,9 @@ import org.openhab.core.library.types.PercentType;
|
||||
* @author David Graeff - "extended color light bulbs" support
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueStateBulb extends HueStatePlug {
|
||||
// https://github.com/openhab/openhab-addons/issues/2881
|
||||
// Apparently the maximum brightness is 254
|
||||
|
||||
public static final int MAX_BRI = 254;
|
||||
public int bri = 0;
|
||||
|
||||
@@ -47,16 +49,15 @@ public class HueStateBulb extends HueStatePlug {
|
||||
*/
|
||||
public HueStateBulb(PercentType brightness, boolean on) {
|
||||
super(on);
|
||||
this.bri = Math.max(1, (int) (brightness.intValue() * MAX_BRI / 100.0 + 0.5));
|
||||
this.bri = StateUtils.hueBrightnessFromPercentType(brightness);
|
||||
}
|
||||
|
||||
public PercentType toBrightnessType() {
|
||||
int bri = this.bri * 100 / MAX_BRI;
|
||||
|
||||
if (!this.on) {
|
||||
bri = 0;
|
||||
if (this.on) {
|
||||
return StateUtils.percentTypeFromHueBrightness(this.bri);
|
||||
} else {
|
||||
return PercentType.ZERO;
|
||||
}
|
||||
return new PercentType(bri);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-1
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.io.hueemulation.internal.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
@@ -24,6 +25,7 @@ import org.openhab.core.library.types.PercentType;
|
||||
* @author Florian Lentz - added xy support
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueStateColorBulb extends HueStateBulb {
|
||||
public static final int MAX_HUE = 65535; // For extended color light bulbs
|
||||
public int hue = 0;
|
||||
@@ -71,7 +73,7 @@ public class HueStateColorBulb extends HueStateBulb {
|
||||
* @param hsb Color information. Sets the hue state to "on" if brightness is > 0.
|
||||
*/
|
||||
public HueStateColorBulb(HSBType hsb) {
|
||||
super(hsb.getBrightness(), hsb.getBrightness().intValue() > 0);
|
||||
super(hsb.getBrightness(), hsb.getBrightness().compareTo(DecimalType.ZERO) > 0);
|
||||
this.hue = (int) (hsb.getHue().intValue() * MAX_HUE / 360.0 + 0.5);
|
||||
this.sat = (int) (hsb.getSaturation().intValue() * MAX_SAT / 100.0 + 0.5);
|
||||
colormode = this.sat > 0 ? ColorMode.hs : ColorMode.ct;
|
||||
|
||||
+2
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.io.hueemulation.internal.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
|
||||
/**
|
||||
@@ -20,6 +21,7 @@ import org.openhab.core.library.types.OnOffType;
|
||||
* @author David Graeff - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueStatePlug extends AbstractHueState {
|
||||
public boolean on;
|
||||
|
||||
|
||||
+7
@@ -35,4 +35,11 @@ public class HueStateChange {
|
||||
public Integer sat_inc;
|
||||
public List<Double> xy_inc;
|
||||
public Integer ct_inc;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "on: " + on + ", bri: " + bri + ", hue: " + hue + ", sat: " + sat + ", effect: " + effect + ", ct: " + ct
|
||||
+ ", alert: " + alert + ", xy: " + xy + ", transitiontime: " + transitiontime + ", bri_inc: " + bri_inc
|
||||
+ ", hue_inc: " + hue_inc + ", sat_inc: " + sat_inc + ", xy_inc: " + xy_inc + ", ct_inc: " + ct_inc;
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -447,10 +447,16 @@ public class LightsAndGroups implements ConfigurationListener, RegistryChangeLis
|
||||
"Invalid request: No state change data received!");
|
||||
}
|
||||
|
||||
logger.debug("Received state change for light {}: {}", id, newState);
|
||||
|
||||
hueDevice.state = StateUtils.colorStateFromItemState(hueDevice.item.getState(), hueDevice.deviceType);
|
||||
|
||||
String itemUID = hueDevice.item.getUID();
|
||||
List<HueResponse> responses = new ArrayList<>();
|
||||
if (newState.on == Boolean.TRUE && newState.bri != null && newState.bri == 0) {
|
||||
// The bulb should be on, but the brightness is 0. Skip the brightness change.
|
||||
newState.bri = null;
|
||||
}
|
||||
Command command = StateUtils.computeCommandByState(responses, "/lights/" + id + "/state", hueDevice.state,
|
||||
newState);
|
||||
|
||||
|
||||
+19
@@ -332,6 +332,25 @@ public class LightsAndGroupsTests {
|
||||
assertThat(stateColorBulb.bri, is(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeOnAndInvalidBriValues() throws Exception {
|
||||
HueLightEntry hueLightEntry = cs.ds.lights.get("2");
|
||||
assertNotNull(hueLightEntry);
|
||||
HueStateColorBulb stateColorBulb = assertInstanceOf(HueStateColorBulb.class, hueLightEntry.state);
|
||||
assertThat(stateColorBulb.on, is(false));
|
||||
assertThat(stateColorBulb.bri, is(1));
|
||||
|
||||
String body = "{'on':true,'bri':0}";
|
||||
ContentResponse response = commonSetup.sendPut("/testuser/lights/2/state", body);
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getContentAsString(), containsString("success"));
|
||||
hueLightEntry = cs.ds.lights.get("2");
|
||||
assertNotNull(hueLightEntry);
|
||||
stateColorBulb = assertInstanceOf(HueStateColorBulb.class, hueLightEntry.state);
|
||||
assertThat(stateColorBulb.on, is(true));
|
||||
assertThat(stateColorBulb.bri, is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeHueSatValues() throws Exception {
|
||||
HueLightEntry hueLightEntry = cs.ds.lights.get("2");
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.io.hueemulation.internal.rest;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.io.hueemulation.internal.StateUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link StateUtils}.
|
||||
*
|
||||
* @author Jacob Laursen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StateUtilsTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCasesForPercentTypeFromHueBrightness")
|
||||
void percentTypeFromHueBrightness(int bri, PercentType expectedPercent) {
|
||||
assertThat(StateUtils.percentTypeFromHueBrightness(bri), is(equalTo(expectedPercent)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCasesForHueBrightnessFromPercentType")
|
||||
void hueBrightnessFromPercentType(PercentType percentValue, int expectedBri) {
|
||||
assertThat(StateUtils.hueBrightnessFromPercentType(percentValue), is(equalTo(expectedBri)));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> provideTestCasesForPercentTypeFromHueBrightness() {
|
||||
return Stream.of( //
|
||||
Arguments.of(0, PercentType.ZERO), //
|
||||
Arguments.of(1, PercentType.valueOf("0.39")), //
|
||||
Arguments.of(2, PercentType.valueOf("0.79")), //
|
||||
Arguments.of(3, PercentType.valueOf("1.18")), //
|
||||
Arguments.of(127, new PercentType(50)), //
|
||||
Arguments.of(253, PercentType.valueOf("99.61")), //
|
||||
Arguments.of(254, PercentType.HUNDRED) //
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> provideTestCasesForHueBrightnessFromPercentType() {
|
||||
return Stream.of( //
|
||||
Arguments.of(PercentType.ZERO, 1), //
|
||||
Arguments.of(PercentType.valueOf("0.5"), 1), //
|
||||
Arguments.of(new PercentType(1), 3), //
|
||||
Arguments.of(new PercentType(50), 127), //
|
||||
Arguments.of(PercentType.HUNDRED, 254) //
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user