From ff1ec5130e4886711d5b15d478377396782ffbeb Mon Sep 17 00:00:00 2001 From: Patrick Koenemann Date: Tue, 9 Nov 2021 22:32:34 +0100 Subject: [PATCH] Adjustments based on code review. Signed-off-by: Patrick Koenemann --- bundles/org.openhab.binding.anel/README.md | 12 +- .../anel/internal/AnelConfiguration.java | 19 ++- .../binding/anel/internal/AnelHandler.java | 14 +- .../anel/internal/AnelHandlerFactory.java | 5 - .../anel/internal/AnelUdpConnector.java | 48 ++++--- .../binding/anel/internal/IAnelConstants.java | 68 +++------ .../internal/auth/AnelAuthentication.java | 24 ++-- .../discovery/AnelDiscoveryService.java | 131 +++++++++--------- .../anel/internal/state/AnelState.java | 131 +----------------- .../anel/internal/state/AnelStateUpdater.java | 110 +++++---------- .../main/resources/OH-INF/binding/binding.xml | 1 - .../resources/OH-INF/thing/thing-types.xml | 91 ++---------- .../anel/internal/AnelAuthenticationTest.java | 24 ++-- .../binding/anel/internal/AnelStateTest.java | 48 +------ .../anel/internal/AnelStateUpdaterTest.java | 69 ++++----- 15 files changed, 252 insertions(+), 543 deletions(-) diff --git a/bundles/org.openhab.binding.anel/README.md b/bundles/org.openhab.binding.anel/README.md index a587b952fbe02..e2189cb5c8f1a 100644 --- a/bundles/org.openhab.binding.anel/README.md +++ b/bundles/org.openhab.binding.anel/README.md @@ -10,13 +10,15 @@ Some NET-PwrCtrl devices also have 8 I/O ports which can either be used to direc There are three kinds of devices ([overview on manufacturer's homepage](https://en.anel.eu/?src=/produkte/produkte.htm)): -| [Anel NET-PwrCtrl HUT](https://en.anel.eu/?src=/produkte/hut_2/hut_2.htm) | [Anel NET-PwrCtrl IO](https://en.anel.eu/?src=/produkte/io/io.htm) | [Anel NET-PwrCtrl HOME](https://de.anel.eu/?src=produkte/home/home.htm) (only German version) | +| [Anel NET-PwrCtrl HUT](https://en.anel.eu/?src=/produkte/hut_2/hut_2.htm)
( _advanced-firmware_ ) | [Anel NET-PwrCtrl IO](https://en.anel.eu/?src=/produkte/io/io.htm)
( _advanced-firmware_ ) | [Anel NET-PwrCtrl HOME](https://de.anel.eu/?src=produkte/home/home.htm)
( _home_ )
(only German version) | | --- | --- | --- | -| [![Anel NET-PwrCtrl HUT 2](https://de.anel.eu/image/leisten/HUT2LV-P_500.jpg)](https://de.anel.eu/?src=produkte/hut_2/hut_2.htm) | [![Anel NET-PwrCtrl IO](https://de.anel.eu/image/leisten/IO-Stecker.png)](https://de.anel.eu/?src=produkte/io/io.htm) | [![Anel NET-PwrCtrl HOME](https://de.anel.eu/image/leisten/HOME-DE-500.gif)](https://de.anel.eu/?src=produkte/home/home.htm) +| [![Anel NET-PwrCtrl HUT 2](https://de.anel.eu/image/leisten/HUT2LV-P_500.jpg)](https://de.anel.eu/?src=produkte/hut_2/hut_2.htm) | [![Anel NET-PwrCtrl IO](https://de.anel.eu/image/leisten/IO-Stecker.png)](https://de.anel.eu/?src=produkte/io/io.htm) | [![Anel NET-PwrCtrl HOME](https://de.anel.eu/image/leisten/HOME-DE-500.gif)](https://de.anel.eu/?src=produkte/home/home.htm) | -* The smallest device, the _HOME_, is the only one with only three power sockets and only available in Germany. -* The _PRO_ and _REDUNDANT_ have eight power sockets and a similar (simplified) firmware as the _HOME_. -* All others (_ADV_, _IO_, and the different _HUT_ variants) have eight power sockets / relays, eight IO ports, and an advanced firmware. +Thing type IDs: + +* *home*: The smallest device, the _HOME_, is the only one with only three power sockets and only available in Germany. +* *simple-firmware*: The _PRO_ and _REDUNDANT_ have eight power sockets and a similar (simplified) firmware as the _HOME_. +* *advanced-firmware*: All others (_ADV_, _IO_, and the different _HUT_ variants) have eight power sockets / relays, eight IO ports, and an advanced firmware. An [additional sensor](https://en.anel.eu/?src=/produkte/sensor_1/sensor_1.htm) may be used for monitoring temperature, humidity, and brightness. The sensor can be attached to a _HUT_ device via an Ethernet cable (max length is 50m). diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelConfiguration.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelConfiguration.java index 6e1590834d482..a30f77f3dc9b6 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelConfiguration.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelConfiguration.java @@ -23,6 +23,14 @@ @NonNullByDefault public class AnelConfiguration { + public @Nullable String hostname; + public @Nullable String user; + public @Nullable String password; + /** Port to send data from openhab to device. */ + public int udpSendPort = IAnelConstants.DEFAULT_SEND_PORT; + /** Openhab receives messages via this port from device. */ + public int udpReceivePort = IAnelConstants.DEFAULT_RECEIVE_PORT; + public AnelConfiguration() { } @@ -35,17 +43,6 @@ public AnelConfiguration(@Nullable String hostname, @Nullable String user, @Null this.udpReceivePort = receivePort; } - @Nullable - public String hostname; - @Nullable - public String user; - @Nullable - public String password; - /** Port to send data from openhab to device. */ - public int udpSendPort = IAnelConstants.DEFAULT_SEND_PORT; - /** Openhab receives messages via this port from device. */ - public int udpReceivePort = IAnelConstants.DEFAULT_RECEIVE_PORT; - @Override public String toString() { final StringBuilder builder = new StringBuilder(); diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandler.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandler.java index 61a4bc2f1280b..42a53b52364f2 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandler.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandler.java @@ -123,6 +123,11 @@ private void initializeConnection() { periodicRefreshTask = scheduler.scheduleWithFixedDelay(this::periodicRefresh, // 0, IAnelConstants.REFRESH_INTERVAL_SEC, TimeUnit.SECONDS); + } catch (InterruptedException e) { + + // OH shutdown - don't log anything, just clean up + dispose(); + } catch (Exception e) { logger.debug("Connection to '{}' failed", config, e); @@ -173,18 +178,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (udpConnector2 == null || !udpConnector2.isConnected() || getThing().getStatus() != ThingStatus.ONLINE) { // don't log initial refresh commands because they may occur before thing is online - if (!(command instanceof RefreshType) && getThing().getStatus() != ThingStatus.ONLINE) { - logger.info("Cannot handle command '{}' for channel '{}' because thing ({}) is not (yet) connected: {}", // + if (!(command instanceof RefreshType)) { + logger.debug("Cannot handle command '{}' for channel '{}' because thing ({}) is not connected: {}", // command, channelUID.getId(), getThing().getStatus(), config); } return; } - @Nullable String anelCommand = null; if (command instanceof RefreshType) { - final @Nullable State update = stateUpdater.getChannelUpdate(channelUID.getId(), state); + final State update = stateUpdater.getChannelUpdate(channelUID.getId(), state); if (update != null) { updateState(channelUID, update); @@ -203,7 +207,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } else if (command instanceof OnOffType) { - final @Nullable State lockedState; + final State lockedState; synchronized (this) { // lock needed to update the state if needed lockedState = commandHandler.getLockedState(state, channelUID.getId()); diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandlerFactory.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandlerFactory.java index 0c9df4fbf6704..96a5d9e5e979b 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandlerFactory.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelHandlerFactory.java @@ -22,8 +22,6 @@ import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandlerFactory; import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The {@link AnelHandlerFactory} is responsible for creating things and thing @@ -35,11 +33,8 @@ @Component(configurationPid = "binding.anel", service = ThingHandlerFactory.class) public class AnelHandlerFactory extends BaseThingHandlerFactory { - private final Logger logger = LoggerFactory.getLogger(AnelHandlerFactory.class); - @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { - logger.debug("asking to support '{}': {}", thingTypeUID, SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)); return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); } diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelUdpConnector.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelUdpConnector.java index 9990ed885b29c..e3cb01c3a0ca8 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelUdpConnector.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/AnelUdpConnector.java @@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.NamedThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,16 +52,16 @@ public class AnelUdpConnector { /** Service to spawn new threads for handling status updates. */ private final ExecutorService executorService; + /** Thread factory for UDP listening thread. */ + private final NamedThreadFactory listeningThreadFactory = new NamedThreadFactory(IAnelConstants.BINDING_ID, true); + /** Socket for receiving UDP packages. */ - @Nullable - private DatagramSocket receivingSocket = null; + private @Nullable DatagramSocket receivingSocket = null; /** Socket for sending UDP packages. */ - @Nullable - private DatagramSocket sendingSocket = null; + private @Nullable DatagramSocket sendingSocket = null; /** The listener that gets notified upon newly received messages. */ - @Nullable - private Consumer listener; + private @Nullable Consumer listener; private int receiveFailures = 0; private boolean listenerActive = false; @@ -95,9 +96,11 @@ public AnelUdpConnector(String host, int udpReceivePort, int udpSendPort, Execut /** * Initialize socket connection to the UDP receive port for the given listener. * - * @throws SocketException + * @throws SocketException Is only thrown if logNotTHrowException = false. + * @throws InterruptedException Typically happens during shutdown. */ - public void connect(Consumer listener, boolean logNotThrowExcpetion) throws SocketException { + public void connect(Consumer listener, boolean logNotThrowExcpetion) + throws SocketException, InterruptedException { if (receivingSocket == null) { try { receivingSocket = new DatagramSocket(receivePort); @@ -106,19 +109,16 @@ public void connect(Consumer listener, boolean logNotThrowExcpetion) thr /*- * Due to the issue with 4 concurrently listening threads [1], we should follow Kais suggestion [2] - * to create our own listening thread. + * to create our own listening daemonized thread. * * [1] https://community.openhab.org/t/anel-net-pwrctrl-binding-for-oh3/123378 * [2] https://www.eclipse.org/forums/index.php/m/1775932/?#msg_1775429 */ - new Thread(this::listen).start(); + listeningThreadFactory.newThread(this::listen).start(); // wait for the listening thread to be active - try { - for (int i = 0; i < 20 && !listenerActive; i++) { - Thread.sleep(100); // wait at most 20 * 100ms = 2sec for the listener to be active - } - } catch (InterruptedException e) { + for (int i = 0; i < 20 && !listenerActive; i++) { + Thread.sleep(100); // wait at most 20 * 100ms = 2sec for the listener to be active } if (!listenerActive) { logger.warn( @@ -144,6 +144,14 @@ public void connect(Consumer listener, boolean logNotThrowExcpetion) thr } private void listen() { + try { + listenUnhandledInterruption(); + } catch (InterruptedException e) { + // OH shutdown - don't log anything, just quit + } + } + + private void listenUnhandledInterruption() throws InterruptedException { logger.info("Anel NET-PwrCtrl listener started for: '{}:{}'", host, receivePort); final Consumer listener2 = listener; @@ -194,12 +202,8 @@ private void listen() { logger.debug( "Unexpected error while listening on port {}; waiting 10sec before the next attempt to listen on that port.", receivePort, e); - try { - for (int i = 0; i < 50 && receivingSocket != null; i++) { - Thread.sleep(200); // 50 * 200ms = 10sec - } - } catch (InterruptedException ie) { - // abort waiting on interrupted exception + for (int i = 0; i < 50 && receivingSocket != null; i++) { + Thread.sleep(200); // 50 * 200ms = 10sec } } else { @@ -212,7 +216,7 @@ private void listen() { /** Close the socket connection. */ public void disconnect() { - logger.info("Anel NET-PwrCtrl listener stopped for: '{}:{}'", host, receivePort); + logger.debug("Anel NET-PwrCtrl listener stopped for: '{}:{}'", host, receivePort); listener = null; final DatagramSocket receivingSocket2 = receivingSocket; if (receivingSocket2 != null) { diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/IAnelConstants.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/IAnelConstants.java index 5cb98a156fc48..c8aacb6d0a420 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/IAnelConstants.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/IAnelConstants.java @@ -15,8 +15,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; @@ -84,37 +82,29 @@ public interface IAnelConstants { String CHANNEL_NAME = "prop#name"; String CHANNEL_TEMPERATURE = "prop#temperature"; - @SuppressWarnings("null") - List CHANNEL_RELAY_NAME = Stream - .of("r1#name", "r2#name", "r3#name", "r4#name", "r5#name", "r6#name", "r7#name", "r8#name") - .collect(Collectors.toUnmodifiableList()); - - @SuppressWarnings("null") - List CHANNEL_RELAY_STATE = Stream - // second character must be the index b/c it is parsed in AnelCommandHandler! - .of("r1#state", "r2#state", "r3#state", "r4#state", "r5#state", "r6#state", "r7#state", "r8#state") - .collect(Collectors.toUnmodifiableList()); - - @SuppressWarnings("null") - List CHANNEL_RELAY_LOCKED = Stream - .of("r1#locked", "r2#locked", "r3#locked", "r4#locked", "r5#locked", "r6#locked", "r7#locked", "r8#locked") - .collect(Collectors.toUnmodifiableList()); - - @SuppressWarnings("null") - List CHANNEL_IO_NAME = Stream - .of("io1#name", "io2#name", "io3#name", "io4#name", "io5#name", "io6#name", "io7#name", "io8#name") - .collect(Collectors.toUnmodifiableList()); - - @SuppressWarnings("null") - List CHANNEL_IO_MODE = Stream - .of("io1#mode", "io2#mode", "io3#mode", "io4#mode", "io5#mode", "io6#mode", "io7#mode", "io8#mode") - .collect(Collectors.toUnmodifiableList()); - - @SuppressWarnings("null") - List CHANNEL_IO_STATE = Stream - // third character must be the index b/c it is parsed in AnelCommandHandler! - .of("io1#state", "io2#state", "io3#state", "io4#state", "io5#state", "io6#state", "io7#state", "io8#state") - .collect(Collectors.toUnmodifiableList()); + List CHANNEL_RELAY_NAME = List.of("r1#name", "r2#name", "r3#name", "r4#name", "r5#name", "r6#name", + "r7#name", "r8#name"); + + // second character must be the index b/c it is parsed in AnelCommandHandler! + List CHANNEL_RELAY_STATE = List.of("r1#state", "r2#state", "r3#state", "r4#state", "r5#state", "r6#state", + "r7#state", "r8#state"); + + List CHANNEL_RELAY_LOCKED = List.of("r1#locked", "r2#locked", "r3#locked", "r4#locked", "r5#locked", + "r6#locked", "r7#locked", "r8#locked"); + + List CHANNEL_IO_NAME = List.of("io1#name", "io2#name", "io3#name", "io4#name", "io5#name", "io6#name", + "io7#name", "io8#name"); + + List CHANNEL_IO_MODE = List.of("io1#mode", "io2#mode", "io3#mode", "io4#mode", "io5#mode", "io6#mode", + "io7#mode", "io8#mode"); + + // third character must be the index b/c it is parsed in AnelCommandHandler! + List CHANNEL_IO_STATE = List.of("io1#state", "io2#state", "io3#state", "io4#state", "io5#state", + "io6#state", "io7#state", "io8#state"); + + String CHANNEL_SENSOR_TEMPERATURE = "sensor#temperature"; + String CHANNEL_SENSOR_HUMIDITY = "sensor#humidity"; + String CHANNEL_SENSOR_BRIGHTNESS = "sensor#brightness"; /** * @param channelId A channel ID. @@ -130,16 +120,4 @@ static int getIndexFromChannel(String channelId) { } return -1; // not a relay or io channel } - - String CHANNEL_SENSOR_TEMPERATURE = "sensor#temperature"; - String CHANNEL_SENSOR_HUMIDITY = "sensor#humidity"; - String CHANNEL_SENSOR_BRIGHTNESS = "sensor#brightness"; - - String CHANNEL_POWER_VOLTAGE_RMS = "power#voltageRMS"; - String CHANNEL_POWER_CURRENT_RMS = "power#currentRMS"; - String CHANNEL_POWER_LINE_FREQUENCY = "power#lineFrequency"; - String CHANNEL_POWER_ACTIVE_POWER = "power#activePower"; - String CHANNEL_POWER_APPARENT_POWER = "power#apparentPower"; - String CHANNEL_POWER_REACTIVE_POWER = "power#reactivePower"; - String CHANNEL_POWER_POWER_FACTOR = "power#powerFactor"; } diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/auth/AnelAuthentication.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/auth/AnelAuthentication.java index c6db711417d19..ea897e50c88ac 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/auth/AnelAuthentication.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/auth/AnelAuthentication.java @@ -28,9 +28,9 @@ public class AnelAuthentication { public enum AuthMethod { - plain, - base64, - xorBase64; + PLAIN, + BASE64, + XORBASE64; private static final Pattern NAME_AND_FIRMWARE_PATTERN = Pattern.compile(":NET-PWRCTRL_0?(\\d+\\.\\d)"); private static final Pattern LAST_SEGMENT_FIRMWARE_PATTERN = Pattern.compile(":(\\d+\\.\\d)$"); @@ -40,22 +40,22 @@ public enum AuthMethod { public static AuthMethod of(String status) { if (status.isEmpty()) { - return plain; // fallback + return PLAIN; // fallback } if (status.trim().endsWith(":xor") || status.contains(":xor:")) { - return xorBase64; + return XORBASE64; } final String firmwareVersion = getFirmwareVersion(status); if (firmwareVersion == null) { - return plain; + return PLAIN; } if (firmwareVersion.compareTo(MIN_FIRMWARE_XOR_BASE64) >= 0) { - return xorBase64; // >= 6.1 + return XORBASE64; // >= 6.1 } if (firmwareVersion.compareTo(MIN_FIRMWARE_BASE64) >= 0) { - return base64; // exactly 6.0 + return BASE64; // exactly 6.0 } - return plain; // fallback + return PLAIN; // fallback } private static @Nullable String getFirmwareVersion(String fullStatusStringOrFirmwareVersion) { @@ -74,15 +74,15 @@ public static AuthMethod of(String status) { public static String getUserPasswordString(@Nullable String user, @Nullable String password, @Nullable AuthMethod authMethod) { final String userPassword = (user == null ? "" : user) + (password == null ? "" : password); - if (authMethod == null || authMethod == AuthMethod.plain) { + if (authMethod == null || authMethod == AuthMethod.PLAIN) { return userPassword; } - if (authMethod == AuthMethod.base64 || password == null || password.isEmpty()) { + if (authMethod == AuthMethod.BASE64 || password == null || password.isEmpty()) { return Base64.getEncoder().encodeToString(userPassword.getBytes()); } - if (authMethod == AuthMethod.xorBase64) { + if (authMethod == AuthMethod.XORBASE64) { final StringBuilder result = new StringBuilder(); // XOR diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/discovery/AnelDiscoveryService.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/discovery/AnelDiscoveryService.java index 78acce6d1c2ad..1c7c3d11e72b9 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/discovery/AnelDiscoveryService.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/discovery/AnelDiscoveryService.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.net.BindException; +import java.nio.channels.ClosedByInterruptException; import java.util.Set; import java.util.TreeSet; @@ -22,6 +23,7 @@ import org.openhab.binding.anel.internal.AnelUdpConnector; import org.openhab.binding.anel.internal.IAnelConstants; import org.openhab.core.common.AbstractUID; +import org.openhab.core.common.NamedThreadFactory; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; @@ -55,7 +57,7 @@ public class AnelDiscoveryService extends AbstractDiscoveryService { private final Logger logger = LoggerFactory.getLogger(AnelDiscoveryService.class); - private boolean scanRunning = false; + private @Nullable Thread scanningThread = null; public AnelDiscoveryService() throws IllegalArgumentException { super(IAnelConstants.SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS); @@ -71,109 +73,106 @@ protected void startScan() { * Do not use the scheduler, otherwise further threads (for handling discovered things) are not started * immediately but only after the scan is complete. */ - new Thread(this::doScan).start(); + final Thread thread = new NamedThreadFactory(IAnelConstants.BINDING_ID, true).newThread(this::doScan); + thread.start(); + scanningThread = thread; } private void doScan() { logger.debug("Starting scan of Anel devices via UDP broadcast messages..."); - if (!scanRunning) { - scanRunning = true; + try { + for (final String broadcastAddress : BROADCAST_ADDRESSES) { - try { - for (final String broadcastAddress : BROADCAST_ADDRESSES) { + // for each available broadcast network address try factory default ports first + scan(broadcastAddress, IAnelConstants.DEFAULT_SEND_PORT, IAnelConstants.DEFAULT_RECEIVE_PORT); - // for each available broadcast network address try factory default ports first - scan(broadcastAddress, IAnelConstants.DEFAULT_SEND_PORT, IAnelConstants.DEFAULT_RECEIVE_PORT); + // try reasonable ports... + for (int[] ports : DISCOVERY_PORTS) { + int sendPort = ports[0]; + int receivePort = ports[1]; - // try reasonable ports... - for (int[] ports : DISCOVERY_PORTS) { - int sendPort = ports[0]; - int receivePort = ports[1]; - - // ...and continue if a device was found, maybe there is yet another device on the next port - while (scan(broadcastAddress, sendPort, receivePort) || sendPort == ports[0]) { - sendPort++; - receivePort++; - } + // ...and continue if a device was found, maybe there is yet another device on the next port + while (scan(broadcastAddress, sendPort, receivePort) || sendPort == ports[0]) { + sendPort++; + receivePort++; } } - } catch (Exception e) { + } + } catch (InterruptedException | ClosedByInterruptException e) { - logger.warn("Unexpected exception during anel device scan", e); + return; // OH shutdown or scan was aborted - } finally { + } catch (Exception e) { - scanRunning = false; - } + logger.warn("Unexpected exception during anel device scan", e); + + } finally { + + scanningThread = null; } logger.debug("Scan finished."); } /* @return Whether or not a device was found for the given broadcast address and port. */ - private boolean scan(String broadcastAddress, int sendPort, int receivePort) throws IOException { - if (scanRunning) { - logger.debug("Scanning {}:{}...", broadcastAddress, sendPort); - final AnelUdpConnector udpConnector = new AnelUdpConnector(broadcastAddress, receivePort, sendPort, - scheduler); - - try { - final boolean[] deviceDiscovered = new boolean[] { false }; - udpConnector.connect(status -> { - // avoid the same device to be discovered multiple times for multiple responses - if (!deviceDiscovered[0]) { - boolean discoverDevice = true; - synchronized (this) { - if (deviceDiscovered[0]) { - discoverDevice = false; // already discovered by another thread - } else { - deviceDiscovered[0] = true; // we discover the device! - } - } - if (discoverDevice) { - // discover device outside synchronized-block - deviceDiscovered(status, sendPort, receivePort); + private boolean scan(String broadcastAddress, int sendPort, int receivePort) + throws IOException, InterruptedException { + logger.debug("Scanning {}:{}...", broadcastAddress, sendPort); + final AnelUdpConnector udpConnector = new AnelUdpConnector(broadcastAddress, receivePort, sendPort, scheduler); + + try { + final boolean[] deviceDiscovered = new boolean[] { false }; + udpConnector.connect(status -> { + // avoid the same device to be discovered multiple times for multiple responses + if (!deviceDiscovered[0]) { + boolean discoverDevice = true; + synchronized (this) { + if (deviceDiscovered[0]) { + discoverDevice = false; // already discovered by another thread + } else { + deviceDiscovered[0] = true; // we discover the device! } } - }, false); - - udpConnector.send(IAnelConstants.BROADCAST_DISCOVERY_MSG); - - // answer expected within 50-600ms on a regular network; wait up to 2sec just to make sure - for (int delay = 0; delay < 10 && !deviceDiscovered[0]; delay++) { - Thread.sleep(100 * DISCOVER_DEVICE_TIMEOUT_SECONDS); // wait 10 x 200ms = 2sec + if (discoverDevice) { + // discover device outside synchronized-block + deviceDiscovered(status, sendPort, receivePort); + } } + }, false); - return deviceDiscovered[0]; + udpConnector.send(IAnelConstants.BROADCAST_DISCOVERY_MSG); - } catch (BindException e) { + // answer expected within 50-600ms on a regular network; wait up to 2sec just to make sure + for (int delay = 0; delay < 10 && !deviceDiscovered[0]; delay++) { + Thread.sleep(100 * DISCOVER_DEVICE_TIMEOUT_SECONDS); // wait 10 x 200ms = 2sec + } - // most likely socket is already in use, ignore this exception. - logger.debug( - "Invalid address {} or one of the ports {} or {} is already in use. Skipping scan of these ports.", - broadcastAddress, sendPort, receivePort); + return deviceDiscovered[0]; - } catch (InterruptedException e) { + } catch (BindException e) { - // interrupted... + // most likely socket is already in use, ignore this exception. + logger.debug( + "Invalid address {} or one of the ports {} or {} is already in use. Skipping scan of these ports.", + broadcastAddress, sendPort, receivePort); - } finally { + } finally { - udpConnector.disconnect(); - } + udpConnector.disconnect(); } return false; } @Override protected synchronized void stopScan() { - scanRunning = false; + final Thread thread = scanningThread; + if (thread != null) { + thread.interrupt(); + } super.stopScan(); } private void deviceDiscovered(String status, int sendPort, int receivePort) { - logger.debug("Discovery result: {}", status); - final String[] segments = status.split(":"); if (segments.length >= 16) { diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelState.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelState.java index 861e56a85b87c..041a96e4fd9db 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelState.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelState.java @@ -45,14 +45,11 @@ public class AnelState { public final String status; /** Device IP address; read-only. */ - @Nullable - public final String ip; + public final @Nullable String ip; /** Device name; read-only. */ - @Nullable - public final String name; + public final @Nullable String name; /** Device mac address; read-only. */ - @Nullable - public final String mac; + public final @Nullable String mac; /** Device relay names; read-only. */ public final String[] relayName = new String[8]; @@ -69,40 +66,14 @@ public class AnelState { public final Boolean[] ioIsInput = new Boolean[8]; /** Device temperature (optional); read-only. */ - @Nullable - public final String temperature; - - /** Power voltage, e.g. "226.2" (optional); read-only. */ - @Nullable - public final String powerVoltageRMS; - /** Power current, e.g. "0.0004" (optional); read-only. */ - @Nullable - public final String powerCurrentRMS; - /** Power line frequency, e.g. "50.044" (optional); read-only. */ - @Nullable - public final String powerLineFrequency; - /** Active power, e.g. "0.03" (optional); read-only. */ - @Nullable - public final String powerActivePower; - /** Apparent power, e.g. "0.00" (optional); read-only. */ - @Nullable - public final String powerApparentPower; - /** Reactive power, e.g. "0.05" (optional); read-only. */ - @Nullable - public final String powerReactivePower; - /** Power factor, e.g. "1.0000" (optional); read-only. */ - @Nullable - public final String powerPowerFactor; + public final @Nullable String temperature; /** Sensor temperature, e.g. "20.61" (optional); read-only. */ - @Nullable - public final String sensorTemperature; + public final @Nullable String sensorTemperature; /** Sensor Humidity, e.g. "40.7" (optional); read-only. */ - @Nullable - public final String sensorHumidity; + public final @Nullable String sensorHumidity; /** Sensor Brightness, e.g. "7.0" (optional); read-only. */ - @Nullable - public final String sensorBrightness; + public final @Nullable String sensorBrightness; private static final AnelState INVALID_STATE = new AnelState(); @@ -119,13 +90,6 @@ private AnelState() { name = null; mac = null; temperature = null; - powerVoltageRMS = null; - powerCurrentRMS = null; - powerLineFrequency = null; - powerActivePower = null; - powerApparentPower = null; - powerReactivePower = null; - powerPowerFactor = null; sensorTemperature = null; sensorHumidity = null; sensorBrightness = null; @@ -192,15 +156,6 @@ private AnelState(@Nullable String status) throws IllegalFormatException { if (segments.length > 34 && "p".equals(segments[27])) { - // optional power measurement (if device supports it and firmware >= 6.0) - powerVoltageRMS = segments[28]; - powerCurrentRMS = segments[29]; - powerLineFrequency = segments[30]; - powerActivePower = segments[31]; - powerApparentPower = segments[32]; - powerReactivePower = segments[33]; - powerPowerFactor = segments[34]; - // optional sensor (if device supports it and firmware >= 6.1) after power management if (segments.length > 38 && "s".equals(segments[35])) { sensorTemperature = segments[36]; @@ -214,15 +169,6 @@ private AnelState(@Nullable String status) throws IllegalFormatException { } else if (segments.length > 31 && "n".equals(segments[27]) && "s".equals(segments[28])) { - // no power measurement - powerVoltageRMS = null; - powerCurrentRMS = null; - powerLineFrequency = null; - powerActivePower = null; - powerApparentPower = null; - powerReactivePower = null; - powerPowerFactor = null; - // but sensor! (if device supports it and firmware >= 6.1) sensorTemperature = segments[29]; sensorHumidity = segments[30]; @@ -230,13 +176,6 @@ private AnelState(@Nullable String status) throws IllegalFormatException { } else { // firmware <= 6.0 or unknown format; skip rest - powerVoltageRMS = null; - powerCurrentRMS = null; - powerLineFrequency = null; - powerActivePower = null; - powerApparentPower = null; - powerReactivePower = null; - powerPowerFactor = null; sensorTemperature = null; sensorBrightness = null; sensorHumidity = null; @@ -281,13 +220,6 @@ public int hashCode() { result = prime * result + Arrays.hashCode(relayName); result = prime * result + Arrays.hashCode(relayState); result = prime * result + ((temperature == null) ? 0 : temperature.hashCode()); - result = prime * result + ((powerActivePower == null) ? 0 : powerActivePower.hashCode()); - result = prime * result + ((powerApparentPower == null) ? 0 : powerApparentPower.hashCode()); - result = prime * result + ((powerCurrentRMS == null) ? 0 : powerCurrentRMS.hashCode()); - result = prime * result + ((powerLineFrequency == null) ? 0 : powerLineFrequency.hashCode()); - result = prime * result + ((powerPowerFactor == null) ? 0 : powerPowerFactor.hashCode()); - result = prime * result + ((powerReactivePower == null) ? 0 : powerReactivePower.hashCode()); - result = prime * result + ((powerVoltageRMS == null) ? 0 : powerVoltageRMS.hashCode()); result = prime * result + ((sensorBrightness == null) ? 0 : sensorBrightness.hashCode()); result = prime * result + ((sensorHumidity == null) ? 0 : sensorHumidity.hashCode()); result = prime * result + ((sensorTemperature == null) ? 0 : sensorTemperature.hashCode()); @@ -338,55 +270,6 @@ public boolean equals(@Nullable Object obj) { } else if (!name.equals(other.name)) { return false; } - if (powerActivePower == null) { - if (other.powerActivePower != null) { - return false; - } - } else if (!powerActivePower.equals(other.powerActivePower)) { - return false; - } - if (powerApparentPower == null) { - if (other.powerApparentPower != null) { - return false; - } - } else if (!powerApparentPower.equals(other.powerApparentPower)) { - return false; - } - if (powerCurrentRMS == null) { - if (other.powerCurrentRMS != null) { - return false; - } - } else if (!powerCurrentRMS.equals(other.powerCurrentRMS)) { - return false; - } - if (powerLineFrequency == null) { - if (other.powerLineFrequency != null) { - return false; - } - } else if (!powerLineFrequency.equals(other.powerLineFrequency)) { - return false; - } - if (powerPowerFactor == null) { - if (other.powerPowerFactor != null) { - return false; - } - } else if (!powerPowerFactor.equals(other.powerPowerFactor)) { - return false; - } - if (powerReactivePower == null) { - if (other.powerReactivePower != null) { - return false; - } - } else if (!powerReactivePower.equals(other.powerReactivePower)) { - return false; - } - if (powerVoltageRMS == null) { - if (other.powerVoltageRMS != null) { - return false; - } - } else if (!powerVoltageRMS.equals(other.powerVoltageRMS)) { - return false; - } if (sensorBrightness == null) { if (other.sensorBrightness != null) { return false; diff --git a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelStateUpdater.java b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelStateUpdater.java index 3a31108035dc1..fccf190a22ef2 100644 --- a/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelStateUpdater.java +++ b/bundles/org.openhab.binding.anel/src/main/java/org/openhab/binding/anel/internal/state/AnelStateUpdater.java @@ -22,17 +22,20 @@ import org.openhab.binding.anel.internal.IAnelConstants; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; +import tech.units.indriya.unit.Units; + /** * Get updates for {@link AnelState}s. * * @author Patrick Koenemann - Initial contribution */ @NonNullByDefault -public class AnelStateUpdater implements IAnelConstants { +public class AnelStateUpdater { public @Nullable State getChannelUpdate(String channelId, @Nullable AnelState state) { if (state == null) { @@ -66,11 +69,11 @@ public class AnelStateUpdater implements IAnelConstants { return getStringState(state.name); } if (IAnelConstants.CHANNEL_TEMPERATURE.equals(channelId)) { - return getDecimalState(state.temperature); + return getTemperatureState(state.temperature); } if (IAnelConstants.CHANNEL_SENSOR_TEMPERATURE.equals(channelId)) { - return getDecimalState(state.sensorTemperature); + return getTemperatureState(state.sensorTemperature); } if (IAnelConstants.CHANNEL_SENSOR_HUMIDITY.equals(channelId)) { return getDecimalState(state.sensorHumidity); @@ -78,28 +81,6 @@ public class AnelStateUpdater implements IAnelConstants { if (IAnelConstants.CHANNEL_SENSOR_BRIGHTNESS.equals(channelId)) { return getDecimalState(state.sensorBrightness); } - - if (IAnelConstants.CHANNEL_POWER_VOLTAGE_RMS.equals(channelId)) { - return getDecimalState(state.powerVoltageRMS); - } - if (IAnelConstants.CHANNEL_POWER_CURRENT_RMS.equals(channelId)) { - return getDecimalState(state.powerCurrentRMS); - } - if (IAnelConstants.CHANNEL_POWER_LINE_FREQUENCY.equals(channelId)) { - return getDecimalState(state.powerLineFrequency); - } - if (IAnelConstants.CHANNEL_POWER_ACTIVE_POWER.equals(channelId)) { - return getDecimalState(state.powerActivePower); - } - if (IAnelConstants.CHANNEL_POWER_APPARENT_POWER.equals(channelId)) { - return getDecimalState(state.powerApparentPower); - } - if (IAnelConstants.CHANNEL_POWER_REACTIVE_POWER.equals(channelId)) { - return getDecimalState(state.powerReactivePower); - } - if (IAnelConstants.CHANNEL_POWER_POWER_FACTOR.equals(channelId)) { - return getDecimalState(state.powerPowerFactor); - } } return null; } @@ -114,12 +95,12 @@ public Map getChannelUpdates(@Nullable AnelState oldState, AnelSt // name and device temperature final State newName = getNewStringState(oldState == null ? null : oldState.name, newState.name); if (newName != null) { - updates.put(CHANNEL_NAME, newName); + updates.put(IAnelConstants.CHANNEL_NAME, newName); } - final State newTemperature = getNewDecimalState(oldState == null ? null : oldState.temperature, + final State newTemperature = getNewTemperatureState(oldState == null ? null : oldState.temperature, newState.temperature); if (newTemperature != null) { - updates.put(CHANNEL_TEMPERATURE, newTemperature); + updates.put(IAnelConstants.CHANNEL_TEMPERATURE, newTemperature); } // relay properties @@ -127,19 +108,19 @@ public Map getChannelUpdates(@Nullable AnelState oldState, AnelSt final State newRelayName = getNewStringState(oldState == null ? null : oldState.relayName[i], newState.relayName[i]); if (newRelayName != null) { - updates.put(CHANNEL_RELAY_NAME.get(i), newRelayName); + updates.put(IAnelConstants.CHANNEL_RELAY_NAME.get(i), newRelayName); } final State newRelayState = getNewSwitchState(oldState == null ? null : oldState.relayState[i], newState.relayState[i]); if (newRelayState != null) { - updates.put(CHANNEL_RELAY_STATE.get(i), newRelayState); + updates.put(IAnelConstants.CHANNEL_RELAY_STATE.get(i), newRelayState); } final State newRelayLocked = getNewSwitchState(oldState == null ? null : oldState.relayLocked[i], newState.relayLocked[i]); if (newRelayLocked != null) { - updates.put(CHANNEL_RELAY_LOCKED.get(i), newRelayLocked); + updates.put(IAnelConstants.CHANNEL_RELAY_LOCKED.get(i), newRelayLocked); } } @@ -147,74 +128,37 @@ public Map getChannelUpdates(@Nullable AnelState oldState, AnelSt for (int i = 0; i < 8; i++) { final State newIOName = getNewStringState(oldState == null ? null : oldState.ioName[i], newState.ioName[i]); if (newIOName != null) { - updates.put(CHANNEL_IO_NAME.get(i), newIOName); + updates.put(IAnelConstants.CHANNEL_IO_NAME.get(i), newIOName); } final State newIOIsInput = getNewSwitchState(oldState == null ? null : oldState.ioIsInput[i], newState.ioIsInput[i]); if (newIOIsInput != null) { - updates.put(CHANNEL_IO_MODE.get(i), newIOIsInput); + updates.put(IAnelConstants.CHANNEL_IO_MODE.get(i), newIOIsInput); } final State newIOState = getNewSwitchState(oldState == null ? null : oldState.ioState[i], newState.ioState[i]); if (newIOState != null) { - updates.put(CHANNEL_IO_STATE.get(i), newIOState); + updates.put(IAnelConstants.CHANNEL_IO_STATE.get(i), newIOState); } } // sensor values - final State newSensorTemperature = getNewDecimalState(oldState == null ? null : oldState.sensorTemperature, + final State newSensorTemperature = getNewTemperatureState(oldState == null ? null : oldState.sensorTemperature, newState.sensorTemperature); if (newSensorTemperature != null) { - updates.put(CHANNEL_SENSOR_TEMPERATURE, newSensorTemperature); + updates.put(IAnelConstants.CHANNEL_SENSOR_TEMPERATURE, newSensorTemperature); } final State newSensorHumidity = getNewDecimalState(oldState == null ? null : oldState.sensorHumidity, newState.sensorHumidity); if (newSensorHumidity != null) { - updates.put(CHANNEL_SENSOR_HUMIDITY, newSensorHumidity); + updates.put(IAnelConstants.CHANNEL_SENSOR_HUMIDITY, newSensorHumidity); } final State newSensorBrightness = getNewDecimalState(oldState == null ? null : oldState.sensorBrightness, newState.sensorBrightness); if (newSensorBrightness != null) { - updates.put(CHANNEL_SENSOR_BRIGHTNESS, newSensorBrightness); - } - - // power measurement - final State newPowerVoltageRMS = getNewDecimalState(oldState == null ? null : oldState.powerVoltageRMS, - newState.powerVoltageRMS); - if (newPowerVoltageRMS != null) { - updates.put(CHANNEL_POWER_VOLTAGE_RMS, newPowerVoltageRMS); - } - final State newPowerCurrentRMS = getNewDecimalState(oldState == null ? null : oldState.powerCurrentRMS, - newState.powerCurrentRMS); - if (newPowerCurrentRMS != null) { - updates.put(CHANNEL_POWER_CURRENT_RMS, newPowerCurrentRMS); - } - final State newPowerLineFrequency = getNewDecimalState(oldState == null ? null : oldState.powerLineFrequency, - newState.powerLineFrequency); - if (newPowerLineFrequency != null) { - updates.put(CHANNEL_POWER_LINE_FREQUENCY, newPowerLineFrequency); - } - final State newPowerActivePower = getNewDecimalState(oldState == null ? null : oldState.powerActivePower, - newState.powerActivePower); - if (newPowerActivePower != null) { - updates.put(CHANNEL_POWER_ACTIVE_POWER, newPowerActivePower); - } - final State newPowerApparentPower = getNewDecimalState(oldState == null ? null : oldState.powerApparentPower, - newState.powerApparentPower); - if (newPowerApparentPower != null) { - updates.put(CHANNEL_POWER_APPARENT_POWER, newPowerApparentPower); - } - final State newPowerReactivePower = getNewDecimalState(oldState == null ? null : oldState.powerReactivePower, - newState.powerReactivePower); - if (newPowerReactivePower != null) { - updates.put(CHANNEL_POWER_REACTIVE_POWER, newPowerReactivePower); - } - final State newPowerPowerFactor = getNewDecimalState(oldState == null ? null : oldState.powerPowerFactor, - newState.powerPowerFactor); - if (newPowerPowerFactor != null) { - updates.put(CHANNEL_POWER_POWER_FACTOR, newPowerPowerFactor); + updates.put(IAnelConstants.CHANNEL_SENSOR_BRIGHTNESS, newSensorBrightness); } return updates; @@ -228,16 +172,28 @@ public Map getChannelUpdates(@Nullable AnelState oldState, AnelSt return value == null ? null : new DecimalType(value); } + private @Nullable State getTemperatureState(@Nullable String value) { + if (value == null || value.trim().isEmpty()) { + return null; + } + final float floatValue = Float.parseFloat(value); + return QuantityType.valueOf(floatValue, Units.CELSIUS); + } + private @Nullable State getSwitchState(@Nullable Boolean value) { return value == null ? null : OnOffType.from(value.booleanValue()); } private @Nullable State getNewStringState(@Nullable String oldValue, @Nullable String newValue) { - return getNewState(oldValue, newValue, value -> new StringType(value)); + return getNewState(oldValue, newValue, StringType::new); } private @Nullable State getNewDecimalState(@Nullable String oldValue, @Nullable String newValue) { - return getNewState(oldValue, newValue, value -> new DecimalType(value)); + return getNewState(oldValue, newValue, DecimalType::new); + } + + private @Nullable State getNewTemperatureState(@Nullable String oldValue, @Nullable String newValue) { + return getNewState(oldValue, newValue, value -> QuantityType.valueOf(Float.parseFloat(value), Units.CELSIUS)); } private @Nullable State getNewSwitchState(@Nullable Boolean oldValue, @Nullable Boolean newValue) { diff --git a/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/binding/binding.xml index a1a832e0c180d..1635ce3daf4df 100644 --- a/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/binding/binding.xml @@ -5,6 +5,5 @@ Anel NET-PwrCtrl Binding This is the binding for Anel NET-PwrCtrl devices. - Patrick Koenemann diff --git a/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/thing/thing-types.xml index 47dbd98944ecb..d9e45864579bb 100644 --- a/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.anel/src/main/resources/OH-INF/thing/thing-types.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - + Anel device with 3 controllable outlets without IO ports. @@ -17,19 +17,17 @@ - + macAddress - + Anel device with 8 controllable outlets without IO ports. @@ -46,19 +44,17 @@ - + macAddress - + Anel device with 8 controllable outlets / relays and possibly 8 IO ports. @@ -84,27 +80,20 @@ - - - - + macAddress - - Anel device properties + + Device properties @@ -138,23 +127,10 @@ - - - Optional power measurement values - - - - - - - - - - String - + The name of the Anel device @@ -222,47 +198,4 @@ - - Number - - Voltage RMS value of the optional power measurement - - - - Number - - Current RMS value of the optional power measurement - - - - Number - - Line frequency of the optional power measurement - - - - Number - - Active power of the optional power measurement - - - - Number - - Apparent power of the optional power measurement - - - - Number - - Reactive Power of the optional power measurement - - - - String - - Power factor of the optional power measurement - - - diff --git a/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelAuthenticationTest.java b/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelAuthenticationTest.java index ca947f07ccd96..ca81fed646ffe 100644 --- a/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelAuthenticationTest.java +++ b/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelAuthenticationTest.java @@ -41,30 +41,30 @@ public class AnelAuthenticationTest { @Test public void authenticationMethod() { - assertThat(AuthMethod.of(""), is(AuthMethod.plain)); - assertThat(AuthMethod.of(" \n"), is(AuthMethod.plain)); - assertThat(AuthMethod.of(STATUS_HUT_V4), is(AuthMethod.plain)); - assertThat(AuthMethod.of(STATUS_HUT_V5), is(AuthMethod.plain)); - assertThat(AuthMethod.of(STATUS_HOME_V4_6), is(AuthMethod.xorBase64)); - assertThat(AuthMethod.of(STATUS_UDP_SPEC_EXAMPLE_V7), is(AuthMethod.xorBase64)); - assertThat(AuthMethod.of(STATUS_PRO_EXAMPLE_V4_5), is(AuthMethod.xorBase64)); - assertThat(AuthMethod.of(STATUS_IO_EXAMPLE_V6_5), is(AuthMethod.xorBase64)); - assertThat(AuthMethod.of(STATUS_EXAMPLE_V6_0), is(AuthMethod.base64)); + assertThat(AuthMethod.of(""), is(AuthMethod.PLAIN)); + assertThat(AuthMethod.of(" \n"), is(AuthMethod.PLAIN)); + assertThat(AuthMethod.of(STATUS_HUT_V4), is(AuthMethod.PLAIN)); + assertThat(AuthMethod.of(STATUS_HUT_V5), is(AuthMethod.PLAIN)); + assertThat(AuthMethod.of(STATUS_HOME_V4_6), is(AuthMethod.XORBASE64)); + assertThat(AuthMethod.of(STATUS_UDP_SPEC_EXAMPLE_V7), is(AuthMethod.XORBASE64)); + assertThat(AuthMethod.of(STATUS_PRO_EXAMPLE_V4_5), is(AuthMethod.XORBASE64)); + assertThat(AuthMethod.of(STATUS_IO_EXAMPLE_V6_5), is(AuthMethod.XORBASE64)); + assertThat(AuthMethod.of(STATUS_EXAMPLE_V6_0), is(AuthMethod.BASE64)); } @Test public void encodeUserPasswordPlain() { - encodeUserPassword(AuthMethod.plain, (u, p) -> u + p); + encodeUserPassword(AuthMethod.PLAIN, (u, p) -> u + p); } @Test public void encodeUserPasswordBase64() { - encodeUserPassword(AuthMethod.base64, (u, p) -> base64(u + p)); + encodeUserPassword(AuthMethod.BASE64, (u, p) -> base64(u + p)); } @Test public void encodeUserPasswordXorBase64() { - encodeUserPassword(AuthMethod.xorBase64, (u, p) -> base64(xor(u + p, p))); + encodeUserPassword(AuthMethod.XORBASE64, (u, p) -> base64(xor(u + p, p))); } private void encodeUserPassword(AuthMethod authMethod, BiFunction expectedEncoding) { diff --git a/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateTest.java b/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateTest.java index c45e1fd9fedd2..a8a1a3fc97592 100644 --- a/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateTest.java +++ b/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateTest.java @@ -45,13 +45,6 @@ public void parseHomeV46Status() { assertNull(state.ioState[i - 1]); assertNull(state.ioIsInput[i - 1]); } - assertNull(state.powerVoltageRMS); - assertNull(state.powerCurrentRMS); - assertNull(state.powerLineFrequency); - assertNull(state.powerActivePower); - assertNull(state.powerApparentPower); - assertNull(state.powerReactivePower); - assertNull(state.powerPowerFactor); assertNull(state.sensorTemperature); assertNull(state.sensorBrightness); assertNull(state.sensorHumidity); @@ -87,13 +80,6 @@ public void parseHutV65Status() { assertThat(state.ioState[i - 1], is(false)); assertThat(state.ioIsInput[i - 1], is(i >= 5)); } - assertNull(state.powerVoltageRMS); - assertNull(state.powerCurrentRMS); - assertNull(state.powerLineFrequency); - assertNull(state.powerActivePower); - assertNull(state.powerApparentPower); - assertNull(state.powerReactivePower); - assertNull(state.powerPowerFactor); assertNull(state.sensorTemperature); assertNull(state.sensorBrightness); assertNull(state.sensorHumidity); @@ -116,20 +102,13 @@ public void parseHutV5Status() { assertThat(state.ioState[i - 1], is(true)); assertThat(state.ioIsInput[i - 1], is(true)); } - assertNull(state.powerVoltageRMS); - assertNull(state.powerCurrentRMS); - assertNull(state.powerLineFrequency); - assertNull(state.powerActivePower); - assertNull(state.powerApparentPower); - assertNull(state.powerReactivePower); - assertNull(state.powerPowerFactor); assertNull(state.sensorTemperature); assertNull(state.sensorBrightness); assertNull(state.sensorHumidity); } @Test - public void parseHutV61StatusWithPowerAndSensor() { + public void parseHutV61StatusAndSensor() { final AnelState state = AnelState.of(STATUS_HUT_V61_POW_SENSOR); assertThat(state.name, equalTo("NET-CONTROL")); assertThat(state.ip, equalTo("192.168.178.148")); @@ -145,20 +124,13 @@ public void parseHutV61StatusWithPowerAndSensor() { assertThat(state.ioState[i - 1], is(false)); assertThat(state.ioIsInput[i - 1], is(false)); } - assertThat(state.powerVoltageRMS, equalTo("225.9")); - assertThat(state.powerCurrentRMS, equalTo("0.0004")); - assertThat(state.powerLineFrequency, equalTo("50.056")); - assertThat(state.powerActivePower, equalTo("0.04")); - assertThat(state.powerApparentPower, equalTo("0.00")); - assertThat(state.powerReactivePower, equalTo("0.0")); - assertThat(state.powerPowerFactor, equalTo("1.0000")); assertThat(state.sensorTemperature, equalTo("20.61")); assertThat(state.sensorHumidity, equalTo("40.7")); assertThat(state.sensorBrightness, equalTo("7.0")); } @Test - public void parseHutV61StatusWithoutPowerWithSensor() { + public void parseHutV61StatusWithSensor() { final AnelState state = AnelState.of(STATUS_HUT_V61_SENSOR); assertThat(state.name, equalTo("NET-CONTROL")); assertThat(state.ip, equalTo("192.168.178.148")); @@ -174,20 +146,13 @@ public void parseHutV61StatusWithoutPowerWithSensor() { assertThat(state.ioState[i - 1], is(false)); assertThat(state.ioIsInput[i - 1], is(false)); } - assertNull(state.powerVoltageRMS); - assertNull(state.powerCurrentRMS); - assertNull(state.powerLineFrequency); - assertNull(state.powerActivePower); - assertNull(state.powerApparentPower); - assertNull(state.powerReactivePower); - assertNull(state.powerPowerFactor); assertThat(state.sensorTemperature, equalTo("20.61")); assertThat(state.sensorHumidity, equalTo("40.7")); assertThat(state.sensorBrightness, equalTo("7.0")); } @Test - public void parseHutV61StatusWithPowerWithoutSensor() { + public void parseHutV61StatusWithoutSensor() { final AnelState state = AnelState.of(STATUS_HUT_V61_POW); assertThat(state.name, equalTo("NET-CONTROL")); assertThat(state.ip, equalTo("192.168.178.148")); @@ -203,13 +168,6 @@ public void parseHutV61StatusWithPowerWithoutSensor() { assertThat(state.ioState[i - 1], is(false)); assertThat(state.ioIsInput[i - 1], is(false)); } - assertThat(state.powerVoltageRMS, equalTo("225.9")); - assertThat(state.powerCurrentRMS, equalTo("0.0004")); - assertThat(state.powerLineFrequency, equalTo("50.056")); - assertThat(state.powerActivePower, equalTo("0.04")); - assertThat(state.powerApparentPower, equalTo("0.00")); - assertThat(state.powerReactivePower, equalTo("0.0")); - assertThat(state.powerPowerFactor, equalTo("1.0000")); assertNull(state.sensorTemperature); assertNull(state.sensorBrightness); assertNull(state.sensorHumidity); diff --git a/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateUpdaterTest.java b/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateUpdaterTest.java index 93a63f41674c0..3703b4c33d434 100644 --- a/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateUpdaterTest.java +++ b/bundles/org.openhab.binding.anel/src/test/java/org/openhab/binding/anel/internal/AnelStateUpdaterTest.java @@ -19,11 +19,13 @@ import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.junit.jupiter.api.Test; import org.openhab.binding.anel.internal.state.AnelState; import org.openhab.binding.anel.internal.state.AnelStateUpdater; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.types.State; @@ -33,7 +35,7 @@ * @author Patrick Koenemann - Initial contribution */ @NonNullByDefault -public class AnelStateUpdaterTest implements IAnelTestStatus { +public class AnelStateUpdaterTest implements IAnelTestStatus, IAnelConstants { private final AnelStateUpdater stateUpdater = new AnelStateUpdater(); @@ -56,11 +58,11 @@ public void fromNullStateUpdatesHome() { Map updates = stateUpdater.getChannelUpdates(null, newState); // then final Map expected = new HashMap<>(); - expected.put(IAnelConstants.CHANNEL_NAME, new StringType("NET-CONTROL")); + expected.put(CHANNEL_NAME, new StringType("NET-CONTROL")); for (int i = 1; i <= 8; i++) { - expected.put(IAnelConstants.CHANNEL_RELAY_NAME.get(i - 1), new StringType("Nr. " + i)); - expected.put(IAnelConstants.CHANNEL_RELAY_STATE.get(i - 1), OnOffType.from(i % 2 == 1)); - expected.put(IAnelConstants.CHANNEL_RELAY_LOCKED.get(i - 1), OnOffType.from(i > 3)); + expected.put(CHANNEL_RELAY_NAME.get(i - 1), new StringType("Nr. " + i)); + expected.put(CHANNEL_RELAY_STATE.get(i - 1), OnOffType.from(i % 2 == 1)); + expected.put(CHANNEL_RELAY_LOCKED.get(i - 1), OnOffType.from(i > 3)); } assertThat(updates, equalTo(expected)); } @@ -72,30 +74,24 @@ public void fromNullStateUpdatesHutPowerSensor() { // when Map updates = stateUpdater.getChannelUpdates(null, newState); // then - final Map expected = new HashMap<>(); - expected.put(IAnelConstants.CHANNEL_NAME, new StringType("NET-CONTROL")); - expected.put(IAnelConstants.CHANNEL_TEMPERATURE, new DecimalType("27.7")); + assertThat(updates.size(), is(5 + 8 * 6)); + assertThat(updates.get(CHANNEL_NAME), equalTo(new StringType("NET-CONTROL"))); + assertTemperature(updates.get(CHANNEL_TEMPERATURE), 27.7); + + assertThat(updates.get(CHANNEL_SENSOR_BRIGHTNESS), equalTo(new DecimalType("7"))); + assertThat(updates.get(CHANNEL_SENSOR_HUMIDITY), equalTo(new DecimalType("40.7"))); + assertTemperature(updates.get(CHANNEL_SENSOR_TEMPERATURE), 20.61); + for (int i = 1; i <= 8; i++) { - expected.put(IAnelConstants.CHANNEL_RELAY_NAME.get(i - 1), new StringType("Nr. " + i)); - expected.put(IAnelConstants.CHANNEL_RELAY_STATE.get(i - 1), OnOffType.from(i <= 3 || i >= 7)); - expected.put(IAnelConstants.CHANNEL_RELAY_LOCKED.get(i - 1), OnOffType.OFF); + assertThat(updates.get(CHANNEL_RELAY_NAME.get(i - 1)), equalTo(new StringType("Nr. " + i))); + assertThat(updates.get(CHANNEL_RELAY_STATE.get(i - 1)), equalTo(OnOffType.from(i <= 3 || i >= 7))); + assertThat(updates.get(CHANNEL_RELAY_LOCKED.get(i - 1)), equalTo(OnOffType.OFF)); } for (int i = 1; i <= 8; i++) { - expected.put(IAnelConstants.CHANNEL_IO_NAME.get(i - 1), new StringType("IO-" + i)); - expected.put(IAnelConstants.CHANNEL_IO_STATE.get(i - 1), OnOffType.OFF); - expected.put(IAnelConstants.CHANNEL_IO_MODE.get(i - 1), OnOffType.OFF); + assertThat(updates.get(CHANNEL_IO_NAME.get(i - 1)), equalTo(new StringType("IO-" + i))); + assertThat(updates.get(CHANNEL_IO_STATE.get(i - 1)), equalTo(OnOffType.OFF)); + assertThat(updates.get(CHANNEL_IO_MODE.get(i - 1)), equalTo(OnOffType.OFF)); } - expected.put(IAnelConstants.CHANNEL_POWER_VOLTAGE_RMS, new DecimalType("225.9")); - expected.put(IAnelConstants.CHANNEL_POWER_CURRENT_RMS, new DecimalType("0.0004")); - expected.put(IAnelConstants.CHANNEL_POWER_LINE_FREQUENCY, new DecimalType("50.056")); - expected.put(IAnelConstants.CHANNEL_POWER_ACTIVE_POWER, new DecimalType("0.04")); - expected.put(IAnelConstants.CHANNEL_POWER_APPARENT_POWER, new DecimalType("0")); - expected.put(IAnelConstants.CHANNEL_POWER_REACTIVE_POWER, new DecimalType("0")); - expected.put(IAnelConstants.CHANNEL_POWER_POWER_FACTOR, new DecimalType("1")); - expected.put(IAnelConstants.CHANNEL_SENSOR_TEMPERATURE, new DecimalType("20.61")); - expected.put(IAnelConstants.CHANNEL_SENSOR_HUMIDITY, new DecimalType("40.7")); - expected.put(IAnelConstants.CHANNEL_SENSOR_BRIGHTNESS, new DecimalType("7")); - assertThat(updates, equalTo(expected)); } @Test @@ -107,7 +103,7 @@ public void singleRelayStateChange() { Map updates = stateUpdater.getChannelUpdates(oldState, newState); // then final Map expected = new HashMap<>(); - expected.put(IAnelConstants.CHANNEL_RELAY_STATE.get(3), OnOffType.ON); + expected.put(CHANNEL_RELAY_STATE.get(3), OnOffType.ON); assertThat(updates, equalTo(expected)); } @@ -119,9 +115,8 @@ public void temperatureChange() { // when Map updates = stateUpdater.getChannelUpdates(oldState, newState); // then - final Map expected = new HashMap<>(); - expected.put(IAnelConstants.CHANNEL_TEMPERATURE, new DecimalType("27.1")); - assertThat(updates, equalTo(expected)); + assertThat(updates.size(), is(1)); + assertTemperature(updates.get(CHANNEL_TEMPERATURE), 27.1); } @Test @@ -132,10 +127,16 @@ public void singleSensorStatesChange() { // when Map updates = stateUpdater.getChannelUpdates(oldState, newState); // then - final Map expected = new HashMap<>(); - expected.put(IAnelConstants.CHANNEL_SENSOR_TEMPERATURE, new DecimalType("20.6")); - expected.put(IAnelConstants.CHANNEL_SENSOR_HUMIDITY, new DecimalType("40")); - expected.put(IAnelConstants.CHANNEL_SENSOR_BRIGHTNESS, new DecimalType("7.1")); - assertThat(updates, equalTo(expected)); + assertThat(updates.size(), is(3)); + assertThat(updates.get(CHANNEL_SENSOR_BRIGHTNESS), equalTo(new DecimalType("7.1"))); + assertThat(updates.get(CHANNEL_SENSOR_HUMIDITY), equalTo(new DecimalType("40"))); + assertTemperature(updates.get(CHANNEL_SENSOR_TEMPERATURE), 20.6); + } + + private void assertTemperature(@Nullable State state, double value) { + assertThat(state, isA(QuantityType.class)); + if (state instanceof QuantityType) { + assertThat(((QuantityType) state).doubleValue(), closeTo(value, 0.0001d)); + } } }