]> git.basschouten.com Git - openhab-addons.git/commitdiff
Support for Thermostat SilentMode (#14779) (#14781)
authorDavid Pace <dev@davidpace.de>
Tue, 11 Apr 2023 20:09:56 +0000 (22:09 +0200)
committerGitHub <noreply@github.com>
Tue, 11 Apr 2023 20:09:56 +0000 (22:09 +0200)
- Add new channel definition for silent mode
- Implement silent mode service
- Add unit tests
- Add documentation
- Fix some minor documentation issues

Closes #14779

Signed-off-by: David Pace <dev@davidpace.de>
bundles/org.openhab.binding.boschshc/README.md
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandler.java
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeService.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeState.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/dto/SilentModeServiceState.java [new file with mode: 0644]
bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/i18n/boschshc.properties
bundles/org.openhab.binding.boschshc/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/thermostat/ThermostatHandlerTest.java
bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeStateTest.java [new file with mode: 0644]

index f7241a0c6816e95cbfcf922a951d7927abfb7df7..6be38759eab06082f048bc595e852d40fa11d5e7 100644 (file)
@@ -7,7 +7,7 @@ Binding for the Bosch Smart Home.
     - [In-Wall Switch](#in-wall-switch)
     - [Compact Smart Plug](#compact-smart-plug)
     - [Twinguard Smoke Detector](#twinguard-smoke-detector)
-    - [Door/Window Contact](#doorwindow-contact)
+    - [Door/Window Contact](#door-window-contact)
     - [Motion Detector](#motion-detector)
     - [Shutter Control](#shutter-control)
     - [Thermostat](#thermostat)
@@ -116,6 +116,7 @@ Radiator thermostat
 | temperature           | Number:Temperature   | &#9744;  | Current measured temperature.                  |
 | valve-tappet-position | Number:Dimensionless | &#9744;  | Current open ratio of valve tappet (0 to 100). |
 | child-lock            | Switch               | &#9745;  | Indicates if child lock is active.             |
+| silent-mode           | Switch               | &#9745;  | Enables or disables silent mode on thermostats. When enabled, the battery usage is higher. |
 | battery-level         | Number               | &#9744;  | Current battery level percentage as integer number. Bosch-specific battery levels are mapped to numbers as follows: `OK`: 100, `LOW_BATTERY`: 10, `CRITICAL_LOW`: 1, `CRITICALLY_LOW_BATTERY`: 1, `NOT_AVAILABLE`: `UNDEF`. |
 | low-battery           | Switch               | &#9744;  | Indicates whether the battery is low (`ON`) or OK (`OFF`). |
 
@@ -193,7 +194,7 @@ A smart bulb connected to the bridge via Zigbee such as a Ledvance Smart+ bulb.
 | brightness      | Dimmer    | &#9745;  | Regulates the brightness on a percentage scale from 0 to 100%. |
 | color           | Color     | &#9745;  | The color of the emitted light.                                |
 
-### Smoke detector
+### Smoke Detector
 
 The smoke detector warns you in case of fire.
 
index f97100d3ea82e22b3b3896c6d388bdda712992df..8b70ce0a499871504cd5f09c6b15bd54ae0af037 100644 (file)
@@ -82,6 +82,7 @@ public class BoschSHCBindingConstants {
     public static final String CHANNEL_COLOR = "color";
     public static final String CHANNEL_BRIGHTNESS = "brightness";
     public static final String CHANNEL_SMOKE_CHECK = "smoke-check";
+    public static final String CHANNEL_SILENT_MODE = "silent-mode";
 
     // static device/service names
     public static final String SERVICE_INTRUSION_DETECTION = "intrusionDetectionSystem";
index fe24681294e5dd4df8ddd02a5b35c9baa587ddca..6a6e242a843f77e67c27ec0b3fbd65bb70a3ce68 100644 (file)
@@ -21,6 +21,8 @@ import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDevic
 import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
 import org.openhab.binding.boschshc.internal.services.childlock.ChildLockService;
 import org.openhab.binding.boschshc.internal.services.childlock.dto.ChildLockServiceState;
+import org.openhab.binding.boschshc.internal.services.silentmode.SilentModeService;
+import org.openhab.binding.boschshc.internal.services.silentmode.dto.SilentModeServiceState;
 import org.openhab.binding.boschshc.internal.services.temperaturelevel.TemperatureLevelService;
 import org.openhab.binding.boschshc.internal.services.temperaturelevel.dto.TemperatureLevelServiceState;
 import org.openhab.binding.boschshc.internal.services.valvetappet.ValveTappetService;
@@ -33,15 +35,18 @@ import org.openhab.core.types.Command;
  * Handler for a thermostat device.
  *
  * @author Christian Oeing - Initial contribution
+ * @author David Pace - Added silent mode service
  */
 @NonNullByDefault
 public final class ThermostatHandler extends AbstractBatteryPoweredDeviceHandler {
 
     private ChildLockService childLockService;
+    private SilentModeService silentModeService;
 
     public ThermostatHandler(Thing thing) {
         super(thing);
         this.childLockService = new ChildLockService();
+        this.silentModeService = new SilentModeService();
     }
 
     @Override
@@ -51,6 +56,7 @@ public final class ThermostatHandler extends AbstractBatteryPoweredDeviceHandler
         this.createService(TemperatureLevelService::new, this::updateChannels, List.of(CHANNEL_TEMPERATURE));
         this.createService(ValveTappetService::new, this::updateChannels, List.of(CHANNEL_VALVE_TAPPET_POSITION));
         this.registerService(this.childLockService, this::updateChannels, List.of(CHANNEL_CHILD_LOCK));
+        this.registerService(this.silentModeService, this::updateChannels, List.of(CHANNEL_SILENT_MODE));
     }
 
     @Override
@@ -61,6 +67,9 @@ public final class ThermostatHandler extends AbstractBatteryPoweredDeviceHandler
             case CHANNEL_CHILD_LOCK:
                 this.handleServiceCommand(this.childLockService, command);
                 break;
+            case CHANNEL_SILENT_MODE:
+                this.handleServiceCommand(this.silentModeService, command);
+                break;
         }
     }
 
@@ -93,4 +102,13 @@ public final class ThermostatHandler extends AbstractBatteryPoweredDeviceHandler
     private void updateChannels(ChildLockServiceState state) {
         super.updateState(CHANNEL_CHILD_LOCK, state.getActiveState());
     }
+
+    /**
+     * Updates the channels which are linked to the {@link SilentModeService} of the device.
+     * 
+     * @param state current state of {@link SilentModeService}
+     */
+    private void updateChannels(SilentModeServiceState state) {
+        super.updateState(CHANNEL_SILENT_MODE, state.toOnOffType());
+    }
 }
diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeService.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeService.java
new file mode 100644 (file)
index 0000000..386e837
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.services.silentmode;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
+import org.openhab.binding.boschshc.internal.services.BoschSHCService;
+import org.openhab.binding.boschshc.internal.services.silentmode.dto.SilentModeServiceState;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.types.Command;
+
+/**
+ * Service to get and set the silent mode of thermostats.
+ * 
+ * @author David Pace - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class SilentModeService extends BoschSHCService<SilentModeServiceState> {
+
+    public SilentModeService() {
+        super("SilentMode", SilentModeServiceState.class);
+    }
+
+    @Override
+    public SilentModeServiceState handleCommand(Command command) throws BoschSHCException {
+        if (command instanceof OnOffType onOffCommand) {
+            SilentModeServiceState serviceState = new SilentModeServiceState();
+            serviceState.mode = SilentModeState.fromOnOffType(onOffCommand);
+            return serviceState;
+        }
+        return super.handleCommand(command);
+    }
+}
diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeState.java
new file mode 100644 (file)
index 0000000..6a931ef
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.services.silentmode;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.OnOffType;
+
+/**
+ * Enum for possible silent mode states.
+ * 
+ * @author David Pace - Initial contribution
+ *
+ */
+@NonNullByDefault
+public enum SilentModeState {
+    MODE_NORMAL,
+    MODE_SILENT;
+
+    public static SilentModeState fromOnOffType(OnOffType onOffType) {
+        return onOffType == OnOffType.ON ? SilentModeState.MODE_SILENT : SilentModeState.MODE_NORMAL;
+    }
+}
diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/dto/SilentModeServiceState.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/services/silentmode/dto/SilentModeServiceState.java
new file mode 100644 (file)
index 0000000..fc50d00
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.services.silentmode.dto;
+
+import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
+import org.openhab.binding.boschshc.internal.services.silentmode.SilentModeState;
+import org.openhab.core.library.types.OnOffType;
+
+/**
+ * Represents the state of the silent mode for thermostats.
+ * <p>
+ * Example JSON for normal mode:
+ * 
+ * <pre>
+ * {
+ *   "@type": "silentModeState",
+ *   "mode": "MODE_NORMAL"
+ * }
+ * </pre>
+ * 
+ * Example JSON for silent mode:
+ * 
+ * <pre>
+ * {
+ *   "@type": "silentModeState",
+ *   "mode": "MODE_SILENT"
+ * }
+ * </pre>
+ * 
+ * @author David Pace - Initial contribution
+ *
+ */
+public class SilentModeServiceState extends BoschSHCServiceState {
+
+    public SilentModeServiceState() {
+        super("silentModeState");
+    }
+
+    public SilentModeState mode;
+
+    public OnOffType toOnOffType() {
+        return mode == SilentModeState.MODE_SILENT ? OnOffType.ON : OnOffType.OFF;
+    }
+}
index 8a564ea8832ab36d040f5f325768579abab6aeb5..2371ad36a11b5899c3d15ffad88cc169d5deacbb 100644 (file)
@@ -70,7 +70,7 @@ channel-type.boschshc.camera-notification.description = Enables or disables noti
 channel-type.boschshc.camera-notification.state.option.ENABLED = Enable notifications
 channel-type.boschshc.camera-notification.state.option.DISABLED = Disable notifications
 channel-type.boschshc.child-lock.label = Child Lock
-channel-type.boschshc.child-lock.description = Indicates if it is possible to set the desired temperature on the device.
+channel-type.boschshc.child-lock.description = Enables or disables the child lock on the device.
 channel-type.boschshc.combined-rating.label = Combined Rating
 channel-type.boschshc.combined-rating.description = Combined rating of the air quality.
 channel-type.boschshc.combined-rating.state.option.GOOD = Good Quality
@@ -107,6 +107,10 @@ channel-type.boschshc.purity.label = Purity
 channel-type.boschshc.purity.description = Purity of the air. A higher value indicates a higher pollution.
 channel-type.boschshc.setpoint-temperature.label = Setpoint Temperature
 channel-type.boschshc.setpoint-temperature.description = Desired temperature.
+channel-type.boschshc.silent-mode.label = Silent Mode
+channel-type.boschshc.silent-mode.description = Enables or disables silent mode on thermostats. When enabled, the battery usage is higher.
+channel-type.boschshc.silent-mode.state.option.MODE_NORMAL = Silent mode disabled (lower battery usage)
+channel-type.boschshc.silent-mode.state.option.MODE_SILENT = Silent mode enabled (higher battery usage)
 channel-type.boschshc.smoke-check.label = Smoke Check State
 channel-type.boschshc.smoke-check.description = State of last smoke detector check.
 channel-type.boschshc.smoke-check.state.option.NONE = None
index fe22ddb9b6f93a96b1bf0bbdde73903cf7e59d9b..03354abe143222d61d1cfbe4e2529063cadbc315 100644 (file)
                        <channel id="temperature" typeId="temperature"/>
                        <channel id="valve-tappet-position" typeId="valve-tappet-position"/>
                        <channel id="child-lock" typeId="child-lock"/>
+                       <channel id="silent-mode" typeId="silent-mode"/>
                        <channel id="battery-level" typeId="system.battery-level"/>
                        <channel id="low-battery" typeId="system.low-battery"/>
                </channels>
        <channel-type id="child-lock">
                <item-type>Switch</item-type>
                <label>Child Lock</label>
-               <description>Indicates if it is possible to set the desired temperature on the device.</description>
+               <description>Enables or disables the child lock on the device.</description>
+       </channel-type>
+
+       <channel-type id="silent-mode">
+               <item-type>Switch</item-type>
+               <label>Silent Mode</label>
+               <description>Enables or disables silent mode on thermostats. When enabled, the battery usage is higher.</description>
+               <state>
+                       <options>
+                               <option value="MODE_NORMAL">Silent mode disabled (lower battery usage)</option>
+                               <option value="MODE_SILENT">Silent mode enabled (higher battery usage)</option>
+                       </options>
+               </state>
        </channel-type>
 
 </thing:thing-descriptions>
index e493c57cc7890c0177ac677731ff9c53b4489856..2430b9ea1b56efb3a3748c2a279ba1c35c35353b 100644 (file)
@@ -30,6 +30,8 @@ import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
 import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
 import org.openhab.binding.boschshc.internal.services.childlock.dto.ChildLockServiceState;
 import org.openhab.binding.boschshc.internal.services.childlock.dto.ChildLockState;
+import org.openhab.binding.boschshc.internal.services.silentmode.SilentModeState;
+import org.openhab.binding.boschshc.internal.services.silentmode.dto.SilentModeServiceState;
 import org.openhab.core.library.types.DecimalType;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.QuantityType;
@@ -51,10 +53,12 @@ import com.google.gson.JsonParser;
  *
  */
 @NonNullByDefault
-public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest<ThermostatHandler> {
+class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTest<ThermostatHandler> {
 
     private @Captor @NonNullByDefault({}) ArgumentCaptor<ChildLockServiceState> childLockServiceStateCaptor;
 
+    private @Captor @NonNullByDefault({}) ArgumentCaptor<SilentModeServiceState> silentModeServiceStateCaptor;
+
     @Override
     protected ThermostatHandler createFixture() {
         return new ThermostatHandler(getThing());
@@ -71,7 +75,7 @@ public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTe
     }
 
     @Test
-    public void testHandleCommand()
+    void testHandleCommandChildLockService()
             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
         getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK),
                 OnOffType.ON);
@@ -81,7 +85,18 @@ public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTe
     }
 
     @Test
-    public void testHandleCommandUnknownCommand() {
+    void testHandleCommandSilentModeService()
+            throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+        getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SILENT_MODE),
+                OnOffType.ON);
+        verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("SilentMode"),
+                silentModeServiceStateCaptor.capture());
+        SilentModeServiceState state = silentModeServiceStateCaptor.getValue();
+        assertSame(SilentModeState.MODE_SILENT, state.mode);
+    }
+
+    @Test
+    void testHandleCommandUnknownCommandChildLockService() {
         getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK),
                 new DecimalType(42));
         ThingStatusInfo expectedThingStatusInfo = ThingStatusInfoBuilder
@@ -93,7 +108,19 @@ public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTe
     }
 
     @Test
-    public void testUpdateChannelsTemperatureLevelService() {
+    void testHandleCommandUnknownCommandSilentModeService() {
+        getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SILENT_MODE),
+                new DecimalType(42));
+        ThingStatusInfo expectedThingStatusInfo = ThingStatusInfoBuilder
+                .create(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR)
+                .withDescription(
+                        "Error when service SilentMode should handle command org.openhab.core.library.types.DecimalType: SilentMode: Can not handle command org.openhab.core.library.types.DecimalType")
+                .build();
+        verify(getCallback()).statusUpdated(getThing(), expectedThingStatusInfo);
+    }
+
+    @Test
+    void testUpdateChannelsTemperatureLevelService() {
         JsonElement jsonObject = JsonParser.parseString(
                 "{\n" + "   \"@type\": \"temperatureLevelState\",\n" + "   \"temperature\": 21.5\n" + " }");
         getFixture().processUpdate("TemperatureLevel", jsonObject);
@@ -103,7 +130,7 @@ public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTe
     }
 
     @Test
-    public void testUpdateChannelsValveTappetService() {
+    void testUpdateChannelsValveTappetService() {
         JsonElement jsonObject = JsonParser
                 .parseString("{\n" + "   \"@type\": \"valveTappetState\",\n" + "   \"position\": 42\n" + " }");
         getFixture().processUpdate("ValveTappet", jsonObject);
@@ -113,11 +140,27 @@ public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTe
     }
 
     @Test
-    public void testUpdateChannelsChildLockService() {
+    void testUpdateChannelsChildLockService() {
         JsonElement jsonObject = JsonParser
                 .parseString("{\n" + "   \"@type\": \"childLockState\",\n" + "   \"childLock\": \"ON\"\n" + " }");
         getFixture().processUpdate("Thermostat", jsonObject);
         verify(getCallback()).stateUpdated(
                 new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), OnOffType.ON);
     }
+
+    @Test
+    void testUpdateChannelsSilentModeService() {
+        JsonElement jsonObject = JsonParser.parseString("{\"@type\": \"silentModeState\", \"mode\": \"MODE_SILENT\"}");
+        getFixture().processUpdate("SilentMode", jsonObject);
+        verify(getCallback()).stateUpdated(
+                new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SILENT_MODE), OnOffType.ON);
+    }
+
+    @Test
+    void testUpdateChannelsSilentModeServiceNormal() {
+        JsonElement jsonObject = JsonParser.parseString("{\"@type\": \"silentModeState\", \"mode\": \"MODE_NORMAL\"}");
+        getFixture().processUpdate("SilentMode", jsonObject);
+        verify(getCallback()).stateUpdated(
+                new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SILENT_MODE), OnOffType.OFF);
+    }
 }
diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeStateTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/silentmode/SilentModeStateTest.java
new file mode 100644 (file)
index 0000000..38ec3ed
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.boschshc.internal.services.silentmode;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.core.library.types.OnOffType;
+
+/**
+ * Unit tests for {@link SilentModeState}.
+ * 
+ * @author David Pace - Initial contribution
+ *
+ */
+@NonNullByDefault
+class SilentModeStateTest {
+
+    @Test
+    void fromOnOffType() {
+        assertSame(SilentModeState.MODE_NORMAL, SilentModeState.fromOnOffType(OnOffType.OFF));
+        assertSame(SilentModeState.MODE_SILENT, SilentModeState.fromOnOffType(OnOffType.ON));
+    }
+}