Skip to content

Commit

Permalink
[solarman] Add support for LSE-3 (LAN Stick Logger) (#17563)
Browse files Browse the repository at this point in the history
* [solarman] Added LSE-3 (LAN Stick Logger) Support (#17559)

Signed-off-by: Peter Kretz <peter.kretz@kretz-net.de>
  • Loading branch information
kretzp authored Nov 11, 2024
1 parent 66f8c82 commit 10048bc
Show file tree
Hide file tree
Showing 17 changed files with 541 additions and 37 deletions.
19 changes: 10 additions & 9 deletions bundles/org.openhab.binding.solarman/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ These data loggers are used by inverters from a lot of manufacturers, just to na

The `solarman:logger` thing supports reading data from a Solarman LSW-3 Stick Logger (it might also work with LSE-3 and maybe others) when connected to a supported inverter.

It was tested on a SUN-12K-SG04LP3-EU only, but because the implementation uses the inverter definitions created as part of Stephan Joubert's Home Assistant plugin it **might** work with the other inverters supported by the plugin.
It was tested on a SUN-12K-SG04LP3-EU only, with LAN Stick LSE-3 in RAW MODBUS solarmanLoggerMode and Wifi Stick in V5 MODBUS solarmanLoggerMode but because the implementation uses the inverter definitions created as part of Stephan Joubert's Home Assistant plugin it **might** work with the other inverters supported by the plugin.

## Thing Configuration

Expand All @@ -25,14 +25,15 @@ The IP address can be obtained from your router and the serial number can either

### `logger` Thing Configuration

| Name | Type | Description | Default | Required | Advanced |
|--------------------|---------|--------------------------------------------------------|---------|----------|----------|
| hostname | text | Hostname or IP address of the Solarman logger | N/A | yes | no |
| serialNumber | text | Serial number of the Solarman logger | N/A | yes | no |
| inverterType | text | The type of inverter connected to the logger | N/A | yes | no |
| port | integer | Port of the Solarman logger | 8899 | no | yes |
| refreshInterval | integer | Interval the device is polled in sec. | 60 | no | yes |
| additionalRequests | text | Additional requests besides the ones in the definition | N/A | no | yes |
| Name | Type | Description | Default | Required | Advanced |
|--------------------|---------|-------------------------------------------------------------------------------------------------------------------|-----------|----------|----------|
| hostname | text | Hostname or IP address of the Solarman logger | N/A | yes | no |
| serialNumber | text | Serial number of the Solarman logger | N/A | yes | no |
| inverterType | text | The type of inverter connected to the logger | N/A | yes | no |
| port | integer | Port of the Solarman logger | 8899 | no | yes |
| refreshInterval | integer | Interval the device is polled in sec. | 60 | no | yes |
| solarmanLoggerMode | option | RAW Modbus for LAN Stick LSE-3 and V5 MODBUS for most Wifi Sticks. If your Wifi stick uses Raw Modbus choose RAW. | V5 MODBUS | no | yes |
| additionalRequests | text | Additional requests besides the ones in the definition | N/A | no | yes |

The `inverterType` parameter governs what registers the binding will read from the logger and what channels it will expose.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,21 @@ public class SolarmanLoggerConfiguration {
public String serialNumber = "";
public String inverterType = "sg04lp3";
public int refreshInterval = 30;
public String solarmanLoggerMode = SolarmanLoggerMode.V5MODBUS.toString();
@Nullable
public String additionalRequests;

public SolarmanLoggerConfiguration() {
}

public SolarmanLoggerConfiguration(String hostname, Integer port, String serialNumber, String inverterType,
int refreshInterval, @Nullable String additionalRequests) {
int refreshInterval, String solarmanLoggerMode, @Nullable String additionalRequests) {
this.hostname = hostname;
this.port = port;
this.serialNumber = serialNumber;
this.inverterType = inverterType;
this.refreshInterval = refreshInterval;
this.solarmanLoggerMode = solarmanLoggerMode;
this.additionalRequests = additionalRequests;
}

Expand All @@ -67,6 +69,10 @@ public int getRefreshInterval() {
return refreshInterval;
}

public SolarmanLoggerMode getSolarmanLoggerMode() {
return SolarmanLoggerMode.valueOf(solarmanLoggerMode);
}

@Nullable
public String getAdditionalRequests() {
return additionalRequests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
import org.openhab.binding.solarman.internal.defmodel.Request;
import org.openhab.binding.solarman.internal.defmodel.Validation;
import org.openhab.binding.solarman.internal.modbus.SolarmanLoggerConnector;
import org.openhab.binding.solarman.internal.modbus.SolarmanV5Protocol;
import org.openhab.binding.solarman.internal.modbus.SolarmanProtocol;
import org.openhab.binding.solarman.internal.modbus.SolarmanProtocolFactory;
import org.openhab.binding.solarman.internal.updater.SolarmanChannelUpdater;
import org.openhab.binding.solarman.internal.updater.SolarmanProcessResult;
import org.openhab.core.thing.Channel;
Expand Down Expand Up @@ -94,7 +95,10 @@ public void initialize() {
logger.debug("Found definition for {}", config.inverterType);
}
}
SolarmanV5Protocol solarmanV5Protocol = new SolarmanV5Protocol(config);

logger.debug("Raw Type {}", config.solarmanLoggerMode);

SolarmanProtocol solarmanProtocol = SolarmanProtocolFactory.createSolarmanProtocol(config);

String additionalRequests = Objects.requireNonNullElse(config.getAdditionalRequests(), "");

Expand All @@ -110,17 +114,17 @@ public void initialize() {

scheduledFuture = scheduler
.scheduleWithFixedDelay(
() -> queryLoggerAndUpdateState(solarmanLoggerConnector, solarmanV5Protocol, mergedRequests,
() -> queryLoggerAndUpdateState(solarmanLoggerConnector, solarmanProtocol, mergedRequests,
paramToChannelMapping, solarmanChannelUpdater),
0, config.refreshInterval, TimeUnit.SECONDS);
}

private void queryLoggerAndUpdateState(SolarmanLoggerConnector solarmanLoggerConnector,
SolarmanV5Protocol solarmanV5Protocol, List<Request> mergedRequests,
SolarmanProtocol solarmanProtocol, List<Request> mergedRequests,
Map<ParameterItem, ChannelUID> paramToChannelMapping, SolarmanChannelUpdater solarmanChannelUpdater) {
try {
SolarmanProcessResult solarmanProcessResult = solarmanChannelUpdater.fetchDataFromLogger(mergedRequests,
solarmanLoggerConnector, solarmanV5Protocol, paramToChannelMapping);
solarmanLoggerConnector, solarmanProtocol, paramToChannelMapping);

if (solarmanProcessResult.hasSuccessfulResponses()) {
updateStatus(ThingStatus.ONLINE);
Expand Down Expand Up @@ -149,7 +153,7 @@ private Map<ParameterItem, ChannelUID> extractChannelMappingFromChannels(List<Ch
}

return new AbstractMap.SimpleEntry<>(new ParameterItem(label, "N/A", "N/A", bcc.uom, bcc.scale, bcc.rule,
parseRegisters(bcc.registers), "N/A", new Validation(), bcc.offset, Boolean.FALSE),
parseRegisters(bcc.registers), "N/A", new Validation(), bcc.offset, Boolean.FALSE, null),
channel.getUID());
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2010-2024 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.solarman.internal;

/**
* @author Peter Kretz - Initial contribution
*/
public enum SolarmanLoggerMode {
V5MODBUS,
RAWMODBUS
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ private Configuration buildConfigurationFromItem(ParameterItem item) {
baseChannelConfig.scale = scale;
}

baseChannelConfig.rule = item.getRule();
if (item.hasLookup() || Boolean.TRUE.equals(item.getIsstr())) {
// Set 5 for Text (String), when isstr is true or Lookup is present
baseChannelConfig.rule = 5;
} else {
baseChannelConfig.rule = item.getRule();
}

baseChannelConfig.registers = convertRegisters(item.getRegisters());
baseChannelConfig.uom = item.getUom();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2024 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.solarman.internal.defmodel;

import org.eclipse.jdt.annotation.NonNullByDefault;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
* @author Peter Kretz - Initial contribution
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@NonNullByDefault
public class Lookup {
private int key;
private String value = "";

public int getKey() {
return key;
}

public void setKey(int key) {
this.key = key;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ public class ParameterItem {
private BigDecimal offset;
@Nullable
private Boolean isstr;
private List<Lookup> lookup = new ArrayList<>();

public ParameterItem() {
}

public ParameterItem(String name, @Nullable String itemClass, @Nullable String stateClass, @Nullable String uom,
@Nullable BigDecimal scale, Integer rule, List<Integer> registers, @Nullable String icon,
@Nullable Validation validation, @Nullable BigDecimal offset, @Nullable Boolean isstr) {
@Nullable Validation validation, @Nullable BigDecimal offset, @Nullable Boolean isstr,
@Nullable List<Lookup> lookup) {
this.name = name;
this.itemClass = itemClass;
this.stateClass = stateClass;
Expand All @@ -64,6 +66,9 @@ public ParameterItem(String name, @Nullable String itemClass, @Nullable String s
this.validation = validation;
this.offset = offset;
this.isstr = isstr;
if (lookup != null) {
this.lookup = lookup;
}
}

public String getName() {
Expand Down Expand Up @@ -153,4 +158,16 @@ public void setIsstr(Boolean isstr) {
public void setItemClass(String itemClass) {
this.itemClass = itemClass;
}

public List<Lookup> getLookup() {
return lookup;
}

public void setLookup(List<Lookup> lookup) {
this.lookup = lookup;
}

public Boolean hasLookup() {
return !lookup.isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public byte[] sendRequest(byte[] reqFrame) throws SolarmanConnectionException {
return new byte[0];
}

private static String bytesToHex(byte[] bytes) {
protected static String bytesToHex(byte[] bytes) {
return IntStream.range(0, bytes.length).mapToObj(i -> String.format("%02X", bytes[i]))
.collect(Collectors.joining());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2024 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.solarman.internal.modbus;

import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarman.internal.modbus.exception.SolarmanException;

/**
* @author Peter Kretz - Initial contribution
*/
@NonNullByDefault
public interface SolarmanProtocol {

Map<Integer, byte[]> readRegisters(SolarmanLoggerConnection solarmanLoggerConnection, byte mbFunctionCode,
int firstReg, int lastReg) throws SolarmanException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2024 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.solarman.internal.modbus;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarman.internal.SolarmanLoggerConfiguration;

/**
* @author Peter Kretz - Added RAW Modbus for LAN Stick
*/
@NonNullByDefault
public class SolarmanProtocolFactory {

public static SolarmanProtocol createSolarmanProtocol(SolarmanLoggerConfiguration solarmanLoggerConfiguration) {
switch (solarmanLoggerConfiguration.getSolarmanLoggerMode()) {
case RAWMODBUS: {
return new SolarmanRawProtocol(solarmanLoggerConfiguration);
}
case V5MODBUS:
default:
return new SolarmanV5Protocol(solarmanLoggerConfiguration);
}
}
}
Loading

0 comments on commit 10048bc

Please sign in to comment.