[satel] Event log reading refactored with other fixes (#18328)
* [satel] Event log reading refactored with other fixes: - partition keypad decoding for I128 WRL - replaced N/A text with device number when device name is unavailable - user decoding for certain cases fixed Signed-off-by: Krzysztof Goworek <krzysztof.goworek@gmail.com>pull/18343/head
parent
64616a0c55
commit
d98bf37cf2
|
@ -14,25 +14,20 @@ package org.openhab.binding.satel.internal.handler;
|
|||
|
||||
import static org.openhab.binding.satel.internal.SatelBindingConstants.*;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.satel.internal.action.SatelEventLogActions;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
|
||||
import org.openhab.binding.satel.internal.command.ReadEventCommand;
|
||||
import org.openhab.binding.satel.internal.command.ReadEventDescCommand;
|
||||
import org.openhab.binding.satel.internal.event.ConnectionStatusEvent;
|
||||
import org.openhab.binding.satel.internal.types.IntegraType;
|
||||
import org.openhab.binding.satel.internal.util.DeviceNameResolver;
|
||||
import org.openhab.binding.satel.internal.util.EventLogReader;
|
||||
import org.openhab.binding.satel.internal.util.EventLogReader.EventDescription;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
|
@ -56,21 +51,16 @@ public class SatelEventLogHandler extends SatelThingHandler {
|
|||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_EVENTLOG);
|
||||
|
||||
private static final String NOT_AVAILABLE_TEXT = "N/A";
|
||||
private static final String DETAILS_SEPARATOR = ", ";
|
||||
private static final long CACHE_CLEAR_INTERVAL = TimeUnit.MINUTES.toMillis(30);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SatelEventLogHandler.class);
|
||||
private final Map<String, @Nullable EventDescriptionCacheEntry> eventDescriptions = new ConcurrentHashMap<>();
|
||||
private final Map<String, @Nullable String> deviceNameCache = new ConcurrentHashMap<>();
|
||||
private @Nullable EventLogReader eventLogReader;
|
||||
private @Nullable ScheduledFuture<?> cacheExpirationJob;
|
||||
private Charset encoding = Charset.defaultCharset();
|
||||
|
||||
/**
|
||||
* Represents single record of the event log.
|
||||
*
|
||||
* @author Krzysztof Goworek
|
||||
*
|
||||
*/
|
||||
public static class EventLogEntry {
|
||||
|
||||
|
@ -138,14 +128,13 @@ public class SatelEventLogHandler extends SatelThingHandler {
|
|||
public void initialize() {
|
||||
super.initialize();
|
||||
|
||||
withBridgeHandlerPresent(bridgeHandler -> {
|
||||
this.encoding = bridgeHandler.getEncoding();
|
||||
});
|
||||
withBridgeHandlerPresent(bridgeHandler -> this.eventLogReader = new EventLogReader(bridgeHandler,
|
||||
new DeviceNameResolver(bridgeHandler)));
|
||||
|
||||
final ScheduledFuture<?> cacheExpirationJob = this.cacheExpirationJob;
|
||||
if (cacheExpirationJob == null || cacheExpirationJob.isCancelled()) {
|
||||
// for simplicity all cache entries are cleared every 30 minutes
|
||||
this.cacheExpirationJob = scheduler.scheduleWithFixedDelay(deviceNameCache::clear, CACHE_CLEAR_INTERVAL,
|
||||
this.cacheExpirationJob = scheduler.scheduleWithFixedDelay(this::clearCache, CACHE_CLEAR_INTERVAL,
|
||||
CACHE_CLEAR_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +148,7 @@ public class SatelEventLogHandler extends SatelThingHandler {
|
|||
cacheExpirationJob.cancel(true);
|
||||
}
|
||||
this.cacheExpirationJob = null;
|
||||
this.eventLogReader = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -199,223 +189,32 @@ public class SatelEventLogHandler extends SatelThingHandler {
|
|||
* @return record data or {@linkplain Optional#empty()} if there is no record under given index
|
||||
*/
|
||||
public Optional<EventLogEntry> readEvent(int eventIndex) {
|
||||
return getEventDescription(eventIndex).flatMap(eventDesc -> {
|
||||
ReadEventCommand readEventCmd = eventDesc.readEventCmd;
|
||||
int currentIndex = readEventCmd.getCurrentIndex();
|
||||
String eventText = eventDesc.getText();
|
||||
boolean upperZone = getBridgeHandler().getIntegraType() == IntegraType.I256_PLUS
|
||||
&& readEventCmd.getUserControlNumber() > 0;
|
||||
String eventDetails;
|
||||
|
||||
switch (eventDesc.getKind()) {
|
||||
case 0:
|
||||
eventDetails = "";
|
||||
break;
|
||||
case 1:
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getZoneExpanderKeypadDescription(readEventCmd.getSource(), upperZone);
|
||||
break;
|
||||
case 2:
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getUserDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 3:
|
||||
eventDetails = getDeviceDescription(DeviceType.EXPANDER, readEventCmd.getPartitionKeypad())
|
||||
+ DETAILS_SEPARATOR + getUserDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 4:
|
||||
eventDetails = getZoneExpanderKeypadDescription(readEventCmd.getSource(), upperZone);
|
||||
break;
|
||||
case 5:
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition());
|
||||
break;
|
||||
case 6:
|
||||
eventDetails = getDeviceDescription(DeviceType.KEYPAD, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getUserDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 7:
|
||||
eventDetails = getUserDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 8:
|
||||
eventDetails = getDeviceDescription(DeviceType.EXPANDER, readEventCmd.getSource());
|
||||
break;
|
||||
case 9:
|
||||
eventDetails = getTelephoneDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 11:
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getDataBusDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 12:
|
||||
if (readEventCmd.getSource() <= getBridgeHandler().getIntegraType().getOnMainboard()) {
|
||||
eventDetails = getOutputExpanderDescription(readEventCmd.getSource(), upperZone);
|
||||
} else {
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getOutputExpanderDescription(readEventCmd.getSource(), upperZone);
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
if (readEventCmd.getSource() <= 128) {
|
||||
eventDetails = getOutputExpanderDescription(readEventCmd.getSource(), upperZone);
|
||||
} else {
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getOutputExpanderDescription(readEventCmd.getSource(), upperZone);
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
eventDetails = getTelephoneDescription(readEventCmd.getPartition()) + DETAILS_SEPARATOR
|
||||
+ getUserDescription(readEventCmd.getSource());
|
||||
break;
|
||||
case 15:
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getDeviceDescription(DeviceType.TIMER, readEventCmd.getSource());
|
||||
break;
|
||||
case 31:
|
||||
// this description consists of two records, so we must read additional record from the log
|
||||
eventDetails = "." + readEventCmd.getSource() + "."
|
||||
+ (readEventCmd.getObject() * 32 + readEventCmd.getUserControlNumber());
|
||||
Optional<EventDescription> eventDescNext = getEventDescription(readEventCmd.getNextIndex());
|
||||
if (eventDescNext.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final EventDescription eventDescNextItem = eventDescNext.get();
|
||||
if (eventDescNextItem.getKind() != 30) {
|
||||
logger.info("Unexpected event record kind {} at index {}", eventDescNextItem.getKind(),
|
||||
readEventCmd.getNextIndex());
|
||||
return Optional.empty();
|
||||
}
|
||||
readEventCmd = eventDescNextItem.readEventCmd;
|
||||
eventText = eventDescNextItem.getText();
|
||||
eventDetails = getDeviceDescription(DeviceType.KEYPAD, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + "ip: " + readEventCmd.getSource() + "."
|
||||
+ (readEventCmd.getObject() * 32 + readEventCmd.getUserControlNumber()) + eventDetails;
|
||||
break;
|
||||
case 32:
|
||||
eventDetails = getDeviceDescription(DeviceType.PARTITION, readEventCmd.getPartition())
|
||||
+ DETAILS_SEPARATOR + getDeviceDescription(DeviceType.ZONE, readEventCmd.getSource());
|
||||
break;
|
||||
default:
|
||||
logger.info("Unsupported device kind code {} at index {}", eventDesc.getKind(),
|
||||
readEventCmd.getCurrentIndex());
|
||||
eventDetails = String.join(DETAILS_SEPARATOR, "kind=" + eventDesc.getKind(),
|
||||
"partition=" + readEventCmd.getPartition(), "source=" + readEventCmd.getSource(),
|
||||
"object=" + readEventCmd.getObject(), "ucn=" + readEventCmd.getUserControlNumber());
|
||||
}
|
||||
|
||||
return Optional.of(new EventLogEntry(currentIndex, readEventCmd.getNextIndex(),
|
||||
readEventCmd.getTimestamp().atZone(getBridgeHandler().getZoneId()), eventText, eventDetails));
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<EventDescription> getEventDescription(int eventIndex) {
|
||||
ReadEventCommand readEventCmd = new ReadEventCommand(eventIndex);
|
||||
if (!getBridgeHandler().sendCommand(readEventCmd, false)) {
|
||||
logger.info("Unable to read event record for given index: {}", eventIndex);
|
||||
final EventLogReader eventLogReader = this.eventLogReader;
|
||||
if (eventLogReader == null) {
|
||||
logger.warn("Unable to read event: handler is not properly initialized");
|
||||
return Optional.empty();
|
||||
} else if (readEventCmd.isEmpty()) {
|
||||
logger.info("No record under given index: {}", eventIndex);
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(readEventDescription(readEventCmd));
|
||||
}
|
||||
}
|
||||
|
||||
private static class EventDescriptionCacheEntry {
|
||||
private final String eventText;
|
||||
private final int descKind;
|
||||
|
||||
EventDescriptionCacheEntry(String eventText, int descKind) {
|
||||
this.eventText = eventText;
|
||||
this.descKind = descKind;
|
||||
}
|
||||
|
||||
String getText() {
|
||||
return eventText;
|
||||
return readEvent(eventLogReader, eventIndex);
|
||||
}
|
||||
|
||||
private Optional<EventLogEntry> readEvent(EventLogReader eventLogReader, int eventIndex) {
|
||||
return eventLogReader.readEvent(eventIndex)
|
||||
.map(eventDescription -> combineWithDetails(eventLogReader, eventDescription));
|
||||
}
|
||||
|
||||
private EventLogEntry combineWithDetails(EventLogReader eventLogReader, EventDescription eventDescription) {
|
||||
String eventDetails = eventLogReader.buildDetails(eventDescription);
|
||||
|
||||
return new EventLogEntry(eventDescription.getCurrentIndex(), eventDescription.getNextIndex(),
|
||||
eventDescription.getTimestamp().atZone(getBridgeHandler().getZoneId()), eventDescription.getText(),
|
||||
eventDetails);
|
||||
}
|
||||
|
||||
private void clearCache() {
|
||||
final EventLogReader eventLogReader = this.eventLogReader;
|
||||
if (eventLogReader != null) {
|
||||
eventLogReader.clearCache();
|
||||
}
|
||||
|
||||
int getKind() {
|
||||
return descKind;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EventDescription extends EventDescriptionCacheEntry {
|
||||
private final ReadEventCommand readEventCmd;
|
||||
|
||||
EventDescription(ReadEventCommand readEventCmd, String eventText, int descKind) {
|
||||
super(eventText, descKind);
|
||||
this.readEventCmd = readEventCmd;
|
||||
}
|
||||
}
|
||||
|
||||
private EventDescription readEventDescription(ReadEventCommand readEventCmd) {
|
||||
int eventCode = readEventCmd.getEventCode();
|
||||
boolean restore = readEventCmd.isRestore();
|
||||
String mapKey = String.format("%d_%b", eventCode, restore);
|
||||
EventDescriptionCacheEntry mapValue = eventDescriptions.computeIfAbsent(mapKey, k -> {
|
||||
ReadEventDescCommand cmd = new ReadEventDescCommand(eventCode, restore, true);
|
||||
if (!getBridgeHandler().sendCommand(cmd, false)) {
|
||||
logger.info("Unable to read event description: {}, {}", eventCode, restore);
|
||||
return null;
|
||||
}
|
||||
return new EventDescriptionCacheEntry(cmd.getText(encoding), cmd.getKind());
|
||||
});
|
||||
if (mapValue == null) {
|
||||
return new EventDescription(readEventCmd, NOT_AVAILABLE_TEXT, 0);
|
||||
} else {
|
||||
return new EventDescription(readEventCmd, mapValue.getText(), mapValue.getKind());
|
||||
}
|
||||
}
|
||||
|
||||
private String getOutputExpanderDescription(int deviceNumber, boolean upperOutput) {
|
||||
if (deviceNumber == 0) {
|
||||
return "mainboard";
|
||||
} else if (deviceNumber <= 128) {
|
||||
return getDeviceDescription(DeviceType.OUTPUT, upperOutput ? 128 + deviceNumber : deviceNumber);
|
||||
} else if (deviceNumber <= 192) {
|
||||
return getDeviceDescription(DeviceType.EXPANDER, deviceNumber);
|
||||
} else {
|
||||
return "invalid output|expander device: " + deviceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
private String getZoneExpanderKeypadDescription(int deviceNumber, boolean upperZone) {
|
||||
if (deviceNumber == 0) {
|
||||
return "mainboard";
|
||||
} else if (deviceNumber <= 128) {
|
||||
return getDeviceDescription(DeviceType.ZONE, upperZone ? 128 + deviceNumber : deviceNumber);
|
||||
} else if (deviceNumber <= 192) {
|
||||
return getDeviceDescription(DeviceType.EXPANDER, deviceNumber);
|
||||
} else {
|
||||
return getDeviceDescription(DeviceType.KEYPAD, deviceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private String getUserDescription(int deviceNumber) {
|
||||
return deviceNumber == 0 ? "user: unknown" : getDeviceDescription(DeviceType.USER, deviceNumber);
|
||||
}
|
||||
|
||||
private String getDataBusDescription(int deviceNumber) {
|
||||
return "data bus: " + deviceNumber;
|
||||
}
|
||||
|
||||
private String getTelephoneDescription(int deviceNumber) {
|
||||
return deviceNumber == 0 ? "telephone: unknown" : getDeviceDescription(DeviceType.TELEPHONE, deviceNumber);
|
||||
}
|
||||
|
||||
private String getDeviceDescription(DeviceType deviceType, int deviceNumber) {
|
||||
return String.format("%s: %s", deviceType.name().toLowerCase(), readDeviceName(deviceType, deviceNumber));
|
||||
}
|
||||
|
||||
private String readDeviceName(DeviceType deviceType, int deviceNumber) {
|
||||
String cacheKey = String.format("%s_%d", deviceType, deviceNumber);
|
||||
String result = deviceNameCache.computeIfAbsent(cacheKey, k -> {
|
||||
ReadDeviceInfoCommand cmd = new ReadDeviceInfoCommand(deviceType, deviceNumber);
|
||||
if (!getBridgeHandler().sendCommand(cmd, false)) {
|
||||
logger.info("Unable to read device info: {}, {}", deviceType, deviceNumber);
|
||||
return null;
|
||||
}
|
||||
return cmd.getName(encoding);
|
||||
});
|
||||
return result == null ? NOT_AVAILABLE_TEXT : result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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.satel.internal.util;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
|
||||
import org.openhab.binding.satel.internal.handler.SatelBridgeHandler;
|
||||
import org.openhab.binding.satel.internal.handler.SatelEventLogHandler;
|
||||
import org.openhab.binding.satel.internal.types.IntegraType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Helper class for getting device names from the alarm system.
|
||||
* Used for friendly descriptions in event log. Names are cached to speed up repeating requests.
|
||||
*
|
||||
* @author Krzysztof Goworek - Initial contribution
|
||||
* @see SatelEventLogHandler
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DeviceNameResolver {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private final Map<String, @Nullable String> nameCache = new ConcurrentHashMap<>();
|
||||
private final SatelBridgeHandler bridgeHandler;
|
||||
|
||||
public DeviceNameResolver(SatelBridgeHandler bridgeHandler) {
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached names.
|
||||
*/
|
||||
public void clearCache() {
|
||||
nameCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the device with given type and number.
|
||||
*
|
||||
* @param deviceType device type
|
||||
* @param deviceNumber device number
|
||||
* @return device name
|
||||
*/
|
||||
public String resolve(DeviceType deviceType, int deviceNumber) {
|
||||
return String.format("%s: %s", deviceType.name().toLowerCase(), readDeviceName(deviceType, deviceNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of output or expander with given number.
|
||||
*
|
||||
* @param deviceNumber device number
|
||||
* @param upperOutput if {@code true} it is upper half of outputs
|
||||
* @return name of output or expander, depending on device number
|
||||
*/
|
||||
public String resolveOutputExpander(int deviceNumber, boolean upperOutput) {
|
||||
if (deviceNumber == 0) {
|
||||
return "mainboard";
|
||||
} else if (deviceNumber <= 128) {
|
||||
return resolve(DeviceType.OUTPUT, upperOutput ? 128 + deviceNumber : deviceNumber);
|
||||
} else if (deviceNumber <= 192) {
|
||||
return resolve(DeviceType.EXPANDER, deviceNumber);
|
||||
} else {
|
||||
return "invalid output|expander device: " + deviceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of zone or expander or keypad with given number.
|
||||
*
|
||||
* @param deviceNumber device number
|
||||
* @param upperZone if {@code true} it is upper half of zones
|
||||
* @return name of zone or expander or keypad, depending on device number
|
||||
*/
|
||||
public String resolveZoneExpanderKeypad(int deviceNumber, boolean upperZone) {
|
||||
if (deviceNumber == 0) {
|
||||
return "mainboard";
|
||||
} else if (deviceNumber <= 128) {
|
||||
return resolve(DeviceType.ZONE, upperZone ? 128 + deviceNumber : deviceNumber);
|
||||
} else if (deviceNumber <= 192) {
|
||||
return resolve(DeviceType.EXPANDER, deviceNumber);
|
||||
} else {
|
||||
return resolve(DeviceType.KEYPAD, deviceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of partition keypad with given number.
|
||||
*
|
||||
* @param deviceNumber device number
|
||||
* @return name of partition keypad
|
||||
*/
|
||||
public String resolvePartitionKeypad(int deviceNumber) {
|
||||
boolean wrlBoard = bridgeHandler.getIntegraType() == IntegraType.I128_LEON
|
||||
|| bridgeHandler.getIntegraType() == IntegraType.I128_SIM300;
|
||||
// Integra 128-WRL has only one expander bus exposed on the mainboard, it can only have 32 expanders
|
||||
// On the second bus it has connected embedded ABAX and GSM modules
|
||||
if (wrlBoard && deviceNumber > 32) {
|
||||
return "mainboard";
|
||||
} else {
|
||||
return resolve(DeviceType.EXPANDER, deviceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of user with given number.
|
||||
*
|
||||
* @param deviceNumber device number
|
||||
* @return name of user
|
||||
*/
|
||||
public String resolveUser(int deviceNumber) {
|
||||
return switch (deviceNumber) {
|
||||
case 0 -> "user: unknown";
|
||||
case 249 -> "INT-AV";
|
||||
case 250 -> "ACCO NET";
|
||||
case 251 -> "SMS";
|
||||
case 252 -> "timer";
|
||||
case 253 -> "function zone";
|
||||
case 254 -> "Quick arm";
|
||||
case 255 -> "service";
|
||||
default -> resolve(DeviceType.USER, deviceNumber);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of data bus given number.
|
||||
*
|
||||
* @param deviceNumber device number
|
||||
* @return name of data bus
|
||||
*/
|
||||
public String resolveDataBus(int deviceNumber) {
|
||||
return "data bus: " + deviceNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of telephone with given number.
|
||||
*
|
||||
* @param deviceNumber device number
|
||||
* @return name of telephone
|
||||
*/
|
||||
public String resolveTelephone(int deviceNumber) {
|
||||
return deviceNumber == 0 ? "telephone: unknown" : resolve(DeviceType.TELEPHONE, deviceNumber);
|
||||
}
|
||||
|
||||
private String readDeviceName(DeviceType deviceType, int deviceNumber) {
|
||||
String cacheKey = String.format("%s_%d", deviceType, deviceNumber);
|
||||
String result = nameCache.computeIfAbsent(cacheKey, k -> {
|
||||
ReadDeviceInfoCommand cmd = new ReadDeviceInfoCommand(deviceType, deviceNumber);
|
||||
if (!bridgeHandler.sendCommand(cmd, false)) {
|
||||
logger.warn("Unable to read device info: {}, {}", deviceType, deviceNumber);
|
||||
return null;
|
||||
}
|
||||
return cmd.getName(bridgeHandler.getEncoding());
|
||||
});
|
||||
return result == null ? Integer.toString(deviceNumber) : result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* 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.satel.internal.util;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
|
||||
import org.openhab.binding.satel.internal.command.ReadEventCommand;
|
||||
import org.openhab.binding.satel.internal.command.ReadEventDescCommand;
|
||||
import org.openhab.binding.satel.internal.handler.SatelBridgeHandler;
|
||||
import org.openhab.binding.satel.internal.types.IntegraType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This is a helper class for reading event log records.
|
||||
*
|
||||
* @author Krzysztof Goworek - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EventLogReader {
|
||||
|
||||
private static final String DETAILS_SEPARATOR = ", ";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private final Map<String, @Nullable EventDescriptionCacheEntry> eventDescriptions = new ConcurrentHashMap<>();
|
||||
private final SatelBridgeHandler bridgeHandler;
|
||||
private final DeviceNameResolver deviceNameResolver;
|
||||
|
||||
public EventLogReader(SatelBridgeHandler bridgeHandler, DeviceNameResolver deviceNameResolver) {
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
this.deviceNameResolver = deviceNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one record from the event log.
|
||||
*
|
||||
* @param eventIndex record index
|
||||
* @return record description wrapped in {@linkplain Optional} or {@linkplain Optional#empty()} if there is no
|
||||
* record under given index or read command failed
|
||||
*/
|
||||
public Optional<EventDescription> readEvent(int eventIndex) {
|
||||
ReadEventCommand readEventCmd = new ReadEventCommand(eventIndex);
|
||||
if (!bridgeHandler.sendCommand(readEventCmd, false)) {
|
||||
logger.warn("Unable to read event record for given index: {}", eventIndex);
|
||||
return Optional.empty();
|
||||
} else if (readEventCmd.isEmpty()) {
|
||||
logger.warn("No record under given index: {}", eventIndex);
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(readEventDescription(readEventCmd));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds detailed text description for given event record.
|
||||
*
|
||||
* @param eventDescription event record
|
||||
* @return string with event details
|
||||
*/
|
||||
public String buildDetails(EventDescription eventDescription) {
|
||||
String eventDetails = getDetails(eventDescription);
|
||||
if (eventDescription.isMultipartEvent()) {
|
||||
// this description consists of two records, so we must read additional record from the log
|
||||
eventDetails = readSecondPartOfDetails(eventDescription).orElse("") + eventDetails;
|
||||
}
|
||||
return eventDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all device names from the cache.
|
||||
*/
|
||||
public void clearCache() {
|
||||
deviceNameResolver.clearCache();
|
||||
}
|
||||
|
||||
private static class EventDescriptionCacheEntry {
|
||||
private final String eventText;
|
||||
private final int descKind;
|
||||
|
||||
private EventDescriptionCacheEntry(String eventText, int descKind) {
|
||||
this.eventText = eventText;
|
||||
this.descKind = descKind;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return eventText;
|
||||
}
|
||||
|
||||
int getKind() {
|
||||
return descKind;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains decoded data of an event record.
|
||||
*/
|
||||
public class EventDescription extends EventDescriptionCacheEntry {
|
||||
private final int currentIndex;
|
||||
private int nextIndex;
|
||||
private final int userControlNumber;
|
||||
private final int partition;
|
||||
private final int partitionKeypad;
|
||||
private final int source;
|
||||
private final int object;
|
||||
private final LocalDateTime timestamp;
|
||||
|
||||
EventDescription(ReadEventCommand readEventCmd, String eventText, int descKind) {
|
||||
super(eventText, descKind);
|
||||
this.currentIndex = readEventCmd.getCurrentIndex();
|
||||
this.nextIndex = readEventCmd.getNextIndex();
|
||||
this.userControlNumber = readEventCmd.getUserControlNumber();
|
||||
this.partition = readEventCmd.getPartition();
|
||||
this.partitionKeypad = readEventCmd.getPartitionKeypad();
|
||||
this.source = readEventCmd.getSource();
|
||||
this.object = readEventCmd.getObject();
|
||||
this.timestamp = readEventCmd.getTimestamp();
|
||||
}
|
||||
|
||||
public int getCurrentIndex() {
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
public int getNextIndex() {
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
public LocalDateTime getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
private void setNextIndex(int nextIndex) {
|
||||
this.nextIndex = nextIndex;
|
||||
}
|
||||
|
||||
private int getUserControlNumber() {
|
||||
return userControlNumber;
|
||||
}
|
||||
|
||||
private int getPartition() {
|
||||
return partition;
|
||||
}
|
||||
|
||||
private int getPartitionKeypad() {
|
||||
return partitionKeypad;
|
||||
}
|
||||
|
||||
private int getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
private int getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
private boolean isMultipartEvent() {
|
||||
return getKind() == 31;
|
||||
}
|
||||
|
||||
private boolean isSecondPart() {
|
||||
if (getKind() != 30) {
|
||||
logger.warn("Unexpected event record kind {} at index {}", getKind(), getCurrentIndex());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private EventDescription readEventDescription(ReadEventCommand readEventCmd) {
|
||||
int eventCode = readEventCmd.getEventCode();
|
||||
boolean restore = readEventCmd.isRestore();
|
||||
String mapKey = String.format("%d_%b", eventCode, restore);
|
||||
EventDescriptionCacheEntry mapValue = eventDescriptions.computeIfAbsent(mapKey, k -> {
|
||||
ReadEventDescCommand cmd = new ReadEventDescCommand(eventCode, restore, true);
|
||||
if (!bridgeHandler.sendCommand(cmd, false)) {
|
||||
logger.warn("Unable to read event description: {}, {}", eventCode, restore);
|
||||
return null;
|
||||
}
|
||||
return new EventDescriptionCacheEntry(cmd.getText(bridgeHandler.getEncoding()), cmd.getKind());
|
||||
});
|
||||
if (mapValue == null) {
|
||||
String eventText = String.format("event #%d%s", eventCode, restore ? " (restore)" : "");
|
||||
return new EventDescription(readEventCmd, eventText, 0);
|
||||
} else {
|
||||
return new EventDescription(readEventCmd, mapValue.getText(), mapValue.getKind());
|
||||
}
|
||||
}
|
||||
|
||||
private String getDetails(EventDescription eventDesc) {
|
||||
boolean upperZone = bridgeHandler.getIntegraType() == IntegraType.I256_PLUS
|
||||
&& eventDesc.getUserControlNumber() > 0;
|
||||
|
||||
return switch (eventDesc.getKind()) {
|
||||
case 0 -> "";
|
||||
case 1 -> deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveZoneExpanderKeypad(eventDesc.getSource(), upperZone);
|
||||
case 2 -> deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveUser(eventDesc.getSource());
|
||||
case 3 -> deviceNameResolver.resolvePartitionKeypad(eventDesc.getPartitionKeypad()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveUser(eventDesc.getSource());
|
||||
case 4 -> deviceNameResolver.resolveZoneExpanderKeypad(eventDesc.getSource(), upperZone);
|
||||
case 5 -> deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition());
|
||||
case 6 -> deviceNameResolver.resolve(DeviceType.KEYPAD, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveUser(eventDesc.getSource());
|
||||
case 7 -> deviceNameResolver.resolveUser(eventDesc.getSource());
|
||||
case 8 -> deviceNameResolver.resolve(DeviceType.EXPANDER, eventDesc.getSource());
|
||||
case 9 -> deviceNameResolver.resolveTelephone(eventDesc.getSource());
|
||||
case 11 -> deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveDataBus(eventDesc.getSource());
|
||||
case 12 -> (eventDesc.getSource() <= bridgeHandler.getIntegraType().getOnMainboard())
|
||||
? deviceNameResolver.resolveOutputExpander(eventDesc.getSource(), upperZone)
|
||||
: deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveOutputExpander(eventDesc.getSource(), upperZone);
|
||||
case 13 -> (eventDesc.getSource() <= 128)
|
||||
? deviceNameResolver.resolveOutputExpander(eventDesc.getSource(), upperZone)
|
||||
: deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveOutputExpander(eventDesc.getSource(), upperZone);
|
||||
case 14 -> deviceNameResolver.resolveTelephone(eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolveUser(eventDesc.getSource());
|
||||
case 15 -> deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolve(DeviceType.TIMER, eventDesc.getSource());
|
||||
case 30 ->
|
||||
deviceNameResolver.resolve(DeviceType.KEYPAD, eventDesc.getPartition()) + DETAILS_SEPARATOR + "ip: "
|
||||
+ eventDesc.getSource() + "." + (eventDesc.getObject() * 32 + eventDesc.getUserControlNumber());
|
||||
case 31 ->
|
||||
"." + eventDesc.getSource() + "." + (eventDesc.getObject() * 32 + eventDesc.getUserControlNumber());
|
||||
case 32 -> deviceNameResolver.resolve(DeviceType.PARTITION, eventDesc.getPartition()) + DETAILS_SEPARATOR
|
||||
+ deviceNameResolver.resolve(DeviceType.ZONE, eventDesc.getSource());
|
||||
default -> {
|
||||
logger.warn("Unsupported device kind code {} at index {}", eventDesc.getKind(),
|
||||
eventDesc.getCurrentIndex());
|
||||
yield String.join(DETAILS_SEPARATOR, "kind=" + eventDesc.getKind(),
|
||||
"partition=" + eventDesc.getPartition(), "source=" + eventDesc.getSource(),
|
||||
"object=" + eventDesc.getObject(), "ucn=" + eventDesc.getUserControlNumber());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Optional<String> readSecondPartOfDetails(EventDescription eventDesc) {
|
||||
return readEvent(eventDesc.getNextIndex()).filter(EventDescription::isSecondPart).map(descNext -> {
|
||||
eventDesc.setNextIndex(descNext.getNextIndex());
|
||||
return getDetails(descNext);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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.satel.internal.handler;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.openhab.binding.satel.internal.SatelBindingConstants.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openhab.binding.satel.internal.action.SatelEventLogActions;
|
||||
import org.openhab.binding.satel.internal.event.ConnectionStatusEvent;
|
||||
import org.openhab.binding.satel.internal.util.EventLogReader;
|
||||
import org.openhab.binding.satel.internal.util.EventLogReader.EventDescription;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.thing.internal.ThingImpl;
|
||||
|
||||
/**
|
||||
* @author Krzysztof Goworek - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SatelEventLogHandlerTest {
|
||||
|
||||
@Mock
|
||||
private ThingHandlerCallback callback;
|
||||
|
||||
@Mock
|
||||
private SatelBridgeHandler bridgeHandler;
|
||||
|
||||
@Mock
|
||||
private EventLogReader eventLogReader;
|
||||
|
||||
private final Thing thing = new ThingImpl(THING_TYPE_EVENTLOG, "thingId");
|
||||
|
||||
@InjectMocks
|
||||
private final SatelEventLogHandler testSubject = new SatelEventLogHandler(thing);
|
||||
|
||||
@Test
|
||||
void handleCommandShouldNotUpdateStateWhenOtherChannelIsGiven() {
|
||||
testSubject.handleCommand(new ChannelUID(thing.getUID(), CHANNEL_DESCRIPTION), new DecimalType(0));
|
||||
|
||||
verify(eventLogReader, never()).readEvent(0);
|
||||
verify(callback, never()).stateUpdated(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleCommandShouldNotUpdateStateWhenOtherCommandIsGiven() {
|
||||
testSubject.handleCommand(new ChannelUID(thing.getUID(), CHANNEL_INDEX), new StringType(""));
|
||||
|
||||
verify(eventLogReader, never()).readEvent(0);
|
||||
verify(callback, never()).stateUpdated(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleCommandShouldNotUpdateStateWhenEventLogReaderIsNotPresent() {
|
||||
testSubject.dispose();
|
||||
|
||||
testSubject.handleCommand(new ChannelUID(thing.getUID(), CHANNEL_INDEX), new DecimalType(0));
|
||||
|
||||
verify(eventLogReader, never()).readEvent(0);
|
||||
verify(callback, never()).stateUpdated(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleCommandShouldNotUpdateStateWhenReadEventFailed() {
|
||||
testSubject.handleCommand(new ChannelUID(thing.getUID(), CHANNEL_INDEX), new DecimalType(0));
|
||||
|
||||
verify(eventLogReader).readEvent(0);
|
||||
verify(callback, never()).stateUpdated(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleCommandShouldUpdateStateWhenSendCommandSucceeded() {
|
||||
LocalDateTime timestamp = LocalDateTime.parse("2020-03-12T12:34:56");
|
||||
EventDescription eventDescription = mock(EventDescription.class);
|
||||
when(eventDescription.getCurrentIndex()).thenReturn(1);
|
||||
when(eventDescription.getNextIndex()).thenReturn(2);
|
||||
when(eventDescription.getTimestamp()).thenReturn(timestamp);
|
||||
when(eventDescription.getText()).thenReturn("description");
|
||||
when(eventLogReader.readEvent(0)).thenReturn(Optional.of(eventDescription));
|
||||
when(eventLogReader.buildDetails(same(eventDescription))).thenReturn("details");
|
||||
when(bridgeHandler.getZoneId()).thenReturn(ZoneId.systemDefault());
|
||||
|
||||
testSubject.handleCommand(new ChannelUID(thing.getUID(), CHANNEL_INDEX), new DecimalType(0));
|
||||
|
||||
verify(callback).stateUpdated(new ChannelUID(thing.getUID(), CHANNEL_INDEX), new DecimalType(1));
|
||||
verify(callback).stateUpdated(new ChannelUID(thing.getUID(), CHANNEL_PREV_INDEX), new DecimalType(2));
|
||||
verify(callback).stateUpdated(new ChannelUID(thing.getUID(), CHANNEL_TIMESTAMP),
|
||||
new DateTimeType(timestamp.atZone(ZoneId.systemDefault())));
|
||||
verify(callback).stateUpdated(new ChannelUID(thing.getUID(), CHANNEL_DESCRIPTION),
|
||||
new StringType("description"));
|
||||
verify(callback).stateUpdated(new ChannelUID(thing.getUID(), CHANNEL_DETAILS), new StringType("details"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void incomingEventShouldUpdateStatusIfConnected() {
|
||||
testSubject.incomingEvent(new ConnectionStatusEvent(true));
|
||||
|
||||
ArgumentCaptor<ThingStatusInfo> statusCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
|
||||
verify(callback).statusUpdated(eq(thing), statusCaptor.capture());
|
||||
assertEquals(ThingStatus.ONLINE, statusCaptor.getValue().getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void incomingEventShouldNotUpdateStatusIfNotConnected() {
|
||||
testSubject.incomingEvent(new ConnectionStatusEvent(false));
|
||||
|
||||
verify(callback, never()).statusUpdated(eq(thing), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getServicesShouldReturnEventLogActions() {
|
||||
Collection<Class<? extends ThingHandlerService>> result = testSubject.getServices();
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertTrue(result.contains(SatelEventLogActions.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEvent() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* 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.satel.internal.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
|
||||
import org.openhab.binding.satel.internal.event.EventDispatcher;
|
||||
import org.openhab.binding.satel.internal.handler.SatelBridgeHandler;
|
||||
import org.openhab.binding.satel.internal.protocol.SatelMessage;
|
||||
import org.openhab.binding.satel.internal.types.IntegraType;
|
||||
|
||||
/**
|
||||
* @author Krzysztof Goworek - Initial contribution
|
||||
*/
|
||||
class DeviceNameResolverTest {
|
||||
|
||||
private final SatelBridgeHandler bridgeHandler = mock(SatelBridgeHandler.class);
|
||||
|
||||
private final EventDispatcher eventDispatcher = mock(EventDispatcher.class);
|
||||
|
||||
private final DeviceNameResolver testSubject = new DeviceNameResolver(bridgeHandler);
|
||||
|
||||
@BeforeEach
|
||||
void setUpBridgeHandler() {
|
||||
when(bridgeHandler.getEncoding()).thenReturn(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveShouldReadDeviceName() {
|
||||
setUpResponse("partition name");
|
||||
|
||||
String result = testSubject.resolve(DeviceType.PARTITION, 1);
|
||||
assertEquals("partition: partition name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveShouldCacheDevice() {
|
||||
setUpResponse("partition name");
|
||||
testSubject.resolve(DeviceType.PARTITION, 1);
|
||||
|
||||
String result = testSubject.resolve(DeviceType.PARTITION, 1);
|
||||
|
||||
assertEquals("partition: partition name", result);
|
||||
verify(bridgeHandler, times(1)).sendCommand(any(), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveShouldReturnDeviceNumberWhenNameNotAvailable() {
|
||||
when(bridgeHandler.sendCommand(any(), eq(false))).thenReturn(false);
|
||||
|
||||
String result = testSubject.resolve(DeviceType.PARTITION, 1);
|
||||
|
||||
assertEquals("partition: 1", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearCacheShouldRemoveCachedName() {
|
||||
setUpResponse("partition name");
|
||||
|
||||
testSubject.resolve(DeviceType.PARTITION, 1);
|
||||
testSubject.clearCache();
|
||||
testSubject.resolve(DeviceType.PARTITION, 1);
|
||||
|
||||
verify(bridgeHandler, times(2)).sendCommand(any(), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveOutputExpanderShouldReturnMainboardWhenDeviceNumberIsZero() {
|
||||
assertEquals("mainboard", testSubject.resolveOutputExpander(0, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveOutputExpanderShouldReturnOutputName() {
|
||||
setUpResponse("output name");
|
||||
|
||||
String result = testSubject.resolveOutputExpander(1, false);
|
||||
|
||||
assertEquals("output: output name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveOutputExpanderShouldReturnResolveUpperOutputName() {
|
||||
when(bridgeHandler.sendCommand(any(), eq(false))).thenReturn(false);
|
||||
|
||||
String result = testSubject.resolveOutputExpander(1, true);
|
||||
|
||||
assertEquals("output: 129", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveOutputExpanderShouldReturnExpanderName() {
|
||||
setUpResponse("expander name");
|
||||
|
||||
String result = testSubject.resolveOutputExpander(129, false);
|
||||
|
||||
assertEquals("expander: expander name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveOutputExpanderShouldHandleInvalidDeviceNumber() {
|
||||
String result = testSubject.resolveOutputExpander(193, false);
|
||||
|
||||
assertEquals("invalid output|expander device: 193", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveZoneExpanderKeypadShouldReturnMainboardWhenDeviceNumberIsZero() {
|
||||
assertEquals("mainboard", testSubject.resolveZoneExpanderKeypad(0, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveZoneExpanderKeypadShouldReturnZoneName() {
|
||||
setUpResponse("zone name");
|
||||
|
||||
String result = testSubject.resolveZoneExpanderKeypad(1, false);
|
||||
|
||||
assertEquals("zone: zone name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveZoneExpanderKeypadShouldReturnUpperZoneName() {
|
||||
when(bridgeHandler.sendCommand(any(), eq(false))).thenReturn(false);
|
||||
|
||||
String result = testSubject.resolveZoneExpanderKeypad(1, true);
|
||||
|
||||
assertEquals("zone: 129", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveZoneExpanderKeypadShouldReturnExpanderName() {
|
||||
setUpResponse("expander name");
|
||||
|
||||
String result = testSubject.resolveZoneExpanderKeypad(129, false);
|
||||
|
||||
assertEquals("expander: expander name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveZoneExpanderKeypadShouldReturnKeypadName() {
|
||||
setUpResponse("keypad name");
|
||||
|
||||
String result = testSubject.resolveZoneExpanderKeypad(193, false);
|
||||
|
||||
assertEquals("keypad: keypad name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvePartitionKeypadShouldReturnPartitionKeypadNameForSecondBus() {
|
||||
setUpResponse("keypad name");
|
||||
|
||||
String result = testSubject.resolvePartitionKeypad(33);
|
||||
|
||||
assertEquals("expander: keypad name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvePartitionKeypadShouldReturnPartitionKeypadNameForWrlLeonBoard() {
|
||||
when(bridgeHandler.getIntegraType()).thenReturn(IntegraType.I128_LEON);
|
||||
setUpResponse("keypad name");
|
||||
|
||||
String result = testSubject.resolvePartitionKeypad(1);
|
||||
|
||||
assertEquals("expander: keypad name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvePartitionKeypadShouldReturnMainboardForWrlLeonBoard() {
|
||||
when(bridgeHandler.getIntegraType()).thenReturn(IntegraType.I128_LEON);
|
||||
|
||||
String result = testSubject.resolvePartitionKeypad(33);
|
||||
|
||||
assertEquals("mainboard", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvePartitionKeypadShouldReturnMainboardForWrlSim300Board() {
|
||||
when(bridgeHandler.getIntegraType()).thenReturn(IntegraType.I128_SIM300);
|
||||
|
||||
String result = testSubject.resolvePartitionKeypad(33);
|
||||
|
||||
assertEquals("mainboard", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveUserShouldReturnUserName() {
|
||||
setUpResponse("user name");
|
||||
|
||||
String result = testSubject.resolveUser(1);
|
||||
|
||||
assertEquals("user: user name", result);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({ "0,user: unknown", "249,INT-AV", "250,ACCO NET", "251,SMS", "252,timer", "253,function zone",
|
||||
"254,Quick arm", "255,service" })
|
||||
void resolveUserShouldReturnStaticNameForSpecificDeviceNumber(int deviceNumber, String expectedResult) {
|
||||
String result = testSubject.resolveUser(deviceNumber);
|
||||
|
||||
assertEquals(expectedResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDataBusShouldReturnDataBusName() {
|
||||
assertEquals("data bus: 1", testSubject.resolveDataBus(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveTelephoneShouldReturnTelephoneName() {
|
||||
setUpResponse("telephone name");
|
||||
|
||||
String result = testSubject.resolveTelephone(1);
|
||||
|
||||
assertEquals("telephone: telephone name", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveTelephoneShouldReturnUnknownTelephoneWhenDeviceNumberIs0() {
|
||||
String result = testSubject.resolveTelephone(0);
|
||||
|
||||
assertEquals("telephone: unknown", result);
|
||||
}
|
||||
|
||||
void setUpResponse(String deviceName) {
|
||||
when(bridgeHandler.sendCommand(isA(ReadDeviceInfoCommand.class), eq(false))).thenAnswer(invocationOnMock -> {
|
||||
ReadDeviceInfoCommand cmd = invocationOnMock.getArgument(0);
|
||||
byte[] payload = new byte[19];
|
||||
byte[] nameBytes = deviceName.getBytes(StandardCharsets.US_ASCII);
|
||||
System.arraycopy(nameBytes, 0, payload, 3, nameBytes.length);
|
||||
cmd.handleResponse(eventDispatcher, new SatelMessage(ReadDeviceInfoCommand.COMMAND_CODE, payload));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* 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.satel.internal.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.satel.internal.command.ReadDeviceInfoCommand.DeviceType;
|
||||
import org.openhab.binding.satel.internal.command.ReadEventCommand;
|
||||
import org.openhab.binding.satel.internal.command.ReadEventDescCommand;
|
||||
import org.openhab.binding.satel.internal.event.EventDispatcher;
|
||||
import org.openhab.binding.satel.internal.handler.SatelBridgeHandler;
|
||||
import org.openhab.binding.satel.internal.protocol.SatelMessage;
|
||||
import org.openhab.binding.satel.internal.types.IntegraType;
|
||||
import org.openhab.binding.satel.internal.util.EventLogReader.EventDescription;
|
||||
|
||||
/**
|
||||
* @author Krzysztof Goworek - Initial contribution
|
||||
*/
|
||||
class EventLogReaderTest {
|
||||
|
||||
private final SatelBridgeHandler bridgeHandler = mock(SatelBridgeHandler.class);
|
||||
|
||||
private final DeviceNameResolver deviceNameResolver = mock(DeviceNameResolver.class);
|
||||
|
||||
private final EventDispatcher eventDispatcher = mock(EventDispatcher.class);
|
||||
|
||||
private final EventLogReader testSubject = new EventLogReader(bridgeHandler, deviceNameResolver);
|
||||
|
||||
@BeforeEach
|
||||
void setUpBridgeHandler() {
|
||||
when(bridgeHandler.getEncoding()).thenReturn(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEventShouldReturnEmptyResultWhenCommandFailed() {
|
||||
when(bridgeHandler.sendCommand(isA(ReadEventCommand.class), eq(false))).thenReturn(false);
|
||||
|
||||
Optional<EventDescription> result = testSubject.readEvent(0);
|
||||
|
||||
assertFalse(result.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEventShouldReturnEmptyResultWhenInvalidIndexIsGiven() {
|
||||
setUpReadEventResponse(new byte[0]);
|
||||
|
||||
Optional<EventDescription> result = testSubject.readEvent(0);
|
||||
|
||||
assertFalse(result.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEventShouldReturnEventCodeWhenReadDescriptionFailed() {
|
||||
setUpReadEventResponse(new byte[] { 0x30, 0x01, 0x10, 0x00, 0x00, 0x01 });
|
||||
|
||||
Optional<EventDescription> result = testSubject.readEvent(0);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
EventDescription eventDescription = result.get();
|
||||
assertEquals(0, eventDescription.getKind());
|
||||
assertEquals("event #1", eventDescription.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEventShouldReturnEventCodeWithRestoreFlagWhenReadDescriptionFailed() {
|
||||
setUpReadEventResponse(new byte[] { 0x30, 0x01, 0x10, 0x00, 0x04, 0x01 });
|
||||
|
||||
Optional<EventDescription> result = testSubject.readEvent(0);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
EventDescription eventDescription = result.get();
|
||||
assertEquals(0, eventDescription.getKind());
|
||||
assertEquals("event #1 (restore)", eventDescription.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEventShouldReturnEventDescription() {
|
||||
setUpReadEventResponse(new byte[] { 0x30, 0x01, 0x10, 0x00, 0x04, 0x01 });
|
||||
setUpReadEventDescResponse(new byte[] { 0x00, 0x00, 0x55, 0x00, 0x00, 'A', 'r', 'm' });
|
||||
|
||||
Optional<EventDescription> result = testSubject.readEvent(0);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
EventDescription eventDescription = result.get();
|
||||
assertEquals(0x55, eventDescription.getKind());
|
||||
assertEquals("Arm", eventDescription.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEventShouldCacheEventDescription() {
|
||||
setUpReadEventResponse(new byte[] { 0x30, 0x01, 0x10, 0x00, 0x04, 0x01 });
|
||||
setUpReadEventDescResponse(new byte[0]);
|
||||
|
||||
testSubject.readEvent(0);
|
||||
testSubject.readEvent(0);
|
||||
|
||||
verify(bridgeHandler).sendCommand(isA(ReadEventDescCommand.class), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind0() {
|
||||
String result = testSubject.buildDetails(createEventDescription(0));
|
||||
|
||||
assertEquals("", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind1() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolveZoneExpanderKeypad(70, false)).thenReturn("zone|expander|keypad");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(1));
|
||||
|
||||
assertEquals("partition, zone|expander|keypad", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind2() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolveUser(70)).thenReturn("user");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(2));
|
||||
|
||||
assertEquals("partition, user", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind3() {
|
||||
when(deviceNameResolver.resolvePartitionKeypad(50)).thenReturn("partition keypad");
|
||||
when(deviceNameResolver.resolveUser(70)).thenReturn("user");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(3));
|
||||
|
||||
assertEquals("partition keypad, user", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind4() {
|
||||
when(bridgeHandler.getIntegraType()).thenReturn(IntegraType.I256_PLUS);
|
||||
when(deviceNameResolver.resolveZoneExpanderKeypad(70, true)).thenReturn("zone|expander|keypad");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(4));
|
||||
|
||||
assertEquals("zone|expander|keypad", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind5() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(5));
|
||||
|
||||
assertEquals("partition", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind6() {
|
||||
when(deviceNameResolver.resolve(DeviceType.KEYPAD, 40)).thenReturn("keypad");
|
||||
when(deviceNameResolver.resolveUser(70)).thenReturn("user");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(6));
|
||||
|
||||
assertEquals("keypad, user", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind7() {
|
||||
when(deviceNameResolver.resolveUser(70)).thenReturn("user");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(7));
|
||||
|
||||
assertEquals("user", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind8() {
|
||||
when(deviceNameResolver.resolve(DeviceType.EXPANDER, 70)).thenReturn("expander");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(8));
|
||||
|
||||
assertEquals("expander", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind9() {
|
||||
when(deviceNameResolver.resolveTelephone(70)).thenReturn("telephone");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(9));
|
||||
|
||||
assertEquals("telephone", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDefaultDetailsForKind10() {
|
||||
String result = testSubject.buildDetails(createEventDescription(10));
|
||||
|
||||
assertEquals("kind=10, partition=40, source=70, object=1, ucn=30", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind11() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolveDataBus(70)).thenReturn("data bus");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(11));
|
||||
|
||||
assertEquals("partition, data bus", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind12() {
|
||||
when(bridgeHandler.getIntegraType()).thenReturn(IntegraType.I256_PLUS);
|
||||
when(deviceNameResolver.resolveOutputExpander(5, false)).thenReturn("output|expander");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(12, 0, 5));
|
||||
|
||||
assertEquals("output|expander", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind12WithPartition() {
|
||||
when(bridgeHandler.getIntegraType()).thenReturn(IntegraType.I256_PLUS);
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolveOutputExpander(70, true)).thenReturn("output|expander");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(12));
|
||||
|
||||
assertEquals("partition, output|expander", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind13() {
|
||||
when(deviceNameResolver.resolveOutputExpander(70, false)).thenReturn("output|expander");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(13));
|
||||
|
||||
assertEquals("output|expander", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind13WithPartition() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolveOutputExpander(130, false)).thenReturn("output|expander");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(13, 0, 130));
|
||||
|
||||
assertEquals("partition, output|expander", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind14() {
|
||||
when(deviceNameResolver.resolveTelephone(40)).thenReturn("telephone");
|
||||
when(deviceNameResolver.resolveUser(70)).thenReturn("user");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(14));
|
||||
|
||||
assertEquals("telephone, user", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind15() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolve(DeviceType.TIMER, 70)).thenReturn("timer");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(15));
|
||||
|
||||
assertEquals("partition, timer", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind30() {
|
||||
when(deviceNameResolver.resolve(DeviceType.KEYPAD, 40)).thenReturn("keypad");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(30));
|
||||
|
||||
assertEquals("keypad, ip: 70.62", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind31() {
|
||||
String result = testSubject.buildDetails(createEventDescription(31));
|
||||
|
||||
assertEquals(".70.62", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDetailsForKind32() {
|
||||
when(deviceNameResolver.resolve(DeviceType.PARTITION, 40)).thenReturn("partition");
|
||||
when(deviceNameResolver.resolve(DeviceType.ZONE, 70)).thenReturn("zone");
|
||||
|
||||
String result = testSubject.buildDetails(createEventDescription(32));
|
||||
|
||||
assertEquals("partition, zone", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReturnDefaultDetailsForKind33() {
|
||||
String result = testSubject.buildDetails(createEventDescription(33));
|
||||
|
||||
assertEquals("kind=33, partition=40, source=70, object=1, ucn=30", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldReadEventForKind31() {
|
||||
setUpReadEventResponse(new byte[] { 0x30, 0x01, 0x10, 0x00, 0x14, 0x01, (byte) 192, (byte) 168 });
|
||||
setUpReadEventDescResponse(new byte[] { 0x00, 0x00, 30, 0x00, 0x00, 'A', 'r', 'm' });
|
||||
when(deviceNameResolver.resolve(DeviceType.KEYPAD, 3)).thenReturn("keypad");
|
||||
EventDescription eventDescription = createEventDescription(31);
|
||||
|
||||
String result = testSubject.buildDetails(eventDescription);
|
||||
|
||||
assertEquals("keypad, ip: 192.168.70.62", result);
|
||||
assertEquals(0, eventDescription.getNextIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildDetailsShouldSkipNextEventForKind31() {
|
||||
setUpReadEventResponse(new byte[] { 0x30, 0x01, 0x10, 0x00, 0x14, 0x01, (byte) 192, (byte) 168 });
|
||||
setUpReadEventDescResponse(new byte[] { 0x00, 0x00, 29, 0x00, 0x00, 'A', 'r', 'm' });
|
||||
EventDescription eventDescription = createEventDescription(31);
|
||||
|
||||
String result = testSubject.buildDetails(eventDescription);
|
||||
|
||||
assertEquals(".70.62", result);
|
||||
assertEquals(20, eventDescription.getNextIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearCacheShouldClearDeviceNameCache() {
|
||||
testSubject.clearCache();
|
||||
|
||||
verify(deviceNameResolver).clearCache();
|
||||
}
|
||||
|
||||
private void setUpReadEventResponse(byte[] responseBytes) {
|
||||
when(bridgeHandler.sendCommand(isA(ReadEventCommand.class), eq(false))).thenAnswer(invocationOnMock -> {
|
||||
ReadEventCommand cmd = invocationOnMock.getArgument(0);
|
||||
byte[] payload = new byte[14];
|
||||
System.arraycopy(responseBytes, 0, payload, 0, responseBytes.length);
|
||||
cmd.handleResponse(eventDispatcher, new SatelMessage(ReadEventCommand.COMMAND_CODE, payload));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setUpReadEventDescResponse(byte[] responseBytes) {
|
||||
when(bridgeHandler.sendCommand(isA(ReadEventDescCommand.class), eq(false))).thenAnswer(invocationOnMock -> {
|
||||
ReadEventDescCommand cmd = invocationOnMock.getArgument(0);
|
||||
byte[] payload = new byte[51];
|
||||
System.arraycopy(responseBytes, 0, payload, 0, responseBytes.length);
|
||||
cmd.handleResponse(eventDispatcher, new SatelMessage(ReadEventDescCommand.COMMAND_CODE, payload));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private EventDescription createEventDescription(int descKind) {
|
||||
return createEventDescription(descKind, 30, 70);
|
||||
}
|
||||
|
||||
private EventDescription createEventDescription(int descKind, int userControlNumber, int source) {
|
||||
return testSubject.new EventDescription(mockReadEventCommand(userControlNumber, source), "", descKind);
|
||||
}
|
||||
|
||||
private ReadEventCommand mockReadEventCommand(int userControlNumber, int source) {
|
||||
ReadEventCommand result = mock(ReadEventCommand.class);
|
||||
when(result.getCurrentIndex()).thenReturn(10);
|
||||
when(result.getNextIndex()).thenReturn(20);
|
||||
when(result.getUserControlNumber()).thenReturn(userControlNumber);
|
||||
when(result.getPartition()).thenReturn(40);
|
||||
when(result.getPartitionKeypad()).thenReturn(50);
|
||||
when(result.getObject()).thenReturn(1);
|
||||
when(result.getSource()).thenReturn(source);
|
||||
when(result.getTimestamp()).thenReturn(LocalDateTime.parse("2020-03-12T12:34:56"));
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue