From bc039779085d7f967dbe4c25c47e73ca62c89d8d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 18 Oct 2026 20:44:31 +0000
Subject: [PATCH 1/9] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index d476d8e4..a1a95277 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
hap
hap-java
Homekit Accessory Protocol for Java
- 2.0.9
+ 2.0.10-SNAPSHOT
jar
https://github.com/hap-java/HAP-Java
@@ -26,7 +26,7 @@
scm:git:https://github.com/hap-java/HAP-Java.git
scm:git:https://github.com/hap-java/HAP-Java.git
https://github.com/hap-java/HAP-Java.git
- hap-2.0.9
+ HEAD
From 25e9efe3b6e198aae42a5bb11c0abd9544e72773 Mon Sep 17 00:00:00 2001
From: udaw
Date: Sun, 18 Oct 2026 22:54:39 +0200
Subject: [PATCH 2/9] Only run deploy in release:perform
maven-release-plugin defaults release:perform to 'deploy site-deploy'
because the pom has a distributionManagement/site element. The
site-deploy runs in the release checkout without git credentials and
fails with "could not read Username", marking every release run as
failed after the artifacts were already published.
Co-Authored-By: Claude Opus 5.5
---
.github/workflows/release.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 38042692..53c2c8b9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,6 +33,7 @@ jobs:
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Maven release
- run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }}
+ # Only deploy; the site is published by the deploy workflow on pushes to master.
+ run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Dgoals=deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From 79ee9a1341af3f47cc8cf78f122618461951178c Mon Sep 17 00:00:00 2001
From: udaw
Date: Sun, 18 Oct 2026 23:18:58 +0200
Subject: [PATCH 3/9] Remove redundant comment from release workflow
Addresses review feedback on #206.
---
.github/workflows/release.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 53c2c8b9..29bfe0e5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,7 +33,6 @@ jobs:
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Maven release
- # Only deploy; the site is published by the deploy workflow on pushes to master.
run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Dgoals=deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From 3389210fa18420d5d75c821fb5a1548ed6748250 Mon Sep 17 00:00:00 2001
From: udaw
Date: Sun, 18 Oct 2026 23:31:33 +0200
Subject: [PATCH 4/9] Add custom min/max constructors to remaining range
characteristics (#162)
Add DEFAULT_MIN_VALUE/DEFAULT_MAX_VALUE (and DEFAULT_STEP for float
characteristics) and a constructor taking min/max (and step) to 19
characteristics that only supported fixed ranges, following the pattern
of RotationSpeedCharacteristic. Existing constructors delegate with the
previous values, so their serialized output is unchanged.
---
CHANGES.md | 3 +
.../impl/audio/VolumeCharacteristic.java | 16 +-
.../battery/BatteryLevelCharacteristic.java | 15 +-
.../impl/common/WaterLavelCharacteristic.java | 19 +-
.../FilterLifeLevelCharacteristic.java | 19 +-
...tyDehumidifierThresholdCharacteristic.java | 27 +-
...dityHumidifierThresholdCharacteristic.java | 27 +-
...CurrentRelativeHumidityCharacteristic.java | 19 +-
.../TargetRelativeHumidityCharacteristic.java | 27 +-
.../lightbulb/BrightnessCharacteristic.java | 16 +-
.../impl/lightbulb/HueCharacteristic.java | 28 +-
.../lightbulb/SaturationCharacteristic.java | 27 +-
.../slat/CurrentTiltAngleCharacteristic.java | 15 +-
.../slat/TargetTiltAngleCharacteristic.java | 16 +-
...rentHorizontalTiltAngleCharacteristic.java | 15 +-
.../CurrentPositionCharacteristic.java | 15 +-
...urrentVerticalTiltAngleCharacteristic.java | 15 +-
...rgetHorizontalTiltAngleCharacteristic.java | 16 +-
.../TargetPositionCharacteristic.java | 16 +-
...TargetVerticalTiltAngleCharacteristic.java | 16 +-
.../impl/CustomRangeCharacteristicsTest.java | 253 ++++++++++++++++++
21 files changed, 574 insertions(+), 46 deletions(-)
create mode 100644 src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java
diff --git a/CHANGES.md b/CHANGES.md
index a50fc7fa..6dfc34b6 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,6 @@
+# HAP-Java 2.0.10
+* Add constructors with custom min/max (and step for float values) to the remaining range characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162)
+
# HAP-Java 2.0.9
* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202)
* Stop logging the SRP shared secret, derived key and M5 message data during pairing at DEBUG level
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java
index 2d1caedd..2a4223d5 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java
@@ -11,8 +11,12 @@
/** Volume characteristic to control audio volume. */
public class VolumeCharacteristic extends IntegerCharacteristic implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = 0;
+ public static final int DEFAULT_MAX_VALUE = 100;
public VolumeCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -20,12 +24,20 @@ public VolumeCharacteristic(
super(
"00000119-0000-1000-8000-0026BB765291",
"volume",
- 0,
- 100,
+ minValue,
+ maxValue,
"percentage",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public VolumeCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java
index 0fa56ff4..ae7097cc 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java
@@ -11,20 +11,31 @@
/** This characteristic describes the current level of the battery. */
public class BatteryLevelCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = 0;
+ public static final int DEFAULT_MAX_VALUE = 100;
public BatteryLevelCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"00000068-0000-1000-8000-0026BB765291",
"battery level",
- 0,
- 100,
+ minValue,
+ maxValue,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public BatteryLevelCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java
index dda11b08..d9d29bac 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java
@@ -11,21 +11,34 @@
/** This characteristic describes the water level. */
public class WaterLavelCharacteristic extends FloatCharacteristic
implements EventableCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public WaterLavelCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"000000B5-0000-1000-8000-0026BB765291",
"water level",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public WaterLavelCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java
index 29cdf8d6..6213769c 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java
@@ -10,21 +10,34 @@
public class FilterLifeLevelCharacteristic extends FloatCharacteristic
implements EventableCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public FilterLifeLevelCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"000000AB-0000-1000-8000-0026BB765291",
"Filter Life Level",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public FilterLifeLevelCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java
index b8a4ca56..84a31fe2 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java
@@ -13,8 +13,14 @@
* turns on.
*/
public class HumidityDehumidifierThresholdCharacteristic extends FloatCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public HumidityDehumidifierThresholdCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -22,13 +28,28 @@ public HumidityDehumidifierThresholdCharacteristic(
super(
"000000C9-0000-1000-8000-0026BB765291",
"humidity threshold dehumidifier",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public HumidityDehumidifierThresholdCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(
+ DEFAULT_MIN_VALUE,
+ DEFAULT_MAX_VALUE,
+ DEFAULT_STEP,
+ getter,
+ setter,
+ subscriber,
+ unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java
index 2ee8adbf..14b78bde 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java
@@ -13,8 +13,14 @@
* turns on.
*/
public class HumidityHumidifierThresholdCharacteristic extends FloatCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public HumidityHumidifierThresholdCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -22,13 +28,28 @@ public HumidityHumidifierThresholdCharacteristic(
super(
"000000CA-0000-1000-8000-0026BB765291",
"humidity threshold humidifier",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public HumidityHumidifierThresholdCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(
+ DEFAULT_MIN_VALUE,
+ DEFAULT_MAX_VALUE,
+ DEFAULT_STEP,
+ getter,
+ setter,
+ subscriber,
+ unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java
index be77125b..7ce18fb4 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java
@@ -9,21 +9,34 @@
/** This characteristic describes the current relative humidity. */
public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public CurrentRelativeHumidityCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"00000010-0000-1000-8000-0026BB765291",
"Current Relative Humidity",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public CurrentRelativeHumidityCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java
index ab8ad7e2..600ca0f7 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java
@@ -10,8 +10,14 @@
/** This characteristic describes the target relative humidity. */
public class TargetRelativeHumidityCharacteristic extends FloatCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public TargetRelativeHumidityCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -19,13 +25,28 @@ public TargetRelativeHumidityCharacteristic(
super(
"00000034-0000-1000-8000-0026BB765291",
"Target Relative Humidity",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public TargetRelativeHumidityCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(
+ DEFAULT_MIN_VALUE,
+ DEFAULT_MAX_VALUE,
+ DEFAULT_STEP,
+ getter,
+ setter,
+ subscriber,
+ unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java
index 693e42ef..b68cb914 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java
@@ -15,8 +15,12 @@
*/
public class BrightnessCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = 0;
+ public static final int DEFAULT_MAX_VALUE = 100;
public BrightnessCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -24,12 +28,20 @@ public BrightnessCharacteristic(
super(
"00000008-0000-1000-8000-0026BB765291",
"level of brightness",
- 0,
- 100,
+ minValue,
+ maxValue,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public BrightnessCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java
index 7a00816b..f41a9231 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java
@@ -10,7 +10,14 @@
/** This characteristic describes hue or color. */
public class HueCharacteristic extends FloatCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 360;
+ public static final double DEFAULT_STEP = 1;
+
public HueCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -18,13 +25,28 @@ public HueCharacteristic(
super(
"00000013-0000-1000-8000-0026BB765291",
"hue or color",
- 0,
- 360,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"arcdegrees",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public HueCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(
+ DEFAULT_MIN_VALUE,
+ DEFAULT_MAX_VALUE,
+ DEFAULT_STEP,
+ getter,
+ setter,
+ subscriber,
+ unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java
index 90694ef9..5d2f7033 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java
@@ -12,8 +12,14 @@
/** This characteristic describes color saturation. */
public class SaturationCharacteristic extends FloatCharacteristic
implements EventableCharacteristic {
+ public static final double DEFAULT_MIN_VALUE = 0;
+ public static final double DEFAULT_MAX_VALUE = 100;
+ public static final double DEFAULT_STEP = 1;
public SaturationCharacteristic(
+ double minValue,
+ double maxValue,
+ double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -21,13 +27,28 @@ public SaturationCharacteristic(
super(
"0000002F-0000-1000-8000-0026BB765291",
"color saturation",
- 0,
- 100,
- 1,
+ minValue,
+ maxValue,
+ minStep,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public SaturationCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(
+ DEFAULT_MIN_VALUE,
+ DEFAULT_MAX_VALUE,
+ DEFAULT_STEP,
+ getter,
+ setter,
+ subscriber,
+ unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java
index 3a4868b2..0f186281 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java
@@ -11,20 +11,31 @@
/** This characteristic describe current tilt angle (between -90 and 90) */
public class CurrentTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = -90;
+ public static final int DEFAULT_MAX_VALUE = 90;
public CurrentTiltAngleCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"000000C1-0000-1000-8000-0026BB765291",
"current tilt angle",
- -90,
- 90,
+ minValue,
+ maxValue,
"arcdegrees",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public CurrentTiltAngleCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java
index 2a327522..44824f35 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java
@@ -12,8 +12,12 @@
/** This characteristic describes the target angle of slats. */
public class TargetTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = -90;
+ public static final int DEFAULT_MAX_VALUE = 90;
public TargetTiltAngleCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -21,12 +25,20 @@ public TargetTiltAngleCharacteristic(
super(
"000000C2-0000-1000-8000-0026BB765291",
"target tilt angle",
- -90,
- 90,
+ minValue,
+ maxValue,
"arcdegrees",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public TargetTiltAngleCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java
index 1255d7ef..272a822f 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java
@@ -11,20 +11,31 @@
/** This characteristic describes the current angle of horizontal slats. */
public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = -90;
+ public static final int DEFAULT_MAX_VALUE = 90;
public CurrentHorizontalTiltAngleCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"0000006C-0000-1000-8000-0026BB765291",
"current horizontal tilt angle",
- -90,
- 90,
+ minValue,
+ maxValue,
"arcdegrees",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public CurrentHorizontalTiltAngleCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java
index d5161461..e9ed063c 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java
@@ -11,20 +11,31 @@
/** This characteristic describes the current position of accessories. */
public class CurrentPositionCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = 0;
+ public static final int DEFAULT_MAX_VALUE = 100;
public CurrentPositionCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"0000006D-0000-1000-8000-0026BB765291",
"current position",
- 0,
- 100,
+ minValue,
+ maxValue,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public CurrentPositionCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java
index c6258a2d..7d716897 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java
@@ -11,20 +11,31 @@
/** This characteristic describes the current angle of vertical slats for accessory. */
public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = -90;
+ public static final int DEFAULT_MAX_VALUE = 90;
public CurrentVerticalTiltAngleCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"0000006E-0000-1000-8000-0026BB765291",
"current vertical tilt angle",
- -90,
- 90,
+ minValue,
+ maxValue,
"arcdegrees",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public CurrentVerticalTiltAngleCharacteristic(
+ Supplier> getter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java
index 38c07576..17639ba6 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java
@@ -12,8 +12,12 @@
/** This characteristic describes the target angle of horizontal slats for accessories. */
public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = -90;
+ public static final int DEFAULT_MAX_VALUE = 90;
public TargetHorizontalTiltAngleCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -21,12 +25,20 @@ public TargetHorizontalTiltAngleCharacteristic(
super(
"0000007B-0000-1000-8000-0026BB765291",
"target horizontal tilt angle",
- -90,
- 90,
+ minValue,
+ maxValue,
"arcdegrees",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public TargetHorizontalTiltAngleCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java
index 6398a0b3..82d06c4b 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java
@@ -12,8 +12,12 @@
/** This characteristic describes the target position of accessories like door, window, blinds. */
public class TargetPositionCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = 0;
+ public static final int DEFAULT_MAX_VALUE = 100;
public TargetPositionCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -21,12 +25,20 @@ public TargetPositionCharacteristic(
super(
"0000007C-0000-1000-8000-0026BB765291",
"target position",
- 0,
- 100,
+ minValue,
+ maxValue,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public TargetPositionCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
+ }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java
index 4d7c3ad7..03fd7a3f 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java
@@ -12,8 +12,12 @@
/** This characteristic describes the target angle of vertical slats. */
public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
+ public static final int DEFAULT_MIN_VALUE = -90;
+ public static final int DEFAULT_MAX_VALUE = 90;
public TargetVerticalTiltAngleCharacteristic(
+ int minValue,
+ int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -21,12 +25,20 @@ public TargetVerticalTiltAngleCharacteristic(
super(
"0000007D-0000-1000-8000-0026BB765291",
"target vertical tilt angle",
- -90,
- 90,
+ minValue,
+ maxValue,
"arcdegrees",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
+
+ public TargetVerticalTiltAngleCharacteristic(
+ Supplier> getter,
+ ExceptionalConsumer setter,
+ Consumer subscriber,
+ Runnable unsubscriber) {
+ this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
+ }
}
diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java
new file mode 100644
index 00000000..800719d6
--- /dev/null
+++ b/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java
@@ -0,0 +1,253 @@
+package io.github.hapjava.characteristics.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.github.hapjava.characteristics.Characteristic;
+import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic;
+import io.github.hapjava.characteristics.impl.battery.BatteryLevelCharacteristic;
+import io.github.hapjava.characteristics.impl.common.WaterLavelCharacteristic;
+import io.github.hapjava.characteristics.impl.filtermaintenance.FilterLifeLevelCharacteristic;
+import io.github.hapjava.characteristics.impl.humidifier.HumidityDehumidifierThresholdCharacteristic;
+import io.github.hapjava.characteristics.impl.humidifier.HumidityHumidifierThresholdCharacteristic;
+import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic;
+import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic;
+import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic;
+import io.github.hapjava.characteristics.impl.lightbulb.HueCharacteristic;
+import io.github.hapjava.characteristics.impl.lightbulb.SaturationCharacteristic;
+import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import javax.json.JsonObject;
+import org.junit.Test;
+
+/** Custom min/max support, see https://github.com/hap-java/HAP-Java/issues/162 */
+public class CustomRangeCharacteristicsTest {
+
+ /** Serialized output of the default constructors, captured before custom ranges were added. */
+ private static final List DEFAULT_CONSTRUCTORS =
+ Arrays.asList(
+ Case.of(
+ "BatteryLevelCharacteristic",
+ new BatteryLevelCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"68\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
+ Case.of(
+ "BrightnessCharacteristic",
+ new BrightnessCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"8\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
+ Case.of(
+ "CurrentPositionCharacteristic",
+ new CurrentPositionCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"6D\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
+ Case.of(
+ "TargetPositionCharacteristic",
+ new TargetPositionCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"7C\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
+ Case.of(
+ "VolumeCharacteristic",
+ new VolumeCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"119\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"percentage\"}"),
+ Case.of(
+ "CurrentTiltAngleCharacteristic",
+ new CurrentTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "TargetTiltAngleCharacteristic",
+ new TargetTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "CurrentHorizontalTiltAngleCharacteristic",
+ new CurrentHorizontalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"6C\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "TargetHorizontalTiltAngleCharacteristic",
+ new TargetHorizontalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"7B\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "CurrentVerticalTiltAngleCharacteristic",
+ new CurrentVerticalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"6E\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "TargetVerticalTiltAngleCharacteristic",
+ new TargetVerticalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"7D\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "CurrentRelativeHumidityCharacteristic",
+ new CurrentRelativeHumidityCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"10\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
+ Case.of(
+ "TargetRelativeHumidityCharacteristic",
+ new TargetRelativeHumidityCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"34\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
+ Case.of(
+ "FilterLifeLevelCharacteristic",
+ new FilterLifeLevelCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"AB\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
+ Case.of(
+ "HueCharacteristic",
+ new HueCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"13\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":360.0,\"minStep\":1.0,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ "SaturationCharacteristic",
+ new SaturationCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"2F\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
+ Case.of(
+ "HumidityDehumidifierThresholdCharacteristic",
+ new HumidityDehumidifierThresholdCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"C9\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
+ Case.of(
+ "HumidityHumidifierThresholdCharacteristic",
+ new HumidityHumidifierThresholdCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"CA\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
+ Case.of(
+ "WaterLavelCharacteristic",
+ new WaterLavelCharacteristic(
+ () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"B5\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"));
+
+ @Test
+ public void defaultConstructorsKeepTheirSerializedOutput() {
+ for (Case c : DEFAULT_CONSTRUCTORS) {
+ assertThat(c.characteristic.toJson(7).join().toString()).as(c.name).isEqualTo(c.expectedJson);
+ }
+ }
+
+ @Test
+ public void integerCharacteristicsAdvertiseCustomRange() {
+ for (Characteristic c :
+ Arrays.asList(
+ new BatteryLevelCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new BrightnessCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new CurrentPositionCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetPositionCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new VolumeCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new CurrentTiltAngleCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetTiltAngleCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new CurrentHorizontalTiltAngleCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetHorizontalTiltAngleCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new CurrentVerticalTiltAngleCharacteristic(
+ 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetVerticalTiltAngleCharacteristic(
+ 10,
+ 20,
+ () -> CompletableFuture.completedFuture(50),
+ v -> {},
+ cb -> {},
+ () -> {}))) {
+ JsonObject json = c.toJson(7).join();
+ String name = c.getClass().getSimpleName();
+ assertThat(json.getInt("minValue")).as(name).isEqualTo(10);
+ assertThat(json.getInt("maxValue")).as(name).isEqualTo(20);
+ assertThat(json.getInt("minStep")).as(name).isEqualTo(1);
+ }
+ }
+
+ @Test
+ public void floatCharacteristicsAdvertiseCustomRangeAndClampToIt() {
+ for (Characteristic c :
+ Arrays.asList(
+ new CurrentRelativeHumidityCharacteristic(
+ 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
+ new TargetRelativeHumidityCharacteristic(
+ 10,
+ 20,
+ 0.5,
+ () -> CompletableFuture.completedFuture(50.0),
+ v -> {},
+ cb -> {},
+ () -> {}),
+ new FilterLifeLevelCharacteristic(
+ 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
+ new HueCharacteristic(
+ 10,
+ 20,
+ 0.5,
+ () -> CompletableFuture.completedFuture(50.0),
+ v -> {},
+ cb -> {},
+ () -> {}),
+ new SaturationCharacteristic(
+ 10,
+ 20,
+ 0.5,
+ () -> CompletableFuture.completedFuture(50.0),
+ v -> {},
+ cb -> {},
+ () -> {}),
+ new HumidityDehumidifierThresholdCharacteristic(
+ 10,
+ 20,
+ 0.5,
+ () -> CompletableFuture.completedFuture(50.0),
+ v -> {},
+ cb -> {},
+ () -> {}),
+ new HumidityHumidifierThresholdCharacteristic(
+ 10,
+ 20,
+ 0.5,
+ () -> CompletableFuture.completedFuture(50.0),
+ v -> {},
+ cb -> {},
+ () -> {}),
+ new WaterLavelCharacteristic(
+ 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}))) {
+ JsonObject json = c.toJson(7).join();
+ String name = c.getClass().getSimpleName();
+ assertThat(json.getJsonNumber("minValue").doubleValue()).as(name).isEqualTo(10.0);
+ assertThat(json.getJsonNumber("maxValue").doubleValue()).as(name).isEqualTo(20.0);
+ assertThat(json.getJsonNumber("minStep").doubleValue()).as(name).isEqualTo(0.5);
+ assertThat(json.getJsonNumber("value").doubleValue()).as(name).isEqualTo(20.0);
+ }
+ }
+
+ private static class Case {
+ final String name;
+ final Characteristic characteristic;
+ final String expectedJson;
+
+ private Case(String name, Characteristic characteristic, String expectedJson) {
+ this.name = name;
+ this.characteristic = characteristic;
+ this.expectedJson = expectedJson;
+ }
+
+ static Case of(String name, Characteristic characteristic, String expectedJson) {
+ return new Case(name, characteristic, expectedJson);
+ }
+ }
+}
From 298f6dd525ce78247ffa9ae15c19559c233eb443 Mon Sep 17 00:00:00 2001
From: udaw
Date: Mon, 19 Oct 2026 10:35:23 +0200
Subject: [PATCH 5/9] Add optional Mute characteristic to Doorbell service
Mute is an optional characteristic of the Doorbell service. The 2.0.1
changelog lists it for the Doorbell service, but it was only wired into
DoorService. DoorService is left unchanged to stay compatible.
---
CHANGES.md | 3 ++
.../services/impl/DoorbellService.java | 14 +++++
.../services/impl/DoorbellServiceTest.java | 51 +++++++++++++++++++
3 files changed, 68 insertions(+)
create mode 100644 src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java
diff --git a/CHANGES.md b/CHANGES.md
index a50fc7fa..2f8e2455 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,6 @@
+# HAP-Java 2.0.10
+* Add optional Mute characteristic to Doorbell service
+
# HAP-Java 2.0.9
* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202)
* Stop logging the SRP shared secret, derived key and M5 message data during pairing at DEBUG level
diff --git a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java
index fd8fc700..06ee325d 100644
--- a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java
+++ b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java
@@ -2,8 +2,10 @@
import io.github.hapjava.accessories.DoorbellAccessory;
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithBrightness;
+import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute;
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName;
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume;
+import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic;
import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic;
import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic;
@@ -42,6 +44,14 @@ public DoorbellService(DoorbellAccessory accessory) {
((AccessoryWithVolume) accessory)::subscribeVolume,
((AccessoryWithVolume) accessory)::unsubscribeVolume));
}
+ if (accessory instanceof AccessoryWithMute) {
+ addOptionalCharacteristic(
+ new MuteCharacteristic(
+ ((AccessoryWithMute) accessory)::isMuted,
+ ((AccessoryWithMute) accessory)::setMute,
+ ((AccessoryWithMute) accessory)::subscribeMuteState,
+ ((AccessoryWithMute) accessory)::unsubscribeMuteState));
+ }
}
public void addOptionalCharacteristic(NameCharacteristic name) {
@@ -55,4 +65,8 @@ public void addOptionalCharacteristic(BrightnessCharacteristic brightness) {
public void addOptionalCharacteristic(VolumeCharacteristic volume) {
addCharacteristic(volume);
}
+
+ public void addOptionalCharacteristic(MuteCharacteristic mute) {
+ addCharacteristic(mute);
+ }
}
diff --git a/src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java b/src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java
new file mode 100644
index 00000000..feb67b94
--- /dev/null
+++ b/src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java
@@ -0,0 +1,51 @@
+package io.github.hapjava.services.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+import io.github.hapjava.accessories.DoorbellAccessory;
+import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute;
+import io.github.hapjava.characteristics.Characteristic;
+import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic;
+import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic;
+import java.util.concurrent.CompletableFuture;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import org.junit.Test;
+
+public class DoorbellServiceTest {
+
+ @Test
+ public void doorbellWithoutOptionalsOnlyHasSwitchEvent() {
+ DoorbellService service = new DoorbellService(mock(DoorbellAccessory.class));
+
+ assertThat(service.getType()).isEqualTo("00000121-0000-1000-8000-0026BB765291");
+ assertThat(service.getCharacteristics())
+ .hasExactlyElementsOfTypes(ProgrammableSwitchEventCharacteristic.class);
+ }
+
+ @Test
+ public void doorbellWithMuteHasMuteCharacteristic() throws Exception {
+ DoorbellAccessory accessory =
+ mock(DoorbellAccessory.class, withSettings().extraInterfaces(AccessoryWithMute.class));
+ when(((AccessoryWithMute) accessory).isMuted())
+ .thenReturn(CompletableFuture.completedFuture(true));
+
+ DoorbellService service = new DoorbellService(accessory);
+
+ assertThat(service.getCharacteristics())
+ .hasExactlyElementsOfTypes(
+ ProgrammableSwitchEventCharacteristic.class, MuteCharacteristic.class);
+
+ Characteristic mute = service.getCharacteristics().get(1);
+ JsonObject json = mute.toJson(2).join();
+ assertThat(json.getString("type")).isEqualTo("11A");
+ assertThat(json.getBoolean("value")).isTrue();
+
+ mute.setValue(JsonValue.FALSE);
+ verify((AccessoryWithMute) accessory).setMute(false);
+ }
+}
From a2a6bffa2b9a636ddb7c0164d8801208a83410a1 Mon Sep 17 00:00:00 2001
From: udaw
Date: Mon, 19 Oct 2026 17:03:53 +0200
Subject: [PATCH 6/9] Limit custom min/max to tilt angle characteristics
Percentage characteristics and hue have fixed ranges by definition, so
only the tilt angles keep the min/max constructors.
---
CHANGES.md | 2 +-
.../impl/audio/VolumeCharacteristic.java | 16 +-
.../battery/BatteryLevelCharacteristic.java | 15 +-
.../impl/common/WaterLavelCharacteristic.java | 19 +-
.../FilterLifeLevelCharacteristic.java | 19 +-
...tyDehumidifierThresholdCharacteristic.java | 27 +-
...dityHumidifierThresholdCharacteristic.java | 27 +-
...CurrentRelativeHumidityCharacteristic.java | 19 +-
.../TargetRelativeHumidityCharacteristic.java | 27 +-
.../lightbulb/BrightnessCharacteristic.java | 16 +-
.../impl/lightbulb/HueCharacteristic.java | 28 +-
.../lightbulb/SaturationCharacteristic.java | 27 +-
.../CurrentPositionCharacteristic.java | 15 +-
.../TargetPositionCharacteristic.java | 16 +-
.../impl/CustomRangeCharacteristicsTest.java | 253 ------------------
.../impl/CustomTiltAngleRangeTest.java | 92 +++++++
16 files changed, 127 insertions(+), 491 deletions(-)
delete mode 100644 src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java
create mode 100644 src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java
diff --git a/CHANGES.md b/CHANGES.md
index 6dfc34b6..f6be4cdb 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,5 @@
# HAP-Java 2.0.10
-* Add constructors with custom min/max (and step for float values) to the remaining range characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162)
+* Add constructors with custom min/max to the tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162)
# HAP-Java 2.0.9
* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202)
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java
index 2a4223d5..2d1caedd 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java
@@ -11,12 +11,8 @@
/** Volume characteristic to control audio volume. */
public class VolumeCharacteristic extends IntegerCharacteristic implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = 0;
- public static final int DEFAULT_MAX_VALUE = 100;
public VolumeCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -24,20 +20,12 @@ public VolumeCharacteristic(
super(
"00000119-0000-1000-8000-0026BB765291",
"volume",
- minValue,
- maxValue,
+ 0,
+ 100,
"percentage",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public VolumeCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java
index ae7097cc..0fa56ff4 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java
@@ -11,31 +11,20 @@
/** This characteristic describes the current level of the battery. */
public class BatteryLevelCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = 0;
- public static final int DEFAULT_MAX_VALUE = 100;
public BatteryLevelCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"00000068-0000-1000-8000-0026BB765291",
"battery level",
- minValue,
- maxValue,
+ 0,
+ 100,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public BatteryLevelCharacteristic(
- Supplier> getter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java
index d9d29bac..dda11b08 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java
@@ -11,34 +11,21 @@
/** This characteristic describes the water level. */
public class WaterLavelCharacteristic extends FloatCharacteristic
implements EventableCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public WaterLavelCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"000000B5-0000-1000-8000-0026BB765291",
"water level",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public WaterLavelCharacteristic(
- Supplier> getter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java
index 6213769c..29cdf8d6 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java
@@ -10,34 +10,21 @@
public class FilterLifeLevelCharacteristic extends FloatCharacteristic
implements EventableCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public FilterLifeLevelCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"000000AB-0000-1000-8000-0026BB765291",
"Filter Life Level",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public FilterLifeLevelCharacteristic(
- Supplier> getter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java
index 84a31fe2..b8a4ca56 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java
@@ -13,14 +13,8 @@
* turns on.
*/
public class HumidityDehumidifierThresholdCharacteristic extends FloatCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public HumidityDehumidifierThresholdCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -28,28 +22,13 @@ public HumidityDehumidifierThresholdCharacteristic(
super(
"000000C9-0000-1000-8000-0026BB765291",
"humidity threshold dehumidifier",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public HumidityDehumidifierThresholdCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(
- DEFAULT_MIN_VALUE,
- DEFAULT_MAX_VALUE,
- DEFAULT_STEP,
- getter,
- setter,
- subscriber,
- unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java
index 14b78bde..2ee8adbf 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java
@@ -13,14 +13,8 @@
* turns on.
*/
public class HumidityHumidifierThresholdCharacteristic extends FloatCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public HumidityHumidifierThresholdCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -28,28 +22,13 @@ public HumidityHumidifierThresholdCharacteristic(
super(
"000000CA-0000-1000-8000-0026BB765291",
"humidity threshold humidifier",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public HumidityHumidifierThresholdCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(
- DEFAULT_MIN_VALUE,
- DEFAULT_MAX_VALUE,
- DEFAULT_STEP,
- getter,
- setter,
- subscriber,
- unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java
index 7ce18fb4..be77125b 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java
@@ -9,34 +9,21 @@
/** This characteristic describes the current relative humidity. */
public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public CurrentRelativeHumidityCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"00000010-0000-1000-8000-0026BB765291",
"Current Relative Humidity",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public CurrentRelativeHumidityCharacteristic(
- Supplier> getter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java
index 600ca0f7..ab8ad7e2 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java
@@ -10,14 +10,8 @@
/** This characteristic describes the target relative humidity. */
public class TargetRelativeHumidityCharacteristic extends FloatCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public TargetRelativeHumidityCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -25,28 +19,13 @@ public TargetRelativeHumidityCharacteristic(
super(
"00000034-0000-1000-8000-0026BB765291",
"Target Relative Humidity",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public TargetRelativeHumidityCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(
- DEFAULT_MIN_VALUE,
- DEFAULT_MAX_VALUE,
- DEFAULT_STEP,
- getter,
- setter,
- subscriber,
- unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java
index b68cb914..693e42ef 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java
@@ -15,12 +15,8 @@
*/
public class BrightnessCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = 0;
- public static final int DEFAULT_MAX_VALUE = 100;
public BrightnessCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -28,20 +24,12 @@ public BrightnessCharacteristic(
super(
"00000008-0000-1000-8000-0026BB765291",
"level of brightness",
- minValue,
- maxValue,
+ 0,
+ 100,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public BrightnessCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java
index f41a9231..7a00816b 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java
@@ -10,14 +10,7 @@
/** This characteristic describes hue or color. */
public class HueCharacteristic extends FloatCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 360;
- public static final double DEFAULT_STEP = 1;
-
public HueCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -25,28 +18,13 @@ public HueCharacteristic(
super(
"00000013-0000-1000-8000-0026BB765291",
"hue or color",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 360,
+ 1,
"arcdegrees",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public HueCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(
- DEFAULT_MIN_VALUE,
- DEFAULT_MAX_VALUE,
- DEFAULT_STEP,
- getter,
- setter,
- subscriber,
- unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java
index 5d2f7033..90694ef9 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java
@@ -12,14 +12,8 @@
/** This characteristic describes color saturation. */
public class SaturationCharacteristic extends FloatCharacteristic
implements EventableCharacteristic {
- public static final double DEFAULT_MIN_VALUE = 0;
- public static final double DEFAULT_MAX_VALUE = 100;
- public static final double DEFAULT_STEP = 1;
public SaturationCharacteristic(
- double minValue,
- double maxValue,
- double minStep,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -27,28 +21,13 @@ public SaturationCharacteristic(
super(
"0000002F-0000-1000-8000-0026BB765291",
"color saturation",
- minValue,
- maxValue,
- minStep,
+ 0,
+ 100,
+ 1,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public SaturationCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(
- DEFAULT_MIN_VALUE,
- DEFAULT_MAX_VALUE,
- DEFAULT_STEP,
- getter,
- setter,
- subscriber,
- unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java
index e9ed063c..d5161461 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java
@@ -11,31 +11,20 @@
/** This characteristic describes the current position of accessories. */
public class CurrentPositionCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = 0;
- public static final int DEFAULT_MAX_VALUE = 100;
public CurrentPositionCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"0000006D-0000-1000-8000-0026BB765291",
"current position",
- minValue,
- maxValue,
+ 0,
+ 100,
"%",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public CurrentPositionCharacteristic(
- Supplier> getter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java
index 82d06c4b..6398a0b3 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java
@@ -12,12 +12,8 @@
/** This characteristic describes the target position of accessories like door, window, blinds. */
public class TargetPositionCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = 0;
- public static final int DEFAULT_MAX_VALUE = 100;
public TargetPositionCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -25,20 +21,12 @@ public TargetPositionCharacteristic(
super(
"0000007C-0000-1000-8000-0026BB765291",
"target position",
- minValue,
- maxValue,
+ 0,
+ 100,
"%",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public TargetPositionCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
- }
}
diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java
deleted file mode 100644
index 800719d6..00000000
--- a/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java
+++ /dev/null
@@ -1,253 +0,0 @@
-package io.github.hapjava.characteristics.impl;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import io.github.hapjava.characteristics.Characteristic;
-import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic;
-import io.github.hapjava.characteristics.impl.battery.BatteryLevelCharacteristic;
-import io.github.hapjava.characteristics.impl.common.WaterLavelCharacteristic;
-import io.github.hapjava.characteristics.impl.filtermaintenance.FilterLifeLevelCharacteristic;
-import io.github.hapjava.characteristics.impl.humidifier.HumidityDehumidifierThresholdCharacteristic;
-import io.github.hapjava.characteristics.impl.humidifier.HumidityHumidifierThresholdCharacteristic;
-import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic;
-import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic;
-import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic;
-import io.github.hapjava.characteristics.impl.lightbulb.HueCharacteristic;
-import io.github.hapjava.characteristics.impl.lightbulb.SaturationCharacteristic;
-import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic;
-import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic;
-import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic;
-import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic;
-import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic;
-import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic;
-import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic;
-import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import javax.json.JsonObject;
-import org.junit.Test;
-
-/** Custom min/max support, see https://github.com/hap-java/HAP-Java/issues/162 */
-public class CustomRangeCharacteristicsTest {
-
- /** Serialized output of the default constructors, captured before custom ranges were added. */
- private static final List DEFAULT_CONSTRUCTORS =
- Arrays.asList(
- Case.of(
- "BatteryLevelCharacteristic",
- new BatteryLevelCharacteristic(
- () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"68\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
- Case.of(
- "BrightnessCharacteristic",
- new BrightnessCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"8\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
- Case.of(
- "CurrentPositionCharacteristic",
- new CurrentPositionCharacteristic(
- () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"6D\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
- Case.of(
- "TargetPositionCharacteristic",
- new TargetPositionCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"7C\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"),
- Case.of(
- "VolumeCharacteristic",
- new VolumeCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"119\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"percentage\"}"),
- Case.of(
- "CurrentTiltAngleCharacteristic",
- new CurrentTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "TargetTiltAngleCharacteristic",
- new TargetTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "CurrentHorizontalTiltAngleCharacteristic",
- new CurrentHorizontalTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"6C\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "TargetHorizontalTiltAngleCharacteristic",
- new TargetHorizontalTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"7B\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "CurrentVerticalTiltAngleCharacteristic",
- new CurrentVerticalTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"6E\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "TargetVerticalTiltAngleCharacteristic",
- new TargetVerticalTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"7D\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "CurrentRelativeHumidityCharacteristic",
- new CurrentRelativeHumidityCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"10\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
- Case.of(
- "TargetRelativeHumidityCharacteristic",
- new TargetRelativeHumidityCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"34\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
- Case.of(
- "FilterLifeLevelCharacteristic",
- new FilterLifeLevelCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"AB\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
- Case.of(
- "HueCharacteristic",
- new HueCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"13\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":360.0,\"minStep\":1.0,\"unit\":\"arcdegrees\"}"),
- Case.of(
- "SaturationCharacteristic",
- new SaturationCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"2F\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
- Case.of(
- "HumidityDehumidifierThresholdCharacteristic",
- new HumidityDehumidifierThresholdCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"C9\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
- Case.of(
- "HumidityHumidifierThresholdCharacteristic",
- new HumidityHumidifierThresholdCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"CA\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"),
- Case.of(
- "WaterLavelCharacteristic",
- new WaterLavelCharacteristic(
- () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"B5\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"));
-
- @Test
- public void defaultConstructorsKeepTheirSerializedOutput() {
- for (Case c : DEFAULT_CONSTRUCTORS) {
- assertThat(c.characteristic.toJson(7).join().toString()).as(c.name).isEqualTo(c.expectedJson);
- }
- }
-
- @Test
- public void integerCharacteristicsAdvertiseCustomRange() {
- for (Characteristic c :
- Arrays.asList(
- new BatteryLevelCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- new BrightnessCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- new CurrentPositionCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- new TargetPositionCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- new VolumeCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- new CurrentTiltAngleCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- new TargetTiltAngleCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- new CurrentHorizontalTiltAngleCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- new TargetHorizontalTiltAngleCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- new CurrentVerticalTiltAngleCharacteristic(
- 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- new TargetVerticalTiltAngleCharacteristic(
- 10,
- 20,
- () -> CompletableFuture.completedFuture(50),
- v -> {},
- cb -> {},
- () -> {}))) {
- JsonObject json = c.toJson(7).join();
- String name = c.getClass().getSimpleName();
- assertThat(json.getInt("minValue")).as(name).isEqualTo(10);
- assertThat(json.getInt("maxValue")).as(name).isEqualTo(20);
- assertThat(json.getInt("minStep")).as(name).isEqualTo(1);
- }
- }
-
- @Test
- public void floatCharacteristicsAdvertiseCustomRangeAndClampToIt() {
- for (Characteristic c :
- Arrays.asList(
- new CurrentRelativeHumidityCharacteristic(
- 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
- new TargetRelativeHumidityCharacteristic(
- 10,
- 20,
- 0.5,
- () -> CompletableFuture.completedFuture(50.0),
- v -> {},
- cb -> {},
- () -> {}),
- new FilterLifeLevelCharacteristic(
- 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}),
- new HueCharacteristic(
- 10,
- 20,
- 0.5,
- () -> CompletableFuture.completedFuture(50.0),
- v -> {},
- cb -> {},
- () -> {}),
- new SaturationCharacteristic(
- 10,
- 20,
- 0.5,
- () -> CompletableFuture.completedFuture(50.0),
- v -> {},
- cb -> {},
- () -> {}),
- new HumidityDehumidifierThresholdCharacteristic(
- 10,
- 20,
- 0.5,
- () -> CompletableFuture.completedFuture(50.0),
- v -> {},
- cb -> {},
- () -> {}),
- new HumidityHumidifierThresholdCharacteristic(
- 10,
- 20,
- 0.5,
- () -> CompletableFuture.completedFuture(50.0),
- v -> {},
- cb -> {},
- () -> {}),
- new WaterLavelCharacteristic(
- 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}))) {
- JsonObject json = c.toJson(7).join();
- String name = c.getClass().getSimpleName();
- assertThat(json.getJsonNumber("minValue").doubleValue()).as(name).isEqualTo(10.0);
- assertThat(json.getJsonNumber("maxValue").doubleValue()).as(name).isEqualTo(20.0);
- assertThat(json.getJsonNumber("minStep").doubleValue()).as(name).isEqualTo(0.5);
- assertThat(json.getJsonNumber("value").doubleValue()).as(name).isEqualTo(20.0);
- }
- }
-
- private static class Case {
- final String name;
- final Characteristic characteristic;
- final String expectedJson;
-
- private Case(String name, Characteristic characteristic, String expectedJson) {
- this.name = name;
- this.characteristic = characteristic;
- this.expectedJson = expectedJson;
- }
-
- static Case of(String name, Characteristic characteristic, String expectedJson) {
- return new Case(name, characteristic, expectedJson);
- }
- }
-}
diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java
new file mode 100644
index 00000000..dcff74fc
--- /dev/null
+++ b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java
@@ -0,0 +1,92 @@
+package io.github.hapjava.characteristics.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.github.hapjava.characteristics.Characteristic;
+import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic;
+import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import javax.json.JsonObject;
+import org.junit.Test;
+
+/** Custom tilt angle ranges, see https://github.com/hap-java/HAP-Java/issues/162 */
+public class CustomTiltAngleRangeTest {
+
+ @Test
+ public void defaultConstructorsKeepTheirSerializedOutput() {
+ // Serialized output captured before custom ranges were added.
+ for (Case c :
+ Arrays.asList(
+ Case.of(
+ new CurrentTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ new TargetTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ new CurrentHorizontalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"6C\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ new TargetHorizontalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"7B\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ new CurrentVerticalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"6E\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
+ Case.of(
+ new TargetVerticalTiltAngleCharacteristic(
+ () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ "{\"iid\":7,\"type\":\"7D\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"))) {
+ assertThat(c.characteristic.toJson(7).join().toString())
+ .as(c.characteristic.getClass().getSimpleName())
+ .isEqualTo(c.expected);
+ }
+ }
+
+ @Test
+ public void customRangeIsAdvertised() {
+ for (Characteristic c :
+ Arrays.asList(
+ new CurrentTiltAngleCharacteristic(
+ 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetTiltAngleCharacteristic(
+ 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new CurrentHorizontalTiltAngleCharacteristic(
+ 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetHorizontalTiltAngleCharacteristic(
+ 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
+ new CurrentVerticalTiltAngleCharacteristic(
+ 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
+ new TargetVerticalTiltAngleCharacteristic(
+ 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}))) {
+ JsonObject json = c.toJson(7).join();
+ String name = c.getClass().getSimpleName();
+ assertThat(json.getInt("minValue")).as(name).isEqualTo(0);
+ assertThat(json.getInt("maxValue")).as(name).isEqualTo(90);
+ assertThat(json.getInt("minStep")).as(name).isEqualTo(1);
+ }
+ }
+
+ private static class Case {
+ final Characteristic characteristic;
+ final String expected;
+
+ private Case(Characteristic characteristic, String expected) {
+ this.characteristic = characteristic;
+ this.expected = expected;
+ }
+
+ static Case of(Characteristic characteristic, String expected) {
+ return new Case(characteristic, expected);
+ }
+ }
+}
From 3f844c2d3f81136507674a55166aa49a78e1e3ed Mon Sep 17 00:00:00 2001
From: Eugen
Date: Tue, 3 Nov 2026 10:11:14 +0100
Subject: [PATCH 7/9] Drop slat tilt angle characteristics from custom min/max
The Home app shows no control for the Slat service, so a custom range
cannot be verified there.
---
CHANGES.md | 2 +-
.../slat/CurrentTiltAngleCharacteristic.java | 15 ++-------------
.../impl/slat/TargetTiltAngleCharacteristic.java | 16 ++--------------
.../impl/CustomTiltAngleRangeTest.java | 14 --------------
4 files changed, 5 insertions(+), 42 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index f6be4cdb..5bc1183a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,5 @@
# HAP-Java 2.0.10
-* Add constructors with custom min/max to the tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162)
+* Add constructors with custom min/max to the window covering tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162)
# HAP-Java 2.0.9
* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202)
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java
index 0f186281..3a4868b2 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java
@@ -11,31 +11,20 @@
/** This characteristic describe current tilt angle (between -90 and 90) */
public class CurrentTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = -90;
- public static final int DEFAULT_MAX_VALUE = 90;
public CurrentTiltAngleCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
Consumer subscriber,
Runnable unsubscriber) {
super(
"000000C1-0000-1000-8000-0026BB765291",
"current tilt angle",
- minValue,
- maxValue,
+ -90,
+ 90,
"arcdegrees",
Optional.of(getter),
Optional.empty(),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public CurrentTiltAngleCharacteristic(
- Supplier> getter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber);
- }
}
diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java
index 44824f35..2a327522 100644
--- a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java
+++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java
@@ -12,12 +12,8 @@
/** This characteristic describes the target angle of slats. */
public class TargetTiltAngleCharacteristic extends IntegerCharacteristic
implements EventableCharacteristic {
- public static final int DEFAULT_MIN_VALUE = -90;
- public static final int DEFAULT_MAX_VALUE = 90;
public TargetTiltAngleCharacteristic(
- int minValue,
- int maxValue,
Supplier> getter,
ExceptionalConsumer setter,
Consumer subscriber,
@@ -25,20 +21,12 @@ public TargetTiltAngleCharacteristic(
super(
"000000C2-0000-1000-8000-0026BB765291",
"target tilt angle",
- minValue,
- maxValue,
+ -90,
+ 90,
"arcdegrees",
Optional.of(getter),
Optional.of(setter),
Optional.of(subscriber),
Optional.of(unsubscriber));
}
-
- public TargetTiltAngleCharacteristic(
- Supplier> getter,
- ExceptionalConsumer setter,
- Consumer subscriber,
- Runnable unsubscriber) {
- this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber);
- }
}
diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java
index dcff74fc..d3cd5175 100644
--- a/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java
+++ b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java
@@ -3,8 +3,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import io.github.hapjava.characteristics.Characteristic;
-import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic;
-import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic;
import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic;
import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic;
import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic;
@@ -22,14 +20,6 @@ public void defaultConstructorsKeepTheirSerializedOutput() {
// Serialized output captured before custom ranges were added.
for (Case c :
Arrays.asList(
- Case.of(
- new CurrentTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
- Case.of(
- new TargetTiltAngleCharacteristic(
- () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
- "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"),
Case.of(
new CurrentHorizontalTiltAngleCharacteristic(
() -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
@@ -56,10 +46,6 @@ public void defaultConstructorsKeepTheirSerializedOutput() {
public void customRangeIsAdvertised() {
for (Characteristic c :
Arrays.asList(
- new CurrentTiltAngleCharacteristic(
- 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
- new TargetTiltAngleCharacteristic(
- 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}),
new CurrentHorizontalTiltAngleCharacteristic(
0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}),
new TargetHorizontalTiltAngleCharacteristic(
From 11b8813cc3042a655827169a00803fe663ba5b3b Mon Sep 17 00:00:00 2001
From: Eugen
Date: Sat, 7 Nov 2026 13:33:56 +0100
Subject: [PATCH 8/9] Close owned mDNS instance and remove shutdown hook on
stop
JmdnsHomekitAdvertiser created its own JmDNS when given an InetAddress but
never closed it, and registered a shutdown hook on every advertise() that was
never removed. Each stopped HomekitRoot therefore left a running JmDNS
instance behind, kept reachable by its shutdown hook.
stop() now removes the shutdown hook, closes the JmDNS only if the advertiser
created it, and resets the advertising state so later changes to
discoverability, MAC or configuration index do not re-register the service.
---
CHANGES.md | 1 +
.../impl/jmdns/JmdnsHomekitAdvertiser.java | 40 ++++++++++++++-----
.../jmdns/JmdnsHomekitAdvertiserTest.java | 25 ++++++++++++
3 files changed, 57 insertions(+), 9 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 2f8e2455..5fc06bed 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,6 @@
# HAP-Java 2.0.10
* Add optional Mute characteristic to Doorbell service
+* Fix leak of mDNS instances and shutdown hooks when stopping `HomekitRoot`
# HAP-Java 2.0.9
* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202)
diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java
index 9086acb3..1073f9b8 100644
--- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java
+++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java
@@ -17,6 +17,8 @@ public class JmdnsHomekitAdvertiser {
private static final String SERVICE_TYPE = "_hap._tcp.local.";
private final JmDNS jmdns;
+ private final boolean ownsJmdns;
+ private Thread shutdownHook;
private boolean discoverable = true;
private static final Logger logger = LoggerFactory.getLogger(JmdnsHomekitAdvertiser.class);
private boolean isAdvertising = false;
@@ -30,11 +32,16 @@ public class JmdnsHomekitAdvertiser {
private int category;
public JmdnsHomekitAdvertiser(JmDNS jmdns) {
- this.jmdns = jmdns;
+ this(jmdns, false);
}
public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostException, IOException {
- jmdns = JmDNS.create(localAddress);
+ this(JmDNS.create(localAddress), true);
+ }
+
+ JmdnsHomekitAdvertiser(JmDNS jmdns, boolean ownsJmdns) {
+ this.jmdns = jmdns;
+ this.ownsJmdns = ownsJmdns;
}
public synchronized void advertise(
@@ -54,18 +61,33 @@ public synchronized void advertise(
registerService();
- Runtime.getRuntime()
- .addShutdownHook(
- new Thread(
- () -> {
- logger.trace("Stopping advertising in response to shutdown.");
- jmdns.unregisterAllServices();
- }));
+ shutdownHook =
+ new Thread(
+ () -> {
+ logger.trace("Stopping advertising in response to shutdown.");
+ jmdns.unregisterAllServices();
+ });
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
isAdvertising = true;
}
public synchronized void stop() {
unregisterService();
+ isAdvertising = false;
+ if (shutdownHook != null) {
+ try {
+ Runtime.getRuntime().removeShutdownHook(shutdownHook);
+ } catch (IllegalStateException e) {
+ // already shutting down
+ }
+ }
+ if (ownsJmdns) {
+ try {
+ jmdns.close();
+ } catch (IOException e) {
+ logger.warn("Failed to close mDNS", e);
+ }
+ }
}
public synchronized void setDiscoverable(boolean discoverable) throws IOException {
diff --git a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java
index 870bd739..731be498 100644
--- a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java
+++ b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java
@@ -2,7 +2,9 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import java.io.IOException;
@@ -54,6 +56,29 @@ public void testSetConfigurationIndex() throws Exception {
assertThat(getArgumentFromUnregister().getPropertyString("c#")).isEqualTo("1");
}
+ @Test
+ public void testStopClosesOwnedJmdns() throws Exception {
+ subject = new JmdnsHomekitAdvertiser(jmdns, true);
+ advertise();
+ subject.stop();
+ verify(jmdns).close();
+ }
+
+ @Test
+ public void testStopDoesNotCloseSuppliedJmdns() throws Exception {
+ advertise();
+ subject.stop();
+ verify(jmdns, never()).close();
+ }
+
+ @Test
+ public void testSetDiscoverableAfterStopDoesNotRegister() throws Exception {
+ advertise();
+ subject.stop();
+ subject.setDiscoverable(false);
+ verify(jmdns).registerService(any(ServiceInfo.class));
+ }
+
private ServiceInfo getArgumentFromUnregister() {
ArgumentCaptor serviceInfoCaptor = ArgumentCaptor.forClass(ServiceInfo.class);
verify(jmdns).unregisterService(serviceInfoCaptor.capture());
From a162ea552137a9a7ca7b5caa7bc3f5ec78369a55 Mon Sep 17 00:00:00 2001
From: eugen
Date: Sat, 14 Nov 2026 01:03:47 +0100
Subject: [PATCH 9/9] Update netty to 4.1.139.Final (#215)
Co-authored-by: Eugen
---
CHANGES.md | 1 +
pom.xml | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/CHANGES.md b/CHANGES.md
index 1bbbbcf7..ab4bb120 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,6 +2,7 @@
* Add constructors with custom min/max to the window covering tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162)
* Add optional Mute characteristic to Doorbell service
* Fix leak of mDNS instances and shutdown hooks when stopping `HomekitRoot`
+* Updated netty to 4.1.139.Final
# HAP-Java 2.0.9
* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202)
diff --git a/pom.xml b/pom.xml
index a1a95277..7bbbb902 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
UTF-8
- 4.1.126.Final
+ 4.1.139.Final