]> git.basschouten.com Git - openhab-addons.git/commitdiff
[solarmax] Support configurable Device Address (#14366)
authorJamie Townsend <jamie_townsend@hotmail.com>
Fri, 17 Feb 2023 13:28:48 +0000 (14:28 +0100)
committerGitHub <noreply@github.com>
Fri, 17 Feb 2023 13:28:48 +0000 (14:28 +0100)
Signed-off-by: Jamie Townsend <jamie_townsend@hotmail.com>
bundles/org.openhab.binding.solarmax/README.md
bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxConfiguration.java
bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandler.java
bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnector.java
bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/i18n/solarmax.properties
bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java

index 088187b236e1d6c24081d4e97cb48792f24379ae..f2323ba0f33efa8233f0932fc0f7b8884ece549b 100644 (file)
@@ -18,7 +18,8 @@ Each inverter requires the following configuration parameters:
 | parameter       | required | default | description                                                          |
 | --------------- | -------- | ------- | -------------------------------------------------------------------- |
 | host            | yes      |         | hostname or IP address of the inverter                               |
-| port            | no       | 12345   | Port number to connect to. This should be `12345` for most inverters |
+| portNumber      | no       | 12345   | Port number to connect to. This should be `12345` for most inverters |
+| deviceAddress   | no       | 1       | Device address for devices connected serially.                       |
 | refreshInterval | no       | 15      | Interval (in seconds) to refresh the channel values.                 |
 
 ## Properties
@@ -61,7 +62,8 @@ _inverter.things:_
 ```java
 Thing solarmax:inverter:solarmax "SolarMax Inverter" [
     host="192.168.1.151",
-    port="12345",
+    portNumber="12345",
+    deviceAddress="1",
     refresh="15"
 ]
 ```
@@ -101,30 +103,11 @@ Number:Temperature heatSinkTemperature "Heat Sink Temperature in degrees celcius
 
 ```
 
-_heatpump.sitemap:_
-
-```perl
-sitemap heatpump label="Heatpump" {
-    Frame label="Heatpump" {
-        Text item=HeatPump_State_Ext
-        Text item=HeatPump_Temperature_1
-        Text item=HeatPump_Outside_Avg
-        Text item=HeatPump_Hours_Heatpump
-        Text item=HeatPump_Hours_Heating
-        Text item=HeatPump_Hours_Warmwater
-        Switch item=HeatPump_heating_operation_mode  mappings=[0="Auto", 1="Auxiliary heater", 2="Party", 3="Holiday", 4="Off"]
-        Setpoint item=HeatPump_heating_temperature minValue=-10 maxValue=10 step=0.5
-        Switch item=HeatPump_warmwater_operation_mode  mappings=[0="Auto", 1="Auxiliary heater", 2="Party", 3="Holiday", 4="Off"]
-        Setpoint item=HeatPump_warmwater_temperature minValue=10 maxValue=65 step=1
-    }
-}
-```
-
 ### SolarMax Commands
 
 During the implementation the SolarMax device was sent all possible 3 character commands and a number of 4 character commands, to see what it responded to.
 The most interesting, identifiable and useful commands were implemented as channels above.
 
-Here is a list of other commands, which are known to return some kind of value: ADR (DeviceAddress / Device Number - only used if the devices are linked serially), AMM, CID, CPG, CPL, CP1, CP2, CP3, CP4, CP5, CYC, DIN, DMO, ETH, FH2, FQR, FWV, IAA, IED, IEE, IEM, ILM, IP4, ISL, ITS, KFS, KHS, KTS, LAN (Language), MAC (MAC Address), PAE, PAM, PDA, PDC, PFA, PIN (Power Installed), PLR, PPC, PRL (AC Power Percent, PSF, PSR, PSS, QAC, QMO, QUC, RA1, RA2, RB1, RB2, REL, RH1, RH2, RPR, RSD, SAC, SAL, SAM, SCH, SNM (IP Broadcast Address??), SPS, SRD, SRS, SYS (Operating State), TCP (probably port number - 12345), TI1, TL1, TL3, TND, TNH, TNL, TP1, TP2, TP3, TV0, TV1, TYP (Type?), UA2, UB2, UGD, UI1, UI2, UI3, ULH, ULL, UMX, UM1, UM2, UM3, UPD, UZK, VCM
+Here is a list of other commands, which are known to return some kind of value: ADR (Device Address), AMM, CID, CPG, CPL, CP1, CP2, CP3, CP4, CP5, CYC, DIN, DMO, ETH, FH2, FQR, FWV, IAA, IED, IEE, IEM, ILM, IP4, ISL, ITS, KFS, KHS, KTS, LAN (Language), MAC (MAC Address), PAE, PAM, PDA, PDC, PFA, PIN (Power Installed), PLR, PPC, PRL (AC Power Percent, PSF, PSR, PSS, QAC, QMO, QUC, RA1, RA2, RB1, RB2, REL, RH1, RH2, RPR, RSD, SAC, SAL, SAM, SCH, SNM (IP Broadcast Address??), SPS, SRD, SRS, SYS (Operating State), TCP (probably port number - 12345), TI1, TL1, TL3, TND, TNH, TNL, TP1, TP2, TP3, TV0, TV1, TYP (Type?), UA2, UB2, UGD, UI1, UI2, UI3, ULH, ULL, UMX, UM1, UM2, UM3, UPD, UZK, VCM
 
 Valid commands which returned a null/empty value during testing: FFK, FRT, GCP, ITN, PLD, PLE, PLF, PLS, PPO, TV2, VLE, VLI, VLO
index 752a69145df3f2bfc75b7fe712832276a946c056..0c94227ed5234fdb0bc72e02f5ef8a045ef37725 100644 (file)
@@ -23,6 +23,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 public class SolarMaxConfiguration {
     public String host = ""; // this will always need to be overridden
     public int portNumber = 12345; // default value is 12345
+    public int deviceAddress = 1; // default value is 1
 
     public int refreshInterval = 15; // default value is 15
 }
index f52f29135f0cea87cad91cdd64bf48adcce4cea9..362ea4df0a1c483bb3076c8f21b01e00072ec420 100644 (file)
@@ -72,8 +72,8 @@ public class SolarMaxHandler extends BaseThingHandler {
      * This is called to start the refresh job and also to reset that refresh job when a config change is done.
      */
     private void configurePolling() {
-        logger.debug("Polling data from {} at {}:{} every {} seconds ", getThing().getUID(), this.config.host,
-                this.config.portNumber, this.config.refreshInterval);
+        logger.debug("Polling data from {} at {}:{} (Device Address {}) every {} seconds ", getThing().getUID(),
+                this.config.host, this.config.portNumber, this.config.deviceAddress, this.config.refreshInterval);
         if (this.config.refreshInterval > 0) {
             if (pollingJob == null || pollingJob.isCancelled()) {
                 pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, this.config.refreshInterval,
@@ -98,10 +98,12 @@ public class SolarMaxHandler extends BaseThingHandler {
     };
 
     private synchronized void updateValuesFromDevice() {
-        logger.debug("Updating data from {} at {}:{} ", getThing().getUID(), this.config.host, this.config.portNumber);
+        logger.debug("Updating data from {} at {}:{} (Device Address {}) ", getThing().getUID(), this.config.host,
+                this.config.portNumber, this.config.deviceAddress);
         // get the data from the SolarMax device
         try {
-            SolarMaxData solarMaxData = SolarMaxConnector.getAllValuesFromSolarMax(config.host, config.portNumber);
+            SolarMaxData solarMaxData = SolarMaxConnector.getAllValuesFromSolarMax(config.host, config.portNumber,
+                    this.config.deviceAddress);
 
             if (solarMaxData.wasCommunicationSuccessful()) {
                 updateStatus(ThingStatus.ONLINE);
index bd7d0ade517172eb73e0978f8bb84d747b2b8e33..be2c51b2340391ac33e0d999b9b8ced255af422d 100644 (file)
@@ -58,16 +58,17 @@ public class SolarMaxConnector {
     private static int responseTimeout = 10000;
 
     /**
-     * gets all known values from the SolarMax device addressable at host:port
+     * gets all known values from the SolarMax device addressable at host:portNumber
      * 
      * @param host hostname or ip address of the SolarMax device to be contacted
-     * @param port port the SolarMax is listening on (default is 12345)
+     * @param portNumber portNumber the SolarMax is listening on (default is 12345)
      * @param commandList a list of commands to be sent to the SolarMax device
      * @return
      * @throws UnknownHostException if the host is unknown
      * @throws SolarMaxException if some other exception occurs
      */
-    public static SolarMaxData getAllValuesFromSolarMax(final String host, int port) throws SolarMaxException {
+    public static SolarMaxData getAllValuesFromSolarMax(final String host, final int portNumber,
+            final int deviceAddress) throws SolarMaxException {
         List<SolarMaxCommandKey> commandList = new ArrayList<>();
 
         for (SolarMaxCommandKey solarMaxCommandKey : SolarMaxCommandKey.values()) {
@@ -81,7 +82,8 @@ public class SolarMaxConnector {
         // get the data from the SolarMax device. If we didn't get as many values back as we asked for, there were
         // communications problems, so set communicationSuccessful appropriately
 
-        Map<SolarMaxCommandKey, @Nullable String> valuesFromSolarMax = getValuesFromSolarMax(host, port, commandList);
+        Map<SolarMaxCommandKey, @Nullable String> valuesFromSolarMax = getValuesFromSolarMax(host, portNumber,
+                deviceAddress, commandList);
         boolean allCommandsAnswered = true;
         for (SolarMaxCommandKey solarMaxCommandKey : commandList) {
             if (!valuesFromSolarMax.containsKey(solarMaxCommandKey)) {
@@ -97,17 +99,18 @@ public class SolarMaxConnector {
     }
 
     /**
-     * gets values from the SolarMax device addressable at host:port
+     * gets values from the SolarMax device addressable at host:portNumber
      * 
      * @param host hostname or ip address of the SolarMax device to be contacted
-     * @param port port the SolarMax is listening on (default is 12345)
+     * @param portNumber portNumber the SolarMax is listening on (default is 12345)
      * @param commandList a list of commands to be sent to the SolarMax device
      * @return
      * @throws UnknownHostException if the host is unknown
      * @throws SolarMaxException if some other exception occurs
      */
-    private static Map<SolarMaxCommandKey, @Nullable String> getValuesFromSolarMax(final String host, int port,
-            final List<SolarMaxCommandKey> commandList) throws SolarMaxException {
+    private static Map<SolarMaxCommandKey, @Nullable String> getValuesFromSolarMax(final String host,
+            final int portNumber, final int deviceAddress, final List<SolarMaxCommandKey> commandList)
+            throws SolarMaxException {
         Socket socket;
 
         Map<SolarMaxCommandKey, @Nullable String> returnMap = new HashMap<>();
@@ -120,7 +123,8 @@ public class SolarMaxConnector {
             requestsRequired = requestsRequired + 1;
         }
         for (int requestNumber = 0; requestNumber < requestsRequired; requestNumber++) {
-            LOGGER.debug("    Requesting data from {}:{} with timeout of {}ms", host, port, responseTimeout);
+            LOGGER.debug("    Requesting data from {}:{} (Device Address {}) with timeout of {}ms", host, portNumber,
+                    deviceAddress, responseTimeout);
 
             int firstCommandNumber = requestNumber * maxConcurrentCommands;
             int lastCommandNumber = (requestNumber + 1) * maxConcurrentCommands;
@@ -130,11 +134,11 @@ public class SolarMaxConnector {
             List<SolarMaxCommandKey> commandsToSend = commandList.subList(firstCommandNumber, lastCommandNumber);
 
             try {
-                socket = getSocketConnection(host, port);
+                socket = getSocketConnection(host, portNumber);
             } catch (UnknownHostException e) {
                 throw new SolarMaxConnectionException(e);
             }
-            returnMap.putAll(getValuesFromSolarMax(socket, commandsToSend));
+            returnMap.putAll(getValuesFromSolarMax(socket, deviceAddress, commandsToSend));
 
             // SolarMax can't deal with requests too close to one another, so just wait a moment
             try {
@@ -158,14 +162,14 @@ public class SolarMaxConnector {
     }
 
     private static Map<SolarMaxCommandKey, @Nullable String> getValuesFromSolarMax(final Socket socket,
-            final List<SolarMaxCommandKey> commandList) throws SolarMaxException {
+            final int deviceAddress, final List<SolarMaxCommandKey> commandList) throws SolarMaxException {
         OutputStream outputStream = null;
         InputStream inputStream = null;
         try {
             outputStream = socket.getOutputStream();
             inputStream = socket.getInputStream();
 
-            return getValuesFromSolarMax(outputStream, inputStream, commandList);
+            return getValuesFromSolarMax(outputStream, inputStream, deviceAddress, commandList);
         } catch (final SolarMaxException | IOException e) {
             throw new SolarMaxException("Error getting input/output streams from socket", e);
         } finally {
@@ -184,10 +188,11 @@ public class SolarMaxConnector {
     }
 
     private static Map<SolarMaxCommandKey, @Nullable String> getValuesFromSolarMax(final OutputStream outputStream,
-            final InputStream inputStream, final List<SolarMaxCommandKey> commandList) throws SolarMaxException {
+            final InputStream inputStream, final int deviceAddress, final List<SolarMaxCommandKey> commandList)
+            throws SolarMaxException {
         Map<SolarMaxCommandKey, @Nullable String> returnedValues;
         String commandString = getCommandString(commandList);
-        String request = contructRequest(commandString);
+        String request = contructRequest(deviceAddress, commandString);
         try {
             LOGGER.trace("    ==>: {}", request);
 
@@ -273,30 +278,31 @@ public class SolarMaxConnector {
         return responseMap;
     }
 
-    private static Socket getSocketConnection(final String host, int port)
+    private static Socket getSocketConnection(final String host, int portNumber)
             throws SolarMaxConnectionException, UnknownHostException {
-        port = (port == 0) ? DEFAULT_PORT : port;
+        portNumber = (portNumber == 0) ? DEFAULT_PORT : portNumber;
 
         Socket socket;
 
         try {
             socket = new Socket();
-            socket.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT);
+            socket.connect(new InetSocketAddress(host, portNumber), CONNECTION_TIMEOUT);
             socket.setSoTimeout(responseTimeout);
         } catch (final UnknownHostException e) {
             throw e;
         } catch (final IOException e) {
-            throw new SolarMaxConnectionException("Error connecting to port '" + port + "' on host '" + host + "'", e);
+            throw new SolarMaxConnectionException(
+                    "Error connecting to portNumber '" + portNumber + "' on host '" + host + "'", e);
         }
 
         return socket;
     }
 
-    public static boolean connectionTest(final String host, int port) throws UnknownHostException {
+    public static boolean connectionTest(final String host, final int portNumber) throws UnknownHostException {
         Socket socket = null;
 
         try {
-            socket = getSocketConnection(host, port);
+            socket = getSocketConnection(host, portNumber);
         } catch (SolarMaxConnectionException e) {
             return false;
         } finally {
@@ -331,9 +337,9 @@ public class SolarMaxConnector {
      * @param questions appears to be able to handle multiple commands. For now, one at a time is good fishing
      * @return the request to be sent to the SolarMax device
      */
-    static String contructRequest(final String questions) {
+    static String contructRequest(final int deviceAddress, final String questions) {
         String src = "FB";
-        String dstHex = String.format("%02X", 1); // destinationDevice defaults to 1 and is ignored with TCP/IP
+        String dstHex = String.format("%02X", deviceAddress); // destinationDevice defaults to 1
         String len = "00";
         String cs = "0000";
         String msg = "64:" + questions;
index 4edfb9bafb847aaabc54700e6fda76cfcb19bc49..c7b4261d3336d9c05fc7fa1310dad289bb9c57ed 100644 (file)
@@ -10,9 +10,11 @@ thing-type.solarmax.inverter.description = Basic thing for the SolarMax Power In
 
 # thing types config
 
+thing-type.config.solarmax.inverter.deviceAddress.label = Device Address
+thing-type.config.solarmax.inverter.deviceAddress.description = Device address for devices connected serially (defaults to 1)
 thing-type.config.solarmax.inverter.host.label = Host
 thing-type.config.solarmax.inverter.host.description = Hostname or IP Address
-thing-type.config.solarmax.inverter.portNumber.label = Port
+thing-type.config.solarmax.inverter.portNumber.label = Port Number
 thing-type.config.solarmax.inverter.portNumber.description = Port Number (defaults to 12345)
 thing-type.config.solarmax.inverter.refreshInterval.label = Refresh Interval
 thing-type.config.solarmax.inverter.refreshInterval.description = Refresh Interval in seconds (defaults to 15)
index 4fdda616b3e078b022d9d041c638ac318d03fd06..ab3ba814a9e2264098b6969594e47f796525c847 100644 (file)
                                <description>Hostname or IP Address</description>
                        </parameter>
                        <parameter name="portNumber" type="integer" required="false">
-                               <label>Port</label>
+                               <label>Port Number</label>
                                <description>Port Number (defaults to 12345)</description>
                                <default>12345</default>
                        </parameter>
+                       <parameter name="deviceAddress" type="integer" required="false">
+                               <label>Device Address</label>
+                               <description>Device address for devices connected serially (defaults to 1)</description>
+                               <default>1</default>
+                       </parameter>
                        <parameter name="refreshInterval" type="integer" required="false">
                                <label>Refresh Interval</label>
                                <description>Refresh Interval in seconds (defaults to 15)</description>
index 84c6024dbefbca1d4635d804890e7b8290fd49a1..3957bef6e92535edeb86784fbddbcb4b6ec76f71 100644 (file)
@@ -42,6 +42,7 @@ public class SolarmaxConnectorFindCommands {
 
     private static final String HOST = "192.168.1.151";
     private static final int PORT = 12345;
+    private static final int DEVICE_ADDRESS = 1;
     private static final int CONNECTION_TIMEOUT = 1000; // ms
 
     @Test
@@ -127,7 +128,7 @@ public class SolarmaxConnectorFindCommands {
 
         Map<String, @Nullable String> responseMap = null;
 
-        responseMap = getValuesFromSolarMax(HOST, PORT, commands);
+        responseMap = getValuesFromSolarMax(HOST, PORT, DEVICE_ADDRESS, commands);
 
         if (responseMap.containsKey(command)) {
             LOGGER.debug("Request: " + command + " Valid Response: " + responseMap.get(command));
@@ -139,8 +140,8 @@ public class SolarmaxConnectorFindCommands {
     /**
      * based on SolarMaxConnector.getValuesFromSolarMax
      */
-    private static Map<String, @Nullable String> getValuesFromSolarMax(final String host, int port,
-            final List<String> commandList) throws SolarMaxException {
+    private static Map<String, @Nullable String> getValuesFromSolarMax(final String host, final int portNumber,
+            final int deviceAddress, final List<String> commandList) throws SolarMaxException {
         Socket socket;
 
         Map<String, @Nullable String> returnMap = new HashMap<>();
@@ -153,7 +154,7 @@ public class SolarmaxConnectorFindCommands {
             requestsRequired = requestsRequired + 1;
         }
         for (int requestNumber = 0; requestNumber < requestsRequired; requestNumber++) {
-            LOGGER.debug("    Requesting data from {}:{} with timeout of {}ms", host, port, CONNECTION_TIMEOUT);
+            LOGGER.debug("    Requesting data from {}:{} with timeout of {}ms", host, portNumber, CONNECTION_TIMEOUT);
 
             int firstCommandNumber = requestNumber * maxConcurrentCommands;
             int lastCommandNumber = (requestNumber + 1) * maxConcurrentCommands;
@@ -163,11 +164,11 @@ public class SolarmaxConnectorFindCommands {
             List<String> commandsToSend = commandList.subList(firstCommandNumber, lastCommandNumber);
 
             try {
-                socket = getSocketConnection(host, port);
+                socket = getSocketConnection(host, portNumber);
             } catch (UnknownHostException e) {
                 throw new SolarMaxConnectionException(e);
             }
-            returnMap.putAll(getValuesFromSolarMax(socket, commandsToSend));
+            returnMap.putAll(getValuesFromSolarMax(socket, deviceAddress, commandsToSend));
 
             // SolarMax can't deal with requests too close to one another, so just wait a moment
             try {
@@ -179,7 +180,7 @@ public class SolarmaxConnectorFindCommands {
         return returnMap;
     }
 
-    private static Map<String, @Nullable String> getValuesFromSolarMax(final Socket socket,
+    private static Map<String, @Nullable String> getValuesFromSolarMax(final Socket socket, final int deviceAddress,
             final List<String> commandList) throws SolarMaxException {
         OutputStream outputStream = null;
         InputStream inputStream = null;
@@ -187,7 +188,7 @@ public class SolarmaxConnectorFindCommands {
             outputStream = socket.getOutputStream();
             inputStream = socket.getInputStream();
 
-            return getValuesFromSolarMax(outputStream, inputStream, commandList);
+            return getValuesFromSolarMax(outputStream, inputStream, deviceAddress, commandList);
         } catch (final SolarMaxException | IOException e) {
             throw new SolarMaxException("Error getting input/output streams from socket", e);
         } finally {
@@ -231,32 +232,34 @@ public class SolarmaxConnectorFindCommands {
         return characters;
     }
 
-    private static Socket getSocketConnection(final String host, int port)
+    private static Socket getSocketConnection(final String host, int portNumber)
             throws SolarMaxConnectionException, UnknownHostException {
-        port = (port == 0) ? SolarmaxConnectorFindCommands.PORT : port;
+        portNumber = (portNumber == 0) ? PORT : portNumber;
 
         Socket socket;
 
         try {
             socket = new Socket();
-            LOGGER.debug("    Connecting to " + host + ":" + port + " with a timeout of " + CONNECTION_TIMEOUT);
-            socket.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT);
+            LOGGER.debug("    Connecting to " + host + ":" + portNumber + " with a timeout of " + CONNECTION_TIMEOUT);
+            socket.connect(new InetSocketAddress(host, portNumber), CONNECTION_TIMEOUT);
             LOGGER.debug("    Connected.");
             socket.setSoTimeout(CONNECTION_TIMEOUT);
         } catch (final UnknownHostException e) {
             throw e;
         } catch (final IOException e) {
-            throw new SolarMaxConnectionException("Error connecting to port '" + port + "' on host '" + host + "'", e);
+            throw new SolarMaxConnectionException(
+                    "Error connecting to port '" + portNumber + "' on host '" + host + "'", e);
         }
 
         return socket;
     }
 
     private static Map<String, @Nullable String> getValuesFromSolarMax(final OutputStream outputStream,
-            final InputStream inputStream, final List<String> commandList) throws SolarMaxException {
+            final InputStream inputStream, final int deviceAddress, final List<String> commandList)
+            throws SolarMaxException {
         Map<String, @Nullable String> returnedValues;
         String commandString = getCommandString(commandList);
-        String request = SolarMaxConnector.contructRequest(commandString);
+        String request = SolarMaxConnector.contructRequest(deviceAddress, commandString);
         try {
             LOGGER.trace("    ==>: {}", request);