[solarmax] Support configurable Device Address (#14366)

Signed-off-by: Jamie Townsend <jamie_townsend@hotmail.com>
This commit is contained in:
Jamie Townsend
2023-02-17 14:28:48 +01:00
committed by GitHub
parent 8274020423
commit 13202ba573
7 changed files with 68 additions and 66 deletions

View 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
}

View 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);

View 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;

View 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)

View File

@@ -37,10 +37,15 @@
<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>

View 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);