diff --git a/CODEOWNERS b/CODEOWNERS
index 41e5ecba69f28..58703359ae9ce 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -251,6 +251,7 @@
/bundles/org.openhab.binding.snmp/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.solaredge/ @alexf2015
/bundles/org.openhab.binding.solarlog/ @johannrichard
+/bundles/org.openhab.binding.solarmax/ @jamietownsend
/bundles/org.openhab.binding.somfymylink/ @loungeflyz
/bundles/org.openhab.binding.somfytahoma/ @octa22
/bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 1b0f7a7afa26e..3390302afe325 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -1236,6 +1236,11 @@
org.openhab.binding.solarlog
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.solarmax
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.somfymylink
diff --git a/bundles/org.openhab.binding.solarmax/NOTICE b/bundles/org.openhab.binding.solarmax/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/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/openhab-addons
diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md
new file mode 100644
index 0000000000000..31dab23c8d4a0
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/README.md
@@ -0,0 +1,70 @@
+# SolarMax Binding
+
+This binding supports SolarMax PV inverters.
+
+## Supported Things
+
+The SolarMax MT Series is support. (tested with 8MT2 devices)
+
+## Discovery
+
+Auto-discovery is currently not available.
+
+## Thing Configuration
+
+The IP address and port number (default 12345) of the device needs to be configured.
+
+
+```
+############################## openHAB SolarMax Binding #############################
+
+# The IP address or hostname of the SolarMax device
+#host=192.168.1.151|SolarMax1
+
+# The port number configured on the SolarMax device
+# Default is 12345
+#portNumber=12345
+
+# The refresh interval (in seconds)
+# Default is 15
+#refreshInterval=15
+```
+
+## Channels
+
+| channel | type | description |
+| ------------------------ | ------ | ------------------------------------------- |
+| LastUpdated | Point | When was the data last read from the device |
+| SoftwareVersion | Point | Software Version installed on the SolarMax device |
+| BuildNumber | Point | Firmware Build Number installed on the SolarMax device |
+| Startups | Point | Number of times the device has started |
+| AcPhase1Current | Point | Ac Phase 1 Current in Amps |
+| AcPhase2Current | Point | Ac Phase 2 Current in Amps |
+| AcPhase3Current | Point | Ac Phase 3 Current in Amps |
+| EnergyGeneratedToday | Point | Energy Generated Today in wH |
+| EnergyGeneratedTotal | Point | Energy Generated since recording began in wH |
+| OperatingHours | Point | Operating Hours since recording began in H |
+| EnergyGeneratedYesterday | Point | Energy Generated Yesterday in wH |
+| EnergyGeneratedLastMonth | Point | Energy Generated Last Month in wH |
+| EnergyGeneratedLastYear | Point | Energy Generated Last Year in wH |
+| EnergyGeneratedThisMonth | Point | Energy Generated This Month in wH |
+| EnergyGeneratedThisYear | Point | Energy Generated This Year in wH |
+| Current Power Generated | Point | Power currently being generated in w |
+| AcFrequency | Point | AcFrequency in Hz |
+| AcPhase1Voltage | Point | Ac Phase1 Voltage in V |
+| AcPhase2Voltage | Point | Ac Phase2 Voltage in V |
+| AcPhase3Voltage | Point | Ac Phase3 Voltage in V |
+| HeatSinkTemperature | Point | Heat Sink Temperature in degrees celcius |
+
+## Full Example
+
+Example Thing Configuration
+```
+UID: solarmax:inverter:7a56fa7252
+label: SolarMax Power Inverter East
+thingTypeUID: solarmax:inverter
+configuration:
+ host: 192.168.1.151
+ refreshInterval: 15
+ portNumber: 12345
+```
diff --git a/bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg b/bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg
new file mode 100644
index 0000000000000..dcbc0fffdff15
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg
@@ -0,0 +1,12 @@
+############################## openHAB SolarMax Binding #############################
+
+# The IP address or hostname of the SolarMax device
+#host=192.168.1.151|SolarMax1
+
+# The port number configured on the SolarMax device
+# Default is 12345
+#portNumber=12345
+
+# The refresh interval (in seconds)
+# Default is 15
+#refreshInterval=15
diff --git a/bundles/org.openhab.binding.solarmax/pom.xml b/bundles/org.openhab.binding.solarmax/pom.xml
new file mode 100644
index 0000000000000..32d07a6947dfa
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.1.0-SNAPSHOT
+
+
+ org.openhab.binding.solarmax
+
+ openHAB Add-ons :: Bundles :: SolarMax Binding
+
+
diff --git a/bundles/org.openhab.binding.solarmax/src/main/feature/feature.xml b/bundles/org.openhab.binding.solarmax/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..bc7ddfe8374b6
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.solarmax/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxBindingConstants.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxBindingConstants.java
new file mode 100644
index 0000000000000..8531d48a41b2e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxBindingConstants.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link SolarMaxBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+@NonNullByDefault
+public class SolarMaxBindingConstants {
+
+ private static final String BINDING_ID = "solarmax";
+ private static final String THING_TYPE_ID = "inverter";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_SOLARMAX = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
+
+ // Config - do we really need to define all the values from SolarMaxConfiguration here?? I think not
+ // ...
+
+ // Channels - do we really need to define all the values from SolarMaxChannel here?? I think not
+ // ...
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxChannel.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxChannel.java
new file mode 100644
index 0000000000000..126650d4fb5c3
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxChannel.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal;
+
+import org.openhab.binding.solarmax.internal.connector.SolarMaxCommandKey;
+
+/**
+ * The {@link SolarMaxChannel} Enum defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+public enum SolarMaxChannel {
+
+ // CHANNEL_UPDATE_VALUES_FROM_DEVICE("UpdateValuesFromDevice"),
+ CHANNEL_LAST_UPDATED("LastUpdated"), //
+ // CHANNEL_DEVICE_ADDRESS(SolarMaxCommandKey.DeviceAddress.name()),
+ CHANNEL_SOFTWARE_VERSION(SolarMaxCommandKey.SoftwareVersion.name()),
+ CHANNEL_BUILD_NUMBER(SolarMaxCommandKey.BuildNumber.name()),
+ CHANNEL_STARTUPS(SolarMaxCommandKey.Startups.name()),
+ CHANNEL_AC_PHASE1_CURRENT(SolarMaxCommandKey.AcPhase1Current.name()),
+ CHANNEL_AC_PHASE2_CURRENT(SolarMaxCommandKey.AcPhase2Current.name()),
+ CHANNEL_AC_PHASE3_CURRENT(SolarMaxCommandKey.AcPhase3Current.name()),
+ CHANNEL_ENERGY_GENERATED_TODAY(SolarMaxCommandKey.EnergyGeneratedToday.name()),
+ CHANNEL_ENERGY_GENERATED_TOTAL(SolarMaxCommandKey.EnergyGeneratedTotal.name()),
+ CHANNEL_OPERATING_HOURS(SolarMaxCommandKey.OperatingHours.name()),
+ CHANNEL_ENERGY_GENERATED_YESTERDAY(SolarMaxCommandKey.EnergyGeneratedYesterday.name()),
+ CHANNEL_ENERGY_GENERATED_LAST_MONTH(SolarMaxCommandKey.EnergyGeneratedLastMonth.name()),
+ CHANNEL_ENERGY_GENERATED_LAST_YEAR(SolarMaxCommandKey.EnergyGeneratedLastYear.name()),
+ CHANNEL_ENERGY_GENERATED_THIS_MONTH(SolarMaxCommandKey.EnergyGeneratedThisMonth.name()),
+ CHANNEL_ENERGY_GENERATED_THIS_YEAR(SolarMaxCommandKey.EnergyGeneratedThisYear.name()),
+ CHANNEL_CURRENT_POWER_GENERATED(SolarMaxCommandKey.CurrentPowerGenerated.name()),
+ CHANNEL_AC_FREQUENCY(SolarMaxCommandKey.AcFrequency.name()),
+ CHANNEL_AC_PHASE1_VOLTAGE(SolarMaxCommandKey.AcPhase1Voltage.name()),
+ CHANNEL_AC_PHASE2_VOLTAGE(SolarMaxCommandKey.AcPhase2Voltage.name()),
+ CHANNEL_AC_PHASE3_VOLTAGE(SolarMaxCommandKey.AcPhase3Voltage.name()),
+ CHANNEL_HEAT_SINK_TEMPERATUR(SolarMaxCommandKey.HeatSinkTemperature.name())
+
+ ;
+
+ private final String channelId;
+
+ private SolarMaxChannel(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxConfiguration.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxConfiguration.java
new file mode 100644
index 0000000000000..8482b9b5587e7
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxConfiguration.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link SolarMaxConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+@NonNullByDefault
+public class SolarMaxConfiguration {
+ public String host = ""; // this will always need to be overridden
+ public int portNumber = 12345; // default value is 12345
+
+ public int refreshInterval = 15; // default value is 15
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandler.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandler.java
new file mode 100644
index 0000000000000..620c8436006a3
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandler.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal;
+
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarmax.internal.connector.SolarMaxCommandKey;
+import org.openhab.binding.solarmax.internal.connector.SolarMaxConnector;
+import org.openhab.binding.solarmax.internal.connector.SolarMaxData;
+import org.openhab.binding.solarmax.internal.connector.SolarMaxException;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SolarMaxHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+public class SolarMaxHandler extends BaseThingHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(SolarMaxHandler.class);
+
+ @Nullable
+ private SolarMaxConfiguration config;
+
+ @Nullable
+ private ScheduledFuture> pollingJob;
+
+ public SolarMaxHandler(final Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void handleCommand(final ChannelUID channelUID, final Command command) {
+ // Read only
+
+ // logger.debug("handleCommand channel: {} command: {}", channelUID, command);
+ // String ch = channelUID.getId();
+ // // if (command instanceof RefreshType) {
+ // // updateChannels();
+ // // return;
+ // // }
+ // if (ch.equals(SolarMaxChannel.CHANNEL_UPDATE_VALUES_FROM_DEVICE.name())) {
+ // // if (ch.equals(SolarMaxBindingConstants.CHANNEL_UPDATE_VALUES_FROM_DEVICE)) {
+ // updateValuesFromDevice();
+ // }
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initializing SolarMax");
+
+ config = getConfigAs(SolarMaxConfiguration.class);
+
+ configurePolling(); // Setup the scheduler
+ }
+
+ /**
+ * 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);
+ if (this.config.refreshInterval > 0) {
+ if (pollingJob == null || pollingJob.isCancelled()) {
+ pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, this.config.refreshInterval,
+ TimeUnit.SECONDS);
+ }
+ }
+ }
+
+ @Override
+ public void dispose() {
+ logger.debug("Disposing SolarMax Handler Thing");
+ // isRunning = false;
+ if (pollingJob != null && !pollingJob.isCancelled()) {
+ pollingJob.cancel(true);
+ }
+ pollingJob = null;
+ }
+
+ /**
+ * Polling event used to get data from the SolarMax device
+ */
+ private Runnable pollingRunnable = () -> {
+ updateValuesFromDevice();
+ };
+
+ private synchronized void updateValuesFromDevice() {
+
+ logger.debug("Updating data from {} at {}:{} ", getThing().getUID(), this.config.host, this.config.portNumber);
+ // get the data from the SolarMax device
+ try {
+ SolarMaxData solarMaxData = SolarMaxConnector.getAllValuesFromSolarMax(config.host, config.portNumber);
+
+ if (solarMaxData.wasCommunicationSuccessful()) {
+ updateChannels(solarMaxData);
+ updateStatus(ThingStatus.ONLINE);
+ return;
+ }
+ } catch (SolarMaxException e) {
+ logger.debug("Error refreshing source {} at {}:{} - {}", getThing().getUID(), this.config.host,
+ this.config.portNumber, e.getMessage());
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "Communication error with the device. Please retry later.");
+ }
+ }
+
+ /*
+ * Update the channels
+ */
+ private void updateChannels(SolarMaxData solarMaxData) {
+ logger.debug("Updating all channels");
+ for (SolarMaxChannel channelConfig : SolarMaxChannel.values()) {
+ String channelId = channelConfig.getChannelId();
+
+ Channel channel = getThing().getChannel(channelId);
+
+ // there are two special channels, where the values don't come from the device
+ // if (channelId.equals(SolarMaxChannel.CHANNEL_UPDATE_VALUES_FROM_DEVICE.getChannelId())) {
+ // // channel isn't read from the device, so ignore this
+
+ // } else
+ if (channelId.equals(SolarMaxChannel.CHANNEL_LAST_UPDATED.getChannelId())) {
+ // channel shows when the device was last read, so handle it specially
+ State state = solarMaxData.getDataDateTime();
+ logger.debug("Update channel state: {} - {}", channelId, state);
+ updateState(channel.getUID(), state);
+
+ } else
+ // must be somthing to collect from the device, so...
+ if (solarMaxData.has(SolarMaxCommandKey.valueOf(channelId))) {
+
+ if (channel == null) {
+ logger.error("No channel found with id: {}", channelId);
+ }
+ // State state = getState(value, channel);
+ // return new DecimalType(versionAsInt);
+ State state = solarMaxData.get(SolarMaxCommandKey.valueOf(channelId));
+
+ if (channel != null && state != null) {
+ logger.debug("Update channel state: {} - {}", channelId, state);
+ updateState(channel.getUID(), state);
+ } else {
+ logger.debug("Error refreshing channel {}: {}", getThing().getUID(), channelId);
+
+ }
+ }
+
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandlerFactory.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandlerFactory.java
new file mode 100644
index 0000000000000..fc5b8a1487afd
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandlerFactory.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal;
+
+import static org.openhab.binding.solarmax.internal.SolarMaxBindingConstants.THING_TYPE_SOLARMAX;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SolarMaxHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.solarmax", service = ThingHandlerFactory.class)
+public class SolarMaxHandlerFactory extends BaseThingHandlerFactory {
+ private final Logger logger = LoggerFactory.getLogger(SolarMaxHandlerFactory.class);
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SOLARMAX);
+
+ @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_SOLARMAX.equals(thingTypeUID)) {
+ logger.debug("Creating Handler {}", thing.getUID());
+ return new SolarMaxHandler(thing);
+ } else {
+ logger.warn("Creating Handler failed - unsupported Thing Type {}", thingTypeUID);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxCommandKey.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxCommandKey.java
new file mode 100644
index 0000000000000..c21bad3918db2
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxCommandKey.java
@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal.connector;
+
+/**
+ * The {@link SolarMaxCommandKey} enum defines the commands that are understood by the SolarMax device
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+public enum SolarMaxCommandKey {
+
+ // Valid commands which returned a non-null value during testing
+ // DeviceAddress("ADR"), // device number - only used if the devices are linked serially
+ // UNKNOWN_AMM("AMM"), //
+ BuildNumber("BDN"), //
+ Startups("CAC"), //
+ // UNKNOWN_CID("CID"), //
+ // UNKNOWN_CPG("CPG"), //
+ // UNKNOWN_CPL("CPL"), //
+ // UNKNOWN_CP1("CP1"), //
+ // UNKNOWN_CP2("CP2"), //
+ // UNKNOWN_CP3("CP3"), //
+ // UNKNOWN_CP4("CP4"), //
+ // UNKNOWN_CP5("CP5"), //
+ // UNKNOWN_CYC("CYC"), //
+ // UNKNOWN_DIN("DIN"), //
+ // UNKNOWN_DMO("DMO"), //
+ // UNKNOWN_ETH("ETH"), //
+ // UNKNOWN_FH2("FH2"), //
+ // UNKNOWN_FQR("FQR"), //
+ // UNKNOWN_FWV("FWV"), //
+ // UNKNOWN_IAA("IAA"), //
+ // UNKNOWN_IED("IED"), //
+ // UNKNOWN_IEE("IEE"), //
+ // UNKNOWN_IEM("IEM"), //
+ // UNKNOWN_ILM("ILM"), //
+ AcPhase1Current("IL1"), //
+ AcPhase2Current("IL2"), //
+ AcPhase3Current("IL3"), //
+ // UNKNOWN_IP4("IP4"), //
+ // UNKNOWN_ISL("ISL"), //
+ // UNKNOWN_ITS("ITS"), //
+ EnergyGeneratedToday("KDY"), //
+ // UNKNOWN_KFS("KFS"), //
+ OperatingHours("KHR"), //
+ // UNKNOWN_KHS("KHS"), //
+ EnergyGeneratedYesterday("KLD"), //
+ EnergyGeneratedLastMonth("KLM"), //
+ EnergyGeneratedLastYear("KLY"), //
+ EnergyGeneratedThisMonth("KMT"), //
+ // UNKNOWN_KTS("KTS"), //
+ EnergyGeneratedTotal("KT0"), //
+ EnergyGeneratedThisYear("KYR"), //
+ // Language("LAN"), //
+ // MacAddress("MAC"), //
+ CurrentPowerGenerated("PAC"), //
+ // UNKNOWN_PAE("PAE"), //
+ // UNKNOWN_PAM("PAM"), //
+ // UNKNOWN_PDA("PDA"), //
+ // UNKNOWN_PDC("PDC"), //
+ // UNKNOWN_PFA("PFA"), //
+ // PowerInstalled("PIN"), //
+ // UNKNOWN_PLR("PLR"), //
+ // UNKNOWN_PPC("PPC"), //
+ // AcPowerPercent("PRL"), //
+ // UNKNOWN_PSF("PSF"), //
+ // UNKNOWN_PSR("PSR"), //
+ // UNKNOWN_PSS("PSS"), //
+ // UNKNOWN_QAC("QAC"), //
+ // UNKNOWN_QMO("QMO"), //
+ // UNKNOWN_QUC("QUC"), //
+ // UNKNOWN_RA1("RA1"), //
+ // UNKNOWN_RA2("RA2"), //
+ // UNKNOWN_RB1("RB1"), //
+ // UNKNOWN_RB2("RB2"), //
+ // UNKNOWN_REL("REL"), //
+ // UNKNOWN_RH1("RH1"), //
+ // UNKNOWN_RH2("RH2"), //
+ // UNKNOWN_RPR("RPR"), //
+ // UNKNOWN_RSD("RSD"), //
+ // UNKNOWN_SAC("SAC"), //
+ // UNKNOWN_SAL("SAL"), //
+ // UNKNOWN_SAM("SAM"), //
+ // UNKNOWN_SCH("SCH"), //
+ // UNKNOWN_SNM("SNM"), // IP Broadcast Address??
+ // UNKNOWN_SPS("SPS"), //
+ // UNKNOWN_SRD("SRD"), //
+ // UNKNOWN_SRS("SRS"), //
+ SoftwareVersion("SWV"), //
+ // OperatingState("SYS"), //
+ // UNKNOWN_TCP("TCP"), // probably port number (12345)
+ // UNKNOWN_TI1("TI1"), //
+ HeatSinkTemperature("TKK"), //
+ // UNKNOWN_TL1("TL1"), //
+ // UNKNOWN_TL3("TL3"), //
+ // UNKNOWN_TND("TND"), //
+ AcFrequency("TNF"), //
+ // UNKNOWN_TNH("TNH"), //
+ // UNKNOWN_TNL("TNL"), //
+ // UNKNOWN_TP1("TP1"), //
+ // UNKNOWN_TP2("TP2"), //
+ // UNKNOWN_TP3("TP3"), //
+ // UNKNOWN_TV0("TV0"), //
+ // UNKNOWN_TV1("TV1"), //
+ // Type("TYP"), //
+ // UNKNOWN_UA2("UA2"), //
+ // UNKNOWN_UB2("UB2"), //
+ // UNKNOWN_UGD("UGD"), //
+ // UNKNOWN_UI1("UI1"), //
+ // UNKNOWN_UI2("UI2"), //
+ // UNKNOWN_UI3("UI3"), //
+ // UNKNOWN_ULH("ULH"), //
+ // UNKNOWN_ULL("ULL"), //
+ AcPhase1Voltage("UL1"), //
+ AcPhase2Voltage("UL2"), //
+ AcPhase3Voltage("UL3"), //
+ // UNKNOWN_UMX("UMX"), //
+ // UNKNOWN_UM1("UM1"), //
+ // UNKNOWN_UM2("UM2"), //
+ // UNKNOWN_UM3("UM3"), //
+ // UNKNOWN_UPD("UPD"), //
+ // UNKNOWN_UZK("UZK"), //
+ // UNKNOWN_VCM("VCM"), //
+ UNKNOWN("UNKNOWN") // really unknown - shouldn't ever be sent to the device
+ ;
+
+ // Valid commands which returned a null/empty value during testing
+ // FFK, FRT, GCP, ITN, PLD, PLE, PLF, PLS, PPO, TV2, VLE, VLI, VLO
+
+ private String commandKey;
+
+ private SolarMaxCommandKey(String commandKey) {
+ this.commandKey = commandKey;
+ }
+
+ public String getCommandKey() {
+ return this.commandKey;
+ }
+
+ public static SolarMaxCommandKey getKeyFromString(String commandKey) {
+
+ for (SolarMaxCommandKey key : SolarMaxCommandKey.values()) {
+ if (key.commandKey.equals(commandKey)) {
+ return key;
+ }
+ }
+ return UNKNOWN;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnectionException.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnectionException.java
new file mode 100644
index 0000000000000..b57e78d1ce374
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnectionException.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal.connector;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link SolarMaxException} Exception is used for connection problems trying to communicate with the SolarMax
+ * device.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+@NonNullByDefault
+public class SolarMaxConnectionException extends SolarMaxException {
+
+ private static final long serialVersionUID = 1L;
+
+ public SolarMaxConnectionException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public SolarMaxConnectionException(final Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnector.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnector.java
new file mode 100644
index 0000000000000..a9a1daddee59b
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnector.java
@@ -0,0 +1,409 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal.connector;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * The {@link SolarMaxConnector} class is used to communicated with the SolarMax device (on a binary level)
+ *
+ * With a little help from https://github.com/sushiguru/solar-pv/blob/master/solmax/pv.php
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+public class SolarMaxConnector {
+
+ /**
+ * default port number of SolarMax devices is...
+ */
+ final private static int DEFAULT_PORT = 12345;
+
+ private static final Logger logger = LoggerFactory.getLogger(SolarMaxConnector.class);
+
+ /**
+ * default timeout for socket connections is 1 second
+ */
+ private static int connectionTimeout = 1000;
+
+ /**
+ * default timeout for socket responses is 10 seconds
+ */
+ private static int responseTimeout = 10000;
+
+ /**
+ * gets all known values from the SolarMax device addressable at host:port
+ *
+ * @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 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 {
+
+ List commandList = new ArrayList<>();
+
+ for (SolarMaxCommandKey solarMaxCommandKey : SolarMaxCommandKey.values()) {
+ if (solarMaxCommandKey != SolarMaxCommandKey.UNKNOWN) {
+ commandList.add(solarMaxCommandKey);
+ }
+ }
+
+ SolarMaxData solarMaxData = new SolarMaxData();
+
+ // 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 valuesFromSolarMax = getValuesFromSolarMax(host, port, commandList);
+ boolean allCommandsAnswered = true;
+ for (SolarMaxCommandKey solarMaxCommandKey : commandList) {
+ if (!valuesFromSolarMax.containsKey(solarMaxCommandKey)) {
+ allCommandsAnswered = false;
+ break;
+ }
+ }
+ solarMaxData.setDataDateTime(ZonedDateTime.now());
+ solarMaxData.setCommunicationSuccessful(allCommandsAnswered);
+ solarMaxData.setData(valuesFromSolarMax);
+
+ return solarMaxData;
+ }
+
+ /**
+ * gets values from the SolarMax device addressable at host:port
+ *
+ * @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 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 getValuesFromSolarMax(final String host, int port,
+ final List commandList) throws SolarMaxException {
+
+ Socket socket;
+
+ Map returnMap = new HashMap<>();
+
+ // SolarMax can't answer correclty if too many commands are send in a single request, so limit it to 16 at a
+ // time
+ int maxConcurrentCommands = 16;
+ int requestsRequired = (commandList.size() / maxConcurrentCommands);
+ if (commandList.size() % maxConcurrentCommands != 0) {
+ requestsRequired = requestsRequired + 1;
+ }
+ for (int requestNumber = 0; requestNumber < requestsRequired; requestNumber++) {
+ logger.debug(" Requesting data from {}:{} with timeout of {}ms", host, port, responseTimeout);
+
+ int firstCommandNumber = requestNumber * maxConcurrentCommands;
+ int lastCommandNumber = (requestNumber + 1) * maxConcurrentCommands;
+ if (lastCommandNumber > commandList.size()) {
+ lastCommandNumber = commandList.size();
+ }
+ List commandsToSend = commandList.subList(firstCommandNumber, lastCommandNumber);
+
+ try {
+ socket = getSocketConnection(host, port);
+ } catch (UnknownHostException e) {
+ throw new SolarMaxConnectionException(e);
+ }
+ returnMap.putAll(getValuesFromSolarMax(socket, commandsToSend));
+
+ // SolarMax can't deal with requests too close to one another, so just wait a moment
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ return returnMap;
+ }
+
+ private static String getCommandString(List commandList) {
+ String commandString = "";
+ for (SolarMaxCommandKey command : commandList) {
+ if (commandString != "") {
+ commandString = commandString + ";";
+ }
+ commandString = commandString + command.getCommandKey();
+ }
+ return commandString;
+ }
+
+ private static Map getValuesFromSolarMax(final Socket socket,
+ final List commandList) throws SolarMaxException {
+ OutputStream outputStream = null;
+ InputStream inputStream = null;
+ try {
+ outputStream = socket.getOutputStream();
+ inputStream = socket.getInputStream();
+
+ return getValuesFromSolarMax(outputStream, inputStream, commandList);
+
+ } catch (final SolarMaxException | IOException e) {
+ throw new SolarMaxException("Error getting input/output streams from socket", e);
+ } finally {
+ try {
+ socket.close();
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ } catch (final IOException e) {
+ // ignore the error, we're dying anyway...
+ }
+ }
+ }
+
+ private static Map getValuesFromSolarMax(final OutputStream outputStream,
+ final InputStream inputStream, final List commandList) throws SolarMaxException {
+
+ Map returnedValues;
+ String commandString = getCommandString(commandList);
+ String request = contructRequest(commandString);
+ try {
+
+ // hard code it for now
+ // request = "{FB;01;46|64:KDY;KMT;KYR;KT0;TNF;TKK;PAC;PRL;IL1;IDC;UL1;UDC;SYS|1199}";
+ // send the message out
+ logger.trace(" ==>: {}", request);
+
+ outputStream.write(request.getBytes());
+ // outputStream.flush();
+
+ String response = "";
+ byte[] responseByte = new byte[1];
+
+ // get everything from the stream
+ while (true) {
+ // read one byte from the stream
+ int bytesRead = inputStream.read(responseByte);
+
+ // if there was nothing left, break
+ if (bytesRead < 1) {
+ break;
+ }
+
+ // add the received byte to the response
+ final String responseString = new String(responseByte);
+ response = response + responseString;
+
+ // if it was the final expected character "}", break
+ if ("}".equals(responseString)) {
+ break;
+ }
+ }
+
+ logger.trace(" <==: {}", response);
+
+ if (!validateResponse(response)) {
+ throw new SolarMaxException("Invalid response received: " + response);
+ }
+
+ returnedValues = extractValuesFromResponse(response);
+
+ return returnedValues;
+
+ } catch (IOException e) {
+ logger.debug("Error communicating via input/output streams: {} ", e.getMessage());
+ throw new SolarMaxException(e);
+ }
+ }
+
+ /**
+ * @param response e.g.
+ * "{01;FB;6D|64:KDY=82;KMT=8F;KYR=23F7;KT0=72F1;TNF=1386;TKK=28;PAC=1F70;PRL=28;IL1=236;UL1=8F9;SYS=4E28,0|19E5}"
+ * @return a map of keys and values
+ */
+ static Map extractValuesFromResponse(String response) {
+
+ final Map responseMap = new HashMap<>();
+
+ // in case there is no response
+ if (response.indexOf("|") == -1) {
+ logger.warn("Response doesn't contain data. Response: {}", response);
+ return responseMap;
+ }
+
+ // extract the body first
+ // start by getting the part of the response between the two pipes
+ String body = response.substring(response.indexOf("|") + 1, response.lastIndexOf("|"));
+
+ // the name/value pairs now lie after the ":"
+ body = body.substring(body.indexOf(":") + 1);
+
+ // split into an array of name=value pairs
+ String[] entries = body.split(";");
+ for (String entry : entries) {
+
+ if (entry.length() != 0) {
+ // could be split on "=" instead of fixed length or made to respect length of command, but they're all 3
+ // characters long (then plus "=")
+ String str = entry.substring(0, 3);
+
+ String responseValue = (entry.length() >= 5) ? entry.substring(4) : null;
+
+ SolarMaxCommandKey key = SolarMaxCommandKey.getKeyFromString(str);
+ if (key != SolarMaxCommandKey.UNKNOWN) {
+ responseMap.put(key, responseValue);
+ }
+ }
+ }
+
+ return responseMap;
+ }
+
+ private static Socket getSocketConnection(final String host, int port)
+ throws SolarMaxConnectionException, UnknownHostException {
+
+ port = (port == 0) ? DEFAULT_PORT : port;
+
+ Socket socket;
+
+ try {
+ socket = new Socket();
+ socket.connect(new InetSocketAddress(host, port), connectionTimeout);
+ 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);
+ }
+
+ return socket;
+ }
+
+ public static boolean connectionTest(final String host, int port) throws UnknownHostException {
+
+ Socket socket = null;
+
+ try {
+ socket = getSocketConnection(host, port);
+ } catch (SolarMaxConnectionException e) {
+ return false;
+ } finally {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ // ignore any error while trying to close the socket
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return timeout for connections in milliseconds
+ */
+ public static int getConnectionTimeout() {
+ return connectionTimeout;
+ }
+
+ /**
+ * @param connectionTimeout timeout for connections in milliseconds
+ */
+ public static void setConnectionTimeout(int connectionTimeout) {
+ SolarMaxConnector.connectionTimeout = connectionTimeout;
+ }
+
+ /**
+ * @return timeout for responses in milliseconds
+ */
+ public static int getResponseTimeout() {
+ return responseTimeout;
+ }
+
+ /**
+ * @param responseTimeout timeout for responses in milliseconds
+ */
+ public static void setResponseTimeout(int responseTimeout) {
+ SolarMaxConnector.responseTimeout = responseTimeout;
+ }
+
+ /**
+ * @param destinationDevice device number - used if devices are daisy-chained. Normally it will be "1"
+ * @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) {
+ String src = "FB";
+ String dstHex = String.format("%02X", 1); // destinationDevice defaults to 1 and is ignored with TCP/IP
+ String len = "00";
+ String cs = "0000";
+ // String msg = is_array(questions) ? "64:" + implode(';', questions) : "64:" + questions;
+ String msg = "64:" + questions;
+ int lenInt = ("{" + src + ";" + dstHex + ";" + len + "|" + msg + "|" + cs + "}").length();
+
+ // given the following, I'd expect problems if the request is longer than 255 characters. Since I'm not sure
+ // though, I won't fixe what isn't (yet) broken
+ String lenHex = String.format("%02X", lenInt);
+
+ String checksum = calculateChecksum16(src + ";" + dstHex + ";" + lenHex + "|" + msg + "|");
+
+ return "{" + src + ";" + dstHex + ";" + lenHex + "|" + msg + "|" + checksum + "}";
+ }
+
+ /**
+ * calculates the "checksum16" of the given string argument
+ */
+ static String calculateChecksum16(String str) {
+
+ byte[] bytes = str.getBytes();
+ int sum = 0;
+
+ // loop through each of the bytes and add them together
+ for (byte aByte : bytes) {
+ sum = sum + aByte;
+ }
+
+ // calculate the "checksum16"
+ sum = sum % (int) Math.pow(2, 16);
+
+ // return Integer.toHexString(sum);
+ return String.format("%04X", sum);
+ }
+
+ static boolean validateResponse(final String header) {
+
+ // // FIXME: 12.11.2016 whole response will be passed in
+ // final String patternString = "/\\{([0-9A-F]{2});FB;([0-9A-F]{2})/";
+ // final Pattern pattern = Pattern.compile(patternString);
+
+ // final Matcher matcher = pattern.matcher(header);
+
+ // boolean matches = matcher.matches();
+ // // return matches;
+ // // FIXME: 10.11.2016
+ return true;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxData.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxData.java
new file mode 100644
index 0000000000000..953e1c631aca3
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxData.java
@@ -0,0 +1,241 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal.connector;
+
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link SolarMaxData} class is a POJO for storing the values returned from the SolarMax device and accessing the
+ * (decoded) values
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+public class SolarMaxData {
+
+ private ZonedDateTime dataDateTime;
+ private boolean communicationSuccessful;
+ private final Map data = new HashMap<>();
+
+ public State getDataDateTime() {
+ return new DateTimeType(dataDateTime);
+ }
+
+ public boolean has(SolarMaxCommandKey key) {
+ return data.containsKey(key);
+ }
+
+ public State get(SolarMaxCommandKey key) {
+ switch (key) {
+ case SoftwareVersion:
+ return getSoftwareVersion();
+
+ case BuildNumber:
+ return getBuildNumber();
+
+ case Startups:
+ return getStartups();
+
+ case AcPhase1Current:
+ return getAcPhase1Current();
+
+ case AcPhase2Current:
+ return getAcPhase2Current();
+
+ case AcPhase3Current:
+ return getAcPhase3Current();
+
+ case EnergyGeneratedToday:
+ return getEnergyGeneratedToday();
+
+ case EnergyGeneratedTotal:
+ return getEnergyGeneratedTotal();
+
+ case OperatingHours:
+ return getOperatingHours();
+
+ case EnergyGeneratedYesterday:
+ return getEnergyGeneratedYesterday();
+
+ case EnergyGeneratedLastMonth:
+ return getEnergyGeneratedLastMonth();
+
+ case EnergyGeneratedLastYear:
+ return getEnergyGeneratedLastYear();
+
+ case EnergyGeneratedThisMonth:
+ return getEnergyGeneratedThisMonth();
+
+ case EnergyGeneratedThisYear:
+ return getEnergyGeneratedThisYear();
+
+ case CurrentPowerGenerated:
+ return getCurrentPowerGenerated();
+
+ case AcFrequency:
+ return getAcFrequency();
+
+ case AcPhase1Voltage:
+ return getAcPhase1Voltage();
+
+ case AcPhase2Voltage:
+ return getAcPhase2Voltage();
+
+ case AcPhase3Voltage:
+ return getAcPhase3Voltage();
+
+ case HeatSinkTemperature:
+ return getHeatSinkTemperature();
+
+ default:
+ return null;
+ }
+ }
+
+ public void setDataDateTime(ZonedDateTime dataDateTime) {
+ this.dataDateTime = dataDateTime;
+ }
+
+ public boolean wasCommunicationSuccessful() {
+ return this.communicationSuccessful;
+ }
+
+ public void setCommunicationSuccessful(boolean communicationSuccessful) {
+ this.communicationSuccessful = communicationSuccessful;
+ }
+
+ public DecimalType getSoftwareVersion() {
+ return getIntegerValueFrom(SolarMaxCommandKey.SoftwareVersion);
+ }
+
+ public DecimalType getBuildNumber() {
+ return getIntegerValueFrom(SolarMaxCommandKey.BuildNumber);
+ }
+
+ public DecimalType getStartups() {
+ return getIntegerValueFrom(SolarMaxCommandKey.Startups);
+ }
+
+ public DecimalType getAcPhase1Current() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcPhase1Current, 0.01);
+ }
+
+ public DecimalType getAcPhase2Current() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcPhase2Current, 0.01);
+ }
+
+ public DecimalType getAcPhase3Current() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcPhase3Current, 0.01);
+ }
+
+ public DecimalType getEnergyGeneratedToday() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedToday, 100);
+ }
+
+ public DecimalType getEnergyGeneratedTotal() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedTotal, 1000);
+ }
+
+ public DecimalType getOperatingHours() {
+ return getIntegerValueFrom(SolarMaxCommandKey.OperatingHours);
+ }
+
+ public DecimalType getEnergyGeneratedYesterday() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedYesterday, 100);
+ }
+
+ public DecimalType getEnergyGeneratedLastMonth() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedLastMonth, 1000);
+ }
+
+ public DecimalType getEnergyGeneratedLastYear() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedLastYear, 1000);
+ }
+
+ public DecimalType getEnergyGeneratedThisMonth() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedThisMonth, 1000);
+ }
+
+ public DecimalType getEnergyGeneratedThisYear() {
+ return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedThisYear, 1000);
+ }
+
+ public DecimalType getCurrentPowerGenerated() {
+ return getIntegerValueFrom(SolarMaxCommandKey.CurrentPowerGenerated, 0.5);
+ }
+
+ public DecimalType getAcFrequency() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcFrequency, 0.01);
+ }
+
+ public DecimalType getAcPhase1Voltage() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcPhase1Voltage, 0.1);
+ }
+
+ public DecimalType getAcPhase2Voltage() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcPhase2Voltage, 0.1);
+ }
+
+ public DecimalType getAcPhase3Voltage() {
+ return getDecimalValueFrom(SolarMaxCommandKey.AcPhase3Voltage, 0.1);
+ }
+
+ public DecimalType getHeatSinkTemperature() {
+ return getIntegerValueFrom(SolarMaxCommandKey.HeatSinkTemperature);
+ }
+
+ private DecimalType getDecimalValueFrom(SolarMaxCommandKey solarMaxCommandKey, double multiplyByFactor) {
+ if (this.data.containsKey(solarMaxCommandKey)) {
+ String valueString = this.data.get(solarMaxCommandKey);
+ if (valueString != null) {
+ int valueInt = Integer.parseInt(valueString, 16);
+ return new DecimalType((float) valueInt * multiplyByFactor);
+ }
+ return null;
+ }
+ return null;
+ }
+
+ private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey, double multiplyByFactor) {
+ if (this.data.containsKey(solarMaxCommandKey)) {
+ String valueString = this.data.get(solarMaxCommandKey);
+ if (valueString != null) {
+ int valueInt = Integer.parseInt(valueString, 16);
+ return new DecimalType((int) (valueInt * multiplyByFactor));
+ }
+ return null;
+ }
+ return null;
+ }
+
+ private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey) {
+ if (this.data.containsKey(solarMaxCommandKey)) {
+ String valueString = this.data.get(solarMaxCommandKey);
+ if (valueString != null) {
+ int valueInt = Integer.parseInt(valueString, 16);
+ return new DecimalType(valueInt);
+ }
+ return null;
+ }
+ return null;
+ }
+
+ protected void setData(Map data) {
+ this.data.putAll(data);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxException.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxException.java
new file mode 100644
index 0000000000000..c4cd118338c49
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxException.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal.connector;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link SolarMaxException} Exception is used for general exceptions related to communications with the SolarMax
+ * device.
+ *
+ * @author Jamie Townsend - Initial contribution
+ */
+@NonNullByDefault
+public class SolarMaxException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public SolarMaxException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public SolarMaxException(final Throwable cause) {
+ super(cause);
+ }
+
+ public SolarMaxException(final String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644
index 0000000000000..c92434fff9952
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/binding/binding.xml
@@ -0,0 +1,9 @@
+
+
+
+ SolarMax Binding
+ This is the binding for SolarMax power inverters, particularly the MT Series
+
+
diff --git a/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..883b9121f4832
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+ Basic thing for the SolarMax Power Inverter binding
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hostname or IP Address
+
+
+
+ Port Number (defaults to 12345)
+ 12345
+
+
+
+ Refresh Interval in seconds (defaults to 15)
+ 15
+
+
+
+
+
+
+
+
+ DateTime
+
+ When was the data last read from the device
+
+
+
+
+ Number
+
+ Software Version installed on the SolarMax device
+
+
+
+
+ Number
+
+ Firmware Build Number installed on the SolarMax device
+
+
+
+
+ Number
+
+ Number of times the device has started
+
+
+
+
+ Number
+
+ Ac Phase 1 Current in Amps
+
+
+
+
+ Number
+
+ Ac Phase 2 Current in Amps
+
+
+
+
+ Number
+
+ Ac Phase 3 Current in Amps
+
+
+
+
+ Number
+
+ Energy Generated Today in wH
+
+
+
+
+ Number
+
+ Energy Generated Total since recording began in wH
+
+
+
+
+ Number
+
+ Operating Hours since recording began in H
+
+
+
+
+ Number
+
+ Energy Generated Yesterday in wH
+
+
+
+
+ Number
+
+ Energy Generated Last Month in wH
+
+
+
+
+ Number
+
+ Energy Generated Last Year in wH
+
+
+
+
+ Number
+
+ Energy Generated This Month in wH
+
+
+
+
+ Number
+
+ Energy Generated This Year in wH
+
+
+
+
+ Number
+
+ Power currently being generated in w
+
+
+
+
+ Number
+
+ AcFrequency in Hz
+
+
+
+
+ Number
+
+ Ac Phase1 Voltage in V
+
+
+
+
+ Number
+
+ Ac Phase2 Voltage in V
+
+
+
+
+ Number
+
+ Ac Phase3 Voltage in V
+
+
+
+
+ Number
+
+ Heat Sink Temperature in degrees celcius
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarMaxDataTest.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarMaxDataTest.java
new file mode 100644
index 0000000000000..4fb3bb49933ec
--- /dev/null
+++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarMaxDataTest.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2010-2021 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.solarmax.internal.connector;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+
+/**
+ * The {@link SolarMaxDataTest} class is used to test the {@link SolaMaxData} class.
+ *
+ * @author Johann Richard - Initial contribution
+ */
+@NonNullByDefault
+public class SolarMaxDataTest {
+
+ @Test
+ public void gettersTest() throws Exception {
+
+ // SolarMaxData solarMaxData = new SolarMaxData().dataDateTime()
+ }
+
+ @Test
+ public void dataDateTimeGetterSetterTest() throws Exception {
+
+ // dataDateTime shouldn't be a problem, but check it anyway
+ ZonedDateTime dateTimeOriginal = ZonedDateTime.now();
+ ZonedDateTime dateTimeUpdated = dateTimeOriginal.plusDays(2);
+
+ SolarMaxData solarMaxData = new SolarMaxData();
+
+ solarMaxData.setDataDateTime(dateTimeOriginal);
+ assertEquals(new DateTimeType(dateTimeOriginal), solarMaxData.getDataDateTime());
+
+ solarMaxData.setDataDateTime(dateTimeUpdated);
+ assertEquals(new DateTimeType(dateTimeUpdated), solarMaxData.getDataDateTime());
+ }
+
+ @Test
+ public void valueGetterSetterTest() throws Exception {
+
+ String softwareVersionOriginal = "3B8B"; // 15243 in hex
+ String softwareVersionUpdated = "3B8C"; // 15244 in hex
+
+ SolarMaxData solarMaxData = new SolarMaxData();
+
+ Map dataOrig = new HashMap<>();
+ dataOrig.put(SolarMaxCommandKey.SoftwareVersion, softwareVersionOriginal);
+ solarMaxData.setData(dataOrig);
+ DecimalType origVersion = solarMaxData.get(SolarMaxCommandKey.SoftwareVersion).as(DecimalType.class);
+ assertNotNull(origVersion);
+ assertEquals(Integer.parseInt(softwareVersionOriginal, 16), origVersion.intValue());
+
+ Map dataUpdated = new HashMap<>();
+ dataUpdated.put(SolarMaxCommandKey.SoftwareVersion, softwareVersionUpdated);
+ solarMaxData.setData(dataUpdated);
+ DecimalType updatedVersion = solarMaxData.get(SolarMaxCommandKey.SoftwareVersion).as(DecimalType.class);
+ assertNotEquals(origVersion, updatedVersion);
+ }
+}
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 3c6b37c4875a2..0b1fc755a0cb8 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -1,574 +1,1124 @@
-
-
+
+
4.0.0
-
+
+
org.openhab.addons
+
org.openhab.addons.reactor
+
3.1.0-SNAPSHOT
+
-
+
org.openhab.addons.bundles
+
org.openhab.addons.reactor.bundles
+
pom
-
+
openHAB Add-ons :: Bundles
-
+
+
+
org.openhab.automation.groovyscripting
+
org.openhab.automation.jythonscripting
+
org.openhab.automation.pidcontroller
+
+
org.openhab.io.homekit
+
org.openhab.io.hueemulation
+
org.openhab.io.imperihome
+
org.openhab.io.neeo
+
org.openhab.io.openhabcloud
+
+
org.openhab.transform.bin2json
+
org.openhab.transform.exec
+
org.openhab.transform.javascript
+
org.openhab.transform.jinja
+
org.openhab.transform.jsonpath
+
org.openhab.transform.map
+
org.openhab.transform.regex
+
org.openhab.transform.scale
+
org.openhab.transform.xpath
+
org.openhab.transform.xslt
+
+
org.openhab.binding.adorne
+
org.openhab.binding.airquality
+
org.openhab.binding.airvisualnode
+
org.openhab.binding.alarmdecoder
+
org.openhab.binding.allplay
+
org.openhab.binding.amazondashbutton
+
org.openhab.binding.amazonechocontrol
+
org.openhab.binding.ambientweather
+
org.openhab.binding.androiddebugbridge
+
org.openhab.binding.astro
+
org.openhab.binding.atlona
+
org.openhab.binding.autelis
+
org.openhab.binding.automower
+
org.openhab.binding.avmfritz
+
org.openhab.binding.bigassfan
+
org.openhab.binding.bluetooth
+
org.openhab.binding.bluetooth.airthings
+
org.openhab.binding.bluetooth.am43
+
org.openhab.binding.bluetooth.bluegiga
+
org.openhab.binding.bluetooth.bluez
+
org.openhab.binding.bluetooth.blukii
+
org.openhab.binding.bluetooth.daikinmadoka
+
org.openhab.binding.bluetooth.enoceanble
+
org.openhab.binding.bluetooth.generic
+
org.openhab.binding.bluetooth.govee
+
org.openhab.binding.bluetooth.roaming
+
org.openhab.binding.bluetooth.ruuvitag
+
org.openhab.binding.boschindego
+
org.openhab.binding.boschshc
+
org.openhab.binding.bosesoundtouch
+
org.openhab.binding.broadlinkthermostat
+
org.openhab.binding.bsblan
+
org.openhab.binding.bticinosmarther
+
org.openhab.binding.buienradar
+
org.openhab.binding.caddx
+
org.openhab.binding.cbus
+
org.openhab.binding.chromecast
+
org.openhab.binding.cm11a
+
org.openhab.binding.comfoair
+
org.openhab.binding.coolmasternet
+
org.openhab.binding.coronastats
+
org.openhab.binding.daikin
+
org.openhab.binding.danfossairunit
+
org.openhab.binding.darksky
+
org.openhab.binding.deconz
+
org.openhab.binding.denonmarantz
+
org.openhab.binding.digiplex
+
org.openhab.binding.digitalstrom
+
org.openhab.binding.dlinksmarthome
+
org.openhab.binding.dmx
+
org.openhab.binding.doorbird
+
org.openhab.binding.draytonwiser
+
org.openhab.binding.dscalarm
+
org.openhab.binding.dsmr
+
org.openhab.binding.dwdpollenflug
+
org.openhab.binding.dwdunwetter
+
org.openhab.binding.ecobee
+
org.openhab.binding.elerotransmitterstick
+
org.openhab.binding.energenie
+
org.openhab.binding.enigma2
+
org.openhab.binding.enocean
+
org.openhab.binding.enturno
+
org.openhab.binding.epsonprojector
+
org.openhab.binding.etherrain
+
org.openhab.binding.evohome
+
org.openhab.binding.exec
+
org.openhab.binding.feed
+
org.openhab.binding.feican
+
org.openhab.binding.fmiweather
+
org.openhab.binding.folderwatcher
+
org.openhab.binding.folding
+
org.openhab.binding.foobot
+
org.openhab.binding.freebox
+
org.openhab.binding.fronius
+
org.openhab.binding.fsinternetradio
+
org.openhab.binding.ftpupload
+
org.openhab.binding.gardena
+
org.openhab.binding.gce
+
org.openhab.binding.generacmobilelink
+
org.openhab.binding.goecharger
+
org.openhab.binding.gpio
+
org.openhab.binding.globalcache
+
org.openhab.binding.gpstracker
+
org.openhab.binding.gree
+
org.openhab.binding.groheondus
+
org.openhab.binding.harmonyhub
+
org.openhab.binding.haywardomnilogic
+
org.openhab.binding.hdanywhere
+
org.openhab.binding.hdpowerview
+
org.openhab.binding.helios
+
org.openhab.binding.heliosventilation
+
org.openhab.binding.heos
+
org.openhab.binding.homematic
+
org.openhab.binding.hpprinter
+
org.openhab.binding.http
+
org.openhab.binding.hue
+
org.openhab.binding.hydrawise
+
org.openhab.binding.hyperion
+
org.openhab.binding.iammeter
+
org.openhab.binding.iaqualink
+
org.openhab.binding.icalendar
+
org.openhab.binding.icloud
+
org.openhab.binding.ihc
+
org.openhab.binding.innogysmarthome
+
org.openhab.binding.insteon
+
org.openhab.binding.ipcamera
+
org.openhab.binding.intesis
+
org.openhab.binding.ipp
+
org.openhab.binding.irobot
+
org.openhab.binding.irtrans
+
org.openhab.binding.ism8
+
org.openhab.binding.jablotron
+
org.openhab.binding.jeelink
+
org.openhab.binding.kaleidescape
+
org.openhab.binding.keba
+
org.openhab.binding.km200
+
org.openhab.binding.knx
+
org.openhab.binding.kodi
+
org.openhab.binding.konnected
+
org.openhab.binding.kostalinverter
+
org.openhab.binding.kvv
+
org.openhab.binding.lametrictime
+
org.openhab.binding.lcn
+
org.openhab.binding.leapmotion
+
org.openhab.binding.lghombot
+
org.openhab.binding.lgtvserial
+
org.openhab.binding.lgwebos
+
org.openhab.binding.lifx
+
org.openhab.binding.linky
+
org.openhab.binding.linuxinput
+
org.openhab.binding.lirc
+
org.openhab.binding.logreader
+
org.openhab.binding.loxone
+
org.openhab.binding.luftdateninfo
+
org.openhab.binding.lutron
+
org.openhab.binding.magentatv
+
org.openhab.binding.mail
+
org.openhab.binding.max
+
org.openhab.binding.mcp23017
+
org.openhab.binding.melcloud
+
org.openhab.binding.meteoalerte
+
org.openhab.binding.meteoblue
+
org.openhab.binding.meteostick
+
org.openhab.binding.miele
+
org.openhab.binding.mihome
+
org.openhab.binding.miio
+
org.openhab.binding.millheat
+
org.openhab.binding.milight
+
org.openhab.binding.minecraft
+
org.openhab.binding.modbus
+
org.openhab.binding.modbus.e3dc
+
org.openhab.binding.modbus.sbc
+
org.openhab.binding.modbus.studer
+
org.openhab.binding.modbus.sunspec
+
org.openhab.binding.modbus.stiebeleltron
+
org.openhab.binding.modbus.helioseasycontrols
+
org.openhab.binding.monopriceaudio
+
org.openhab.binding.mpd
+
org.openhab.binding.mqtt
+
org.openhab.binding.mqtt.espmilighthub
+
org.openhab.binding.mqtt.generic
+
org.openhab.binding.mqtt.homeassistant
+
org.openhab.binding.mqtt.homie
+
org.openhab.binding.myq
+
org.openhab.binding.mystrom
+
org.openhab.binding.nanoleaf
+
org.openhab.binding.neato
+
org.openhab.binding.neeo
+
org.openhab.binding.neohub
+
org.openhab.binding.nest
+
org.openhab.binding.netatmo
+
org.openhab.binding.network
+
org.openhab.binding.networkupstools
+
org.openhab.binding.nibeheatpump
+
org.openhab.binding.nibeuplink
+
org.openhab.binding.nikobus
+
org.openhab.binding.nikohomecontrol
+
org.openhab.binding.novafinedust
+
org.openhab.binding.ntp
+
org.openhab.binding.nuki
+
org.openhab.binding.nuvo
+
org.openhab.binding.nzwateralerts
+
org.openhab.binding.oceanic
+
org.openhab.binding.ojelectronics
+
org.openhab.binding.omnikinverter
+
org.openhab.binding.omnilink
+
org.openhab.binding.onebusaway
+
org.openhab.binding.onewiregpio
+
org.openhab.binding.onewire
+
org.openhab.binding.onkyo
+
org.openhab.binding.opengarage
+
org.openhab.binding.opensprinkler
+
org.openhab.binding.openthermgateway
+
org.openhab.binding.openuv
+
org.openhab.binding.openweathermap
+
org.openhab.binding.openwebnet
+
org.openhab.binding.oppo
+
org.openhab.binding.orvibo
+
org.openhab.binding.paradoxalarm
+
org.openhab.binding.pentair
+
org.openhab.binding.phc
+
org.openhab.binding.pilight
+
org.openhab.binding.pioneeravr
+
org.openhab.binding.pixometer
+
org.openhab.binding.pjlinkdevice
+
org.openhab.binding.playstation
+
org.openhab.binding.plclogo
+
org.openhab.binding.plugwise
+
org.openhab.binding.powermax
+
org.openhab.binding.pulseaudio
+
org.openhab.binding.pushbullet
+
org.openhab.binding.pushover
+
org.openhab.binding.radiothermostat
+
org.openhab.binding.regoheatpump
+
org.openhab.binding.revogi
+
org.openhab.binding.remoteopenhab
+
org.openhab.binding.rfxcom
+
org.openhab.binding.rme
+
org.openhab.binding.robonect
+
org.openhab.binding.roku
+
org.openhab.binding.rotel
+
org.openhab.binding.russound
+
org.openhab.binding.sagercaster
+
org.openhab.binding.samsungtv
+
org.openhab.binding.satel
+
org.openhab.binding.senechome
+
org.openhab.binding.seneye
+
org.openhab.binding.sensebox
+
org.openhab.binding.sensibo
+
org.openhab.binding.serial
+
org.openhab.binding.serialbutton
+
org.openhab.binding.shelly
+
org.openhab.binding.silvercrestwifisocket
+
org.openhab.binding.siemensrds
+
org.openhab.binding.sinope
+
org.openhab.binding.sleepiq
+
org.openhab.binding.smaenergymeter
+
org.openhab.binding.smartmeter
+
org.openhab.binding.smhi
+
org.openhab.binding.smartthings
+
org.openhab.binding.snmp
+
org.openhab.binding.solaredge
+
org.openhab.binding.solarlog
+ org.openhab.binding.solarmax
+
org.openhab.binding.somfymylink
+
org.openhab.binding.somfytahoma
+
org.openhab.binding.sonos
+
org.openhab.binding.sonyaudio
+
org.openhab.binding.sonyprojector
+
org.openhab.binding.spotify
+
org.openhab.binding.squeezebox
+
org.openhab.binding.surepetcare
+
org.openhab.binding.synopanalyzer
+
org.openhab.binding.systeminfo
+
org.openhab.binding.tacmi
+
org.openhab.binding.tado
+
org.openhab.binding.tankerkoenig
+
org.openhab.binding.telegram
+
org.openhab.binding.teleinfo
+
org.openhab.binding.tellstick
+
org.openhab.binding.tesla
+
org.openhab.binding.tibber
+
org.openhab.binding.tivo
+
org.openhab.binding.touchwand
+
org.openhab.binding.tplinksmarthome
+
org.openhab.binding.tr064
+
org.openhab.binding.tradfri
+
org.openhab.binding.unifi
+
org.openhab.binding.unifiedremote
+
org.openhab.binding.upnpcontrol
+
org.openhab.binding.upb
+
org.openhab.binding.urtsi
+
org.openhab.binding.valloxmv
+
org.openhab.binding.vektiva
+
org.openhab.binding.velbus
+
org.openhab.binding.velux
+
org.openhab.binding.venstarthermostat
+
org.openhab.binding.verisure
+
org.openhab.binding.vigicrues
+
org.openhab.binding.vitotronic
+
org.openhab.binding.volvooncall
+
org.openhab.binding.weathercompany
+
org.openhab.binding.weatherunderground
+
org.openhab.binding.wemo
+
org.openhab.binding.wifiled
+
org.openhab.binding.windcentrale
+
org.openhab.binding.wlanthermo
+
org.openhab.binding.wled
+
org.openhab.binding.xmltv
+
org.openhab.binding.xmppclient
+
org.openhab.binding.yamahareceiver
+
org.openhab.binding.yioremote
+
org.openhab.binding.yeelight
+
org.openhab.binding.zoneminder
+
org.openhab.binding.zway
+
+
org.openhab.persistence.dynamodb
+
org.openhab.persistence.influxdb
+
org.openhab.persistence.jdbc
+
org.openhab.persistence.jpa
+
org.openhab.persistence.mapdb
+
org.openhab.persistence.mongodb
+
org.openhab.persistence.rrd4j
+
+
org.openhab.voice.googletts
+
org.openhab.voice.mactts
+
org.openhab.voice.marytts
+
org.openhab.voice.picotts
+
org.openhab.voice.pollytts
+
org.openhab.voice.voicerss
+
+
-
+
+
target/dependency
+
+
-
+
+
+
org.lastnpe.eea
+
eea-all
+
${eea.version}
+
+
+
+
org.openhab.core.bom
+
org.openhab.core.bom.compile
+
pom
+
provided
+
+
+
org.openhab.core.bom
+
org.openhab.core.bom.openhab-core
+
pom
+
provided
+
+
+
commons-net
+
commons-net
+
+
+
+
+
org.openhab.core.bom
+
org.openhab.core.bom.test
+
pom
+
test
+
+
+
+
org.apache.karaf.features
+
framework
+
${karaf.version}
+
kar
+
true
+
+
+
*
+
*
+
+
+
+
+
+
org.apache.karaf.features
+
standard
+
${karaf.version}
+
features
+
xml
+
provided
+
+
-
+
+
+
+
+
org.apache.maven.plugins
+
maven-jar-plugin
+
+
+
${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
true
+
+
+
+
org.apache.karaf.tooling
+
karaf-maven-plugin
+
${karaf.version}
+
true
+
+
80
+
true
+
true
+
false
+
true
+
true
+
+
+
+
compile
+
+
features-generate-descriptor
+
+
generate-resources
+
+
${feature.directory}
+
+
+
+
karaf-feature-verification
+
+
verify
+
+
verify
+
+
+
+
mvn:org.apache.karaf.features/framework/${karaf.version}/xml/features
+
mvn:org.apache.karaf.features/standard/${karaf.version}/xml/features
+
+
file:${project.build.directory}/feature/feature.xml
+
+
org.apache.karaf.features:framework
+
${oh.java.version}
+
+
framework
+
+
+
openhab-*
+
+
false
+
true
+
first
+
+
+
+
+
+
-
+
+
+
biz.aQute.bnd
+
bnd-maven-plugin
+
+
+
org.apache.maven.plugins
+
maven-source-plugin
+
+
+
attach-sources
+
+
jar-no-fork
+
+
+
+
+
+
org.apache.karaf.tooling
+
karaf-maven-plugin
+
+
+
+
org.apache.maven.plugins
+
maven-dependency-plugin
+
3.1.1
+
+
+
embed-dependencies
+
+
unpack-dependencies
+
+
+
runtime
+
jar
+
javax.activation,org.apache.karaf.features,org.lastnpe.eea
+
${dep.noembedding}
+
${project.build.directory}/classes
+
true
+
true
+
true
+
jar
+
+
+
+
unpack-eea
+
+
unpack
+
+
+
+
+
org.lastnpe.eea
+
eea-all
+
${eea.version}
+
true
+
+
+
+
+
+
+
+
-
+
+
+
+
no-embed-dependencies
+
+
+
noEmbedDependencies.profile
+
+
+
+
+
+
org.apache.maven.plugins
+
maven-dependency-plugin
+
+
+
embed-dependencies
+
none
+
+
+
+
+
+
+
-
+