From 2cac465ac6afcc28177ad18fe6b5028b4060bba4 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Sun, 8 Dec 2024 00:33:36 +0100 Subject: [PATCH 1/2] Fix RuntimeExceptions Add test coverage Signed-off-by: Jacob Laursen --- .../DigiplexResponseResolver.java | 59 ++++- .../internal/handler/DigiplexAreaHandler.java | 10 + .../internal/handler/DigiplexZoneHandler.java | 10 + .../DigiplexResponseResolverTest.java | 222 ++++++++++++++++++ 4 files changed, 291 insertions(+), 10 deletions(-) create mode 100644 bundles/org.openhab.binding.digiplex/src/test/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolverTest.java diff --git a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java index d485a370db78a..65c2c45515c96 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java +++ b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java @@ -35,7 +35,6 @@ public class DigiplexResponseResolver { private static final String OK = "&ok"; - // TODO: handle failures private static final String FAIL = "&fail"; public static DigiplexResponse resolveResponse(String message) { @@ -53,21 +52,42 @@ public static DigiplexResponse resolveResponse(String message) { return CommunicationStatus.OK; } case "ZL": // zone label - zoneNo = Integer.valueOf(message.substring(2, 5)); + if (message.length() < 5) { + return new UnknownResponse(message); + } + try { + zoneNo = Integer.valueOf(message.substring(2, 5)); + } catch (NumberFormatException e) { + return new UnknownResponse(message); + } if (message.contains(FAIL)) { return ZoneLabelResponse.failure(zoneNo); } else { return ZoneLabelResponse.success(zoneNo, message.substring(5).trim()); } case "AL": // area label - areaNo = Integer.valueOf(message.substring(2, 5)); + if (message.length() < 5) { + return new UnknownResponse(message); + } + try { + areaNo = Integer.valueOf(message.substring(2, 5)); + } catch (NumberFormatException e) { + return new UnknownResponse(message); + } if (message.contains(FAIL)) { return AreaLabelResponse.failure(areaNo); } else { return AreaLabelResponse.success(areaNo, message.substring(5).trim()); } case "RZ": // zone status - zoneNo = Integer.valueOf(message.substring(2, 5)); + if (message.length() < 10) { + return new UnknownResponse(message); + } + try { + zoneNo = Integer.valueOf(message.substring(2, 5)); + } catch (NumberFormatException e) { + return new UnknownResponse(message); + } if (message.contains(FAIL)) { return ZoneStatusResponse.failure(zoneNo); } else { @@ -79,10 +99,20 @@ public static DigiplexResponse resolveResponse(String message) { toBoolean(message.charAt(9))); // battery low } case "RA": // area status - areaNo = Integer.valueOf(message.substring(2, 5)); + if (message.length() < 10) { + return new UnknownResponse(message); + } + try { + areaNo = Integer.valueOf(message.substring(2, 5)); + } catch (NumberFormatException e) { + return new UnknownResponse(message); + } if (message.contains(FAIL)) { return AreaStatusResponse.failure(areaNo); } else { + if (message.length() < 12) { + return new UnknownResponse(message); + } return AreaStatusResponse.success(areaNo, // zone number AreaStatus.fromMessage(message.charAt(5)), // status toBoolean(message.charAt(6)), // zone in memory @@ -95,7 +125,11 @@ public static DigiplexResponse resolveResponse(String message) { case "AA": // area arm case "AQ": // area quick arm case "AD": // area disarm - areaNo = Integer.valueOf(message.substring(2, 5)); + try { + areaNo = Integer.valueOf(message.substring(2, 5)); + } catch (NumberFormatException e) { + return new UnknownResponse(message); + } if (message.contains(FAIL)) { return AreaArmDisarmResponse.failure(areaNo, ArmDisarmType.fromMessage(commandType)); } else { @@ -104,7 +138,7 @@ public static DigiplexResponse resolveResponse(String message) { case "UL": // user label case "PG": // PGM events default: - if (message.startsWith("G")) { + if (message.startsWith("G") && message.length() >= 12) { return resolveSystemEvent(message); } else { return new UnknownResponse(message); @@ -117,9 +151,14 @@ private static boolean toBoolean(char value) { } private static DigiplexResponse resolveSystemEvent(String message) { - int eventGroup = Integer.parseInt(message.substring(1, 4)); - int eventNumber = Integer.parseInt(message.substring(5, 8)); - int areaNumber = Integer.parseInt(message.substring(9, 12)); + int eventGroup, eventNumber, areaNumber; + try { + eventGroup = Integer.parseInt(message.substring(1, 4)); + eventNumber = Integer.parseInt(message.substring(5, 8)); + areaNumber = Integer.parseInt(message.substring(9, 12)); + } catch (NumberFormatException e) { + return new UnknownResponse(message); + } switch (eventGroup) { case 0: return new ZoneStatusEvent(eventNumber, ZoneStatus.CLOSED, areaNumber); diff --git a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexAreaHandler.java b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexAreaHandler.java index 4eaeff5b42cbd..d29ad7e027d59 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexAreaHandler.java +++ b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexAreaHandler.java @@ -33,6 +33,7 @@ import org.openhab.binding.digiplex.internal.communication.ArmType; import org.openhab.binding.digiplex.internal.communication.DigiplexMessageHandler; import org.openhab.binding.digiplex.internal.communication.DigiplexRequest; +import org.openhab.binding.digiplex.internal.communication.UnknownResponse; import org.openhab.binding.digiplex.internal.communication.events.AreaEvent; import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.StringType; @@ -45,6 +46,8 @@ import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link DigiplexAreaHandler} is responsible for handling commands, which are @@ -55,6 +58,8 @@ @NonNullByDefault public class DigiplexAreaHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(DigiplexAreaHandler.class); + private @Nullable DigiplexAreaConfiguration config; private @Nullable DigiplexBridgeHandler bridgeHandler; private DigiplexAreaMessageHandler visitor = new DigiplexAreaMessageHandler(); @@ -308,5 +313,10 @@ public void handleAreaEvent(AreaEvent event) { tempStatus.ifPresent(s -> updateState(AREA_STATUS, s.toStringType())); } } + + @Override + public void handleUnknownResponse(UnknownResponse response) { + logger.debug("Unknown response: {}", response.message); + } } } diff --git a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexZoneHandler.java b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexZoneHandler.java index b38bb5d11cdab..0fd371d468d94 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexZoneHandler.java +++ b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/handler/DigiplexZoneHandler.java @@ -22,6 +22,7 @@ import org.openhab.binding.digiplex.internal.DigiplexBindingConstants; import org.openhab.binding.digiplex.internal.communication.DigiplexMessageHandler; import org.openhab.binding.digiplex.internal.communication.DigiplexRequest; +import org.openhab.binding.digiplex.internal.communication.UnknownResponse; import org.openhab.binding.digiplex.internal.communication.ZoneStatusRequest; import org.openhab.binding.digiplex.internal.communication.ZoneStatusResponse; import org.openhab.binding.digiplex.internal.communication.events.ZoneEvent; @@ -41,6 +42,8 @@ import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link DigiplexZoneHandler} is responsible for handling commands, which are @@ -51,6 +54,8 @@ @NonNullByDefault public class DigiplexZoneHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(DigiplexZoneHandler.class); + private @Nullable DigiplexBridgeHandler bridgeHandler; private DigiplexZoneMessageHandler messageHandler = new DigiplexZoneMessageHandler(); private int zoneNo; @@ -236,5 +241,10 @@ public void handleZoneEvent(ZoneEvent event) { updateAreaNo(event.getAreaNo()); } } + + @Override + public void handleUnknownResponse(UnknownResponse response) { + logger.debug("Unknown response: {}", response.message); + } } } diff --git a/bundles/org.openhab.binding.digiplex/src/test/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolverTest.java b/bundles/org.openhab.binding.digiplex/src/test/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolverTest.java new file mode 100644 index 0000000000000..d70957bd4f8a8 --- /dev/null +++ b/bundles/org.openhab.binding.digiplex/src/test/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolverTest.java @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2010-2024 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.digiplex.internal.communication; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.openhab.binding.digiplex.internal.communication.events.GenericEvent; + +/** + * Tests for {@link DigiplexResponseResolver} + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +public class DigiplexResponseResolverTest { + @ParameterizedTest + @MethodSource("provideTestCasesForResolveResponseReturnsUnknownResponseWhenMessageIsMalformed") + void resolveResponseReturnsUnknownResponseWhenMessageIsMalformed(String message) { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(UnknownResponse.class))); + if (actual instanceof UnknownResponse unknownResponse) { + assertThat(unknownResponse.message, is(equalTo(message))); + } + } + + private static Stream provideTestCasesForResolveResponseReturnsUnknownResponseWhenMessageIsMalformed() { + return Stream.of( // + Arguments.of("CO&"), Arguments.of("ZL&fail"), Arguments.of("ZL12"), Arguments.of("AL&fail"), + Arguments.of("AL12"), Arguments.of("RZZZ3COOOO&fail"), Arguments.of("RZ123C"), + Arguments.of("RZ123COOO"), Arguments.of("RA&fail"), Arguments.of("RA123DOOXOO"), + Arguments.of("AA&fail"), Arguments.of("UL "), Arguments.of("PG "), Arguments.of("GGGGGGGGGGGG"), + Arguments.of("G1234567890"), Arguments.of("ZZZZ")); + } + + @Test + void resolveResponseReturnsCommunicationStatusSuccessWhenWellformed() { + String message = "CO&ok"; + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(CommunicationStatus.class))); + if (actual instanceof CommunicationStatus communicationStatus) { + assertThat(communicationStatus.success, is(true)); + } + } + + @Test + void resolveResponseReturnsCommunicationStatusFailureWhenMessageContainsFail() { + String message = "CO&fail"; + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(CommunicationStatus.class))); + if (actual instanceof CommunicationStatus communicationStatus) { + assertThat(communicationStatus.success, is(false)); + } + } + + @ParameterizedTest + @MethodSource("provideTestCasesForResolveResponseReturnsZoneLabelResponse") + void resolveResponseReturnsZoneLabelResponse(String message, boolean expectedSuccess, int expectedZoneNo, + String expectedName) { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(ZoneLabelResponse.class))); + if (actual instanceof ZoneLabelResponse zoneLabelResponse) { + assertThat(zoneLabelResponse.success, is(expectedSuccess)); + assertThat(zoneLabelResponse.zoneNo, is(expectedZoneNo)); + assertThat(zoneLabelResponse.zoneName, is(expectedName)); + } + } + + private static Stream provideTestCasesForResolveResponseReturnsZoneLabelResponse() { + return Stream.of( // + Arguments.of("ZL123", true, 123, ""), Arguments.of("ZL123test ", true, 123, "test"), + Arguments.of("ZL123&fail", false, 123, null), Arguments.of("ZL123test&fail", false, 123, null)); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForResolveResponseReturnsAreaLabelResponse") + void resolveResponseReturnsAreaLabelResponse(String message, boolean expectedSuccess, int expectedAreaNo, + String expectedName) { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(AreaLabelResponse.class))); + if (actual instanceof AreaLabelResponse areaLabelResponse) { + assertThat(areaLabelResponse.success, is(expectedSuccess)); + assertThat(areaLabelResponse.areaNo, is(expectedAreaNo)); + assertThat(areaLabelResponse.areaName, is(expectedName)); + } + } + + private static Stream provideTestCasesForResolveResponseReturnsAreaLabelResponse() { + return Stream.of( // + Arguments.of("AL123", true, 123, ""), Arguments.of("AL123test ", true, 123, "test"), + Arguments.of("AL123&fail", false, 123, null), Arguments.of("AL123test&fail", false, 123, null)); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForResolveResponseReturnsZoneStatusResponse") + void resolveResponseReturnsZoneStatusResponse(String message, boolean expectedSuccess, int expectedZoneNo, + ZoneStatus expectedZoneStatus, boolean expectedAlarm, boolean expectedFireAlarm, + boolean expectedSupervisionLost, boolean expectedLowBattery) { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(ZoneStatusResponse.class))); + if (actual instanceof ZoneStatusResponse zoneStatusResponse) { + assertThat(zoneStatusResponse.success, is(expectedSuccess)); + assertThat(zoneStatusResponse.zoneNo, is(expectedZoneNo)); + assertThat(zoneStatusResponse.status, is(expectedZoneStatus)); + assertThat(zoneStatusResponse.alarm, is(expectedAlarm)); + assertThat(zoneStatusResponse.fireAlarm, is(expectedFireAlarm)); + assertThat(zoneStatusResponse.supervisionLost, is(expectedSupervisionLost)); + assertThat(zoneStatusResponse.lowBattery, is(expectedLowBattery)); + } + } + + private static Stream provideTestCasesForResolveResponseReturnsZoneStatusResponse() { + return Stream.of( // + Arguments.of("RZ123COOOO", true, 123, ZoneStatus.CLOSED, false, false, false, false), + Arguments.of("RZ123OOOOO", true, 123, ZoneStatus.OPEN, false, false, false, false), + Arguments.of("RZ123TOOOO", true, 123, ZoneStatus.TAMPERED, false, false, false, false), + Arguments.of("RZ123FOOOO", true, 123, ZoneStatus.FIRE_LOOP_TROUBLE, false, false, false, false), + Arguments.of("RZ123uOOOO", true, 123, ZoneStatus.UNKNOWN, false, false, false, false), + Arguments.of("RZ123cOOOO", true, 123, ZoneStatus.UNKNOWN, false, false, false, false), + Arguments.of("RZ123cXOOO", true, 123, ZoneStatus.UNKNOWN, true, false, false, false), + Arguments.of("RZ123cOXOO", true, 123, ZoneStatus.UNKNOWN, false, true, false, false), + Arguments.of("RZ123cOOXO", true, 123, ZoneStatus.UNKNOWN, false, false, true, false), + Arguments.of("RZ123cOOOX", true, 123, ZoneStatus.UNKNOWN, false, false, false, true), + Arguments.of("RZ123&fail", false, 123, null, false, false, false, false), + Arguments.of("RZ123COOOO&fail", false, 123, null, false, false, false, false)); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForResolveResponseReturnsAreaStatusResponse") + void resolveResponseReturnsAreaStatusResponse(String message, boolean expectedSuccess, int expectedAreaNo, + AreaStatus expectedAreaStatus, boolean expectedZoneInMemory, boolean expectedTrouble, boolean expectedReady, + boolean expectedInProgramming, boolean expectedAlarm, boolean expectedStrobe) { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(AreaStatusResponse.class))); + if (actual instanceof AreaStatusResponse areaStatusResponse) { + assertThat(areaStatusResponse.success, is(expectedSuccess)); + assertThat(areaStatusResponse.areaNo, is(expectedAreaNo)); + assertThat(areaStatusResponse.status, is(expectedAreaStatus)); + assertThat(areaStatusResponse.zoneInMemory, is(expectedZoneInMemory)); + assertThat(areaStatusResponse.trouble, is(expectedTrouble)); + assertThat(areaStatusResponse.ready, is(expectedReady)); + assertThat(areaStatusResponse.inProgramming, is(expectedInProgramming)); + assertThat(areaStatusResponse.alarm, is(expectedAlarm)); + assertThat(areaStatusResponse.strobe, is(expectedStrobe)); + } + } + + private static Stream provideTestCasesForResolveResponseReturnsAreaStatusResponse() { + return Stream.of( // + Arguments.of("RA123DOOXOOO", true, 123, AreaStatus.DISARMED, false, false, false, false, false, false), + Arguments.of("RA123AOOXOOO", true, 123, AreaStatus.ARMED, false, false, false, false, false, false), + Arguments.of("RA123FOOXOOO", true, 123, AreaStatus.ARMED_FORCE, false, false, false, false, false, + false), + Arguments.of("RA123SOOXOOO", true, 123, AreaStatus.ARMED_STAY, false, false, false, false, false, + false), + Arguments.of("RA123IOOXOOO", true, 123, AreaStatus.ARMED_INSTANT, false, false, false, false, false, + false), + Arguments.of("RA123uOOXOOO", true, 123, AreaStatus.UNKNOWN, false, false, false, false, false, false), + Arguments.of("RA123dOOXOOO", true, 123, AreaStatus.UNKNOWN, false, false, false, false, false, false), + Arguments.of("RA123dXOXOOO", true, 123, AreaStatus.UNKNOWN, true, false, false, false, false, false), + Arguments.of("RA123dOXxOOO", true, 123, AreaStatus.UNKNOWN, false, true, false, false, false, false), + Arguments.of("RA123dOOOOOO", true, 123, AreaStatus.UNKNOWN, false, false, true, false, false, false), + Arguments.of("RA123dOOXXOO", true, 123, AreaStatus.UNKNOWN, false, false, false, true, false, false), + Arguments.of("RA123dOOXOXO", true, 123, AreaStatus.UNKNOWN, false, false, false, false, true, false), + Arguments.of("RA123dOOXOOX", true, 123, AreaStatus.UNKNOWN, false, false, false, false, false, true), + Arguments.of("RA123&fail", false, 123, null, false, false, false, false, false, false), + Arguments.of("RA123DOOXOOO&fail", false, 123, null, false, false, false, false, false, false)); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForResolveResponseReturnsAreaArmDisarmResponse") + void resolveResponseReturnsAreaArmDisarmResponse(String message, boolean expectedSuccess, int expectedAreaNo, + ArmDisarmType expectedType) { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse(message); + assertThat(actual, is(instanceOf(AreaArmDisarmResponse.class))); + if (actual instanceof AreaArmDisarmResponse armDisarmResponse) { + assertThat(armDisarmResponse.success, is(expectedSuccess)); + assertThat(armDisarmResponse.areaNo, is(expectedAreaNo)); + assertThat(armDisarmResponse.type, is(expectedType)); + } + } + + private static Stream provideTestCasesForResolveResponseReturnsAreaArmDisarmResponse() { + return Stream.of( // + Arguments.of("AA123", true, 123, ArmDisarmType.ARM), + Arguments.of("AQ123", true, 123, ArmDisarmType.QUICK_ARM), + Arguments.of("AD123", true, 123, ArmDisarmType.DISARM), + Arguments.of("AA123&fail", false, 123, ArmDisarmType.ARM), + Arguments.of("AQ123&fail", false, 123, ArmDisarmType.QUICK_ARM), + Arguments.of("AD123&fail", false, 123, ArmDisarmType.DISARM)); + } + + @Test + void resolveResponseReturnsGenericEventWhenWellformed() { + DigiplexResponse actual = DigiplexResponseResolver.resolveResponse("G123 456 789"); + assertThat(actual, is(instanceOf(GenericEvent.class))); + if (actual instanceof GenericEvent genericEvent) { + assertThat(genericEvent.getEventGroup(), is(123)); + assertThat(genericEvent.getEventNumber(), is(456)); + assertThat(genericEvent.getAreaNo(), is(789)); + } + } +} From 15c7684f29505ec0f9e188a7dce48c96aa15ce79 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Sun, 8 Dec 2024 00:52:43 +0100 Subject: [PATCH 2/2] Remove duplicated parsing Signed-off-by: Jacob Laursen --- .../DigiplexResponseResolver.java | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java index 65c2c45515c96..0984a2672d5e2 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java +++ b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/communication/DigiplexResponseResolver.java @@ -13,6 +13,7 @@ package org.openhab.binding.digiplex.internal.communication; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.digiplex.internal.communication.events.AreaEvent; import org.openhab.binding.digiplex.internal.communication.events.AreaEventType; import org.openhab.binding.digiplex.internal.communication.events.GenericEvent; @@ -42,7 +43,7 @@ public static DigiplexResponse resolveResponse(String message) { return new UnknownResponse(message); } - int zoneNo, areaNo; + Integer zoneNo, areaNo; String commandType = message.substring(0, 2); switch (commandType) { case "CO": // communication status @@ -52,12 +53,8 @@ public static DigiplexResponse resolveResponse(String message) { return CommunicationStatus.OK; } case "ZL": // zone label - if (message.length() < 5) { - return new UnknownResponse(message); - } - try { - zoneNo = Integer.valueOf(message.substring(2, 5)); - } catch (NumberFormatException e) { + zoneNo = getZoneOrArea(message); + if (zoneNo == null) { return new UnknownResponse(message); } if (message.contains(FAIL)) { @@ -66,12 +63,8 @@ public static DigiplexResponse resolveResponse(String message) { return ZoneLabelResponse.success(zoneNo, message.substring(5).trim()); } case "AL": // area label - if (message.length() < 5) { - return new UnknownResponse(message); - } - try { - areaNo = Integer.valueOf(message.substring(2, 5)); - } catch (NumberFormatException e) { + areaNo = getZoneOrArea(message); + if (areaNo == null) { return new UnknownResponse(message); } if (message.contains(FAIL)) { @@ -80,17 +73,16 @@ public static DigiplexResponse resolveResponse(String message) { return AreaLabelResponse.success(areaNo, message.substring(5).trim()); } case "RZ": // zone status - if (message.length() < 10) { - return new UnknownResponse(message); - } - try { - zoneNo = Integer.valueOf(message.substring(2, 5)); - } catch (NumberFormatException e) { + zoneNo = getZoneOrArea(message); + if (zoneNo == null) { return new UnknownResponse(message); } if (message.contains(FAIL)) { return ZoneStatusResponse.failure(zoneNo); } else { + if (message.length() < 10) { + return new UnknownResponse(message); + } return ZoneStatusResponse.success(zoneNo, // zone number ZoneStatus.fromMessage(message.charAt(5)), // status toBoolean(message.charAt(6)), // alarm @@ -99,12 +91,8 @@ public static DigiplexResponse resolveResponse(String message) { toBoolean(message.charAt(9))); // battery low } case "RA": // area status - if (message.length() < 10) { - return new UnknownResponse(message); - } - try { - areaNo = Integer.valueOf(message.substring(2, 5)); - } catch (NumberFormatException e) { + areaNo = getZoneOrArea(message); + if (areaNo == null) { return new UnknownResponse(message); } if (message.contains(FAIL)) { @@ -125,9 +113,8 @@ public static DigiplexResponse resolveResponse(String message) { case "AA": // area arm case "AQ": // area quick arm case "AD": // area disarm - try { - areaNo = Integer.valueOf(message.substring(2, 5)); - } catch (NumberFormatException e) { + areaNo = getZoneOrArea(message); + if (areaNo == null) { return new UnknownResponse(message); } if (message.contains(FAIL)) { @@ -146,6 +133,17 @@ public static DigiplexResponse resolveResponse(String message) { } } + private static @Nullable Integer getZoneOrArea(String message) { + if (message.length() < 5) { + return null; + } + try { + return Integer.valueOf(message.substring(2, 5)); + } catch (NumberFormatException e) { + return null; + } + } + private static boolean toBoolean(char value) { return value != 'O'; }