From 529a63f80e64a518c094c825ab9ea4689fdea627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Wed, 27 Nov 2019 14:48:31 +0100 Subject: [PATCH 01/22] [energenie] oh1 migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- CODEOWNERS | 1 + bundles/org.openhab.binding.energenie/NOTICE | 13 + .../org.openhab.binding.energenie/README.md | 95 +++++ bundles/org.openhab.binding.energenie/pom.xml | 16 + .../src/main/feature/feature.xml | 9 + .../internal/EnergenieBindingConstants.java | 84 ++++ .../internal/EnergenieHandlerFactory.java | 61 +++ .../config/EnergenieConfiguration.java | 27 ++ .../internal/handler/EnergenieHandler.java | 358 ++++++++++++++++++ .../internal/handler/EnergeniePWMHandler.java | 201 ++++++++++ .../resources/ESH-INF/binding/binding.xml | 10 + .../ESH-INF/i18n/energenie_xx_XX.properties | 17 + .../resources/ESH-INF/thing/thing-types.xml | 174 +++++++++ bundles/pom.xml | 1 + 14 files changed, 1067 insertions(+) create mode 100644 bundles/org.openhab.binding.energenie/NOTICE create mode 100644 bundles/org.openhab.binding.energenie/README.md create mode 100644 bundles/org.openhab.binding.energenie/pom.xml create mode 100644 bundles/org.openhab.binding.energenie/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java create mode 100644 bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties create mode 100644 bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/CODEOWNERS b/CODEOWNERS index 48f1fc1855175..e8ce863eefa44 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -48,6 +48,7 @@ /bundles/org.openhab.binding.dwdpollenflug/ @DerOetzi /bundles/org.openhab.binding.dwdunwetter/ @limdul79 /bundles/org.openhab.binding.elerotransmitterstick/ @vbier +/bundles/org.openhab.binding.energenie/ @hmerk /bundles/org.openhab.binding.enocean/ @fruggy83 /bundles/org.openhab.binding.enturno/ @klocsson /bundles/org.openhab.binding.etherrain/ @dfad1469 diff --git a/bundles/org.openhab.binding.energenie/NOTICE b/bundles/org.openhab.binding.energenie/NOTICE new file mode 100644 index 0000000000000..4c20ef446c1e4 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons diff --git a/bundles/org.openhab.binding.energenie/README.md b/bundles/org.openhab.binding.energenie/README.md new file mode 100644 index 0000000000000..f0bc49aff79b4 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/README.md @@ -0,0 +1,95 @@ +# Gembird energenie Binding + +This binding integrates the Gembird energenie range of power extenders by using the Energenie Data Exchange Protocol and power reading devices through HTTP interface. + + +## Supported Things + +The Binding supports PM2-LAN, PMS-LAN, PMS2-LAN or PMS-WLAN power extenders as well as PWM-LAN power measurement devices. + +## Discovery + +Gembird energenie devices don't support autodiscovery. All Things need to be created manually either in PaperUI or within Things files. + +## Binding Configuration + +The Binding does not need any specific configuration + +## Thing Configuration + +The device requires the IP-address and a password as a configuration value in order for the binding to know where to access it and to login to the device. + +| Parameter | Description | +|-----------|------------------------------------------------------| +| host | IP-Address of energenie device | +| password | Password to access energenie device, defaults to "1" | + +## Channels + +The following channels are supported by PM2-LAN, PMS-LAN, PMS2-LAN or PMS-WLAN devices + +| channel | type | description | +|----------|--------|----------------------------------------------------| +| socket1 | Switch | This is the control channel for the first socket | +| socket2 | Switch | This is the control channel for the second socket | +| socket3 | Switch | This is the control channel for the third socket | +| socket4 | Switch | This is the control channel for the fourth socket | + +PWM-LAN devices support the following channels + +| channel | type | description | +|----------|--------|------------------------------------------| +| voltage | Number | Channel for output voltage measurement | +| current | Number | Channel for output current measurement | +| power | Number | Channel for output power measurement | +| energy | Number | channel for output energy measurement | + + +## Full Example + +Things + +``` +Thing energenie:pm2lan:pm2lan [ host="xxx.xxx.xxx.xxx", password="your password" ] +Thing energenie:pmslan:pmslan [ host="xxx.xxx.xxx.xxx", password="your password" ] +Thing energenie:pms2lan:pms2lan [ host="xxx.xxx.xxx.xxx", password="your password" ] +Thing energenie:pmswlan:pmswlan [ host="xxx.xxx.xxx.xxx", password="your password" ] +Thing energenie:pwmlan:pwmlan [ host="xxx.xxx.xxx.xxx", password="your password" ] +``` +Items + +``` +//Power extenders +Switch Socket1 { channel="energenie:pm2lan:pm2lan:socket1" } +Switch Socket2 { channel="energenie:pm2lan:pm2lan:socket2" } +Switch Socket3 { channel="energenie:pm2lan:pm2lan:socket3" } +Switch Socket4 { channel="energenie:pm2lan:pm2lan:socket4" } + +//Power measurement +Number Voltage { channel="energenie:pwmlan:pwmlan:voltage" } +Number Current { channel="energenie:pwmlan:pwmlan:current" } +Number Power { channel="energenie:pwmlan:pwmlan:power" } +Number Energy { channel="energenie:pwmlan:pwmlan:energy" } +``` + +Sitemap + +``` +sitemap energenie label="Energenie Devices" +{ + Frame { + // Power extenders + Switch item=socket1 + Switch item=socket2 + Switch item=socket3 + Switch item=socket4 + + // Power measurement + Number item=Voltage + Number item=Current + Number item=Power + Number item=Energy + } +} +``` + \ No newline at end of file diff --git a/bundles/org.openhab.binding.energenie/pom.xml b/bundles/org.openhab.binding.energenie/pom.xml new file mode 100644 index 0000000000000..ec4e2910cd93b --- /dev/null +++ b/bundles/org.openhab.binding.energenie/pom.xml @@ -0,0 +1,16 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.energenie + + openHAB Add-ons :: Bundles :: Energenie Binding + + diff --git a/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml b/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml new file mode 100644 index 0000000000000..85b7e1383dcc1 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.energenie/${project.version} + + diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java new file mode 100644 index 0000000000000..232bf35ce9440 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2010-2019 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.energenie.internal; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link EnergenieBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +public class EnergenieBindingConstants { + + private static final String BINDING_ID = "energenie"; + + // private static final String HOST = "host"; + public static final int TCP_PORT = 5000; + + public static final int STRTCRYP_LEN = 4; + public static final int CTRLCRYP_LEN = 4; + public static final int KEY_LEN = 8; + public static final int TASK_LEN = 4; + public static final int SOLUTION_LEN = 4; + + public static final String STATE_ON = "0x11"; + public static final String STATE_ON_NO_VOLTAGE = "0x12"; + public static final String STATE_OFF = "0x22"; + public static final String STATE_OFF_NO_VOLTAGE = "0x21"; + + // public static final byte STATE_INVALID = 0xFF; // for internal use + + public static final String V21_STATE_ON = "0x41"; + public static final String V21_STATE_OFF = "0x82"; + + public static final String WLAN_STATE_ON = "0x51"; + public static final String WLAN_STATE_OFF = "0x92"; + + public static final byte SWITCH_ON = 0x01; + public static final byte SWITCH_OFF = 0x02; + public static final byte DONT_SWITCH = 0x04; + + public static final int SOCKET_COUNT = 4; // AC power sockets, not network ones + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_PM2LAN = new ThingTypeUID(BINDING_ID, "pm2lan"); + public static final ThingTypeUID THING_TYPE_PMSLAN = new ThingTypeUID(BINDING_ID, "pmslan"); + public static final ThingTypeUID THING_TYPE_PMS2LAN = new ThingTypeUID(BINDING_ID, "pms2lan"); + public static final ThingTypeUID THING_TYPE_PMSWLAN = new ThingTypeUID(BINDING_ID, "pmswlan"); + public static final ThingTypeUID THING_TYPE_PWMLAN = new ThingTypeUID(BINDING_ID, "pwmlan"); + + // List of all Channel ids + public static final String SOCKET_1 = "socket1"; + public static final String SOCKET_2 = "socket2"; + public static final String SOCKET_3 = "socket3"; + public static final String SOCKET_4 = "socket4"; + + public static final String VOLTAGE = "voltage"; + public static final String CURRENT = "current"; + public static final String POWER = "power"; + public static final String ENERGY = "energy"; + + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( + Stream.of(THING_TYPE_PM2LAN, THING_TYPE_PMSLAN, THING_TYPE_PMS2LAN, THING_TYPE_PMSLAN, THING_TYPE_PWMLAN) + .collect(Collectors.toSet())); + +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java new file mode 100644 index 0000000000000..9a8acdbaf7249 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 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.energenie.internal; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.energenie.internal.handler.EnergenieHandler; +import org.openhab.binding.energenie.internal.handler.EnergeniePWMHandler; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link EnergenieHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.energenie", service = ThingHandlerFactory.class) +public class EnergenieHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = EnergenieBindingConstants.SUPPORTED_THING_TYPES_UIDS; + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMSLAN.equals(thingTypeUID) + || THING_TYPE_PMS2LAN.equals(thingTypeUID) || THING_TYPE_PMSWLAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing); + } + if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { + return new EnergeniePWMHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java new file mode 100644 index 0000000000000..6f7c78f6bd0cc --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2019 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.energenie.internal.config; + +/** + * The {@link EnergenieConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Hans-Jörg Merk - Initial contribution + */ +public class EnergenieConfiguration { + + /** + * Sample configuration parameter. Replace with your own. + */ + public String host = ""; + public String password = ""; +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java new file mode 100644 index 0000000000000..ffc408b7a4552 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -0,0 +1,358 @@ +/** + * Copyright (c) 2010-2019 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.energenie.internal.handler; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link EnergenieHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +public class EnergenieHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class); + + private String protocol = "EG_PROTO_V20"; + private String host = ""; + private String password = ""; + + @Nullable + Socket socket = null; + @Nullable + private OutputStream output = null; + @Nullable + private InputStream input = null; + + private byte[] key = new byte[KEY_LEN]; + private byte[] task = new byte[TASK_LEN]; + private byte[] solution = new byte[SOLUTION_LEN]; + + /** + * The default refresh interval in Seconds. + */ + private int DEFAULT_REFRESH_INTERVAL = 60; + @Nullable + private ScheduledFuture refreshJob; + private Runnable refreshRunnable = new Runnable() { + + @Override + public void run() { + try { + socket = new Socket(host, TCP_PORT); + socket.setSoTimeout(1500); + updateStatus(ThingStatus.ONLINE); + output = socket.getOutputStream(); + input = socket.getInputStream(); + authorize(); + socket.close(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); + } + } + }; + + public EnergenieHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + // refresh not supported + } else if (command instanceof OnOffType) { + byte[] ctrl = { DONT_SWITCH, DONT_SWITCH, DONT_SWITCH, DONT_SWITCH }; + switch (channelUID.getId()) { + case SOCKET_1: + ctrl[0] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + break; + case SOCKET_2: + ctrl[1] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + break; + case SOCKET_3: + ctrl[2] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + break; + case SOCKET_4: + ctrl[3] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + break; + } + String ctrlString = getByteString(ctrl); + logger.trace("byte control (int) '{}' (hex)'{}'", ctrl, ctrlString); + + try { + socket = new Socket(host, TCP_PORT); + socket.setSoTimeout(1500); + output = socket.getOutputStream(); + input = socket.getInputStream(); + authorize(); + byte[] controlmessage = encryptControls(ctrl); + output.write(controlmessage); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); + } + } + } + + @Override + public void initialize() { + Configuration config = getConfig(); + + if ((config.get("host") != null) && (config.get("password") != null)) { + host = (String) config.get("host"); + password = (String) config.get("password"); + key = getKey(); + logger.debug("Initializing EnergenieHandler for Host '{}'", host); + if (getThing().getThingTypeUID().getId().equals("pmslan")) { + protocol = "EG_PROTO_V20"; + } else if (getThing().getThingTypeUID().getId().equals("pmswlan")) { + protocol = "EG_PROTO_WLAN"; + } else { + protocol = "EG_PROTO_V21"; + } + + try { + socket = new Socket(host, TCP_PORT); + socket.setSoTimeout(1500); + updateStatus(ThingStatus.ONLINE); + output = socket.getOutputStream(); + input = socket.getInputStream(); + authorize(); + socket.close(); + onUpdate(); + + } catch (UnknownHostException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Can't find host: {}:{}.", host, TCP_PORT); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); + } + + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Can not access device , IP-Address or password not set"); + } + } + + @Override + public void dispose() { + logger.debug("EnergenieHandler disposed."); + try { + if (socket != null) { + socket.close(); + } + } catch (IOException e) { + } + if (refreshJob != null && !refreshJob.isCancelled()) { + refreshJob.cancel(true); + refreshJob = null; + } + } + + public void authorize() { + byte[] message = { 0x11 }; + byte[] response = new byte[4]; + byte[] statcryp = new byte[4]; + + try { + if (output != null && input != null) { + output.write(message); + logger.trace("Start Condition '{}' send to EG", message); + input.read(response); + for (int i = 0; i < 4; i++) { + task[i] = response[i]; + } + String taskString = getByteString(task); + logger.trace("EG responded with task (int) '{}' (hex) '{}'", task, taskString); + + byte[] solutionMessage = calculateSolution(); + + output.write(solutionMessage); + logger.trace("Solution '{}' send to EG", solutionMessage); + + input.read(statcryp); + String statcrypString = getByteString(statcryp); + logger.trace("EG responded with statcryp (int) '{}' (hex) '{}'", statcryp, statcrypString); + + byte[] status = decryptStatus(statcryp); + + String statusString = getByteString(status); + logger.trace("EG responded with status (int) '{}' (hex) '{}'", status, statusString); + + stateUpdate(status); + } + } catch (UnknownHostException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Can't find host: {}:{}.", host, TCP_PORT); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); + } + } + + public byte[] getKey() { + int passwordLength = password.length(); + String passwordString = password; + for (int i = 0; i < (8 - passwordLength); i++) { + passwordString = passwordString + " "; + } + byte[] key = passwordString.getBytes(); + String ts = getByteString(key); + return key; + } + + public String getByteString(byte[] byteArray) { + StringBuilder sb = new StringBuilder(); + for (byte b : byteArray) { + sb.append(String.format("0x%02X ", b)); + } + String byteString = sb.toString(); + return byteString; + } + + public byte[] calculateSolution() { + int[] uIntTask = new int[4]; + for (int i = 0; i < 4; i++) { + uIntTask[i] = Byte.toUnsignedInt(task[i]); + } + + int test = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); + + int solutionLoword = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); + + byte loword[] = ByteBuffer.allocate(4).putInt(solutionLoword).array(); + + int solutionHiword = (((uIntTask[1] ^ key[3]) * key[1]) ^ (key[7] | (key[5] << 8)) ^ uIntTask[3]); + byte hiword[] = ByteBuffer.allocate(4).putInt(solutionHiword).array(); + + solution[0] = loword[3]; + solution[1] = loword[2]; + solution[2] = hiword[3]; + solution[3] = hiword[2]; + + return solution; + } + + public byte[] decryptStatus(byte[] statcryp) { + byte[] status = new byte[4]; + int[] status_i = new int[4]; + for (int i = 0; i < 4; i++) { + status_i[i] = ((((statcryp[3 - i] - key[1]) ^ key[0]) - task[3]) ^ task[2]); + status[i] = (byte) status_i[i]; + } + return status; + } + + public byte[] encryptControls(byte[] controls) { + byte[] ctrlcryp = new byte[CTRLCRYP_LEN]; + int[] ctrlcryp_i = new int[CTRLCRYP_LEN]; + for (int i = 0; i < 4; i++) { + ctrlcryp_i[i] = (((controls[3 - i] ^ task[2]) + task[3]) ^ key[0]) + key[1]; + ctrlcryp[i] = (byte) ctrlcryp_i[i]; + } + + return ctrlcryp; + } + + public void stateUpdate(byte[] status) { + String statusOn = STATE_ON; + String statusOff = STATE_OFF; + byte stat = status[0]; + switch (protocol) { + case "EG_PROTO_V20": + statusOn = STATE_ON; + statusOff = STATE_OFF; + break; + case "EG_PROTO_V21": + statusOn = V21_STATE_ON; + statusOff = V21_STATE_OFF; + break; + case "EG_PROTO_WLAN": + statusOn = WLAN_STATE_ON; + statusOff = WLAN_STATE_OFF; + break; + } + + for (int i = 0; i < 4; i++) { + String socket = SOCKET_1; + switch (i) { + case 0: + socket = SOCKET_1; + break; + case 1: + socket = SOCKET_2; + break; + case 2: + socket = SOCKET_3; + break; + case 3: + socket = SOCKET_4; + break; + } + stat = status[i]; + + StringBuilder sbStatus = new StringBuilder(); + sbStatus.append(String.format("%02x", stat)); + String stringStatus = sbStatus.toString(); + stringStatus = "0x" + stringStatus; + + if (stringStatus.equals(statusOn)) { + updateState(socket, OnOffType.ON); + } else if (stringStatus.equals(statusOff)) { + updateState(socket, OnOffType.OFF); + } + } + + } + + private synchronized void onUpdate() { + if (refreshJob == null || refreshJob.isCancelled()) { + Configuration config = getThing().getConfiguration(); + int refreshInterval = DEFAULT_REFRESH_INTERVAL; + Object refreshConfig = config.get("refresh"); + if (refreshConfig != null) { + refreshInterval = ((BigDecimal) refreshConfig).intValue(); + } + refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 5, refreshInterval, TimeUnit.SECONDS); + } + } + +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java new file mode 100644 index 0000000000000..0841b9cb1df06 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2010-2019 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.energenie.internal.handler; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link EnergeniePWMHandler} is responsible for reading states and update PWM channels. + * + * @author Hans-Jörg Merk - Initial contribution + */ + +@NonNullByDefault +public class EnergeniePWMHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(EnergeniePWMHandler.class); + + private String host = ""; + private String password = ""; + + /** the timeout to use for connecting to a given host (defaults to 5000 milliseconds) */ + private int timeout = 6000; + + /** + * The default refresh interval in Seconds. + */ + private int DEFAULT_REFRESH_INTERVAL = 60; + @Nullable + private ScheduledFuture refreshJob; + private Runnable refreshRunnable = new Runnable() { + + @Override + public void run() { + getState(); + } + }; + + public EnergeniePWMHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // PWM devices don't support any commands, just value reading + } + + @Override + public void initialize() { + Configuration config = getConfig(); + + if ((config.get("host") != null) && (config.get("password") != null)) { + host = (String) config.get("host"); + password = (String) config.get("password"); + logger.debug("Initializing EnergeniePWMHandler for Host '{}'", host); + updateStatus(ThingStatus.ONLINE); + onUpdate(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Can not access device , IP-Address or password not set"); + } + } + + @Override + public void dispose() { + logger.debug("EnergeniePWMHandler disposed."); + if (refreshJob != null && !refreshJob.isCancelled()) { + refreshJob.cancel(true); + refreshJob = null; + } + } + + public void getState() { + String url = "http://" + host + "/login.html"; + String urlParameters = "pw=" + password; + InputStream urlContent = new ByteArrayInputStream(urlParameters.getBytes(Charset.forName("UTF-8"))); + String loginResponseString = null; + + try { + logger.trace("sendlogin to {} with password {}", host, password); + logger.trace("sending 'POST' request to URL : {}", url); + loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", timeout); + + if (loginResponseString != null) { + readState(loginResponseString, "voltage"); + readState(loginResponseString, "current"); + readState(loginResponseString, "power"); + readState(loginResponseString, "energy"); + try { + HttpUtil.executeUrl("POST", url, timeout); + logger.trace("logout from ip {}", host); + } catch (Exception e) { + logger.error("failed to logout from ip {}", host); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + } catch (Exception e) { + logger.error("energenie: failed to login to ip {}", host); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + public void readState(String loginResponseString, String channel) { + String stateResponseSearch = ""; + int start = 0, stop = 0, divisor = 1; + double value = 0.0; + + switch (channel) { + case VOLTAGE: + stateResponseSearch = "var V = "; + start = 9; + stop = 20; + divisor = 10; + + break; + case CURRENT: + stateResponseSearch = "var I = "; + start = 9; + stop = 20; + divisor = 100; + break; + case POWER: + stateResponseSearch = "var P="; + start = 6; + stop = 20; + divisor = 466; + break; + case ENERGY: + stateResponseSearch = "var E="; + start = 6; + stop = 20; + divisor = 25600; + break; + } + + int findState = loginResponseString.lastIndexOf(stateResponseSearch); + if (findState > 0) { + logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); + String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); + logger.trace("transformed state response = {}", slicedResponseTmp); + String[] slicedResponse = slicedResponseTmp.split(";"); + logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); + if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { + value = Double.parseDouble(slicedResponse[0]) / divisor; + } else { + value = -1.0; + } + State valueState = new DecimalType(value); + updateState(channel, valueState); + + } else { + logger.trace("searchstring '{} not found", stateResponseSearch); + } + + } + + private synchronized void onUpdate() { + if (refreshJob == null || refreshJob.isCancelled()) { + Configuration config = getThing().getConfiguration(); + int refreshInterval = DEFAULT_REFRESH_INTERVAL; + Object refreshConfig = config.get("refresh"); + if (refreshConfig != null) { + refreshInterval = ((BigDecimal) refreshConfig).intValue(); + } + refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 5, refreshInterval, TimeUnit.SECONDS); + } + } + +} diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..24b1425e6db0f --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + Energenie Binding + This is the binding for Energenie. + Hans-Jörg Merk + + diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties new file mode 100644 index 0000000000000..3613da9f1806b --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties @@ -0,0 +1,17 @@ +# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE +# FIXME: please do not add the file to the repo if you add or change no content +# binding +binding.energenie.name = +binding.energenie.description = + +# thing types +thing-type.energenie.sample.label = +thing-type.energenie.sample.description = + +# thing type config description +thing-type.config.energenie.sample.config1.label = +thing-type.config.energenie.sample.config1.description = + +# channel types +channel-type.energenie.sample-channel.label = +channel-type.energenie.sample-channel.description = diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..7c515505b1477 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,174 @@ + + + + + + Gembird energenie programmable power strip with LAN interface + + + + + + + + + + + + The IP address of this device + + + + The password of this device + 1 + + + + + + + + Gembird energenie programmable power strip with LAN interface + + + + + + + + + + + + The IP address of this device + + + + The password of this device + 1 + + + + + + + + Gembird energenie programmable power strip with LAN interface + + + + + + + + + + + + The IP address of this device + + + + The password of this device + 1 + + + + + + + + Gembird energenie programmable power strip with WLAN interface + + + + + + + + + + + + The IP address of this device + + + + The password of this device + 1 + + + + + + + Gembird energenie energy meter with LAN interface + + + + + + + + + + + + The IP address of this device + + + + + + + Switch + + Channel representing the state of socket 1 + + + + Switch + + Channel representing the state of socket 2 + + + + Switch + + Channel representing the state of socket 3 + + + + Switch + + Channel representing the state of socket 4 + + + + Number + + Channel representing the voltage + + + + Number + + Channel representing the current + + + + Number + + Channel representing the power consumption + + + + Number + + Channel representing the energy consumption + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 132ec52fca9f6..2566c42672532 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -83,6 +83,7 @@ org.openhab.binding.dwdunwetter org.openhab.binding.ecobee org.openhab.binding.elerotransmitterstick + org.openhab.binding.energenie org.openhab.binding.enocean org.openhab.binding.enturno org.openhab.binding.etherrain From 75b06ab6b4895ad24ce2bdc98ff91258a3d651c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Thu, 28 Nov 2019 08:52:38 +0100 Subject: [PATCH 02/22] Changes after first review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../internal/EnergenieHandlerFactory.java | 11 +- .../config/EnergenieConfiguration.java | 8 +- .../internal/handler/EnergenieHandler.java | 71 +++------ .../internal/handler/EnergeniePWMHandler.java | 54 +++---- .../main/resources/ESH-INF/config/config.xml | 21 +++ .../ESH-INF/i18n/energenie_xx_XX.properties | 17 --- .../resources/ESH-INF/thing/thing-types.xml | 143 +++++++----------- 7 files changed, 135 insertions(+), 190 deletions(-) create mode 100644 bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml delete mode 100644 bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java index 9a8acdbaf7249..4e92a30bdc5fb 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java @@ -48,9 +48,14 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMSLAN.equals(thingTypeUID) - || THING_TYPE_PMS2LAN.equals(thingTypeUID) || THING_TYPE_PMSWLAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing); + if (THING_TYPE_PMSLAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, "EG_PROTO_V20"); + } + if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, "EG_PROTO_V21"); + } + if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, "EG_PROTO_WLAN"); } if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { return new EnergeniePWMHandler(thing); diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java index 6f7c78f6bd0cc..e577a40ea0e9c 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java @@ -19,9 +19,11 @@ */ public class EnergenieConfiguration { - /** - * Sample configuration parameter. Replace with your own. - */ public String host = ""; public String password = ""; + /** + * The default refresh interval in Seconds. + */ + public int DEFAULT_REFRESH_INTERVAL = 60; + } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index ffc408b7a4552..52129e1a1944c 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigDecimal; import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; @@ -26,7 +25,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -35,6 +33,7 @@ import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; +import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,25 +48,21 @@ public class EnergenieHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class); - private String protocol = "EG_PROTO_V20"; + private String egprotocol = "EG_PROTO_V20"; private String host = ""; private String password = ""; + private int refreshInterval; - @Nullable - Socket socket = null; - @Nullable - private OutputStream output = null; - @Nullable - private InputStream input = null; + private @Nullable EnergenieConfiguration config; + + private @Nullable Socket socket = null; + private @Nullable OutputStream output = null; + private @Nullable InputStream input = null; private byte[] key = new byte[KEY_LEN]; private byte[] task = new byte[TASK_LEN]; private byte[] solution = new byte[SOLUTION_LEN]; - /** - * The default refresh interval in Seconds. - */ - private int DEFAULT_REFRESH_INTERVAL = 60; @Nullable private ScheduledFuture refreshJob; private Runnable refreshRunnable = new Runnable() { @@ -89,8 +84,9 @@ public void run() { } }; - public EnergenieHandler(Thing thing) { + public EnergenieHandler(Thing thing, String protocol) { super(thing); + egprotocol = protocol; } @Override @@ -101,16 +97,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { byte[] ctrl = { DONT_SWITCH, DONT_SWITCH, DONT_SWITCH, DONT_SWITCH }; switch (channelUID.getId()) { case SOCKET_1: - ctrl[0] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + ctrl[0] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; break; case SOCKET_2: - ctrl[1] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + ctrl[1] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; break; case SOCKET_3: - ctrl[2] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + ctrl[2] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; break; case SOCKET_4: - ctrl[3] = command.equals(OnOffType.ON) ? SWITCH_ON : SWITCH_OFF; + ctrl[3] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; break; } String ctrlString = getByteString(ctrl); @@ -124,6 +120,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { authorize(); byte[] controlmessage = encryptControls(ctrl); output.write(controlmessage); + socket.close(); } catch (IOException e) { updateStatus(ThingStatus.OFFLINE); logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); @@ -133,20 +130,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - Configuration config = getConfig(); + EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); + + this.config = config; - if ((config.get("host") != null) && (config.get("password") != null)) { - host = (String) config.get("host"); - password = (String) config.get("password"); + if (config.host != null && config.password != null) { + host = config.host; + password = config.password; + refreshInterval = config.DEFAULT_REFRESH_INTERVAL; key = getKey(); - logger.debug("Initializing EnergenieHandler for Host '{}'", host); - if (getThing().getThingTypeUID().getId().equals("pmslan")) { - protocol = "EG_PROTO_V20"; - } else if (getThing().getThingTypeUID().getId().equals("pmswlan")) { - protocol = "EG_PROTO_WLAN"; - } else { - protocol = "EG_PROTO_V21"; - } + logger.debug("Initializing EnergenieHandler for Host '{}'", config.host); try { socket = new Socket(host, TCP_PORT); @@ -235,7 +228,6 @@ public byte[] getKey() { passwordString = passwordString + " "; } byte[] key = passwordString.getBytes(); - String ts = getByteString(key); return key; } @@ -254,8 +246,6 @@ public byte[] calculateSolution() { uIntTask[i] = Byte.toUnsignedInt(task[i]); } - int test = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); - int solutionLoword = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); byte loword[] = ByteBuffer.allocate(4).putInt(solutionLoword).array(); @@ -296,7 +286,7 @@ public void stateUpdate(byte[] status) { String statusOn = STATE_ON; String statusOff = STATE_OFF; byte stat = status[0]; - switch (protocol) { + switch (egprotocol) { case "EG_PROTO_V20": statusOn = STATE_ON; statusOff = STATE_OFF; @@ -328,12 +318,7 @@ public void stateUpdate(byte[] status) { break; } stat = status[i]; - - StringBuilder sbStatus = new StringBuilder(); - sbStatus.append(String.format("%02x", stat)); - String stringStatus = sbStatus.toString(); - stringStatus = "0x" + stringStatus; - + String stringStatus = String.format("0x%02x", stat); if (stringStatus.equals(statusOn)) { updateState(socket, OnOffType.ON); } else if (stringStatus.equals(statusOff)) { @@ -345,12 +330,6 @@ public void stateUpdate(byte[] status) { private synchronized void onUpdate() { if (refreshJob == null || refreshJob.isCancelled()) { - Configuration config = getThing().getConfiguration(); - int refreshInterval = DEFAULT_REFRESH_INTERVAL; - Object refreshConfig = config.get("refresh"); - if (refreshConfig != null) { - refreshInterval = ((BigDecimal) refreshConfig).intValue(); - } refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 5, refreshInterval, TimeUnit.SECONDS); } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 0841b9cb1df06..45065ecdd8d3b 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -15,15 +15,14 @@ import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; -import java.math.BigDecimal; import java.nio.charset.Charset; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -33,6 +32,7 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,25 +47,16 @@ public class EnergeniePWMHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(EnergeniePWMHandler.class); + private static final int HTTP_TIMEOUT_MILLISECONDS = 6000; + + private @Nullable EnergenieConfiguration config; + private String host = ""; private String password = ""; + private int refreshInterval; - /** the timeout to use for connecting to a given host (defaults to 5000 milliseconds) */ - private int timeout = 6000; - - /** - * The default refresh interval in Seconds. - */ - private int DEFAULT_REFRESH_INTERVAL = 60; @Nullable private ScheduledFuture refreshJob; - private Runnable refreshRunnable = new Runnable() { - - @Override - public void run() { - getState(); - } - }; public EnergeniePWMHandler(Thing thing) { super(thing); @@ -78,11 +69,14 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - Configuration config = getConfig(); + EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); + + this.config = config; - if ((config.get("host") != null) && (config.get("password") != null)) { - host = (String) config.get("host"); - password = (String) config.get("password"); + if (config.host != null && config.password != null) { + host = config.host; + password = config.password; + refreshInterval = config.DEFAULT_REFRESH_INTERVAL; logger.debug("Initializing EnergeniePWMHandler for Host '{}'", host); updateStatus(ThingStatus.ONLINE); onUpdate(); @@ -110,7 +104,7 @@ public void getState() { try { logger.trace("sendlogin to {} with password {}", host, password); logger.trace("sending 'POST' request to URL : {}", url); - loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", timeout); + loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", HTTP_TIMEOUT_MILLISECONDS); if (loginResponseString != null) { readState(loginResponseString, "voltage"); @@ -118,16 +112,16 @@ public void getState() { readState(loginResponseString, "power"); readState(loginResponseString, "energy"); try { - HttpUtil.executeUrl("POST", url, timeout); + HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); logger.trace("logout from ip {}", host); - } catch (Exception e) { - logger.error("failed to logout from ip {}", host); + } catch (IOException e) { + logger.debug("failed to logout from {} with ip {}", thing.getUID(), host, e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } - } catch (Exception e) { - logger.error("energenie: failed to login to ip {}", host); + } catch (IOException e) { + logger.debug("energenie: failed to login to {} with ip {}", thing.getUID(), host, e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } @@ -188,13 +182,7 @@ public void readState(String loginResponseString, String channel) { private synchronized void onUpdate() { if (refreshJob == null || refreshJob.isCancelled()) { - Configuration config = getThing().getConfiguration(); - int refreshInterval = DEFAULT_REFRESH_INTERVAL; - Object refreshConfig = config.get("refresh"); - if (refreshConfig != null) { - refreshInterval = ((BigDecimal) refreshConfig).intValue(); - } - refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 5, refreshInterval, TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(this::getState, 5, refreshInterval, TimeUnit.SECONDS); } } diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml new file mode 100644 index 0000000000000..0054b2ca4706e --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml @@ -0,0 +1,21 @@ + + + + + + + The IP address of this device + network-address + + + + The password of this device + 1 + password + + + + \ No newline at end of file diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties deleted file mode 100644 index 3613da9f1806b..0000000000000 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/i18n/energenie_xx_XX.properties +++ /dev/null @@ -1,17 +0,0 @@ -# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE -# FIXME: please do not add the file to the repo if you add or change no content -# binding -binding.energenie.name = -binding.energenie.description = - -# thing types -thing-type.energenie.sample.label = -thing-type.energenie.sample.description = - -# thing type config description -thing-type.config.energenie.sample.config1.label = -thing-type.config.energenie.sample.config1.description = - -# channel types -channel-type.energenie.sample-channel.label = -channel-type.energenie.sample-channel.description = diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml index 7c515505b1477..02d39084d61a2 100644 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml @@ -5,107 +5,64 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - Gembird energenie programmable power strip with LAN interface + + Energenie programmable power strip with LAN interface - - - - - - + + + + + - - - - The IP address of this device - - - - The password of this device - 1 - - + - - Gembird energenie programmable power strip with LAN interface + + Energenie programmable power strip with LAN interface - - - - - - + + + + + - - - - The IP address of this device - - - - The password of this device - 1 - - + - - Gembird energenie programmable power strip with LAN interface + + Energenie programmable power strip with LAN interface - - - - - - + + + + + - - - - The IP address of this device - - - - The password of this device - 1 - - + - - Gembird energenie programmable power strip with WLAN interface + + Energenie programmable power strip with WLAN interface - - - - - - + + + + + - - - - The IP address of this device - - - - The password of this device - 1 - - + + - - Gembird energenie energy meter with LAN interface + + Energenie energy meter with LAN interface @@ -114,15 +71,21 @@ - - - - The IP address of this device - - + + + + A Energenie relay channel + + + + + + + + Switch @@ -148,27 +111,31 @@ - Number + Number:ElectricPotential Channel representing the voltage + - Number + Number:ElectricCurrent Channel representing the current + - Number + Number:Power Channel representing the power consumption + - Number + Number:Energy Channel representing the energy consumption + From 7d7739e9b942c79ddd7818a196744d0bfae75d32 Mon Sep 17 00:00:00 2001 From: Hilbrand Bouwkamp Date: Thu, 28 Nov 2019 12:28:51 +0100 Subject: [PATCH 03/22] Use enum of PMWState Signed-off-by: Hilbrand Bouwkamp --- .../internal/handler/EnergeniePWMHandler.java | 133 ++++++++++-------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 45065ecdd8d3b..188d5c38a9eb9 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.energenie.internal.handler; -import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -21,9 +19,12 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -31,6 +32,7 @@ import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.io.net.http.HttpUtil; import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; import org.slf4j.Logger; @@ -45,6 +47,69 @@ @NonNullByDefault public class EnergeniePWMHandler extends BaseThingHandler { + public enum PWMState { + VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), + CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), + POWER("var P=", 6, 20, 466, SmartHomeUnits.WATT), + ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.KILOWATT_HOUR); + + private final Logger logger = LoggerFactory.getLogger(PWMState.class); + + private final String stateResponseSearch; + private final int start; + private final int stop; + private final int divisor; + private final Unit unit; + + private PWMState(final String stateResponseSearch, final int start, final int stop, final int divisor, + final Unit unit) { + this.stateResponseSearch = stateResponseSearch; + this.start = start; + this.stop = stop; + this.divisor = divisor; + this.unit = unit; + } + + public String getStateResponseSearch() { + return stateResponseSearch; + } + + public int getStart() { + return start; + } + + public int getStop() { + return stop; + } + + public int getDivisor() { + return divisor; + } + + public State readState(final String loginResponseString) { + final int findState = loginResponseString.lastIndexOf(stateResponseSearch); + + if (findState > 0) { + logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); + final String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); + logger.trace("transformed state response = {}", slicedResponseTmp); + final String[] slicedResponse = slicedResponseTmp.split(";"); + logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); + final double value; + + if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { + value = Double.parseDouble(slicedResponse[0]) / divisor; + } else { + value = -1.0; + } + return QuantityType.valueOf(value, unit); + } else { + logger.trace("searchstring '{} not found", stateResponseSearch); + return UnDefType.UNDEF; + } + } + } + private final Logger logger = LoggerFactory.getLogger(EnergeniePWMHandler.class); private static final int HTTP_TIMEOUT_MILLISECONDS = 6000; @@ -107,10 +172,10 @@ public void getState() { loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", HTTP_TIMEOUT_MILLISECONDS); if (loginResponseString != null) { - readState(loginResponseString, "voltage"); - readState(loginResponseString, "current"); - readState(loginResponseString, "power"); - readState(loginResponseString, "energy"); + updateState("voltage", PWMState.VOLTAGE.readState(loginResponseString)); + updateState("current", PWMState.CURRENT.readState(loginResponseString)); + updateState("power", PWMState.POWER.readState(loginResponseString)); + updateState("energy", PWMState.ENERGY.readState(loginResponseString)); try { HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); logger.trace("logout from ip {}", host); @@ -126,60 +191,6 @@ public void getState() { } } - public void readState(String loginResponseString, String channel) { - String stateResponseSearch = ""; - int start = 0, stop = 0, divisor = 1; - double value = 0.0; - - switch (channel) { - case VOLTAGE: - stateResponseSearch = "var V = "; - start = 9; - stop = 20; - divisor = 10; - - break; - case CURRENT: - stateResponseSearch = "var I = "; - start = 9; - stop = 20; - divisor = 100; - break; - case POWER: - stateResponseSearch = "var P="; - start = 6; - stop = 20; - divisor = 466; - break; - case ENERGY: - stateResponseSearch = "var E="; - start = 6; - stop = 20; - divisor = 25600; - break; - } - - int findState = loginResponseString.lastIndexOf(stateResponseSearch); - if (findState > 0) { - logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); - String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); - logger.trace("transformed state response = {}", slicedResponseTmp); - String[] slicedResponse = slicedResponseTmp.split(";"); - logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); - if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { - value = Double.parseDouble(slicedResponse[0]) / divisor; - } else { - value = -1.0; - } - State valueState = new DecimalType(value); - updateState(channel, valueState); - - } else { - logger.trace("searchstring '{} not found", stateResponseSearch); - } - - } - private synchronized void onUpdate() { if (refreshJob == null || refreshJob.isCancelled()) { refreshJob = scheduler.scheduleWithFixedDelay(this::getState, 5, refreshInterval, TimeUnit.SECONDS); From a970195cbf0151e731033ab26c34863fba3e763d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Thu, 28 Nov 2019 13:58:46 +0100 Subject: [PATCH 04/22] Update EnergeniePWMHandler.java --- .../binding/energenie/internal/handler/EnergeniePWMHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 188d5c38a9eb9..440ba5ef6d177 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -51,7 +51,7 @@ public enum PWMState { VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), POWER("var P=", 6, 20, 466, SmartHomeUnits.WATT), - ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.KILOWATT_HOUR); + ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.WATT_HOUR); private final Logger logger = LoggerFactory.getLogger(PWMState.class); From c9fc2a103be7fe2fcd1acac54675d6ab3b98f0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Sat, 30 Nov 2019 13:29:45 +0100 Subject: [PATCH 05/22] Changes after second review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../org.openhab.binding.energenie/README.md | 12 +++++------ .../internal/EnergenieHandlerFactory.java | 10 +++------- .../config/EnergenieConfiguration.java | 7 +++---- .../internal/handler/EnergenieHandler.java | 20 +++---------------- .../internal/handler/EnergeniePWMHandler.java | 6 +++--- .../resources/ESH-INF/thing/thing-types.xml | 16 +++++++-------- 6 files changed, 26 insertions(+), 45 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/README.md b/bundles/org.openhab.binding.energenie/README.md index f0bc49aff79b4..9b8fc85bc78ee 100644 --- a/bundles/org.openhab.binding.energenie/README.md +++ b/bundles/org.openhab.binding.energenie/README.md @@ -37,12 +37,12 @@ The following channels are supported by PM2-LAN, PMS-LAN, PMS2-LAN or PMS-WLAN d PWM-LAN devices support the following channels -| channel | type | description | -|----------|--------|------------------------------------------| -| voltage | Number | Channel for output voltage measurement | -| current | Number | Channel for output current measurement | -| power | Number | Channel for output power measurement | -| energy | Number | channel for output energy measurement | +| channel | type | description | +|----------|--------------------------|------------------------------------------| +| voltage | Number:ElectricPotential | Channel for output voltage measurement | +| current | Number:ElectricCurrent | Channel for output current measurement | +| power | Number:Power | Channel for output power measurement | +| energy | Number:Energy | channel for output energy measurement | ## Full Example diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java index 4e92a30bdc5fb..0c1df7ae61e88 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java @@ -50,17 +50,13 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (THING_TYPE_PMSLAN.equals(thingTypeUID)) { return new EnergenieHandler(thing, "EG_PROTO_V20"); - } - if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { + } else if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { return new EnergenieHandler(thing, "EG_PROTO_V21"); - } - if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { + } else if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { return new EnergenieHandler(thing, "EG_PROTO_WLAN"); - } - if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { + } else if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { return new EnergeniePWMHandler(thing); } - return null; } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java index e577a40ea0e9c..e5519a601d677 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java @@ -18,12 +18,11 @@ * @author Hans-Jörg Merk - Initial contribution */ public class EnergenieConfiguration { - - public String host = ""; - public String password = ""; /** * The default refresh interval in Seconds. */ - public int DEFAULT_REFRESH_INTERVAL = 60; + public static final int DEFAULT_REFRESH_INTERVAL = 60; + public String host = ""; + public String password = ""; } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 52129e1a1944c..ffe752aeba394 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -46,6 +46,7 @@ @NonNullByDefault public class EnergenieHandler extends BaseThingHandler { + private static final String CHANNEL_SOCKET_PREFIX = "socket"; private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class); private String egprotocol = "EG_PROTO_V20"; @@ -137,7 +138,7 @@ public void initialize() { if (config.host != null && config.password != null) { host = config.host; password = config.password; - refreshInterval = config.DEFAULT_REFRESH_INTERVAL; + refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; key = getKey(); logger.debug("Initializing EnergenieHandler for Host '{}'", config.host); @@ -300,23 +301,8 @@ public void stateUpdate(byte[] status) { statusOff = WLAN_STATE_OFF; break; } - for (int i = 0; i < 4; i++) { - String socket = SOCKET_1; - switch (i) { - case 0: - socket = SOCKET_1; - break; - case 1: - socket = SOCKET_2; - break; - case 2: - socket = SOCKET_3; - break; - case 3: - socket = SOCKET_4; - break; - } + String socket = CHANNEL_SOCKET_PREFIX + (i + 1); stat = status[i]; String stringStatus = String.format("0x%02x", stat); if (stringStatus.equals(statusOn)) { diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 440ba5ef6d177..824f7e8ad8575 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -15,7 +15,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -141,7 +141,7 @@ public void initialize() { if (config.host != null && config.password != null) { host = config.host; password = config.password; - refreshInterval = config.DEFAULT_REFRESH_INTERVAL; + refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; logger.debug("Initializing EnergeniePWMHandler for Host '{}'", host); updateStatus(ThingStatus.ONLINE); onUpdate(); @@ -163,7 +163,7 @@ public void dispose() { public void getState() { String url = "http://" + host + "/login.html"; String urlParameters = "pw=" + password; - InputStream urlContent = new ByteArrayInputStream(urlParameters.getBytes(Charset.forName("UTF-8"))); + InputStream urlContent = new ByteArrayInputStream(urlParameters.getBytes(StandardCharsets.UTF_8)); String loginResponseString = null; try { diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml index 02d39084d61a2..e09a56f59ab5e 100644 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml @@ -19,7 +19,7 @@ - + Energenie programmable power strip with LAN interface @@ -33,7 +33,7 @@ - + Energenie programmable power strip with LAN interface @@ -47,7 +47,7 @@ - + Energenie programmable power strip with WLAN interface @@ -61,7 +61,7 @@ - + Energenie energy meter with LAN interface @@ -112,28 +112,28 @@ Number:ElectricPotential - + Channel representing the voltage Number:ElectricCurrent - + Channel representing the current Number:Power - + Channel representing the power consumption Number:Energy - + Channel representing the energy consumption From 4640098471f000251750eb75dcaa8bb521ae974b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Sat, 30 Nov 2019 15:14:39 +0100 Subject: [PATCH 06/22] Fix channels in XML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../resources/ESH-INF/thing/thing-types.xml | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml index e09a56f59ab5e..b3427d036c6e2 100644 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml @@ -8,11 +8,12 @@ Energenie programmable power strip with LAN interface - - - - - + + + + + + @@ -22,11 +23,12 @@ Energenie programmable power strip with LAN interface - - - - - + + + + + + @@ -36,11 +38,12 @@ Energenie programmable power strip with LAN interface - - - - - + + + + + + @@ -50,11 +53,12 @@ Energenie programmable power strip with WLAN interface - - - - - + + + + + + @@ -75,17 +79,6 @@ - - - A Energenie relay channel - - - - - - - - Switch From 080585140522e403738f9f2fa46a5b00326afad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Tue, 14 Jan 2020 15:06:14 +0100 Subject: [PATCH 07/22] Minor correction to thing-types.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../org.openhab.binding.energenie/.classpath | 32 ++ .../org.openhab.binding.energenie/.project | 23 + .../org.openhab.binding.energenie/README.md | 2 +- bundles/org.openhab.binding.energenie/pom.xml | 2 +- .../internal/EnergenieBindingConstants.java | 163 +++--- .../internal/EnergenieHandlerFactory.java | 124 ++--- .../config/EnergenieConfiguration.java | 59 +- .../internal/handler/EnergenieHandler.java | 506 +++++++----------- .../internal/handler/EnergeniePWMHandler.java | 400 +++++++------- .../internal/handler/EnergenieSocket.java | 211 ++++++++ .../resources/ESH-INF/binding/binding.xml | 20 +- .../main/resources/ESH-INF/config/config.xml | 4 +- .../resources/ESH-INF/thing/thing-types.xml | 268 +++++----- 13 files changed, 968 insertions(+), 846 deletions(-) create mode 100644 bundles/org.openhab.binding.energenie/.classpath create mode 100644 bundles/org.openhab.binding.energenie/.project create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java diff --git a/bundles/org.openhab.binding.energenie/.classpath b/bundles/org.openhab.binding.energenie/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.energenie/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.energenie/.project b/bundles/org.openhab.binding.energenie/.project new file mode 100644 index 0000000000000..32000eb2789d5 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.energenie + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.energenie/README.md b/bundles/org.openhab.binding.energenie/README.md index 9b8fc85bc78ee..df17d638212fc 100644 --- a/bundles/org.openhab.binding.energenie/README.md +++ b/bundles/org.openhab.binding.energenie/README.md @@ -56,6 +56,7 @@ Thing energenie:pms2lan:pms2lan [ host="xxx.xxx.xxx.xxx", password="your passwor Thing energenie:pmswlan:pmswlan [ host="xxx.xxx.xxx.xxx", password="your password" ] Thing energenie:pwmlan:pwmlan [ host="xxx.xxx.xxx.xxx", password="your password" ] ``` + Items ``` @@ -92,4 +93,3 @@ sitemap energenie label="Energenie Devices" } } ``` - \ No newline at end of file diff --git a/bundles/org.openhab.binding.energenie/pom.xml b/bundles/org.openhab.binding.energenie/pom.xml index ec4e2910cd93b..16409e6c8e613 100644 --- a/bundles/org.openhab.binding.energenie/pom.xml +++ b/bundles/org.openhab.binding.energenie/pom.xml @@ -6,7 +6,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.0-SNAPSHOT + 3.0.0-SNAPSHOT org.openhab.binding.energenie diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java index 232bf35ce9440..fa40899f741b5 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieBindingConstants.java @@ -1,84 +1,79 @@ -/** - * Copyright (c) 2010-2019 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.energenie.internal; - -import java.util.Collections; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * The {@link EnergenieBindingConstants} class defines common constants, which are - * used across the whole binding. - * - * @author Hans-Jörg Merk - Initial contribution - */ -@NonNullByDefault -public class EnergenieBindingConstants { - - private static final String BINDING_ID = "energenie"; - - // private static final String HOST = "host"; - public static final int TCP_PORT = 5000; - - public static final int STRTCRYP_LEN = 4; - public static final int CTRLCRYP_LEN = 4; - public static final int KEY_LEN = 8; - public static final int TASK_LEN = 4; - public static final int SOLUTION_LEN = 4; - - public static final String STATE_ON = "0x11"; - public static final String STATE_ON_NO_VOLTAGE = "0x12"; - public static final String STATE_OFF = "0x22"; - public static final String STATE_OFF_NO_VOLTAGE = "0x21"; - - // public static final byte STATE_INVALID = 0xFF; // for internal use - - public static final String V21_STATE_ON = "0x41"; - public static final String V21_STATE_OFF = "0x82"; - - public static final String WLAN_STATE_ON = "0x51"; - public static final String WLAN_STATE_OFF = "0x92"; - - public static final byte SWITCH_ON = 0x01; - public static final byte SWITCH_OFF = 0x02; - public static final byte DONT_SWITCH = 0x04; - - public static final int SOCKET_COUNT = 4; // AC power sockets, not network ones - - // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_PM2LAN = new ThingTypeUID(BINDING_ID, "pm2lan"); - public static final ThingTypeUID THING_TYPE_PMSLAN = new ThingTypeUID(BINDING_ID, "pmslan"); - public static final ThingTypeUID THING_TYPE_PMS2LAN = new ThingTypeUID(BINDING_ID, "pms2lan"); - public static final ThingTypeUID THING_TYPE_PMSWLAN = new ThingTypeUID(BINDING_ID, "pmswlan"); - public static final ThingTypeUID THING_TYPE_PWMLAN = new ThingTypeUID(BINDING_ID, "pwmlan"); - - // List of all Channel ids - public static final String SOCKET_1 = "socket1"; - public static final String SOCKET_2 = "socket2"; - public static final String SOCKET_3 = "socket3"; - public static final String SOCKET_4 = "socket4"; - - public static final String VOLTAGE = "voltage"; - public static final String CURRENT = "current"; - public static final String POWER = "power"; - public static final String ENERGY = "energy"; - - public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( - Stream.of(THING_TYPE_PM2LAN, THING_TYPE_PMSLAN, THING_TYPE_PMS2LAN, THING_TYPE_PMSLAN, THING_TYPE_PWMLAN) - .collect(Collectors.toSet())); - -} +/** + * Copyright (c) 2010-2020 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.energenie.internal; + +import java.util.Collections; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link EnergenieBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +public class EnergenieBindingConstants { + + private static final String BINDING_ID = "energenie"; + + public static final int TCP_PORT = 5000; + + public static final int STATCRYP_LEN = 4; + public static final int CTRLCRYP_LEN = 4; + public static final int KEY_LEN = 8; + public static final int TASK_LEN = 4; + public static final int SOLUTION_LEN = 4; + + public static final String STATE_ON = "0x11"; + public static final String STATE_ON_NO_VOLTAGE = "0x12"; + public static final String STATE_OFF = "0x22"; + public static final String STATE_OFF_NO_VOLTAGE = "0x21"; + + public static final String V21_STATE_ON = "0x41"; + public static final String V21_STATE_OFF = "0x82"; + + public static final String WLAN_STATE_ON = "0x51"; + public static final String WLAN_STATE_OFF = "0x92"; + + public static final byte SWITCH_ON = 0x01; + public static final byte SWITCH_OFF = 0x02; + public static final byte DONT_SWITCH = 0x04; + + public static final int SOCKET_COUNT = 4; // AC power sockets, not network ones + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_PM2LAN = new ThingTypeUID(BINDING_ID, "pm2lan"); + public static final ThingTypeUID THING_TYPE_PMSLAN = new ThingTypeUID(BINDING_ID, "pmslan"); + public static final ThingTypeUID THING_TYPE_PMS2LAN = new ThingTypeUID(BINDING_ID, "pms2lan"); + public static final ThingTypeUID THING_TYPE_PMSWLAN = new ThingTypeUID(BINDING_ID, "pmswlan"); + public static final ThingTypeUID THING_TYPE_PWMLAN = new ThingTypeUID(BINDING_ID, "pwmlan"); + + // List of all Channel ids + public static final Pattern CHANNEL_SOCKET = Pattern.compile("socket(\\d)"); + + public static final String VOLTAGE = "voltage"; + public static final String CURRENT = "current"; + public static final String POWER = "power"; + public static final String ENERGY = "energy"; + + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( + Stream.of(THING_TYPE_PM2LAN, THING_TYPE_PMSLAN, THING_TYPE_PMS2LAN, THING_TYPE_PMSLAN, THING_TYPE_PWMLAN) + .collect(Collectors.toSet())); + +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java index 0c1df7ae61e88..b75d89e00ef33 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java @@ -1,62 +1,62 @@ -/** - * Copyright (c) 2010-2019 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.energenie.internal; - -import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; - -import java.util.Set; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.openhab.binding.energenie.internal.handler.EnergenieHandler; -import org.openhab.binding.energenie.internal.handler.EnergeniePWMHandler; -import org.osgi.service.component.annotations.Component; - -/** - * The {@link EnergenieHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author Hans-Jörg Merk - Initial contribution - */ -@NonNullByDefault -@Component(configurationPid = "binding.energenie", service = ThingHandlerFactory.class) -public class EnergenieHandlerFactory extends BaseThingHandlerFactory { - - private static final Set SUPPORTED_THING_TYPES_UIDS = EnergenieBindingConstants.SUPPORTED_THING_TYPES_UIDS; - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Override - protected @Nullable ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (THING_TYPE_PMSLAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing, "EG_PROTO_V20"); - } else if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing, "EG_PROTO_V21"); - } else if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing, "EG_PROTO_WLAN"); - } else if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { - return new EnergeniePWMHandler(thing); - } - return null; - } -} +/** + * Copyright (c) 2010-2020 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.energenie.internal; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.energenie.internal.handler.EnergenieHandler; +import org.openhab.binding.energenie.internal.handler.EnergeniePWMHandler; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link EnergenieHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.energenie", service = ThingHandlerFactory.class) +public class EnergenieHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = EnergenieBindingConstants.SUPPORTED_THING_TYPES_UIDS; + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_PMSLAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, "EG_PROTO_V20"); + } else if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, "EG_PROTO_V21"); + } else if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, "EG_PROTO_WLAN"); + } else if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { + return new EnergeniePWMHandler(thing); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java index e5519a601d677..b2c74434c7fcf 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/config/EnergenieConfiguration.java @@ -1,28 +1,31 @@ -/** - * Copyright (c) 2010-2019 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.energenie.internal.config; - -/** - * The {@link EnergenieConfiguration} class contains fields mapping thing configuration parameters. - * - * @author Hans-Jörg Merk - Initial contribution - */ -public class EnergenieConfiguration { - /** - * The default refresh interval in Seconds. - */ - public static final int DEFAULT_REFRESH_INTERVAL = 60; - - public String host = ""; - public String password = ""; -} +/** + * Copyright (c) 2010-2020 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.energenie.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link EnergenieConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +public class EnergenieConfiguration { + /** + * The default refresh interval in Seconds. + */ + public static final int DEFAULT_REFRESH_INTERVAL = 60; + + public String host = ""; + public String password = ""; +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index ffe752aeba394..9b500eaee8b49 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -1,323 +1,183 @@ -/** - * Copyright (c) 2010-2019 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.energenie.internal.handler; - -import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.RefreshType; -import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link EnergenieHandler} is responsible for handling commands, which are - * sent to one of the channels. - * - * @author Hans-Jörg Merk - Initial contribution - */ -@NonNullByDefault -public class EnergenieHandler extends BaseThingHandler { - - private static final String CHANNEL_SOCKET_PREFIX = "socket"; - private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class); - - private String egprotocol = "EG_PROTO_V20"; - private String host = ""; - private String password = ""; - private int refreshInterval; - - private @Nullable EnergenieConfiguration config; - - private @Nullable Socket socket = null; - private @Nullable OutputStream output = null; - private @Nullable InputStream input = null; - - private byte[] key = new byte[KEY_LEN]; - private byte[] task = new byte[TASK_LEN]; - private byte[] solution = new byte[SOLUTION_LEN]; - - @Nullable - private ScheduledFuture refreshJob; - private Runnable refreshRunnable = new Runnable() { - - @Override - public void run() { - try { - socket = new Socket(host, TCP_PORT); - socket.setSoTimeout(1500); - updateStatus(ThingStatus.ONLINE); - output = socket.getOutputStream(); - input = socket.getInputStream(); - authorize(); - socket.close(); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE); - logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); - } - } - }; - - public EnergenieHandler(Thing thing, String protocol) { - super(thing); - egprotocol = protocol; - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - if (command instanceof RefreshType) { - // refresh not supported - } else if (command instanceof OnOffType) { - byte[] ctrl = { DONT_SWITCH, DONT_SWITCH, DONT_SWITCH, DONT_SWITCH }; - switch (channelUID.getId()) { - case SOCKET_1: - ctrl[0] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; - break; - case SOCKET_2: - ctrl[1] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; - break; - case SOCKET_3: - ctrl[2] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; - break; - case SOCKET_4: - ctrl[3] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; - break; - } - String ctrlString = getByteString(ctrl); - logger.trace("byte control (int) '{}' (hex)'{}'", ctrl, ctrlString); - - try { - socket = new Socket(host, TCP_PORT); - socket.setSoTimeout(1500); - output = socket.getOutputStream(); - input = socket.getInputStream(); - authorize(); - byte[] controlmessage = encryptControls(ctrl); - output.write(controlmessage); - socket.close(); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE); - logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); - } - } - } - - @Override - public void initialize() { - EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); - - this.config = config; - - if (config.host != null && config.password != null) { - host = config.host; - password = config.password; - refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; - key = getKey(); - logger.debug("Initializing EnergenieHandler for Host '{}'", config.host); - - try { - socket = new Socket(host, TCP_PORT); - socket.setSoTimeout(1500); - updateStatus(ThingStatus.ONLINE); - output = socket.getOutputStream(); - input = socket.getInputStream(); - authorize(); - socket.close(); - onUpdate(); - - } catch (UnknownHostException e) { - updateStatus(ThingStatus.OFFLINE); - logger.debug("Can't find host: {}:{}.", host, TCP_PORT); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE); - logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); - } - - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Can not access device , IP-Address or password not set"); - } - } - - @Override - public void dispose() { - logger.debug("EnergenieHandler disposed."); - try { - if (socket != null) { - socket.close(); - } - } catch (IOException e) { - } - if (refreshJob != null && !refreshJob.isCancelled()) { - refreshJob.cancel(true); - refreshJob = null; - } - } - - public void authorize() { - byte[] message = { 0x11 }; - byte[] response = new byte[4]; - byte[] statcryp = new byte[4]; - - try { - if (output != null && input != null) { - output.write(message); - logger.trace("Start Condition '{}' send to EG", message); - input.read(response); - for (int i = 0; i < 4; i++) { - task[i] = response[i]; - } - String taskString = getByteString(task); - logger.trace("EG responded with task (int) '{}' (hex) '{}'", task, taskString); - - byte[] solutionMessage = calculateSolution(); - - output.write(solutionMessage); - logger.trace("Solution '{}' send to EG", solutionMessage); - - input.read(statcryp); - String statcrypString = getByteString(statcryp); - logger.trace("EG responded with statcryp (int) '{}' (hex) '{}'", statcryp, statcrypString); - - byte[] status = decryptStatus(statcryp); - - String statusString = getByteString(status); - logger.trace("EG responded with status (int) '{}' (hex) '{}'", status, statusString); - - stateUpdate(status); - } - } catch (UnknownHostException e) { - updateStatus(ThingStatus.OFFLINE); - logger.debug("Can't find host: {}:{}.", host, TCP_PORT); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE); - logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT); - } - } - - public byte[] getKey() { - int passwordLength = password.length(); - String passwordString = password; - for (int i = 0; i < (8 - passwordLength); i++) { - passwordString = passwordString + " "; - } - byte[] key = passwordString.getBytes(); - return key; - } - - public String getByteString(byte[] byteArray) { - StringBuilder sb = new StringBuilder(); - for (byte b : byteArray) { - sb.append(String.format("0x%02X ", b)); - } - String byteString = sb.toString(); - return byteString; - } - - public byte[] calculateSolution() { - int[] uIntTask = new int[4]; - for (int i = 0; i < 4; i++) { - uIntTask[i] = Byte.toUnsignedInt(task[i]); - } - - int solutionLoword = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); - - byte loword[] = ByteBuffer.allocate(4).putInt(solutionLoword).array(); - - int solutionHiword = (((uIntTask[1] ^ key[3]) * key[1]) ^ (key[7] | (key[5] << 8)) ^ uIntTask[3]); - byte hiword[] = ByteBuffer.allocate(4).putInt(solutionHiword).array(); - - solution[0] = loword[3]; - solution[1] = loword[2]; - solution[2] = hiword[3]; - solution[3] = hiword[2]; - - return solution; - } - - public byte[] decryptStatus(byte[] statcryp) { - byte[] status = new byte[4]; - int[] status_i = new int[4]; - for (int i = 0; i < 4; i++) { - status_i[i] = ((((statcryp[3 - i] - key[1]) ^ key[0]) - task[3]) ^ task[2]); - status[i] = (byte) status_i[i]; - } - return status; - } - - public byte[] encryptControls(byte[] controls) { - byte[] ctrlcryp = new byte[CTRLCRYP_LEN]; - int[] ctrlcryp_i = new int[CTRLCRYP_LEN]; - for (int i = 0; i < 4; i++) { - ctrlcryp_i[i] = (((controls[3 - i] ^ task[2]) + task[3]) ^ key[0]) + key[1]; - ctrlcryp[i] = (byte) ctrlcryp_i[i]; - } - - return ctrlcryp; - } - - public void stateUpdate(byte[] status) { - String statusOn = STATE_ON; - String statusOff = STATE_OFF; - byte stat = status[0]; - switch (egprotocol) { - case "EG_PROTO_V20": - statusOn = STATE_ON; - statusOff = STATE_OFF; - break; - case "EG_PROTO_V21": - statusOn = V21_STATE_ON; - statusOff = V21_STATE_OFF; - break; - case "EG_PROTO_WLAN": - statusOn = WLAN_STATE_ON; - statusOff = WLAN_STATE_OFF; - break; - } - for (int i = 0; i < 4; i++) { - String socket = CHANNEL_SOCKET_PREFIX + (i + 1); - stat = status[i]; - String stringStatus = String.format("0x%02x", stat); - if (stringStatus.equals(statusOn)) { - updateState(socket, OnOffType.ON); - } else if (stringStatus.equals(statusOff)) { - updateState(socket, OnOffType.OFF); - } - } - - } - - private synchronized void onUpdate() { - if (refreshJob == null || refreshJob.isCancelled()) { - refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 5, refreshInterval, TimeUnit.SECONDS); - } - } - -} +/** + * Copyright (c) 2010-2020 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.energenie.internal.handler; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.time.Duration; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.cache.ExpiringCache; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.util.ThingHandlerHelper; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link EnergenieHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Hans-Jörg Merk - Initial contribution + */ +@NonNullByDefault +public class EnergenieHandler extends BaseThingHandler { + + private static final String CHANNEL_SOCKET_PREFIX = "socket"; + private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class); + + /** + * Use cache for refresh command to not update again when call is made within 2 seconds of previous call. + */ + private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(2), this::refreshState); + private final String statusOn; + private final String statusOff; + + private @Nullable EnergenieSocket energenieSocket; + private @Nullable ScheduledFuture refreshJob; + + private int refreshInterval; + private @Nullable String host; + + public EnergenieHandler(final Thing thing, final String protocol) { + super(thing); + switch (protocol) { + case "EG_PROTO_V20": + statusOn = STATE_ON; + statusOff = STATE_OFF; + break; + case "EG_PROTO_V21": + statusOn = V21_STATE_ON; + statusOff = V21_STATE_OFF; + break; + case "EG_PROTO_WLAN": + statusOn = WLAN_STATE_ON; + statusOff = WLAN_STATE_OFF; + break; + default: + statusOn = STATE_ON; + statusOff = STATE_OFF; + break; + } + } + + @Override + public void handleCommand(final ChannelUID channelUID, final Command command) { + if (command instanceof RefreshType) { + refreshCache.getValue(); + } else if (command instanceof OnOffType) { + final byte[] ctrl = { DONT_SWITCH, DONT_SWITCH, DONT_SWITCH, DONT_SWITCH }; + final Matcher matcher = CHANNEL_SOCKET.matcher(channelUID.getId()); + + try { + boolean found = false; + + if (matcher.find()) { + final int index = Integer.parseInt(matcher.group(1)) - 1; + + if (index >= 0 && index < SOCKET_COUNT) { + ctrl[index] = OnOffType.ON.equals(command) ? SWITCH_ON : SWITCH_OFF; + stateUpdate(energenieSocket.sendCommand(ctrl)); + found = true; + } + } + if (!found) { + logger.debug("Invalid channel id {}, should be value between 1 and {}", channelUID, SOCKET_COUNT); + } + } catch (final IOException e) { + updateStatus(ThingStatus.OFFLINE); + logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT, e); + } + } + } + + @Override + public void initialize() { + final EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); + + if (config.host != null && config.password != null) { + refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; + host = config.host; + logger.debug("Initializing EnergenieHandler for Host '{}'", config.host); + energenieSocket = new EnergenieSocket(config.host, config.password); + + updateStatus(ThingStatus.UNKNOWN); + refreshJob = scheduler.scheduleWithFixedDelay(this::refreshState, 5, refreshInterval, TimeUnit.SECONDS); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Can not access device , IP-Address or password not set"); + } + } + + @Override + public void dispose() { + logger.debug("EnergenieHandler disposed."); + final ScheduledFuture refreshJob = this.refreshJob; + + if (refreshJob != null && !refreshJob.isCancelled()) { + refreshJob.cancel(true); + this.refreshJob = null; + } + } + + private @Nullable Boolean refreshState() { + final EnergenieSocket socket = this.energenieSocket; + + if (socket != null) { + try { + stateUpdate(socket.retrieveStatus()); + if (thing.getStatus() != ThingStatus.ONLINE && ThingHandlerHelper.isHandlerInitialized(thing)) { + updateStatus(ThingStatus.ONLINE); + } + return Boolean.TRUE; + } catch (final UnknownHostException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Can't find host"); + logger.debug("Can't find host: {}:{}.", host, TCP_PORT, e); + } catch (final IOException e) { + logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT, e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Couldn't get I/O for the connection"); + } catch (final RuntimeException e) { + logger.debug("Unexpected error", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage()); + } + } + return null; + } + + public void stateUpdate(final byte[] status) { + for (int i = 0; i < 4; i++) { + final String socket = CHANNEL_SOCKET_PREFIX + (i + 1); + final String stringStatus = String.format("0x%02x", status[i]); + + if (stringStatus.equals(statusOn)) { + updateState(socket, OnOffType.ON); + } else if (stringStatus.equals(statusOff)) { + updateState(socket, OnOffType.OFF); + } + } + } +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 824f7e8ad8575..9412c950760fd 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -1,200 +1,200 @@ -/** - * Copyright (c) 2010-2019 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.energenie.internal.handler; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.QuantityType; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.State; -import org.eclipse.smarthome.core.types.UnDefType; -import org.eclipse.smarthome.io.net.http.HttpUtil; -import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link EnergeniePWMHandler} is responsible for reading states and update PWM channels. - * - * @author Hans-Jörg Merk - Initial contribution - */ - -@NonNullByDefault -public class EnergeniePWMHandler extends BaseThingHandler { - - public enum PWMState { - VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), - CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), - POWER("var P=", 6, 20, 466, SmartHomeUnits.WATT), - ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.WATT_HOUR); - - private final Logger logger = LoggerFactory.getLogger(PWMState.class); - - private final String stateResponseSearch; - private final int start; - private final int stop; - private final int divisor; - private final Unit unit; - - private PWMState(final String stateResponseSearch, final int start, final int stop, final int divisor, - final Unit unit) { - this.stateResponseSearch = stateResponseSearch; - this.start = start; - this.stop = stop; - this.divisor = divisor; - this.unit = unit; - } - - public String getStateResponseSearch() { - return stateResponseSearch; - } - - public int getStart() { - return start; - } - - public int getStop() { - return stop; - } - - public int getDivisor() { - return divisor; - } - - public State readState(final String loginResponseString) { - final int findState = loginResponseString.lastIndexOf(stateResponseSearch); - - if (findState > 0) { - logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); - final String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); - logger.trace("transformed state response = {}", slicedResponseTmp); - final String[] slicedResponse = slicedResponseTmp.split(";"); - logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); - final double value; - - if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { - value = Double.parseDouble(slicedResponse[0]) / divisor; - } else { - value = -1.0; - } - return QuantityType.valueOf(value, unit); - } else { - logger.trace("searchstring '{} not found", stateResponseSearch); - return UnDefType.UNDEF; - } - } - } - - private final Logger logger = LoggerFactory.getLogger(EnergeniePWMHandler.class); - - private static final int HTTP_TIMEOUT_MILLISECONDS = 6000; - - private @Nullable EnergenieConfiguration config; - - private String host = ""; - private String password = ""; - private int refreshInterval; - - @Nullable - private ScheduledFuture refreshJob; - - public EnergeniePWMHandler(Thing thing) { - super(thing); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - // PWM devices don't support any commands, just value reading - } - - @Override - public void initialize() { - EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); - - this.config = config; - - if (config.host != null && config.password != null) { - host = config.host; - password = config.password; - refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; - logger.debug("Initializing EnergeniePWMHandler for Host '{}'", host); - updateStatus(ThingStatus.ONLINE); - onUpdate(); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Can not access device , IP-Address or password not set"); - } - } - - @Override - public void dispose() { - logger.debug("EnergeniePWMHandler disposed."); - if (refreshJob != null && !refreshJob.isCancelled()) { - refreshJob.cancel(true); - refreshJob = null; - } - } - - public void getState() { - String url = "http://" + host + "/login.html"; - String urlParameters = "pw=" + password; - InputStream urlContent = new ByteArrayInputStream(urlParameters.getBytes(StandardCharsets.UTF_8)); - String loginResponseString = null; - - try { - logger.trace("sendlogin to {} with password {}", host, password); - logger.trace("sending 'POST' request to URL : {}", url); - loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", HTTP_TIMEOUT_MILLISECONDS); - - if (loginResponseString != null) { - updateState("voltage", PWMState.VOLTAGE.readState(loginResponseString)); - updateState("current", PWMState.CURRENT.readState(loginResponseString)); - updateState("power", PWMState.POWER.readState(loginResponseString)); - updateState("energy", PWMState.ENERGY.readState(loginResponseString)); - try { - HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); - logger.trace("logout from ip {}", host); - } catch (IOException e) { - logger.debug("failed to logout from {} with ip {}", thing.getUID(), host, e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); - } - } - - } catch (IOException e) { - logger.debug("energenie: failed to login to {} with ip {}", thing.getUID(), host, e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); - } - } - - private synchronized void onUpdate() { - if (refreshJob == null || refreshJob.isCancelled()) { - refreshJob = scheduler.scheduleWithFixedDelay(this::getState, 5, refreshInterval, TimeUnit.SECONDS); - } - } - -} +/** + * Copyright (c) 2010-2020 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.energenie.internal.handler; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link EnergeniePWMHandler} is responsible for reading states and update PWM channels. + * + * @author Hans-Jörg Merk - Initial contribution + */ + +@NonNullByDefault +public class EnergeniePWMHandler extends BaseThingHandler { + + public enum PWMState { + VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), + CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), + POWER("var P=", 6, 20, 466, SmartHomeUnits.WATT), + ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.WATT_HOUR); + + private final Logger logger = LoggerFactory.getLogger(PWMState.class); + + private final String stateResponseSearch; + private final int start; + private final int stop; + private final int divisor; + private final Unit unit; + + private PWMState(final String stateResponseSearch, final int start, final int stop, final int divisor, + final Unit unit) { + this.stateResponseSearch = stateResponseSearch; + this.start = start; + this.stop = stop; + this.divisor = divisor; + this.unit = unit; + } + + public String getStateResponseSearch() { + return stateResponseSearch; + } + + public int getStart() { + return start; + } + + public int getStop() { + return stop; + } + + public int getDivisor() { + return divisor; + } + + public State readState(final String loginResponseString) { + final int findState = loginResponseString.lastIndexOf(stateResponseSearch); + + if (findState > 0) { + logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); + final String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); + logger.trace("transformed state response = {}", slicedResponseTmp); + final String[] slicedResponse = slicedResponseTmp.split(";"); + logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); + final double value; + + if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { + value = Double.parseDouble(slicedResponse[0]) / divisor; + } else { + value = -1.0; + } + return QuantityType.valueOf(value, unit); + } else { + logger.trace("searchstring '{} not found", stateResponseSearch); + return UnDefType.UNDEF; + } + } + } + + private final Logger logger = LoggerFactory.getLogger(EnergeniePWMHandler.class); + + private static final int HTTP_TIMEOUT_MILLISECONDS = 6000; + + private @Nullable EnergenieConfiguration config; + + private String host = ""; + private String password = ""; + private int refreshInterval; + + @Nullable + private ScheduledFuture refreshJob; + + public EnergeniePWMHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // PWM devices don't support any commands, just value reading + } + + @Override + public void initialize() { + EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); + + this.config = config; + + if (config.host != null && config.password != null) { + host = config.host; + password = config.password; + refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; + logger.debug("Initializing EnergeniePWMHandler for Host '{}'", host); + updateStatus(ThingStatus.ONLINE); + onUpdate(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Can not access device , IP-Address or password not set"); + } + } + + @Override + public void dispose() { + logger.debug("EnergeniePWMHandler disposed."); + if (refreshJob != null && !refreshJob.isCancelled()) { + refreshJob.cancel(true); + refreshJob = null; + } + } + + public void getState() { + String url = "http://" + host + "/login.html"; + String urlParameters = "pw=" + password; + InputStream urlContent = new ByteArrayInputStream(urlParameters.getBytes(StandardCharsets.UTF_8)); + String loginResponseString = null; + + try { + logger.trace("sendlogin to {} with password {}", host, password); + logger.trace("sending 'POST' request to URL : {}", url); + loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", HTTP_TIMEOUT_MILLISECONDS); + + if (loginResponseString != null) { + updateState("voltage", PWMState.VOLTAGE.readState(loginResponseString)); + updateState("current", PWMState.CURRENT.readState(loginResponseString)); + updateState("power", PWMState.POWER.readState(loginResponseString)); + updateState("energy", PWMState.ENERGY.readState(loginResponseString)); + try { + HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); + logger.trace("logout from ip {}", host); + } catch (IOException e) { + logger.debug("failed to logout from {} with ip {}", thing.getUID(), host, e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + } catch (IOException e) { + logger.debug("energenie: failed to login to {} with ip {}", thing.getUID(), host, e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + private synchronized void onUpdate() { + if (refreshJob == null || refreshJob.isCancelled()) { + refreshJob = scheduler.scheduleWithFixedDelay(this::getState, 5, refreshInterval, TimeUnit.SECONDS); + } + } + +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java new file mode 100644 index 0000000000000..d11d094b29bdd --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2010-2020 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.energenie.internal.handler; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.util.HexUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class handling the Socket connections. + * + * @author Hans-Jörg Merk - Initial contribution + * @author Hilbrand Bouwkamp - Moved Socket to it's own class + */ +@NonNullByDefault +class EnergenieSocket { + + private static final int SOCKET_TIMEOUT_MILLISECONDS = 1500; + private static final byte[] MESSAGE = { 0x11 }; + + private final Logger logger = LoggerFactory.getLogger(EnergenieSocket.class); + + private @Nullable Socket socket = null; + private @Nullable OutputStream output = null; + private @Nullable InputStream input = null; + + private final String host; + private final byte[] key; + + public EnergenieSocket(final String host, final String password) { + this.host = host; + key = getKey(password); + } + + private byte[] getKey(final String password) { + final int passwordLength = password.length(); + String passwordString = password; + for (int i = 0; i < (8 - passwordLength); i++) { + passwordString = passwordString + " "; + } + return passwordString.getBytes(); + } + + public synchronized byte[] sendCommand(final byte[] ctrl) throws IOException { + try { + final TaskStat taskStat = authorize(); + final OutputStream output = this.output; + final InputStream input = this.input; + + if (output == null || input == null) { + throw new IOException("No connection"); + } else { + if (logger.isTraceEnabled()) { + logger.trace("Control message send to EG (int) '{}' (hex)'{}'", ctrl, HexUtils.bytesToHex(ctrl)); + } + output.write(encryptControls(ctrl, taskStat.task)); + readStatus(input, taskStat); + return updateStatus(taskStat); + } + } finally { + close(); + } + } + + public synchronized byte[] retrieveStatus() throws IOException { + try { + return updateStatus(authorize()); + } finally { + close(); + } + } + + private TaskStat authorize() throws IOException { + connect(); + final OutputStream output = this.output; + final InputStream input = this.input; + + if (output == null || input == null) { + throw new IOException("No connection"); + } + output.write(MESSAGE); + logger.trace("Start Condition '{}' send to EG", MESSAGE); + final TaskStat taskStat = new TaskStat(); + input.read(taskStat.task); + + if (logger.isTraceEnabled()) { + logger.trace("EG responded with task (int) '{}' (hex) '{}'", taskStat.task, + HexUtils.bytesToHex(taskStat.task)); + } + final byte[] solutionMessage = calculateSolution(taskStat.task); + + output.write(solutionMessage); + logger.trace("Solution '{}' send to EG", solutionMessage); + readStatus(input, taskStat); + return taskStat; + } + + private void readStatus(final InputStream input, final TaskStat taskStat) throws IOException { + input.read(taskStat.statcryp); + if (logger.isTraceEnabled()) { + logger.trace("EG responded with statcryp (int) '{}' (hex) '{}'", taskStat.statcryp, + HexUtils.bytesToHex(taskStat.statcryp)); + } + } + + private byte[] updateStatus(final TaskStat taskStat) throws IOException { + final InputStream input = this.input; + + if (input == null) { + throw new IOException("No connection"); + } else { + final byte[] status = decryptStatus(taskStat); + + if (logger.isTraceEnabled()) { + logger.trace("EG responded with status (int) '{}' (hex) '{}'", status, HexUtils.bytesToHex(status)); + } + return status; + } + } + + public void connect() throws UnknownHostException, IOException { + final Socket socket = this.socket; + + if (socket == null || socket.isClosed()) { + final Socket newSocket = new Socket(host, TCP_PORT); + this.socket = newSocket; + + newSocket.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS); + output = newSocket.getOutputStream(); + input = newSocket.getInputStream(); + } + } + + private void close() { + final Socket socket = this.socket; + + if (socket != null) { + try { + socket.close(); + } catch (final IOException e) { + logger.trace("Error closing socket", e); + } + this.socket = null; + } + } + + private byte[] calculateSolution(final byte[] task) { + final int[] uIntTask = new int[4]; + + for (int i = 0; i < 4; i++) { + uIntTask[i] = Byte.toUnsignedInt(task[i]); + } + final int solutionLoword = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); + final byte loword[] = ByteBuffer.allocate(4).putInt(solutionLoword).array(); + + final int solutionHiword = (((uIntTask[1] ^ key[3]) * key[1]) ^ (key[7] | (key[5] << 8)) ^ uIntTask[3]); + final byte hiword[] = ByteBuffer.allocate(4).putInt(solutionHiword).array(); + final byte[] solution = new byte[SOLUTION_LEN]; + + solution[0] = loword[3]; + solution[1] = loword[2]; + solution[2] = hiword[3]; + solution[3] = hiword[2]; + + return solution; + } + + private byte[] decryptStatus(final TaskStat taskStat) { + final byte[] status = new byte[4]; + + for (int i = 0; i < 4; i++) { + status[i] = (byte) ((((taskStat.statcryp[3 - i] - key[1]) ^ key[0]) - taskStat.task[3]) ^ taskStat.task[2]); + } + return status; + } + + private byte[] encryptControls(final byte[] controls, final byte[] task) { + final byte[] ctrlcryp = new byte[CTRLCRYP_LEN]; + + for (int i = 0; i < 4; i++) { + ctrlcryp[i] = (byte) ((((controls[3 - i] ^ task[2]) + task[3]) ^ key[0]) + key[1]); + } + return ctrlcryp; + } + + private class TaskStat { + final byte[] task = new byte[TASK_LEN]; + final byte[] statcryp = new byte[STATCRYP_LEN]; + } +} diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml index 24b1425e6db0f..8582bce616318 100644 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/binding/binding.xml @@ -1,10 +1,10 @@ - - - - Energenie Binding - This is the binding for Energenie. - Hans-Jörg Merk - - + + + + Energenie Binding + This is the binding for Energenie. + Hans-Jörg Merk + + diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml index 0054b2ca4706e..5b79eac6206ee 100644 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/config/config.xml @@ -3,7 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0" xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd"> - @@ -17,5 +16,4 @@ password - - \ No newline at end of file + diff --git a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml index b3427d036c6e2..947734b5333ab 100644 --- a/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.energenie/src/main/resources/ESH-INF/thing/thing-types.xml @@ -1,134 +1,134 @@ - - - - - - Energenie programmable power strip with LAN interface - - - - - - - - - - - - - - - Energenie programmable power strip with LAN interface - - - - - - - - - - - - - - - Energenie programmable power strip with LAN interface - - - - - - - - - - - - - - - Energenie programmable power strip with WLAN interface - - - - - - - - - - - - - - - Energenie energy meter with LAN interface - - - - - - - - - - - - - - Switch - - Channel representing the state of socket 1 - - - - Switch - - Channel representing the state of socket 2 - - - - Switch - - Channel representing the state of socket 3 - - - - Switch - - Channel representing the state of socket 4 - - - - Number:ElectricPotential - - Channel representing the voltage - - - - - Number:ElectricCurrent - - Channel representing the current - - - - - Number:Power - - Channel representing the power consumption - - - - - Number:Energy - - Channel representing the energy consumption - - - - + + + + + + Energenie programmable power strip with LAN interface + + + + + + + + + + + + + + + Energenie programmable power strip with LAN interface + + + + + + + + + + + + + + + Energenie programmable power strip with LAN interface + + + + + + + + + + + + + + + Energenie programmable power strip with WLAN interface + + + + + + + + + + + + + + + Energenie energy meter with LAN interface + + + + + + + + + + + + + + Switch + + Channel representing the state of socket 1 + + + + Switch + + Channel representing the state of socket 2 + + + + Switch + + Channel representing the state of socket 3 + + + + Switch + + Channel representing the state of socket 4 + + + + Number:ElectricPotential + + Channel representing the voltage + + + + + Number:ElectricCurrent + + Channel representing the current + + + + + Number:Power + + Channel representing the power consumption + + + + + Number:Energy + + Channel representing the energy consumption + + + + From 0959157d23667aa5c7ef27273f922ea3b82c9578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Sat, 21 Mar 2020 23:00:03 +0100 Subject: [PATCH 08/22] Sync PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../org.openhab.binding.energenie/README.md | 4 +- bundles/org.openhab.binding.energenie/pom.xml | 2 +- .../src/main/feature/feature.xml | 2 +- .../internal/handler/EnergenieHandler.java | 3 +- .../internal/handler/EnergeniePWMHandler.java | 6 +- .../internal/handler/EnergenieSocket.java | 107 +++++++----------- 6 files changed, 49 insertions(+), 75 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/README.md b/bundles/org.openhab.binding.energenie/README.md index df17d638212fc..893baa85d15b3 100644 --- a/bundles/org.openhab.binding.energenie/README.md +++ b/bundles/org.openhab.binding.energenie/README.md @@ -9,7 +9,9 @@ The Binding supports PM2-LAN, PMS-LAN, PMS2-LAN or PMS-WLAN power extenders as w ## Discovery -Gembird energenie devices don't support autodiscovery. All Things need to be created manually either in PaperUI or within Things files. +Gembird energenie devices don't support autodiscovery. +All Things need to be created manually either in PaperUI or within Things files. + ## Binding Configuration diff --git a/bundles/org.openhab.binding.energenie/pom.xml b/bundles/org.openhab.binding.energenie/pom.xml index 16409e6c8e613..050bd24b6a006 100644 --- a/bundles/org.openhab.binding.energenie/pom.xml +++ b/bundles/org.openhab.binding.energenie/pom.xml @@ -6,7 +6,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.0.0-SNAPSHOT + 2.5.2-SNAPSHOT org.openhab.binding.energenie diff --git a/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml b/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml index 85b7e1383dcc1..7bc80e9c06c0f 100644 --- a/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.energenie/src/main/feature/feature.xml @@ -1,6 +1,6 @@ - mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features openhab-runtime-base diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 9b500eaee8b49..0a4bb0e8db98f 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -154,8 +154,7 @@ public void dispose() { } return Boolean.TRUE; } catch (final UnknownHostException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Can't find host"); - logger.debug("Can't find host: {}:{}.", host, TCP_PORT, e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Can't find host: " + e.getMessage()); } catch (final IOException e) { logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT, e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 9412c950760fd..44d259b5f515d 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -120,8 +120,7 @@ public State readState(final String loginResponseString) { private String password = ""; private int refreshInterval; - @Nullable - private ScheduledFuture refreshJob; + private @Nullable ScheduledFuture refreshJob; public EnergeniePWMHandler(Thing thing) { super(thing); @@ -180,8 +179,7 @@ public void getState() { HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); logger.trace("logout from ip {}", host); } catch (IOException e) { - logger.debug("failed to logout from {} with ip {}", thing.getUID(), host, e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "failed to logout: "+ e.getMessage()); } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java index d11d094b29bdd..48e054f693f6f 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -14,6 +14,7 @@ import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -22,7 +23,6 @@ import java.nio.ByteBuffer; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.util.HexUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,11 +40,6 @@ class EnergenieSocket { private static final byte[] MESSAGE = { 0x11 }; private final Logger logger = LoggerFactory.getLogger(EnergenieSocket.class); - - private @Nullable Socket socket = null; - private @Nullable OutputStream output = null; - private @Nullable InputStream input = null; - private final String host; private final byte[] key; @@ -63,10 +58,9 @@ private byte[] getKey(final String password) { } public synchronized byte[] sendCommand(final byte[] ctrl) throws IOException { - try { - final TaskStat taskStat = authorize(); - final OutputStream output = this.output; - final InputStream input = this.input; + try (final TaskSocket taskSocket = authorize()) { + final OutputStream output = taskSocket.socket.getOutputStream(); + final InputStream input = taskSocket.socket.getInputStream(); if (output == null || input == null) { throw new IOException("No connection"); @@ -74,63 +68,58 @@ public synchronized byte[] sendCommand(final byte[] ctrl) throws IOException { if (logger.isTraceEnabled()) { logger.trace("Control message send to EG (int) '{}' (hex)'{}'", ctrl, HexUtils.bytesToHex(ctrl)); } - output.write(encryptControls(ctrl, taskStat.task)); - readStatus(input, taskStat); - return updateStatus(taskStat); + output.write(encryptControls(ctrl, taskSocket.task)); + readStatus(input, taskSocket); + return updateStatus(taskSocket); } - } finally { - close(); } } public synchronized byte[] retrieveStatus() throws IOException { - try { - return updateStatus(authorize()); - } finally { - close(); + try (final TaskSocket taskSocket = authorize()) { + return updateStatus(taskSocket); } } - private TaskStat authorize() throws IOException { - connect(); - final OutputStream output = this.output; - final InputStream input = this.input; + private TaskSocket authorize() throws IOException { + final TaskSocket taskSocket = new TaskSocket(); + final OutputStream output = taskSocket.socket.getOutputStream(); + final InputStream input = taskSocket.socket.getInputStream(); if (output == null || input == null) { throw new IOException("No connection"); } output.write(MESSAGE); logger.trace("Start Condition '{}' send to EG", MESSAGE); - final TaskStat taskStat = new TaskStat(); - input.read(taskStat.task); + input.read(taskSocket.task); if (logger.isTraceEnabled()) { - logger.trace("EG responded with task (int) '{}' (hex) '{}'", taskStat.task, - HexUtils.bytesToHex(taskStat.task)); + logger.trace("EG responded with task (int) '{}' (hex) '{}'", taskSocket.task, + HexUtils.bytesToHex(taskSocket.task)); } - final byte[] solutionMessage = calculateSolution(taskStat.task); + final byte[] solutionMessage = calculateSolution(taskSocket.task); output.write(solutionMessage); logger.trace("Solution '{}' send to EG", solutionMessage); - readStatus(input, taskStat); - return taskStat; + readStatus(input, taskSocket); + return taskSocket; } - private void readStatus(final InputStream input, final TaskStat taskStat) throws IOException { - input.read(taskStat.statcryp); + private void readStatus(final InputStream input, final TaskSocket taskSocket) throws IOException { + input.read(taskSocket.statcryp); if (logger.isTraceEnabled()) { - logger.trace("EG responded with statcryp (int) '{}' (hex) '{}'", taskStat.statcryp, - HexUtils.bytesToHex(taskStat.statcryp)); + logger.trace("EG responded with statcryp (int) '{}' (hex) '{}'", taskSocket.statcryp, + HexUtils.bytesToHex(taskSocket.statcryp)); } } - private byte[] updateStatus(final TaskStat taskStat) throws IOException { - final InputStream input = this.input; + private byte[] updateStatus(final TaskSocket taskSocket) throws IOException { + final InputStream input = taskSocket.socket.getInputStream(); if (input == null) { throw new IOException("No connection"); } else { - final byte[] status = decryptStatus(taskStat); + final byte[] status = decryptStatus(taskSocket); if (logger.isTraceEnabled()) { logger.trace("EG responded with status (int) '{}' (hex) '{}'", status, HexUtils.bytesToHex(status)); @@ -139,32 +128,6 @@ private byte[] updateStatus(final TaskStat taskStat) throws IOException { } } - public void connect() throws UnknownHostException, IOException { - final Socket socket = this.socket; - - if (socket == null || socket.isClosed()) { - final Socket newSocket = new Socket(host, TCP_PORT); - this.socket = newSocket; - - newSocket.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS); - output = newSocket.getOutputStream(); - input = newSocket.getInputStream(); - } - } - - private void close() { - final Socket socket = this.socket; - - if (socket != null) { - try { - socket.close(); - } catch (final IOException e) { - logger.trace("Error closing socket", e); - } - this.socket = null; - } - } - private byte[] calculateSolution(final byte[] task) { final int[] uIntTask = new int[4]; @@ -186,11 +149,12 @@ private byte[] calculateSolution(final byte[] task) { return solution; } - private byte[] decryptStatus(final TaskStat taskStat) { + private byte[] decryptStatus(final TaskSocket taskSocket) { final byte[] status = new byte[4]; for (int i = 0; i < 4; i++) { - status[i] = (byte) ((((taskStat.statcryp[3 - i] - key[1]) ^ key[0]) - taskStat.task[3]) ^ taskStat.task[2]); + status[i] = (byte) ((((taskSocket.statcryp[3 - i] - key[1]) ^ key[0]) - taskSocket.task[3]) + ^ taskSocket.task[2]); } return status; } @@ -204,8 +168,19 @@ private byte[] encryptControls(final byte[] controls, final byte[] task) { return ctrlcryp; } - private class TaskStat { + private class TaskSocket implements Closeable { + final Socket socket; final byte[] task = new byte[TASK_LEN]; final byte[] statcryp = new byte[STATCRYP_LEN]; + + public TaskSocket() throws UnknownHostException, IOException { + socket = new Socket(host, TCP_PORT); + socket.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS); + } + + @Override + public void close() throws IOException { + socket.close(); + } } } From a3e0a435fd944a6f5b5a6ff08a17c3700567f673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Sat, 21 Mar 2020 23:50:13 +0100 Subject: [PATCH 09/22] Changes after Review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- bom/openhab-addons/pom.xml | 5 ++++ .../internal/handler/EnergenieHandler.java | 20 +++++++------- .../internal/handler/EnergeniePWMHandler.java | 26 ++++++++++++------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 00064b888e0fa..3772d424888d7 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -231,6 +231,11 @@ org.openhab.binding.elerotransmitterstick ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.energenie + ${project.version} + org.openhab.addons.bundles org.openhab.binding.enocean diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 0a4bb0e8db98f..d95060eb61a77 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -50,9 +50,9 @@ public class EnergenieHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(EnergenieHandler.class); /** - * Use cache for refresh command to not update again when call is made within 2 seconds of previous call. + * Use cache for refresh command to not update again when call is made within 4 seconds of previous call. */ - private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(2), this::refreshState); + private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(4), this::refreshState); private final String statusOn; private final String statusOff; @@ -118,7 +118,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { public void initialize() { final EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); - if (config.host != null && config.password != null) { + if (!config.host.isEmpty() && !config.password.isEmpty()) { refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; host = config.host; logger.debug("Initializing EnergenieHandler for Host '{}'", config.host); @@ -154,7 +154,8 @@ public void dispose() { } return Boolean.TRUE; } catch (final UnknownHostException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Can't find host: " + e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Can't find host: " + e.getMessage()); } catch (final IOException e) { logger.debug("Couldn't get I/O for the connection to: {}:{}.", host, TCP_PORT, e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, @@ -171,12 +172,13 @@ public void stateUpdate(final byte[] status) { for (int i = 0; i < 4; i++) { final String socket = CHANNEL_SOCKET_PREFIX + (i + 1); final String stringStatus = String.format("0x%02x", status[i]); + updateState(socket, OnOffType.from(stringStatus.equals(statusOn))); - if (stringStatus.equals(statusOn)) { - updateState(socket, OnOffType.ON); - } else if (stringStatus.equals(statusOff)) { - updateState(socket, OnOffType.OFF); - } + // if (stringStatus.equals(statusOn)) { + // updateState(socket, OnOffType.ON); + // } else if (stringStatus.equals(statusOff)) { + // updateState(socket, OnOffType.OFF); + // } } } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 44d259b5f515d..0b6b22b43da44 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -97,12 +97,18 @@ public State readState(final String loginResponseString) { logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); final double value; - if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { - value = Double.parseDouble(slicedResponse[0]) / divisor; - } else { - value = -1.0; + try { + if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { + value = Double.parseDouble(slicedResponse[0]) / divisor; + } else { + value = -1.0; + } + return QuantityType.valueOf(value, unit); + + } catch (NumberFormatException e) { + logger.debug("Could not Parse State", e); + return UnDefType.UNDEF; } - return QuantityType.valueOf(value, unit); } else { logger.trace("searchstring '{} not found", stateResponseSearch); return UnDefType.UNDEF; @@ -137,7 +143,7 @@ public void initialize() { this.config = config; - if (config.host != null && config.password != null) { + if (!config.host.isEmpty() && !config.password.isEmpty()) { host = config.host; password = config.password; refreshInterval = EnergenieConfiguration.DEFAULT_REFRESH_INTERVAL; @@ -153,9 +159,11 @@ public void initialize() { @Override public void dispose() { logger.debug("EnergeniePWMHandler disposed."); + final ScheduledFuture refreshJob = this.refreshJob; + if (refreshJob != null && !refreshJob.isCancelled()) { refreshJob.cancel(true); - refreshJob = null; + this.refreshJob = null; } } @@ -166,7 +174,6 @@ public void getState() { String loginResponseString = null; try { - logger.trace("sendlogin to {} with password {}", host, password); logger.trace("sending 'POST' request to URL : {}", url); loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", HTTP_TIMEOUT_MILLISECONDS); @@ -179,7 +186,8 @@ public void getState() { HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); logger.trace("logout from ip {}", host); } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "failed to logout: "+ e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "failed to logout: " + e.getMessage()); } } From d80677d4993cfc41de1737723ea2ec7fab468c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Wed, 25 Mar 2020 08:38:49 +0100 Subject: [PATCH 10/22] changes after review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- bundles/org.openhab.binding.energenie/pom.xml | 2 +- .../energenie/internal/handler/EnergenieHandler.java | 6 ------ .../energenie/internal/handler/EnergeniePWMHandler.java | 7 ++++++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/pom.xml b/bundles/org.openhab.binding.energenie/pom.xml index 050bd24b6a006..18af6658c937f 100644 --- a/bundles/org.openhab.binding.energenie/pom.xml +++ b/bundles/org.openhab.binding.energenie/pom.xml @@ -6,7 +6,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.2-SNAPSHOT + 2.5.4-SNAPSHOT org.openhab.binding.energenie diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index d95060eb61a77..09b3fd825edd7 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -173,12 +173,6 @@ public void stateUpdate(final byte[] status) { final String socket = CHANNEL_SOCKET_PREFIX + (i + 1); final String stringStatus = String.format("0x%02x", status[i]); updateState(socket, OnOffType.from(stringStatus.equals(statusOn))); - - // if (stringStatus.equals(statusOn)) { - // updateState(socket, OnOffType.ON); - // } else if (stringStatus.equals(statusOff)) { - // updateState(socket, OnOffType.OFF); - // } } } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 0b6b22b43da44..7d290498a1f6c 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -134,7 +134,12 @@ public EnergeniePWMHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { - // PWM devices don't support any commands, just value reading + if (command instanceof RefreshType) { + try { + getState(); + } catch (Exception e) { + logger.debug("Exception during poll", e); + } } @Override From 4ade7dccc71f091275d4d35d56446083fb1a1be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Wed, 25 Mar 2020 08:52:25 +0100 Subject: [PATCH 11/22] fix compilation errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../binding/energenie/internal/handler/EnergenieHandler.java | 1 + .../binding/energenie/internal/handler/EnergeniePWMHandler.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 09b3fd825edd7..756206b2c5a55 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -54,6 +54,7 @@ public class EnergenieHandler extends BaseThingHandler { */ private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(4), this::refreshState); private final String statusOn; + @SuppressWarnings("unused") private final String statusOff; private @Nullable EnergenieSocket energenieSocket; diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 7d290498a1f6c..9780001cd7c54 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -31,6 +31,7 @@ import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.io.net.http.HttpUtil; @@ -140,6 +141,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } catch (Exception e) { logger.debug("Exception during poll", e); } + } } @Override From 49837f19c26bb89ac6ab0a903d6db80327cc1faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Wed, 25 Mar 2020 22:59:39 +0100 Subject: [PATCH 12/22] Changes after review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../org.openhab.binding.energenie/README.md | 8 +- .../internal/EnergeniePWMStateEnum.java | 97 +++++++++++++++++++ .../internal/handler/EnergenieHandler.java | 6 -- .../internal/handler/EnergeniePWMHandler.java | 91 ++--------------- .../internal/handler/EnergenieSocket.java | 4 +- 5 files changed, 112 insertions(+), 94 deletions(-) create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java diff --git a/bundles/org.openhab.binding.energenie/README.md b/bundles/org.openhab.binding.energenie/README.md index 893baa85d15b3..359592db3b986 100644 --- a/bundles/org.openhab.binding.energenie/README.md +++ b/bundles/org.openhab.binding.energenie/README.md @@ -82,10 +82,10 @@ sitemap energenie label="Energenie Devices" { Frame { // Power extenders - Switch item=socket1 - Switch item=socket2 - Switch item=socket3 - Switch item=socket4 + Switch item=Socket1 + Switch item=Socket2 + Switch item=Socket3 + Switch item=Socket4 // Power measurement Number item=Voltage diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java new file mode 100644 index 0000000000000..9cf5cc7642626 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010-2020 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.energenie.internal; + +import javax.measure.Unit; + +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link EnergeniePWMStateEnum} contains informations for parsing the readState() result. + * + * @author Hans-Jörg Merk - Initial contribution + */ + +public enum EnergeniePWMStateEnum { + VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), + CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), + POWER("var P=", 6, 20, 466, SmartHomeUnits.WATT), + ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.WATT_HOUR); + + private final Logger logger = LoggerFactory.getLogger(EnergeniePWMStateEnum.class); + + private final String stateResponseSearch; + private final int start; + private final int stop; + private final int divisor; + private final Unit unit; + + private EnergeniePWMStateEnum(final String stateResponseSearch, final int start, final int stop, final int divisor, + final Unit unit) { + this.stateResponseSearch = stateResponseSearch; + this.start = start; + this.stop = stop; + this.divisor = divisor; + this.unit = unit; + } + + public String getStateResponseSearch() { + return stateResponseSearch; + } + + public int getStart() { + return start; + } + + public int getStop() { + return stop; + } + + public int getDivisor() { + return divisor; + } + + public State readState(final String loginResponseString) { + final int findState = loginResponseString.lastIndexOf(stateResponseSearch); + + if (findState > 0) { + logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); + final String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); + logger.trace("transformed state response = {}", slicedResponseTmp); + final String[] slicedResponse = slicedResponseTmp.split(";"); + logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); + final double value; + + try { + if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { + value = Double.parseDouble(slicedResponse[0]) / divisor; + } else { + value = -1.0; + } + return QuantityType.valueOf(value, unit); + + } catch (NumberFormatException e) { + logger.debug("Could not Parse State", e); + return UnDefType.UNDEF; + } + } else { + logger.trace("searchstring '{} not found", stateResponseSearch); + return UnDefType.UNDEF; + } + } +} diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 756206b2c5a55..0a3a931299e30 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -54,8 +54,6 @@ public class EnergenieHandler extends BaseThingHandler { */ private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(4), this::refreshState); private final String statusOn; - @SuppressWarnings("unused") - private final String statusOff; private @Nullable EnergenieSocket energenieSocket; private @Nullable ScheduledFuture refreshJob; @@ -68,19 +66,15 @@ public EnergenieHandler(final Thing thing, final String protocol) { switch (protocol) { case "EG_PROTO_V20": statusOn = STATE_ON; - statusOff = STATE_OFF; break; case "EG_PROTO_V21": statusOn = V21_STATE_ON; - statusOff = V21_STATE_OFF; break; case "EG_PROTO_WLAN": statusOn = WLAN_STATE_ON; - statusOff = WLAN_STATE_OFF; break; default: statusOn = STATE_ON; - statusOff = STATE_OFF; break; } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 9780001cd7c54..2efeeb41ae147 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -19,12 +19,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.measure.Unit; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.QuantityType; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -32,9 +28,8 @@ import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; -import org.eclipse.smarthome.core.types.State; -import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.energenie.internal.EnergeniePWMStateEnum; import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,75 +43,6 @@ @NonNullByDefault public class EnergeniePWMHandler extends BaseThingHandler { - public enum PWMState { - VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), - CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), - POWER("var P=", 6, 20, 466, SmartHomeUnits.WATT), - ENERGY("var E=", 6, 20, 25600, SmartHomeUnits.WATT_HOUR); - - private final Logger logger = LoggerFactory.getLogger(PWMState.class); - - private final String stateResponseSearch; - private final int start; - private final int stop; - private final int divisor; - private final Unit unit; - - private PWMState(final String stateResponseSearch, final int start, final int stop, final int divisor, - final Unit unit) { - this.stateResponseSearch = stateResponseSearch; - this.start = start; - this.stop = stop; - this.divisor = divisor; - this.unit = unit; - } - - public String getStateResponseSearch() { - return stateResponseSearch; - } - - public int getStart() { - return start; - } - - public int getStop() { - return stop; - } - - public int getDivisor() { - return divisor; - } - - public State readState(final String loginResponseString) { - final int findState = loginResponseString.lastIndexOf(stateResponseSearch); - - if (findState > 0) { - logger.trace("searchstring {} found at position {}", stateResponseSearch, findState); - final String slicedResponseTmp = loginResponseString.substring(findState + start, findState + stop); - logger.trace("transformed state response = {}", slicedResponseTmp); - final String[] slicedResponse = slicedResponseTmp.split(";"); - logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); - final double value; - - try { - if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { - value = Double.parseDouble(slicedResponse[0]) / divisor; - } else { - value = -1.0; - } - return QuantityType.valueOf(value, unit); - - } catch (NumberFormatException e) { - logger.debug("Could not Parse State", e); - return UnDefType.UNDEF; - } - } else { - logger.trace("searchstring '{} not found", stateResponseSearch); - return UnDefType.UNDEF; - } - } - } - private final Logger logger = LoggerFactory.getLogger(EnergeniePWMHandler.class); private static final int HTTP_TIMEOUT_MILLISECONDS = 6000; @@ -137,7 +63,7 @@ public EnergeniePWMHandler(Thing thing) { public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { try { - getState(); + scheduler.execute(this::getState); } catch (Exception e) { logger.debug("Exception during poll", e); } @@ -174,7 +100,7 @@ public void dispose() { } } - public void getState() { + public synchronized void getState() { String url = "http://" + host + "/login.html"; String urlParameters = "pw=" + password; InputStream urlContent = new ByteArrayInputStream(urlParameters.getBytes(StandardCharsets.UTF_8)); @@ -185,10 +111,10 @@ public void getState() { loginResponseString = HttpUtil.executeUrl("POST", url, urlContent, "TEXT/PLAIN", HTTP_TIMEOUT_MILLISECONDS); if (loginResponseString != null) { - updateState("voltage", PWMState.VOLTAGE.readState(loginResponseString)); - updateState("current", PWMState.CURRENT.readState(loginResponseString)); - updateState("power", PWMState.POWER.readState(loginResponseString)); - updateState("energy", PWMState.ENERGY.readState(loginResponseString)); + updateState("voltage", EnergeniePWMStateEnum.VOLTAGE.readState(loginResponseString)); + updateState("current", EnergeniePWMStateEnum.CURRENT.readState(loginResponseString)); + updateState("power", EnergeniePWMStateEnum.POWER.readState(loginResponseString)); + updateState("energy", EnergeniePWMStateEnum.ENERGY.readState(loginResponseString)); try { HttpUtil.executeUrl("POST", url, HTTP_TIMEOUT_MILLISECONDS); logger.trace("logout from ip {}", host); @@ -205,8 +131,9 @@ public void getState() { } private synchronized void onUpdate() { + ScheduledFuture refreshJob = this.refreshJob; if (refreshJob == null || refreshJob.isCancelled()) { - refreshJob = scheduler.scheduleWithFixedDelay(this::getState, 5, refreshInterval, TimeUnit.SECONDS); + this.refreshJob = scheduler.scheduleWithFixedDelay(this::getState, 5, refreshInterval, TimeUnit.SECONDS); } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java index 48e054f693f6f..6ef7b62034bbd 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -45,10 +45,10 @@ class EnergenieSocket { public EnergenieSocket(final String host, final String password) { this.host = host; - key = getKey(password); + this.key = getKey(password); } - private byte[] getKey(final String password) { + private static byte[] getKey(final String password) { final int passwordLength = password.length(); String passwordString = password; for (int i = 0; i < (8 - passwordLength); i++) { From cd01b4f00c6874a52e3049f677323dc2523f2950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Thu, 26 Mar 2020 11:17:58 +0100 Subject: [PATCH 13/22] Remove Double.parseDouble division in Enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../binding/energenie/internal/EnergeniePWMStateEnum.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java index 9cf5cc7642626..6355bf407ce99 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java @@ -78,11 +78,7 @@ public State readState(final String loginResponseString) { final double value; try { - if (Double.parseDouble(slicedResponse[0]) / 1 == Double.parseDouble(slicedResponse[0])) { - value = Double.parseDouble(slicedResponse[0]) / divisor; - } else { - value = -1.0; - } + value = Double.parseDouble(slicedResponse[0]) / divisor; return QuantityType.valueOf(value, unit); } catch (NumberFormatException e) { From f4f136cbde898b529e5699e06da30e6936fa5fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Fri, 1 May 2020 01:03:16 +0200 Subject: [PATCH 14/22] Changes after review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../internal/handler/EnergenieHandler.java | 6 +++--- .../internal/handler/EnergenieSocket.java | 16 ++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 0a3a931299e30..009d30828c001 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -138,7 +138,7 @@ public void dispose() { } } - private @Nullable Boolean refreshState() { + private boolean refreshState() { final EnergenieSocket socket = this.energenieSocket; if (socket != null) { @@ -147,7 +147,7 @@ public void dispose() { if (thing.getStatus() != ThingStatus.ONLINE && ThingHandlerHelper.isHandlerInitialized(thing)) { updateStatus(ThingStatus.ONLINE); } - return Boolean.TRUE; + return true; } catch (final UnknownHostException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Can't find host: " + e.getMessage()); @@ -160,7 +160,7 @@ public void dispose() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage()); } } - return null; + return false; } public void stateUpdate(final byte[] status) { diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java index 6ef7b62034bbd..aa7d006d83c8f 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -90,6 +90,7 @@ private TaskSocket authorize() throws IOException { throw new IOException("No connection"); } output.write(MESSAGE); + output.flush(); logger.trace("Start Condition '{}' send to EG", MESSAGE); input.read(taskSocket.task); @@ -100,6 +101,7 @@ private TaskSocket authorize() throws IOException { final byte[] solutionMessage = calculateSolution(taskSocket.task); output.write(solutionMessage); + output.flush(); logger.trace("Solution '{}' send to EG", solutionMessage); readStatus(input, taskSocket); return taskSocket; @@ -114,18 +116,12 @@ private void readStatus(final InputStream input, final TaskSocket taskSocket) th } private byte[] updateStatus(final TaskSocket taskSocket) throws IOException { - final InputStream input = taskSocket.socket.getInputStream(); - - if (input == null) { - throw new IOException("No connection"); - } else { - final byte[] status = decryptStatus(taskSocket); + final byte[] status = decryptStatus(taskSocket); - if (logger.isTraceEnabled()) { - logger.trace("EG responded with status (int) '{}' (hex) '{}'", status, HexUtils.bytesToHex(status)); - } - return status; + if (logger.isTraceEnabled()) { + logger.trace("EG responded with status (int) '{}' (hex) '{}'", status, HexUtils.bytesToHex(status)); } + return status; } private byte[] calculateSolution(final byte[] task) { From ba422bbdf185c7d9ff115f6a6e1d20ae164a78cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Fri, 1 May 2020 01:06:40 +0200 Subject: [PATCH 15/22] Fixed pom version error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- bundles/org.openhab.binding.energenie/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.energenie/pom.xml b/bundles/org.openhab.binding.energenie/pom.xml index 18af6658c937f..6f9af69ac4fc9 100644 --- a/bundles/org.openhab.binding.energenie/pom.xml +++ b/bundles/org.openhab.binding.energenie/pom.xml @@ -6,7 +6,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.4-SNAPSHOT + 2.5.5-SNAPSHOT org.openhab.binding.energenie From 8532a25cf789a204168ae1dca2a95083791bb8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Fri, 1 May 2020 12:31:44 +0200 Subject: [PATCH 16/22] More changes according to review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../binding/energenie/internal/EnergeniePWMStateEnum.java | 4 ++-- .../energenie/internal/handler/EnergeniePWMHandler.java | 6 +----- .../binding/energenie/internal/handler/EnergenieSocket.java | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java index 6355bf407ce99..12f17c89001b7 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergeniePWMStateEnum.java @@ -14,6 +14,7 @@ import javax.measure.Unit; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.eclipse.smarthome.core.types.State; @@ -27,6 +28,7 @@ * @author Hans-Jörg Merk - Initial contribution */ +@NonNullByDefault public enum EnergeniePWMStateEnum { VOLTAGE("var V = ", 9, 20, 10, SmartHomeUnits.VOLT), CURRENT("var V = ", 9, 20, 100, SmartHomeUnits.AMPERE), @@ -76,11 +78,9 @@ public State readState(final String loginResponseString) { final String[] slicedResponse = slicedResponseTmp.split(";"); logger.trace("transformed state response = {} - {}", slicedResponse[0], slicedResponse[1]); final double value; - try { value = Double.parseDouble(slicedResponse[0]) / divisor; return QuantityType.valueOf(value, unit); - } catch (NumberFormatException e) { logger.debug("Could not Parse State", e); return UnDefType.UNDEF; diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 2efeeb41ae147..72dfc5d84388d 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -62,11 +62,7 @@ public EnergeniePWMHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { - try { - scheduler.execute(this::getState); - } catch (Exception e) { - logger.debug("Exception during poll", e); - } + scheduler.execute(this::getState); } } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java index aa7d006d83c8f..90a3ab5a6ebc4 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -69,6 +69,7 @@ public synchronized byte[] sendCommand(final byte[] ctrl) throws IOException { logger.trace("Control message send to EG (int) '{}' (hex)'{}'", ctrl, HexUtils.bytesToHex(ctrl)); } output.write(encryptControls(ctrl, taskSocket.task)); + output.flush(); readStatus(input, taskSocket); return updateStatus(taskSocket); } From 226e6221aed4e4a77b93d6c659806191a5534712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Mon, 4 May 2020 23:25:36 +0200 Subject: [PATCH 17/22] fix timing issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../binding/energenie/internal/handler/EnergenieHandler.java | 4 ++-- .../energenie/internal/handler/EnergeniePWMHandler.java | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 009d30828c001..888720da7bd98 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -52,7 +52,7 @@ public class EnergenieHandler extends BaseThingHandler { /** * Use cache for refresh command to not update again when call is made within 4 seconds of previous call. */ - private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(4), this::refreshState); + private final ExpiringCache refreshCache = new ExpiringCache<>(Duration.ofSeconds(5), this::refreshState); private final String statusOn; private @Nullable EnergenieSocket energenieSocket; @@ -120,7 +120,7 @@ public void initialize() { energenieSocket = new EnergenieSocket(config.host, config.password); updateStatus(ThingStatus.UNKNOWN); - refreshJob = scheduler.scheduleWithFixedDelay(this::refreshState, 5, refreshInterval, TimeUnit.SECONDS); + refreshJob = scheduler.scheduleWithFixedDelay(this::refreshState, 6, refreshInterval, TimeUnit.SECONDS); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Can not access device , IP-Address or password not set"); diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index 72dfc5d84388d..ebd10a686879b 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -47,8 +47,6 @@ public class EnergeniePWMHandler extends BaseThingHandler { private static final int HTTP_TIMEOUT_MILLISECONDS = 6000; - private @Nullable EnergenieConfiguration config; - private String host = ""; private String password = ""; private int refreshInterval; @@ -70,8 +68,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { public void initialize() { EnergenieConfiguration config = getConfigAs(EnergenieConfiguration.class); - this.config = config; - if (!config.host.isEmpty() && !config.password.isEmpty()) { host = config.host; password = config.password; From 75b286f6641b893d88239c0d52e9b0cc89544acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Tue, 5 May 2020 22:33:22 +0200 Subject: [PATCH 18/22] EnergenieSocket - move from InputStream to DataInputStream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../internal/handler/EnergenieSocket.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java index 90a3ab5a6ebc4..e9df4aaff927f 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -15,8 +15,8 @@ import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; import java.io.Closeable; +import java.io.DataInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; @@ -60,9 +60,9 @@ private static byte[] getKey(final String password) { public synchronized byte[] sendCommand(final byte[] ctrl) throws IOException { try (final TaskSocket taskSocket = authorize()) { final OutputStream output = taskSocket.socket.getOutputStream(); - final InputStream input = taskSocket.socket.getInputStream(); + final DataInputStream input = new DataInputStream(taskSocket.socket.getInputStream()); - if (output == null || input == null) { + if (output == null) { throw new IOException("No connection"); } else { if (logger.isTraceEnabled()) { @@ -85,15 +85,14 @@ public synchronized byte[] retrieveStatus() throws IOException { private TaskSocket authorize() throws IOException { final TaskSocket taskSocket = new TaskSocket(); final OutputStream output = taskSocket.socket.getOutputStream(); - final InputStream input = taskSocket.socket.getInputStream(); - - if (output == null || input == null) { + final DataInputStream input = new DataInputStream(taskSocket.socket.getInputStream()); + if (output == null) { throw new IOException("No connection"); } output.write(MESSAGE); output.flush(); logger.trace("Start Condition '{}' send to EG", MESSAGE); - input.read(taskSocket.task); + input.readFully(taskSocket.task); if (logger.isTraceEnabled()) { logger.trace("EG responded with task (int) '{}' (hex) '{}'", taskSocket.task, @@ -108,8 +107,8 @@ private TaskSocket authorize() throws IOException { return taskSocket; } - private void readStatus(final InputStream input, final TaskSocket taskSocket) throws IOException { - input.read(taskSocket.statcryp); + private void readStatus(final DataInputStream input, final TaskSocket taskSocket) throws IOException { + input.readFully(taskSocket.statcryp); if (logger.isTraceEnabled()) { logger.trace("EG responded with statcryp (int) '{}' (hex) '{}'", taskSocket.statcryp, HexUtils.bytesToHex(taskSocket.statcryp)); From e9dd91e9c53c956662f22bb07df3f8f52ceb998f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Wed, 6 May 2020 08:30:11 +0200 Subject: [PATCH 19/22] Move protocol to own Enum class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../internal/EnergenieHandlerFactory.java | 6 +-- .../internal/EnergenieProtocolEnum.java | 40 +++++++++++++++++++ .../internal/handler/EnergenieHandler.java | 18 ++------- .../internal/handler/EnergenieSocket.java | 4 +- 4 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java index b75d89e00ef33..c8ae233469796 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java @@ -49,11 +49,11 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_PMSLAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing, "EG_PROTO_V20"); + return new EnergenieHandler(thing, EnergenieProtocolEnum.V20); } else if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing, "EG_PROTO_V21"); + return new EnergenieHandler(thing, EnergenieProtocolEnum.V21); } else if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { - return new EnergenieHandler(thing, "EG_PROTO_WLAN"); + return new EnergenieHandler(thing, EnergenieProtocolEnum.WLAN); } else if (THING_TYPE_PWMLAN.equals(thingTypeUID)) { return new EnergeniePWMHandler(thing); } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java new file mode 100644 index 0000000000000..97b8db3670fc9 --- /dev/null +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2020 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.energenie.internal; + +import static org.openhab.binding.energenie.internal.EnergenieBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link EnergenieProtocolEnum} contains informations for parsing the readState() result. + * + * @author Hans-Jörg Merk - Initial contribution + */ + +@NonNullByDefault +public enum EnergenieProtocolEnum { + V20(STATE_ON), + V21(V21_STATE_ON), + WLAN(WLAN_STATE_ON); + + private final String statusOn; + + private EnergenieProtocolEnum(String statusOn) { + this.statusOn = statusOn; + } + + public String getStatusOn() { + return statusOn; + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 888720da7bd98..88cafcfba8d22 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -33,6 +33,7 @@ import org.eclipse.smarthome.core.thing.util.ThingHandlerHelper; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; +import org.openhab.binding.energenie.internal.EnergenieProtocolEnum; import org.openhab.binding.energenie.internal.config.EnergenieConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,22 +62,9 @@ public class EnergenieHandler extends BaseThingHandler { private int refreshInterval; private @Nullable String host; - public EnergenieHandler(final Thing thing, final String protocol) { + public EnergenieHandler(final Thing thing, final EnergenieProtocolEnum protocol) { super(thing); - switch (protocol) { - case "EG_PROTO_V20": - statusOn = STATE_ON; - break; - case "EG_PROTO_V21": - statusOn = V21_STATE_ON; - break; - case "EG_PROTO_WLAN": - statusOn = WLAN_STATE_ON; - break; - default: - statusOn = STATE_ON; - break; - } + this.statusOn = protocol.getStatusOn(); } @Override diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java index e9df4aaff927f..ab9e476f60860 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieSocket.java @@ -131,10 +131,10 @@ private byte[] calculateSolution(final byte[] task) { uIntTask[i] = Byte.toUnsignedInt(task[i]); } final int solutionLoword = (((uIntTask[0] ^ key[2]) * key[0]) ^ (key[6] | (key[4] << 8)) ^ uIntTask[2]); - final byte loword[] = ByteBuffer.allocate(4).putInt(solutionLoword).array(); + final byte[] loword = ByteBuffer.allocate(4).putInt(solutionLoword).array(); final int solutionHiword = (((uIntTask[1] ^ key[3]) * key[1]) ^ (key[7] | (key[5] << 8)) ^ uIntTask[3]); - final byte hiword[] = ByteBuffer.allocate(4).putInt(solutionHiword).array(); + final byte[] hiword = ByteBuffer.allocate(4).putInt(solutionHiword).array(); final byte[] solution = new byte[SOLUTION_LEN]; solution[0] = loword[3]; From 27b822794425b2a718d8b666ba039382279eb94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Wed, 6 May 2020 08:42:47 +0200 Subject: [PATCH 20/22] Add newline at end of EnergenieProtocolEnum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../binding/energenie/internal/EnergenieProtocolEnum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java index 97b8db3670fc9..1e3709890df6d 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieProtocolEnum.java @@ -37,4 +37,4 @@ private EnergenieProtocolEnum(String statusOn) { public String getStatusOn() { return statusOn; } -} \ No newline at end of file +} From 40297680a2d7be46b710b9cba98e3fe388923ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Thu, 7 May 2020 08:12:25 +0200 Subject: [PATCH 21/22] Small fixes after review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- bundles/org.openhab.binding.energenie/NOTICE | 2 +- bundles/org.openhab.binding.energenie/README.md | 2 -- .../binding/energenie/internal/handler/EnergenieHandler.java | 2 +- .../binding/energenie/internal/handler/EnergeniePWMHandler.java | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.energenie/NOTICE b/bundles/org.openhab.binding.energenie/NOTICE index 4c20ef446c1e4..38d625e349232 100644 --- a/bundles/org.openhab.binding.energenie/NOTICE +++ b/bundles/org.openhab.binding.energenie/NOTICE @@ -10,4 +10,4 @@ https://www.eclipse.org/legal/epl-2.0/. == Source Code -https://github.com/openhab/openhab2-addons +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.energenie/README.md b/bundles/org.openhab.binding.energenie/README.md index 359592db3b986..602843cbb12fd 100644 --- a/bundles/org.openhab.binding.energenie/README.md +++ b/bundles/org.openhab.binding.energenie/README.md @@ -12,7 +12,6 @@ The Binding supports PM2-LAN, PMS-LAN, PMS2-LAN or PMS-WLAN power extenders as w Gembird energenie devices don't support autodiscovery. All Things need to be created manually either in PaperUI or within Things files. - ## Binding Configuration The Binding does not need any specific configuration @@ -46,7 +45,6 @@ PWM-LAN devices support the following channels | power | Number:Power | Channel for output power measurement | | energy | Number:Energy | channel for output energy measurement | - ## Full Example Things diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java index 88cafcfba8d22..cc6b945adedee 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergenieHandler.java @@ -120,7 +120,7 @@ public void dispose() { logger.debug("EnergenieHandler disposed."); final ScheduledFuture refreshJob = this.refreshJob; - if (refreshJob != null && !refreshJob.isCancelled()) { + if (refreshJob != null) { refreshJob.cancel(true); this.refreshJob = null; } diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java index ebd10a686879b..5cf140864fe65 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/handler/EnergeniePWMHandler.java @@ -86,7 +86,7 @@ public void dispose() { logger.debug("EnergeniePWMHandler disposed."); final ScheduledFuture refreshJob = this.refreshJob; - if (refreshJob != null && !refreshJob.isCancelled()) { + if (refreshJob != null) { refreshJob.cancel(true); this.refreshJob = null; } From 1942dd46cec28253125e511ec7c78e9f6326bc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=B6rg=20Merk?= Date: Thu, 7 May 2020 08:21:21 +0200 Subject: [PATCH 22/22] Correction to protocol version for PM2LAN device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans-Jörg Merk --- .../binding/energenie/internal/EnergenieHandlerFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java index c8ae233469796..9bb7af2ea4979 100644 --- a/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java +++ b/bundles/org.openhab.binding.energenie/src/main/java/org/openhab/binding/energenie/internal/EnergenieHandlerFactory.java @@ -50,7 +50,9 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (THING_TYPE_PMSLAN.equals(thingTypeUID)) { return new EnergenieHandler(thing, EnergenieProtocolEnum.V20); - } else if (THING_TYPE_PM2LAN.equals(thingTypeUID) || THING_TYPE_PMS2LAN.equals(thingTypeUID)) { + } else if (THING_TYPE_PM2LAN.equals(thingTypeUID)) { + return new EnergenieHandler(thing, EnergenieProtocolEnum.V20); + } else if (THING_TYPE_PMS2LAN.equals(thingTypeUID)) { return new EnergenieHandler(thing, EnergenieProtocolEnum.V21); } else if (THING_TYPE_PMSWLAN.equals(thingTypeUID)) { return new EnergenieHandler(thing, EnergenieProtocolEnum.WLAN);