[boschshc] Provide alarm channel for smoke detectors

* add new channel definition for alarm channel in thing-types.xml
* add alarm channel to Smoke Detector and Smoke Detector II
* add update instruction sets in binding.xml
* re-generate i18n properties file
* add constant for new channel
* implement alarm service
* register service package in tests
* extend abstract smoke detector handler
* add unit tests
* add documentation

Signed-off-by: David Pace <dev@davidpace.de>
pull/18194/head
David Pace 2025-01-26 21:31:47 +01:00
parent dfea7a13cf
commit 5f7531c820
13 changed files with 325 additions and 33 deletions

View File

@ -296,9 +296,12 @@ The smoke detector warns you in case of fire.
**Thing Type ID**: `smoke-detector`
| Channel Type ID | Item Type | Writable | Description |
| ------------------ | -------------------- | :------: | ------------------------------------------------------------------------------------------------- |
| smoke-check | String | &#9745; | State of the smoke check. Also used to request a new smoke check. |
| Channel Type ID | Item Type | Writable | Description |
| ----------------| ----------| :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| alarm | String | &#9745; | Alarm state of the smoke detector. Possible values are: `IDLE_OFF`, `PRIMARY_ALARM`, `SECONDARY_ALARM` and `INTRUSION_ALARM`. |
| smoke-check | String | &#9745; | State of the smoke check. Also used to request a new smoke check. |
| 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`). |
### Smoke Detector II
@ -306,12 +309,13 @@ The smoke detector warns you in case of fire.
**Thing Type ID**: `smoke-detector`
| Channel Type ID | Item Type | Writable | Description |
|-------------------|-------------| :------: |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| smoke-check | String | &#9745; | State of the smoke check. Also used to request a new smoke check. |
| 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`). |
| signal-strength | Number | &#9744; | Communication quality between the device and the Smart Home Controller. Possible values range between 0 (unknown) and 4 (best signal strength). |
| Channel Type ID | Item Type | Writable | Description |
|-----------------|-----------| :------: |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| alarm | String | &#9745; | Alarm state of the smoke detector. Possible values are: `IDLE_OFF`, `PRIMARY_ALARM`, `SECONDARY_ALARM` and `INTRUSION_ALARM`. |
| smoke-check | String | &#9745; | State of the smoke check. Also used to request a new smoke check. |
| 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`). |
| signal-strength | Number | &#9744; | Communication quality between the device and the Smart Home Controller. Possible values range between 0 (unknown) and 4 (best signal strength). |
### User-defined States

View File

@ -81,10 +81,10 @@ public class BoschShcCommandExtension extends AbstractConsoleCommandExtension im
* <code>src/main/java/org/openhab/binding/boschshc/internal/services</code>.
*/
List<String> getAllBoschShcServices() {
return List.of("airqualitylevel", "batterylevel", "binaryswitch", "bypass", "cameranotification", "childlock",
"childprotection", "communicationquality", "hsbcoloractuator", "humiditylevel", "illuminance",
"impulseswitch", "intrusion", "keypad", "latestmotion", "multilevelswitch", "powermeter", "powerswitch",
"privacymode", "roomclimatecontrol", "shuttercontact", "shuttercontrol", "silentmode",
return List.of("airqualitylevel", "alarm", "batterylevel", "binaryswitch", "bypass", "cameranotification",
"childlock", "childprotection", "communicationquality", "hsbcoloractuator", "humiditylevel",
"illuminance", "impulseswitch", "intrusion", "keypad", "latestmotion", "multilevelswitch", "powermeter",
"powerswitch", "privacymode", "roomclimatecontrol", "shuttercontact", "shuttercontrol", "silentmode",
"smokedetectorcheck", "temperaturelevel", "userstate", "valvetappet", "waterleakagesensor",
"waterleakagesensorcheck", "waterleakagesensortilt");
}

View File

@ -12,12 +12,15 @@
*/
package org.openhab.binding.boschshc.internal.devices;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ALARM;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.alarm.AlarmService;
import org.openhab.binding.boschshc.internal.services.alarm.dto.AlarmServiceState;
import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.SmokeDetectorCheckService;
import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.dto.SmokeDetectorCheckServiceState;
import org.openhab.core.library.types.StringType;
@ -34,10 +37,12 @@ import org.openhab.core.types.Command;
@NonNullByDefault
public abstract class AbstractSmokeDetectorHandler extends AbstractBatteryPoweredDeviceHandler {
private AlarmService alarmService;
private SmokeDetectorCheckService smokeDetectorCheckService;
protected AbstractSmokeDetectorHandler(Thing thing) {
super(thing);
this.alarmService = new AlarmService();
this.smokeDetectorCheckService = new SmokeDetectorCheckService();
}
@ -45,6 +50,7 @@ public abstract class AbstractSmokeDetectorHandler extends AbstractBatteryPowere
protected void initializeServices() throws BoschSHCException {
super.initializeServices();
this.registerService(alarmService, this::updateChannels, List.of(CHANNEL_ALARM));
this.registerService(smokeDetectorCheckService, this::updateChannels, List.of(CHANNEL_SMOKE_CHECK));
}
@ -53,12 +59,19 @@ public abstract class AbstractSmokeDetectorHandler extends AbstractBatteryPowere
super.handleCommand(channelUID, command);
switch (channelUID.getId()) {
case CHANNEL_ALARM:
this.handleServiceCommand(this.alarmService, command);
break;
case CHANNEL_SMOKE_CHECK:
this.handleServiceCommand(this.smokeDetectorCheckService, command);
break;
}
}
private void updateChannels(AlarmServiceState state) {
updateState(CHANNEL_ALARM, new StringType(state.value.toString()));
}
private void updateChannels(SmokeDetectorCheckServiceState state) {
updateState(CHANNEL_SMOKE_CHECK, new StringType(state.value.toString()));
}

View File

@ -116,6 +116,7 @@ public class BoschSHCBindingConstants {
public static final String CHANNEL_IMPULSE_SWITCH = "impulse-switch";
public static final String CHANNEL_IMPULSE_LENGTH = "impulse-length";
public static final String CHANNEL_INSTANT_OF_LAST_IMPULSE = "instant-of-last-impulse";
public static final String CHANNEL_ALARM = "alarm";
// numbered channels
// the rationale for introducing numbered channels was discussed in
// https://github.com/openhab/openhab-addons/pull/16400

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2025 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.alarm;
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.alarm.dto.AlarmServiceState;
import org.openhab.binding.boschshc.internal.services.alarm.dto.AlarmState;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
/**
* Alarm service for smoke detectors.
*
* @author David Pace - Initial contribution
*
*/
@NonNullByDefault
public class AlarmService extends BoschSHCService<AlarmServiceState> {
public AlarmService() {
super("Alarm", AlarmServiceState.class);
}
@Override
public AlarmServiceState handleCommand(Command command) throws BoschSHCException {
if (command instanceof StringType stringCommand) {
AlarmServiceState state = new AlarmServiceState();
state.value = AlarmState.from(stringCommand.toFullString());
return state;
}
return super.handleCommand(command);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2010-2025 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.alarm.dto;
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
/**
* State for alarm services of smoke detectors.
*
* @author David Pace - Initial contribution
*
*/
public class AlarmServiceState extends BoschSHCServiceState {
public AlarmServiceState() {
super("alarmState");
}
public AlarmState value;
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2010-2025 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.alarm.dto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Possible states of smoke detector alarms.
*
* @author David Pace - Initial contribution
*
*/
public enum AlarmState {
IDLE_OFF,
PRIMARY_ALARM,
SECONDARY_ALARM,
INTRUSION_ALARM;
private static final Logger LOGGER = LoggerFactory.getLogger(AlarmState.class);
public static AlarmState from(String identifier) {
try {
return valueOf(identifier);
} catch (IllegalArgumentException e) {
LOGGER.warn("Unsupported alarm state: {}", identifier);
return IDLE_OFF;
}
}
}

View File

@ -84,6 +84,12 @@ channel-type.boschshc.alarm-state.state.option.PRE_ALARM = Alarm is about to go
channel-type.boschshc.alarm-state.state.option.ALARM_ON = Alarm was triggered
channel-type.boschshc.alarm-state.state.option.ALARM_MUTED = Alarm is muted
channel-type.boschshc.alarm-state.state.option.UNKNOWN = Alarm status is unknown
channel-type.boschshc.alarm.label = Alarm
channel-type.boschshc.alarm.description = Alarm state of the smoke detector.
channel-type.boschshc.alarm.state.option.IDLE_OFF = Alarm off
channel-type.boschshc.alarm.state.option.PRIMARY_ALARM = Primary alarm
channel-type.boschshc.alarm.state.option.SECONDARY_ALARM = Secondary alarm
channel-type.boschshc.alarm.state.option.INTRUSION_ALARM = Intrusion alarm
channel-type.boschshc.arm-action.label = Arm Action
channel-type.boschshc.arm-action.description = Arms the intrusion detection system using the given profile ID.
channel-type.boschshc.arming-state.label = Arming State

View File

@ -344,6 +344,7 @@
<description>The smoke detector warns you in case of fire.</description>
<channels>
<channel id="alarm" typeId="alarm"/>
<channel id="smoke-check" typeId="smoke-check"/>
<channel id="battery-level" typeId="system.battery-level"/>
<channel id="low-battery" typeId="system.low-battery"/>
@ -412,6 +413,7 @@
<description>The smoke detector warns you in case of fire.</description>
<channels>
<channel id="alarm" typeId="alarm"/>
<channel id="smoke-check" typeId="smoke-check"/>
<channel id="battery-level" typeId="system.battery-level"/>
<channel id="low-battery" typeId="system.low-battery"/>
@ -887,4 +889,19 @@
<state readOnly="true"/>
</channel-type>
<channel-type id="alarm">
<item-type>String</item-type>
<label>Alarm</label>
<description>Alarm state of the smoke detector.</description>
<category>Alarm</category>
<state readOnly="false">
<options>
<option value="IDLE_OFF">Alarm off</option>
<option value="PRIMARY_ALARM">Primary alarm</option>
<option value="SECONDARY_ALARM">Secondary alarm</option>
<option value="INTRUSION_ALARM">Intrusion alarm</option>
</options>
</state>
</channel-type>
</thing:thing-descriptions>

View File

@ -22,4 +22,20 @@
</instruction-set>
</thing-type>
<thing-type uid="boschshc:smoke-detector">
<instruction-set targetVersion="1">
<add-channel id="alarm">
<type>boschshc:alarm</type>
</add-channel>
</instruction-set>
</thing-type>
<thing-type uid="boschshc:smoke-detector-2">
<instruction-set targetVersion="1">
<add-channel id="alarm">
<type>boschshc:alarm</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>

View File

@ -14,7 +14,10 @@ package org.openhab.binding.boschshc.internal.devices;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_ALARM;
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -24,7 +27,8 @@ import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.openhab.binding.boschshc.internal.devices.smokedetector.SmokeDetectorHandler;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.alarm.dto.AlarmServiceState;
import org.openhab.binding.boschshc.internal.services.alarm.dto.AlarmState;
import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.SmokeDetectorCheckState;
import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.dto.SmokeDetectorCheckServiceState;
import org.openhab.core.library.types.OnOffType;
@ -52,32 +56,34 @@ public abstract class AbstractSmokeDetectorHandlerTest<T extends AbstractSmokeDe
@Captor
private @NonNullByDefault({}) ArgumentCaptor<SmokeDetectorCheckServiceState> smokeDetectorCheckStateCaptor;
@Captor
private @NonNullByDefault({}) ArgumentCaptor<AlarmServiceState> alarmStateCaptor;
@Test
public void testHandleCommand()
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
public void testHandleCommandSmokeTest() throws InterruptedException, TimeoutException, ExecutionException {
// valid commands with valid thing & channel
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK),
new StringType(SmokeDetectorCheckState.SMOKE_TEST_REQUESTED.toString()));
verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("SmokeDetectorCheck"),
smokeDetectorCheckStateCaptor.capture());
SmokeDetectorCheckServiceState state = smokeDetectorCheckStateCaptor.getValue();
assertSame(SmokeDetectorCheckState.SMOKE_TEST_REQUESTED, state.value);
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK),
new StringType(SmokeDetectorCheckState.NONE.toString()));
verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("SmokeDetectorCheck"),
smokeDetectorCheckStateCaptor.capture());
state = smokeDetectorCheckStateCaptor.getValue();
assertSame(SmokeDetectorCheckState.NONE, state.value);
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK),
new StringType(SmokeDetectorCheckState.SMOKE_TEST_OK.toString()));
verify(getBridgeHandler(), times(3)).putState(eq(getDeviceID()), eq("SmokeDetectorCheck"),
smokeDetectorCheckStateCaptor.capture());
state = smokeDetectorCheckStateCaptor.getValue();
assertSame(SmokeDetectorCheckState.SMOKE_TEST_OK, state.value);
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK),
new StringType(SmokeDetectorCheckState.SMOKE_TEST_FAILED.toString()));
verify(getBridgeHandler(), times(4)).putState(eq(getDeviceID()), eq("SmokeDetectorCheck"),
smokeDetectorCheckStateCaptor.capture());
@ -86,10 +92,8 @@ public abstract class AbstractSmokeDetectorHandlerTest<T extends AbstractSmokeDe
}
@Test
public void testHandleCommandPlayPauseType()
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
PlayPauseType.PLAY);
public void testHandleCommandPlayPauseType() throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK), PlayPauseType.PLAY);
verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("SmokeDetectorCheck"),
smokeDetectorCheckStateCaptor.capture());
SmokeDetectorCheckServiceState state = smokeDetectorCheckStateCaptor.getValue();
@ -97,10 +101,8 @@ public abstract class AbstractSmokeDetectorHandlerTest<T extends AbstractSmokeDe
}
@Test
public void testHandleCommandUnknownCommand()
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
OnOffType.ON);
public void testHandleCommandUnknownCommand() {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK), OnOffType.ON);
ThingStatusInfo expectedThingStatusInfo = ThingStatusInfoBuilder
.create(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR)
.withDescription(
@ -113,8 +115,7 @@ public abstract class AbstractSmokeDetectorHandlerTest<T extends AbstractSmokeDe
public void testUpdateChannelSmokeDetectorCheckServiceStateNone() {
JsonElement jsonObject = JsonParser.parseString("{\"@type\":\"smokeDetectorCheckState\",\"value\":NONE}");
getFixture().processUpdate("SmokeDetectorCheck", jsonObject);
verify(getCallback()).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK),
new StringType("NONE"));
}
@ -123,8 +124,38 @@ public abstract class AbstractSmokeDetectorHandlerTest<T extends AbstractSmokeDe
JsonElement jsonObject = JsonParser
.parseString("{\"@type\":\"smokeDetectorCheckState\",\"value\":SMOKE_TEST_REQUESTED}");
getFixture().processUpdate("SmokeDetectorCheck", jsonObject);
verify(getCallback()).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SMOKE_CHECK),
verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), CHANNEL_SMOKE_CHECK),
new StringType("SMOKE_TEST_REQUESTED"));
}
@Test
public void testUpdateChannelsAlarm() {
String json = """
{
"@type": "alarmState",
"value": IDLE_OFF
}
""";
JsonElement jsonObject = JsonParser.parseString(json);
getFixture().processUpdate("Alarm", jsonObject);
verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), CHANNEL_ALARM),
new StringType("IDLE_OFF"));
}
@Test
public void testHandleCommandAlarm() throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_ALARM), new StringType("PRIMARY_ALARM"));
verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("Alarm"), alarmStateCaptor.capture());
AlarmServiceState state = alarmStateCaptor.getValue();
assertSame(AlarmState.PRIMARY_ALARM, state.value);
}
@Test
public void testHandleCommandAlarmUnknownAlarmState()
throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_ALARM), new StringType("INVALID"));
verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("Alarm"), alarmStateCaptor.capture());
AlarmServiceState state = alarmStateCaptor.getValue();
assertSame(AlarmState.IDLE_OFF, state.value);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2010-2025 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.alarm;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.alarm.dto.AlarmServiceState;
import org.openhab.binding.boschshc.internal.services.alarm.dto.AlarmState;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
/**
* Unit tests for {@link AlarmService}.
*
* @author David Pace - Initial contribution
*/
@NonNullByDefault
class AlarmServiceTest {
private @NonNullByDefault({}) AlarmService fixture;
@BeforeEach
public void beforeEach() {
fixture = new AlarmService();
}
@Test
void testHandleCommandValidCommand() throws BoschSHCException {
AlarmServiceState state = fixture.handleCommand(new StringType("IDLE_OFF"));
assertNotNull(state);
assertSame(AlarmState.IDLE_OFF, state.value);
}
@Test
void testHandleCommandInvalidCommand() {
assertThrows(BoschSHCException.class, () -> fixture.handleCommand(new DecimalType(0)));
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2010-2025 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.alarm.dto;
import static org.junit.jupiter.api.Assertions.assertSame;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link AlarmState}.
*
* @author David Pace - Initial contribution
*
*/
@NonNullByDefault
class AlarmStateTest {
@Test
void testFromValidIdentifier() {
assertSame(AlarmState.PRIMARY_ALARM, AlarmState.from("PRIMARY_ALARM"));
}
@Test
void testFromInvalidIdentifier() {
assertSame(AlarmState.IDLE_OFF, AlarmState.from("INVALID"));
}
}