From b075d53aa53454470d80bfb584018eac539a34ca Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 29 Mar 2021 22:00:15 +0200 Subject: [PATCH 01/21] SolarMax Binding Initial implementation Signed-off-by: Jamie Townsend --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + bundles/org.openhab.binding.solarmax/NOTICE | 13 + .../org.openhab.binding.solarmax/README.md | 70 ++ .../cfg/solarmax.cfg | 12 + bundles/org.openhab.binding.solarmax/pom.xml | 17 + .../src/main/feature/feature.xml | 9 + .../internal/SolarMaxBindingConstants.java | 38 ++ .../solarmax/internal/SolarMaxChannel.java | 60 ++ .../internal/SolarMaxConfiguration.java | 28 + .../solarmax/internal/SolarMaxHandler.java | 173 +++++ .../internal/SolarMaxHandlerFactory.java | 60 ++ .../connector/SolarMaxCommandKey.java | 159 +++++ .../SolarMaxConnectionException.java | 35 + .../internal/connector/SolarMaxConnector.java | 409 ++++++++++++ .../internal/connector/SolarMaxData.java | 241 +++++++ .../internal/connector/SolarMaxException.java | 39 ++ .../main/resources/OH-INF/binding/binding.xml | 9 + .../resources/OH-INF/thing/thing-types.xml | 209 ++++++ .../internal/connector/SolarMaxDataTest.java | 79 +++ bundles/pom.xml | 623 ++++++++++++++++-- 21 files changed, 2232 insertions(+), 57 deletions(-) create mode 100644 bundles/org.openhab.binding.solarmax/NOTICE create mode 100644 bundles/org.openhab.binding.solarmax/README.md create mode 100644 bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg create mode 100644 bundles/org.openhab.binding.solarmax/pom.xml create mode 100644 bundles/org.openhab.binding.solarmax/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxBindingConstants.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxChannel.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxConfiguration.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandler.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxHandlerFactory.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxCommandKey.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnectionException.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxConnector.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxData.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/connector/SolarMaxException.java create mode 100644 bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/thing/thing-types.xml create mode 100644 bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarMaxDataTest.java diff --git a/CODEOWNERS b/CODEOWNERS index c09e2b9f1e3be..59158ca576421 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -285,6 +285,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.solarwatt/ @sven-carstens /bundles/org.openhab.binding.somfymylink/ @loungeflyz /bundles/org.openhab.binding.somfytahoma/ @octa22 diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index b443a50b6af3f..3cad207a3d385 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1416,6 +1416,11 @@ org.openhab.binding.solarlog ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.solarmax + ${project.version} + org.openhab.addons.bundles org.openhab.binding.solarwatt 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 36f531048665a..3e14e4e2fbe88 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -1,618 +1,1127 @@ - - + + 4.0.0 - + + org.openhab.addons + org.openhab.addons.reactor - 3.2.0-SNAPSHOT + + 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.jrubyscripting - org.openhab.automation.jsscripting + org.openhab.automation.jythonscripting + org.openhab.automation.pidcontroller - org.openhab.automation.pwm + + org.openhab.io.homekit + org.openhab.io.hueemulation + org.openhab.io.imperihome - org.openhab.io.metrics + 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.ahawastecollection - org.openhab.binding.airq + 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.amplipi + org.openhab.binding.androiddebugbridge - org.openhab.binding.anel + org.openhab.binding.astro + org.openhab.binding.atlona + org.openhab.binding.autelis + org.openhab.binding.automower + org.openhab.binding.avmfritz - org.openhab.binding.benqprojector + 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.bmwconnecteddrive + 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.dali + org.openhab.binding.danfossairunit + org.openhab.binding.darksky - org.openhab.binding.dbquery + org.openhab.binding.deconz + org.openhab.binding.denonmarantz - org.openhab.binding.deutschebahn + org.openhab.binding.digiplex + org.openhab.binding.digitalstrom + org.openhab.binding.dlinksmarthome + org.openhab.binding.dmx - org.openhab.binding.dominoswiss + 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.ecotouch - org.openhab.binding.ekey + org.openhab.binding.elerotransmitterstick + org.openhab.binding.energenie + org.openhab.binding.enigma2 + org.openhab.binding.enocean - org.openhab.binding.enphase + 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.haassohnpelletstove + org.openhab.binding.harmonyhub + org.openhab.binding.haywardomnilogic - org.openhab.binding.hccrubbishcollection + org.openhab.binding.hdanywhere + org.openhab.binding.hdpowerview + org.openhab.binding.helios + org.openhab.binding.heliosventilation + org.openhab.binding.heos - org.openhab.binding.homeconnect + org.openhab.binding.homematic - org.openhab.binding.homewizard + 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.ipobserver + 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.luxtronikheatpump + org.openhab.binding.magentatv + org.openhab.binding.mail + org.openhab.binding.max + org.openhab.binding.mcp23017 - org.openhab.binding.mecmeter + org.openhab.binding.melcloud + org.openhab.binding.meteoalerte + org.openhab.binding.meteoblue + org.openhab.binding.meteostick + org.openhab.binding.miele - org.openhab.binding.mielecloud + org.openhab.binding.mihome + org.openhab.binding.miio - org.openhab.binding.mikrotik + 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.orbitbhyve + 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.plugwiseha + org.openhab.binding.powermax - org.openhab.binding.proteusecometer + org.openhab.binding.pulseaudio + org.openhab.binding.pushbullet + org.openhab.binding.pushover - org.openhab.binding.pushsafer - org.openhab.binding.qbus + org.openhab.binding.radiothermostat + org.openhab.binding.regoheatpump + org.openhab.binding.revogi + org.openhab.binding.remoteopenhab - org.openhab.binding.renault - org.openhab.binding.resol + 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.semsportal + 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.sncf + org.openhab.binding.snmp + org.openhab.binding.solaredge + org.openhab.binding.solarlog + + org.openhab.binding.solarmax + org.openhab.binding.solarwatt + org.openhab.binding.somfymylink + org.openhab.binding.somfytahoma + org.openhab.binding.sonos + org.openhab.binding.sonyaudio + org.openhab.binding.sonyprojector - org.openhab.binding.souliss + 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.tapocontrol + 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.twitter + 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.vdr + org.openhab.binding.vektiva + org.openhab.binding.velbus + org.openhab.binding.velux + org.openhab.binding.venstarthermostat - org.openhab.binding.ventaair + org.openhab.binding.verisure + org.openhab.binding.vigicrues + org.openhab.binding.vitotronic + org.openhab.binding.volvooncall - org.openhab.binding.warmup + org.openhab.binding.weathercompany + org.openhab.binding.weatherunderground - org.openhab.binding.webthing + org.openhab.binding.wemo + org.openhab.binding.wifiled + org.openhab.binding.windcentrale + org.openhab.binding.wlanthermo + org.openhab.binding.wled - org.openhab.binding.wolfsmartset + 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 + + + + + + + - + From f64bf20b06c616c826db1940888071c02bbdac15 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Tue, 30 Mar 2021 15:58:01 +0200 Subject: [PATCH 02/21] #10413 camelCaserizeTheChannelNames Signed-off-by: Jamie Townsend --- .../solarmax/internal/SolarMaxChannel.java | 42 +++++----- .../connector/SolarMaxCommandKey.java | 40 ++++----- .../internal/connector/SolarMaxData.java | 80 +++++++++--------- .../resources/OH-INF/thing/thing-types.xml | 84 +++++++++---------- .../internal/connector/SolarMaxDataTest.java | 8 +- 5 files changed, 127 insertions(+), 127 deletions(-) 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 index 126650d4fb5c3..a660966b782ec 100644 --- 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 @@ -23,28 +23,28 @@ public enum SolarMaxChannel { // CHANNEL_UPDATE_VALUES_FROM_DEVICE("UpdateValuesFromDevice"), - CHANNEL_LAST_UPDATED("LastUpdated"), // + 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()) + 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()) ; 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 index c21bad3918db2..1b724f162fa11 100644 --- 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 @@ -22,8 +22,8 @@ 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"), // + buildNumber("BDN"), // + startups("CAC"), // // UNKNOWN_CID("CID"), // // UNKNOWN_CPG("CPG"), // // UNKNOWN_CPL("CPL"), // @@ -44,26 +44,26 @@ public enum SolarMaxCommandKey { // UNKNOWN_IEE("IEE"), // // UNKNOWN_IEM("IEM"), // // UNKNOWN_ILM("ILM"), // - AcPhase1Current("IL1"), // - AcPhase2Current("IL2"), // - AcPhase3Current("IL3"), // + acPhase1Current("IL1"), // + acPhase2Current("IL2"), // + acPhase3Current("IL3"), // // UNKNOWN_IP4("IP4"), // // UNKNOWN_ISL("ISL"), // // UNKNOWN_ITS("ITS"), // - EnergyGeneratedToday("KDY"), // + energyGeneratedToday("KDY"), // // UNKNOWN_KFS("KFS"), // - OperatingHours("KHR"), // + operatingHours("KHR"), // // UNKNOWN_KHS("KHS"), // - EnergyGeneratedYesterday("KLD"), // - EnergyGeneratedLastMonth("KLM"), // - EnergyGeneratedLastYear("KLY"), // - EnergyGeneratedThisMonth("KMT"), // + energyGeneratedYesterday("KLD"), // + energyGeneratedLastMonth("KLM"), // + energyGeneratedLastYear("KLY"), // + energyGeneratedThisMonth("KMT"), // // UNKNOWN_KTS("KTS"), // - EnergyGeneratedTotal("KT0"), // - EnergyGeneratedThisYear("KYR"), // + energyGeneratedTotal("KT0"), // + energyGeneratedThisYear("KYR"), // // Language("LAN"), // // MacAddress("MAC"), // - CurrentPowerGenerated("PAC"), // + currentPowerGenerated("PAC"), // // UNKNOWN_PAE("PAE"), // // UNKNOWN_PAM("PAM"), // // UNKNOWN_PDA("PDA"), // @@ -96,15 +96,15 @@ public enum SolarMaxCommandKey { // UNKNOWN_SPS("SPS"), // // UNKNOWN_SRD("SRD"), // // UNKNOWN_SRS("SRS"), // - SoftwareVersion("SWV"), // + softwareVersion("SWV"), // // OperatingState("SYS"), // // UNKNOWN_TCP("TCP"), // probably port number (12345) // UNKNOWN_TI1("TI1"), // - HeatSinkTemperature("TKK"), // + heatSinkTemperature("TKK"), // // UNKNOWN_TL1("TL1"), // // UNKNOWN_TL3("TL3"), // // UNKNOWN_TND("TND"), // - AcFrequency("TNF"), // + acFrequency("TNF"), // // UNKNOWN_TNH("TNH"), // // UNKNOWN_TNL("TNL"), // // UNKNOWN_TP1("TP1"), // @@ -121,9 +121,9 @@ public enum SolarMaxCommandKey { // UNKNOWN_UI3("UI3"), // // UNKNOWN_ULH("ULH"), // // UNKNOWN_ULL("ULL"), // - AcPhase1Voltage("UL1"), // - AcPhase2Voltage("UL2"), // - AcPhase3Voltage("UL3"), // + acPhase1Voltage("UL1"), // + acPhase2Voltage("UL2"), // + acPhase3Voltage("UL3"), // // UNKNOWN_UMX("UMX"), // // UNKNOWN_UM1("UM1"), // // UNKNOWN_UM2("UM2"), // 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 index 953e1c631aca3..cd9ff8c7d1705 100644 --- 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 @@ -42,64 +42,64 @@ public boolean has(SolarMaxCommandKey key) { public State get(SolarMaxCommandKey key) { switch (key) { - case SoftwareVersion: + case softwareVersion: return getSoftwareVersion(); - case BuildNumber: + case buildNumber: return getBuildNumber(); - case Startups: + case startups: return getStartups(); - case AcPhase1Current: + case acPhase1Current: return getAcPhase1Current(); - case AcPhase2Current: + case acPhase2Current: return getAcPhase2Current(); - case AcPhase3Current: + case acPhase3Current: return getAcPhase3Current(); - case EnergyGeneratedToday: + case energyGeneratedToday: return getEnergyGeneratedToday(); - case EnergyGeneratedTotal: + case energyGeneratedTotal: return getEnergyGeneratedTotal(); - case OperatingHours: + case operatingHours: return getOperatingHours(); - case EnergyGeneratedYesterday: + case energyGeneratedYesterday: return getEnergyGeneratedYesterday(); - case EnergyGeneratedLastMonth: + case energyGeneratedLastMonth: return getEnergyGeneratedLastMonth(); - case EnergyGeneratedLastYear: + case energyGeneratedLastYear: return getEnergyGeneratedLastYear(); - case EnergyGeneratedThisMonth: + case energyGeneratedThisMonth: return getEnergyGeneratedThisMonth(); - case EnergyGeneratedThisYear: + case energyGeneratedThisYear: return getEnergyGeneratedThisYear(); - case CurrentPowerGenerated: + case currentPowerGenerated: return getCurrentPowerGenerated(); - case AcFrequency: + case acFrequency: return getAcFrequency(); - case AcPhase1Voltage: + case acPhase1Voltage: return getAcPhase1Voltage(); - case AcPhase2Voltage: + case acPhase2Voltage: return getAcPhase2Voltage(); - case AcPhase3Voltage: + case acPhase3Voltage: return getAcPhase3Voltage(); - case HeatSinkTemperature: + case heatSinkTemperature: return getHeatSinkTemperature(); default: @@ -120,83 +120,83 @@ public void setCommunicationSuccessful(boolean communicationSuccessful) { } public DecimalType getSoftwareVersion() { - return getIntegerValueFrom(SolarMaxCommandKey.SoftwareVersion); + return getIntegerValueFrom(SolarMaxCommandKey.softwareVersion); } public DecimalType getBuildNumber() { - return getIntegerValueFrom(SolarMaxCommandKey.BuildNumber); + return getIntegerValueFrom(SolarMaxCommandKey.buildNumber); } public DecimalType getStartups() { - return getIntegerValueFrom(SolarMaxCommandKey.Startups); + return getIntegerValueFrom(SolarMaxCommandKey.startups); } public DecimalType getAcPhase1Current() { - return getDecimalValueFrom(SolarMaxCommandKey.AcPhase1Current, 0.01); + return getDecimalValueFrom(SolarMaxCommandKey.acPhase1Current, 0.01); } public DecimalType getAcPhase2Current() { - return getDecimalValueFrom(SolarMaxCommandKey.AcPhase2Current, 0.01); + return getDecimalValueFrom(SolarMaxCommandKey.acPhase2Current, 0.01); } public DecimalType getAcPhase3Current() { - return getDecimalValueFrom(SolarMaxCommandKey.AcPhase3Current, 0.01); + return getDecimalValueFrom(SolarMaxCommandKey.acPhase3Current, 0.01); } public DecimalType getEnergyGeneratedToday() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedToday, 100); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedToday, 100); } public DecimalType getEnergyGeneratedTotal() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedTotal, 1000); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedTotal, 1000); } public DecimalType getOperatingHours() { - return getIntegerValueFrom(SolarMaxCommandKey.OperatingHours); + return getIntegerValueFrom(SolarMaxCommandKey.operatingHours); } public DecimalType getEnergyGeneratedYesterday() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedYesterday, 100); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedYesterday, 100); } public DecimalType getEnergyGeneratedLastMonth() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedLastMonth, 1000); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedLastMonth, 1000); } public DecimalType getEnergyGeneratedLastYear() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedLastYear, 1000); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedLastYear, 1000); } public DecimalType getEnergyGeneratedThisMonth() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedThisMonth, 1000); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedThisMonth, 1000); } public DecimalType getEnergyGeneratedThisYear() { - return getIntegerValueFrom(SolarMaxCommandKey.EnergyGeneratedThisYear, 1000); + return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedThisYear, 1000); } public DecimalType getCurrentPowerGenerated() { - return getIntegerValueFrom(SolarMaxCommandKey.CurrentPowerGenerated, 0.5); + return getIntegerValueFrom(SolarMaxCommandKey.currentPowerGenerated, 0.5); } public DecimalType getAcFrequency() { - return getDecimalValueFrom(SolarMaxCommandKey.AcFrequency, 0.01); + return getDecimalValueFrom(SolarMaxCommandKey.acFrequency, 0.01); } public DecimalType getAcPhase1Voltage() { - return getDecimalValueFrom(SolarMaxCommandKey.AcPhase1Voltage, 0.1); + return getDecimalValueFrom(SolarMaxCommandKey.acPhase1Voltage, 0.1); } public DecimalType getAcPhase2Voltage() { - return getDecimalValueFrom(SolarMaxCommandKey.AcPhase2Voltage, 0.1); + return getDecimalValueFrom(SolarMaxCommandKey.acPhase2Voltage, 0.1); } public DecimalType getAcPhase3Voltage() { - return getDecimalValueFrom(SolarMaxCommandKey.AcPhase3Voltage, 0.1); + return getDecimalValueFrom(SolarMaxCommandKey.acPhase3Voltage, 0.1); } public DecimalType getHeatSinkTemperature() { - return getIntegerValueFrom(SolarMaxCommandKey.HeatSinkTemperature); + return getIntegerValueFrom(SolarMaxCommandKey.heatSinkTemperature); } private DecimalType getDecimalValueFrom(SolarMaxCommandKey solarMaxCommandKey, double multiplyByFactor) { 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 index 883b9121f4832..2d07bc6144a00 100644 --- 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 @@ -11,27 +11,27 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -59,147 +59,147 @@ Update the values from the SolarMax device --> - + 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 index 4fb3bb49933ec..9804cd17dc98b 100644 --- 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 @@ -64,16 +64,16 @@ public void valueGetterSetterTest() throws Exception { SolarMaxData solarMaxData = new SolarMaxData(); Map dataOrig = new HashMap<>(); - dataOrig.put(SolarMaxCommandKey.SoftwareVersion, softwareVersionOriginal); + dataOrig.put(SolarMaxCommandKey.softwareVersion, softwareVersionOriginal); solarMaxData.setData(dataOrig); - DecimalType origVersion = solarMaxData.get(SolarMaxCommandKey.SoftwareVersion).as(DecimalType.class); + 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); + dataUpdated.put(SolarMaxCommandKey.softwareVersion, softwareVersionUpdated); solarMaxData.setData(dataUpdated); - DecimalType updatedVersion = solarMaxData.get(SolarMaxCommandKey.SoftwareVersion).as(DecimalType.class); + DecimalType updatedVersion = solarMaxData.get(SolarMaxCommandKey.softwareVersion).as(DecimalType.class); assertNotEquals(origVersion, updatedVersion); } } From dc49dfebd6b2cafdc1948f3fe5825e9e7915ce27 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Thu, 1 Apr 2021 17:00:24 +0200 Subject: [PATCH 03/21] #10413 Delete commented code and Refactor Brute Force Command Discovery into something commitable Signed-off-by: Jamie Townsend --- .../org.openhab.binding.solarmax/README.md | 54 +-- .../solarmax/internal/SolarMaxChannel.java | 2 - .../solarmax/internal/SolarMaxHandler.java | 19 - .../connector/SolarMaxCommandKey.java | 96 +---- .../internal/connector/SolarMaxConnector.java | 17 +- .../resources/OH-INF/thing/thing-types.xml | 7 - .../internal/connector/SolarMaxDataTest.java | 6 - .../SolarmaxConnectorFindCommands.java | 343 ++++++++++++++++++ 8 files changed, 378 insertions(+), 166 deletions(-) create mode 100644 bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md index 31dab23c8d4a0..41c093307c8c0 100644 --- a/bundles/org.openhab.binding.solarmax/README.md +++ b/bundles/org.openhab.binding.solarmax/README.md @@ -32,29 +32,37 @@ The IP address and port number (default 12345) of the device needs to be configu ## 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 | +| 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 | + +### SolarMax Commands + +During the implementation the SolarMax device was sent all possible 3 character commands and a number of 4 character commands, to see what it responded to. The most interesting, identifiable and useful commands were implemented as channels above. + +Here is a list of other commands, which are know to return some kind of value: ADR (DeviceAddress / Device Number - only used if the devices are linked serially), AMM, CID, CPG, CPL, CP1, CP2, CP3, CP4, CP5, CYC, DIN, DMO, ETH, FH2, FQR, FWV, IAA, IED, IEE, IEM, ILM, IP4, ISL, ITS, KFS, KHS, KTS, LAN (Language), MAC (MAC Address), PAE, PAM, PDA, PDC, PFA, PIN (Power Installed), PLR, PPC, PRL (AC Power Percent, PSF, PSR, PSS, QAC, QMO, QUC, RA1, RA2, RB1, RB2, REL, RH1, RH2, RPR, RSD, SAC, SAL, SAM, SCH, SNM (IP Broadcast Address??), SPS, SRD, SRS, SYS (Operating State), TCP (probably port number - 12345), TI1, TL1, TL3, TND, TNH, TNL, TP1, TP2, TP3, TV0, TV1, TYP (Type?), UA2, UB2, UGD, UI1, UI2, UI3, ULH, ULL, UMX, UM1, UM2, UM3, UPD, UZK, VCM + +Valid commands which returned a null/empty value during testing: FFK, FRT, GCP, ITN, PLD, PLE, PLF, PLS, PPO, TV2, VLE, VLI, VLO ## Full Example 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 index a660966b782ec..5848228e12531 100644 --- 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 @@ -22,9 +22,7 @@ */ 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()), 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 index 620c8436006a3..08cd7909653fb 100644 --- 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 @@ -54,17 +54,6 @@ public SolarMaxHandler(final Thing 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 @@ -93,7 +82,6 @@ private void configurePolling() { @Override public void dispose() { logger.debug("Disposing SolarMax Handler Thing"); - // isRunning = false; if (pollingJob != null && !pollingJob.isCancelled()) { pollingJob.cancel(true); } @@ -137,11 +125,6 @@ private void updateChannels(SolarMaxData solarMaxData) { 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(); @@ -155,8 +138,6 @@ private void updateChannels(SolarMaxData solarMaxData) { 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) { 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 index 1b724f162fa11..22f293fd0ecad 100644 --- 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 @@ -19,124 +19,32 @@ */ public enum SolarMaxCommandKey { + // for further commands, that are not implemented here, see this binding's README.md file + // 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) { 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 index a9a1daddee59b..83aae347baea0 100644 --- 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 @@ -191,13 +191,9 @@ private static Map getValuesFromSolarMax(final Outpu 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]; @@ -360,7 +356,6 @@ static String contructRequest(final String questions) { 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(); @@ -394,16 +389,8 @@ static String calculateChecksum16(String str) { } 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 + // probably should implement a patter matcher with a patternString like "/\\{([0-9A-F]{2});FB;([0-9A-F]{2})/", + // but for now... return true; } } 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 index 2d07bc6144a00..f88b2cd1a2c51 100644 --- 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 @@ -10,7 +10,6 @@ Basic thing for the SolarMax Power Inverter binding - @@ -53,12 +52,6 @@ - - DateTime 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 index 9804cd17dc98b..7ee86533ceff8 100644 --- 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 @@ -33,12 +33,6 @@ @NonNullByDefault public class SolarMaxDataTest { - @Test - public void gettersTest() throws Exception { - - // SolarMaxData solarMaxData = new SolarMaxData().dataDateTime() - } - @Test public void dataDateTimeGetterSetterTest() throws Exception { diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java new file mode 100644 index 0000000000000..59a085aa13bfb --- /dev/null +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -0,0 +1,343 @@ +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.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SolarmaxConnectorFindCommands { + + private static final Logger logger = LoggerFactory.getLogger(SolarMaxConnector.class); + + static final String host = "192.168.1.151"; + static final int port = 12345; + static final int deviceId = 1; + static final int connectionTimeout = 1000; // ms + + @Test + public void testForCommands() throws UnknownHostException, SolarMaxException { + + List validCommands = new ArrayList<>(); + List commandsToCheck = new ArrayList(); + List failedCommands = new ArrayList<>(); + int failedCommandRetry = 0; + String lastFailedCommand = ""; + + for (String first : getCharacters()) { + for (String second : getCharacters()) { + for (String third : getCharacters()) { + commandsToCheck.add(first + second + third); + + // specifically searching for "E" errors with 4 characters (I know now that they don't exist ;-) + // commandsToCheck.add("E" + first + second + third); + } + } + } + + // if you only want to try specific commands, perhaps because they failed in the big run, comment out the above + // and use this instead + // commandsToCheck.addAll(Arrays.asList("RH1", "RH2", "RH3", "TP1", "TP2", "TP3", "UL1", "UL2", "UL3", "UMX", + // "UM1", "UM2", "UM3", "UPD", "TCP")); + + while (!commandsToCheck.isEmpty()) { + if (commandsToCheck.size() % 100 == 0) { + System.out.println(commandsToCheck.size() + " left to check"); + } + try { + if (checkIsValidCommand(commandsToCheck.get(0))) { + validCommands.add(commandsToCheck.get(0)); + commandsToCheck.remove(0); + } else { + commandsToCheck.remove(0); + } + } catch (Exception e) { + // TODO Auto-generated catch block + System.out.println("Sleeping after Exception: " + e.getLocalizedMessage()); + if (lastFailedCommand.equals(commandsToCheck.get(0))) { + failedCommandRetry = failedCommandRetry + 1; + if (failedCommandRetry >= 5) { + failedCommands.add(commandsToCheck.get(0)); + commandsToCheck.remove(0); + } + } else { + failedCommandRetry = 0; + lastFailedCommand = commandsToCheck.get(0); + } + try { + // Backoff somewhat nicely + Thread.sleep(2 * failedCommandRetry * failedCommandRetry * failedCommandRetry); + } catch (InterruptedException e1) { + // do nothing + } + } + try { + Thread.sleep(10); + } catch (InterruptedException e1) { + // do nothing + } + + } + + System.out.println(); + System.out.println("Valid commands:"); + + for (String validCommand : validCommands) { + System.out.println(validCommand); + } + + System.out.println(); + System.out.println("Failed commands:"); + + for (String failedCommand : failedCommands) { + System.out.print(failedCommand + "\", \""); + } + } + + private boolean checkIsValidCommand(String command) + throws InterruptedException, UnknownHostException, SolarMaxException { + + List commands = new ArrayList(); + commands.add(command); + + Map responseMap = null; + + responseMap = getValuesFromSolarMax(host, port, commands); + + if (responseMap.containsKey(command)) { + System.out.println("Request: " + command + " Valid Response: " + responseMap.get(command)); + return true; + } + return false; + } + + /** + * based on SolarMaxConnector.getValuesFromSolarMax + */ + 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, connectionTimeout); + + 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 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 List getCharacters() { + List characters = new ArrayList<>(); + for (char c = 'a'; c <= 'z'; c++) { + characters.add(Character.toString(c)); + } + for (char c = 'A'; c <= 'Z'; c++) { + characters.add(Character.toString(c)); + } + characters.add("0"); + characters.add("1"); + characters.add("2"); + characters.add("3"); + characters.add("4"); + characters.add("5"); + characters.add("6"); + characters.add("7"); + characters.add("8"); + characters.add("9"); + + characters.add("."); + characters.add("-"); + characters.add("_"); + + return characters; + } + + private static Socket getSocketConnection(final String host, int port) + throws SolarMaxConnectionException, UnknownHostException { + + port = (port == 0) ? SolarmaxConnectorFindCommands.port : port; + + Socket socket; + + try { + socket = new Socket(); + logger.debug(" Connecting to " + host + ":" + port + " with a timeout of " + connectionTimeout); + socket.connect(new InetSocketAddress(host, port), connectionTimeout); + logger.debug(" Connected."); + socket.setSoTimeout(connectionTimeout); + } catch (final UnknownHostException e) { + throw e; + } catch (final IOException e) { + throw new SolarMaxConnectionException("Error connecting to port '" + port + "' on host '" + host + "'", e); + } + + return socket; + } + + private static Map getValuesFromSolarMax(final OutputStream outputStream, + final InputStream inputStream, final List commandList) throws SolarMaxException { + + Map returnedValues; + String commandString = getCommandString(commandList); + String request = SolarMaxConnector.contructRequest(commandString); + try { + + logger.trace(" ==>: {}", request); + + outputStream.write(request.getBytes()); + + 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); + } + } + + static String getCommandString(List commandList) { + String commandString = ""; + for (String command : commandList) { + if (commandString != "") { + commandString = commandString + ";"; + } + commandString = commandString + command; + } + return commandString; + } + + /** + * @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 responseKey = entry.substring(0, 3); + + String responseValue = (entry.length() >= 5) ? entry.substring(4) : null; + + responseMap.put(responseKey, responseValue); + } + } + + return responseMap; + } +} From ca53328742c66b9b5e27296e8af8afc8ac8e4b21 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Thu, 1 Apr 2021 17:00:24 +0200 Subject: [PATCH 04/21] 10413 Delete commented code and Refactor Brute Force Command Discovery into something commitable Signed-off-by: Jamie Townsend --- .../binding/solarmax/internal/connector/SolarMaxConnector.java | 2 +- .../internal/connector/SolarmaxConnectorFindCommands.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 index 83aae347baea0..17f22d5fe7a04 100644 --- 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 @@ -145,7 +145,7 @@ private static Map getValuesFromSolarMax(final Strin return returnMap; } - private static String getCommandString(List commandList) { + static String getCommandString(List commandList) { String commandString = ""; for (SolarMaxCommandKey command : commandList) { if (commandString != "") { diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java index 59a085aa13bfb..f7fb60ff49fcd 100644 --- a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -41,6 +41,7 @@ public void testForCommands() throws UnknownHostException, SolarMaxException { // specifically searching for "E" errors with 4 characters (I know now that they don't exist ;-) // commandsToCheck.add("E" + first + second + third); } + commandsToCheck.add("E" + first + second); } } @@ -99,7 +100,7 @@ public void testForCommands() throws UnknownHostException, SolarMaxException { System.out.println("Failed commands:"); for (String failedCommand : failedCommands) { - System.out.print(failedCommand + "\", \""); + System.out.println(failedCommand + "\", \""); } } From ce1f0184fff9a590714a82d6b3853171ab2e7d8b Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 5 Apr 2021 07:23:09 +0200 Subject: [PATCH 05/21] #10413 Codestyle Signed-off-by: Jamie Townsend --- .../internal/connector/SolarmaxConnectorFindCommands.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java index f7fb60ff49fcd..b1325f2fbec17 100644 --- a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -19,10 +19,9 @@ public class SolarmaxConnectorFindCommands { private static final Logger logger = LoggerFactory.getLogger(SolarMaxConnector.class); - static final String host = "192.168.1.151"; - static final int port = 12345; - static final int deviceId = 1; - static final int connectionTimeout = 1000; // ms + private static final String host = "192.168.1.151"; + private static final int port = 12345; + private static final int connectionTimeout = 1000; // ms @Test public void testForCommands() throws UnknownHostException, SolarMaxException { From 38a743a3066e99536dbc242c15419ac9e4cf09d7 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 5 Apr 2021 07:36:19 +0200 Subject: [PATCH 06/21] 10413 Codestyle Signed-off-by: Jamie Townsend --- bundles/pom.xml | 1125 ++++++++++++++++++++++++----------------------- 1 file changed, 564 insertions(+), 561 deletions(-) diff --git a/bundles/pom.xml b/bundles/pom.xml index 3e14e4e2fbe88..550249226ec30 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -1,556 +1,559 @@ - - + + 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.mecmeter + 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 @@ -558,570 +561,570 @@ org.openhab.binding.solarwatt 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 - + - + - + - + - + - + - + - + From 01b73be54d3b195e2b2842e683acf360e4192047 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 12 Apr 2021 22:47:31 +0200 Subject: [PATCH 07/21] #10413 corrected sat-plugin errors Signed-off-by: Jamie Townsend --- .../internal/connector/SolarMaxDataTest.java | 2 +- .../SolarmaxConnectorFindCommands.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) 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 index 7ee86533ceff8..20f5c2f5f0953 100644 --- 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 @@ -28,7 +28,7 @@ /** * The {@link SolarMaxDataTest} class is used to test the {@link SolaMaxData} class. * - * @author Johann Richard - Initial contribution + * @author Jamie Townsend - Initial contribution */ @NonNullByDefault public class SolarMaxDataTest { diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java index b1325f2fbec17..5465620346932 100644 --- a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -1,3 +1,15 @@ +/** + * 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; @@ -15,6 +27,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * The {@link SolarmaxConnectorFindCommands} class wass used to brute-force detect different replies from the SolarMax + * device + * + * @author Jamie Townsend - Initial contribution + */ public class SolarmaxConnectorFindCommands { private static final Logger logger = LoggerFactory.getLogger(SolarMaxConnector.class); From fc55d3a0c1d2875a45592ba9700dfe01d03b4e8f Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Tue, 27 Apr 2021 22:44:53 +0200 Subject: [PATCH 08/21] #10413 updates from code reviews in PR #10414 Signed-off-by: Jamie Townsend --- .../org.openhab.binding.solarmax/README.md | 86 +++++++------------ .../resources/OH-INF/thing/thing-types.xml | 34 ++++---- 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md index 41c093307c8c0..6383f9ec0ea96 100644 --- a/bundles/org.openhab.binding.solarmax/README.md +++ b/bundles/org.openhab.binding.solarmax/README.md @@ -4,7 +4,8 @@ This binding supports SolarMax PV inverters. ## Supported Things -The SolarMax MT Series is support. (tested with 8MT2 devices) +This binding only has a single `inverter` thing that can be added manually. +The SolarMax MT Series is supported (tested with 8MT2 devices). ## Discovery @@ -12,67 +13,46 @@ Auto-discovery is currently not available. ## Thing Configuration -The IP address and port number (default 12345) of the device needs to be configured. +Each inverter requires the following configuration parameters: +| parameter | required | default | description | +| --------------- | -------- | ------- | -------------------------------------------------------------------- | +| host | yes | | hostname or IP address of the inverter | +| port | no | 12345 | Port number to connect to. This should be `12345` for most inverters | +| refreshInterval | no | 15 | Interval (in seconds) to refresh the channel values. | -``` -############################## 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 | +| channel | type | description | +| ------------------------ | -------- | ------------------------------------------------------ | +| lastUpdated | DateTime | Time when data was 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 | +| currentPowerGenerated | 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 | ### SolarMax Commands -During the implementation the SolarMax device was sent all possible 3 character commands and a number of 4 character commands, to see what it responded to. The most interesting, identifiable and useful commands were implemented as channels above. +During the implementation the SolarMax device was sent all possible 3 character commands and a number of 4 character commands, to see what it responded to. +The most interesting, identifiable and useful commands were implemented as channels above. Here is a list of other commands, which are know to return some kind of value: ADR (DeviceAddress / Device Number - only used if the devices are linked serially), AMM, CID, CPG, CPL, CP1, CP2, CP3, CP4, CP5, CYC, DIN, DMO, ETH, FH2, FQR, FWV, IAA, IED, IEE, IEM, ILM, IP4, ISL, ITS, KFS, KHS, KTS, LAN (Language), MAC (MAC Address), PAE, PAM, PDA, PDC, PFA, PIN (Power Installed), PLR, PPC, PRL (AC Power Percent, PSF, PSR, PSS, QAC, QMO, QUC, RA1, RA2, RB1, RB2, REL, RH1, RH2, RPR, RSD, SAC, SAL, SAM, SCH, SNM (IP Broadcast Address??), SPS, SRD, SRS, SYS (Operating State), TCP (probably port number - 12345), TI1, TL1, TL3, TND, TNH, TNL, TP1, TP2, TP3, TV0, TV1, TYP (Type?), UA2, UB2, UGD, UI1, UI2, UI3, ULH, ULL, UMX, UM1, UM2, UM3, UPD, UZK, VCM Valid commands which returned a null/empty value during testing: FFK, FRT, GCP, ITN, PLD, PLE, PLF, PLS, PPO, TV2, VLE, VLI, VLO - -## 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/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.solarmax/src/main/resources/OH-INF/thing/thing-types.xml index f88b2cd1a2c51..95132639b9822 100644 --- 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 @@ -55,7 +55,7 @@ DateTime - When was the data last read from the device + Time when data was last read from the device @@ -81,35 +81,35 @@ - Number + Number:ElectricCurrent Ac Phase 1 Current in Amps - Number + Number:ElectricCurrent Ac Phase 2 Current in Amps - Number + Number:ElectricCurrent Ac Phase 3 Current in Amps - Number + Number:Energy Energy Generated Today in wH - Number + Number:Energy Energy Generated Total since recording began in wH @@ -123,77 +123,77 @@ - Number + Number:Energy Energy Generated Yesterday in wH - Number + Number:Energy Energy Generated Last Month in wH - Number + Number:Energy Energy Generated Last Year in wH - Number + Number:Energy Energy Generated This Month in wH - Number + Number:Energy Energy Generated This Year in wH - Number + Number:Power Power currently being generated in w - Number + Number:Frequency AcFrequency in Hz - Number + Number:ElectricPotential Ac Phase1 Voltage in V - Number + Number:ElectricPotential Ac Phase2 Voltage in V - Number + Number:ElectricPotential Ac Phase3 Voltage in V - Number + Number:Temperature Heat Sink Temperature in degrees celcius From 23eab82866fcab8af406815e9474559b5bf8c587 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Wed, 28 Apr 2021 08:21:45 +0200 Subject: [PATCH 09/21] 10413 mvn spotless:apply Signed-off-by: Jamie Townsend --- bundles/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bundles/pom.xml b/bundles/pom.xml index 550249226ec30..a66837e860b32 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -152,6 +152,8 @@ org.openhab.binding.daikin + org.openhab.binding.dali + org.openhab.binding.danfossairunit org.openhab.binding.darksky From 32f92510960239d4252b28eb801cf237050f9caa Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Wed, 15 Dec 2021 23:51:31 +0100 Subject: [PATCH 10/21] 10413 Updated to 3.2.0-SNAPSHOT Signed-off-by: Jamie Townsend --- bundles/org.openhab.binding.solarmax/pom.xml | 2 +- .../solarmax/internal/connector/SolarMaxConnector.java | 2 +- .../internal/connector/SolarmaxConnectorFindCommands.java | 2 +- bundles/pom.xml | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.solarmax/pom.xml b/bundles/org.openhab.binding.solarmax/pom.xml index 32d07a6947dfa..d46eafbc16166 100644 --- a/bundles/org.openhab.binding.solarmax/pom.xml +++ b/bundles/org.openhab.binding.solarmax/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.1.0-SNAPSHOT + 3.2.0-SNAPSHOT org.openhab.binding.solarmax 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 index 17f22d5fe7a04..daaf2eba1a8c6 100644 --- 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 @@ -148,7 +148,7 @@ private static Map getValuesFromSolarMax(final Strin static String getCommandString(List commandList) { String commandString = ""; for (SolarMaxCommandKey command : commandList) { - if (commandString != "") { + if (!commandString.equals("")) { commandString = commandString + ";"; } commandString = commandString + command.getCommandKey(); diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java index 5465620346932..3033b134fd14a 100644 --- a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -311,7 +311,7 @@ private static Map getValuesFromSolarMax(final OutputStream outp static String getCommandString(List commandList) { String commandString = ""; for (String command : commandList) { - if (commandString != "") { + if (!commandString.equals("")) { commandString = commandString + ";"; } commandString = commandString + command; diff --git a/bundles/pom.xml b/bundles/pom.xml index a66837e860b32..33bc62a23d808 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -10,7 +10,7 @@ org.openhab.addons.reactor - 3.1.0-SNAPSHOT + 3.2.0-SNAPSHOT @@ -559,9 +559,9 @@ org.openhab.binding.solarlog org.openhab.binding.solarmax - + org.openhab.binding.solarwatt - + org.openhab.binding.somfymylink org.openhab.binding.somfytahoma From 5eb27eea7c81969749a01c988fec7975c66643b1 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 10 Jan 2022 08:06:45 +0100 Subject: [PATCH 11/21] Fixed conflicts introduced by foreign commit. Signed-off-by: Jamie Townsend --- bundles/org.openhab.binding.solarmax/pom.xml | 2 +- bundles/pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.solarmax/pom.xml b/bundles/org.openhab.binding.solarmax/pom.xml index d46eafbc16166..ad75cd8896786 100644 --- a/bundles/org.openhab.binding.solarmax/pom.xml +++ b/bundles/org.openhab.binding.solarmax/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 3.2.0-SNAPSHOT + 3.3.0-SNAPSHOT org.openhab.binding.solarmax diff --git a/bundles/pom.xml b/bundles/pom.xml index 30704f2139098..3be5ba6881624 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -184,9 +184,9 @@ org.openhab.binding.ecobee org.openhab.binding.ecotouch - + org.openhab.binding.ekey - + org.openhab.binding.electroluxair org.openhab.binding.elerotransmitterstick @@ -496,7 +496,7 @@ org.openhab.binding.powermax org.openhab.binding.proteusecometer - + org.openhab.binding.publictransportswitzerland org.openhab.binding.pulseaudio From 83788208720560c3ad450c3183d4cb64582a6c7e Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 10 Jan 2022 08:50:26 +0100 Subject: [PATCH 12/21] Updated copyright years Signed-off-by: Jamie Townsend --- .../binding/solarmax/internal/SolarMaxBindingConstants.java | 2 +- .../openhab/binding/solarmax/internal/SolarMaxChannel.java | 2 +- .../binding/solarmax/internal/SolarMaxConfiguration.java | 2 +- .../openhab/binding/solarmax/internal/SolarMaxHandler.java | 2 +- .../binding/solarmax/internal/SolarMaxHandlerFactory.java | 2 +- .../solarmax/internal/connector/SolarMaxCommandKey.java | 2 +- .../internal/connector/SolarMaxConnectionException.java | 2 +- .../solarmax/internal/connector/SolarMaxConnector.java | 4 ++-- .../binding/solarmax/internal/connector/SolarMaxData.java | 2 +- .../solarmax/internal/connector/SolarMaxException.java | 2 +- .../binding/solarmax/internal/connector/SolarMaxDataTest.java | 2 +- .../internal/connector/SolarmaxConnectorFindCommands.java | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) 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 index 8531d48a41b2e..5e52d12cf5097 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index 5848228e12531..97b3cad15f932 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index 8482b9b5587e7..f939cf036d2ee 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index 08cd7909653fb..c5875013a6491 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index fc5b8a1487afd..029bc08070b52 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index 22f293fd0ecad..c01a50f511053 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index b57e78d1ce374..5bfc2ea1a84d5 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index daaf2eba1a8c6..ca81de6bfb362 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. @@ -148,7 +148,7 @@ private static Map getValuesFromSolarMax(final Strin static String getCommandString(List commandList) { String commandString = ""; for (SolarMaxCommandKey command : commandList) { - if (!commandString.equals("")) { + if (!"".equals(commandString)) { commandString = commandString + ";"; } commandString = commandString + command.getCommandKey(); 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 index cd9ff8c7d1705..6d0f015e6c4d1 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index c4cd118338c49..1fcab786a683f 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. 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 index 20f5c2f5f0953..01d18bd319ff1 100644 --- 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 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java index 3033b134fd14a..8f7f762da3b78 100644 --- a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2010-2022 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. From 1c07e63684539b01eab5169b1539ac418be3d3f2 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Tue, 25 Jan 2022 10:27:13 +0100 Subject: [PATCH 13/21] Ran mvn spotless:apply to resolve formatting issues Signed-off-by: Jamie Townsend --- bundles/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/pom.xml b/bundles/pom.xml index bbaf136fa4b98..9a7437f080e61 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -623,4 +623,4 @@ - \ No newline at end of file + From 46a375d31dcca58674d0048af9a26a6589148642 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Fri, 28 Jan 2022 11:35:00 +0100 Subject: [PATCH 14/21] Updates from review Signed-off-by: Jamie Townsend --- .../org.openhab.binding.solarmax/README.md | 46 +++++------ .../cfg/solarmax.cfg | 12 --- .../internal/SolarMaxBindingConstants.java | 6 -- .../solarmax/internal/SolarMaxChannel.java | 2 + .../solarmax/internal/SolarMaxHandler.java | 7 +- .../connector/SolarMaxCommandKey.java | 5 +- .../internal/connector/SolarMaxConnector.java | 63 +++++---------- .../internal/connector/SolarMaxData.java | 18 ++++- .../resources/OH-INF/thing/thing-types.xml | 26 +++--- .../internal/connector/SolarMaxDataTest.java | 10 ++- .../SolarmaxConnectorFindCommands.java | 81 +++++++++---------- 11 files changed, 122 insertions(+), 154 deletions(-) delete mode 100644 bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md index 6383f9ec0ea96..9c1d3f0664c72 100644 --- a/bundles/org.openhab.binding.solarmax/README.md +++ b/bundles/org.openhab.binding.solarmax/README.md @@ -24,29 +24,29 @@ Each inverter requires the following configuration parameters: ## Channels -| channel | type | description | -| ------------------------ | -------- | ------------------------------------------------------ | -| lastUpdated | DateTime | Time when data was 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 | -| currentPowerGenerated | 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 | +| channel | type | description | +| ------------------------ | ------------------------ | ------------------------------------------------------ | +| lastUpdated | DateTime | Time when data was last read from the device | +| softwareVersion | Number | Software Version installed on the SolarMax device | +| buildNumber | Number | Firmware Build Number installed on the SolarMax device | +| startups | Number | Number of times the device has started | +| acPhase1Current | Number:ElectricCurrent | Ac Phase 1 Current in Amps | +| acPhase2Current | Number:ElectricCurrent | Ac Phase 2 Current in Amps | +| acPhase3Current | Number:ElectricCurrent | Ac Phase 3 Current in Amps | +| energyGeneratedToday | Number:Energy | Energy Generated Today in Wh | +| energyGeneratedTotal | Number:Energy | Energy Generated since recording began in Wh | +| operatingHours | Number | Operating Hours since recording began in h | +| energyGeneratedYesterday | Number:Energy | Energy Generated Yesterday in Wh | +| energyGeneratedLastMonth | Number:Energy | Energy Generated Last Month in Wh | +| energyGeneratedLastYear | Number:Energy | Energy Generated Last Year in Wh | +| energyGeneratedThisMonth | Number:Energy | Energy Generated This Month in Wh | +| energyGeneratedThisYear | Number:Energy | Energy Generated This Year in Wh | +| currentPowerGenerated | Number:Power | Power currently being generated in W | +| acFrequency | Number:Frequency | AcFrequency in Hz | +| acPhase1Voltage | Number:ElectricPotential | Ac Phase1 Voltage in V | +| acPhase2Voltage | Number:ElectricPotential | Ac Phase2 Voltage in V | +| acPhase3Voltage | Number:ElectricPotential | Ac Phase3 Voltage in V | +| heatSinkTemperature | Number:Temperature | Heat Sink Temperature in degrees celcius | ### SolarMax Commands diff --git a/bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg b/bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg deleted file mode 100644 index dcbc0fffdff15..0000000000000 --- a/bundles/org.openhab.binding.solarmax/cfg/solarmax.cfg +++ /dev/null @@ -1,12 +0,0 @@ -############################## 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/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 index 5e52d12cf5097..53c0a787b84d1 100644 --- 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 @@ -29,10 +29,4 @@ public class SolarMaxBindingConstants { // 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 index 97b3cad15f932..f06843fc4b851 100644 --- 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 @@ -12,6 +12,7 @@ */ package org.openhab.binding.solarmax.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solarmax.internal.connector.SolarMaxCommandKey; /** @@ -20,6 +21,7 @@ * * @author Jamie Townsend - Initial contribution */ +@NonNullByDefault public enum SolarMaxChannel { CHANNEL_LAST_UPDATED("lastUpdated"), // 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 index c5875013a6491..1ecf3f3ef7fa1 100644 --- 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 @@ -15,6 +15,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.solarmax.internal.connector.SolarMaxCommandKey; import org.openhab.binding.solarmax.internal.connector.SolarMaxConnector; @@ -37,12 +38,12 @@ * * @author Jamie Townsend - Initial contribution */ +@NonNullByDefault public class SolarMaxHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(SolarMaxHandler.class); - @Nullable - private SolarMaxConfiguration config; + private SolarMaxConfiguration config = getConfigAs(SolarMaxConfiguration.class); @Nullable private ScheduledFuture pollingJob; @@ -96,7 +97,6 @@ public void dispose() { }; 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 { @@ -134,7 +134,6 @@ private void updateChannels(SolarMaxData solarMaxData) { } 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); } 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 index c01a50f511053..ac0eb199b67b6 100644 --- 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 @@ -12,13 +12,15 @@ */ package org.openhab.binding.solarmax.internal.connector; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link SolarMaxCommandKey} enum defines the commands that are understood by the SolarMax device * * @author Jamie Townsend - Initial contribution */ +@NonNullByDefault public enum SolarMaxCommandKey { - // for further commands, that are not implemented here, see this binding's README.md file // Valid commands which returned a non-null value during testing @@ -56,7 +58,6 @@ public String getCommandKey() { } public static SolarMaxCommandKey getKeyFromString(String commandKey) { - for (SolarMaxCommandKey key : SolarMaxCommandKey.values()) { if (key.commandKey.equals(commandKey)) { return key; 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 index ca81de6bfb362..ec8d1c8835ed3 100644 --- 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 @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,19 +37,20 @@ * * @author Jamie Townsend - Initial contribution */ +@NonNullByDefault public class SolarMaxConnector { /** * default port number of SolarMax devices is... */ - final private static int DEFAULT_PORT = 12345; + private static final int DEFAULT_PORT = 12345; - private static final Logger logger = LoggerFactory.getLogger(SolarMaxConnector.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SolarMaxConnector.class); /** * default timeout for socket connections is 1 second */ - private static int connectionTimeout = 1000; + private static final int CONNECTION_TIMEOUT = 1000; /** * default timeout for socket responses is 10 seconds @@ -65,7 +68,6 @@ public class SolarMaxConnector { * @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()) { @@ -79,7 +81,7 @@ public static SolarMaxData getAllValuesFromSolarMax(final String host, int port) // 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); + Map valuesFromSolarMax = getValuesFromSolarMax(host, port, commandList); boolean allCommandsAnswered = true; for (SolarMaxCommandKey solarMaxCommandKey : commandList) { if (!valuesFromSolarMax.containsKey(solarMaxCommandKey)) { @@ -104,12 +106,11 @@ public static SolarMaxData getAllValuesFromSolarMax(final String host, int port) * @throws UnknownHostException if the host is unknown * @throws SolarMaxException if some other exception occurs */ - private static Map getValuesFromSolarMax(final String host, int port, + private static Map getValuesFromSolarMax(final String host, int port, final List commandList) throws SolarMaxException { - Socket socket; - Map returnMap = new HashMap<>(); + 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 @@ -119,7 +120,7 @@ private static Map getValuesFromSolarMax(final Strin 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 {}:{} with timeout of {}ms", host, port, responseTimeout); int firstCommandNumber = requestNumber * maxConcurrentCommands; int lastCommandNumber = (requestNumber + 1) * maxConcurrentCommands; @@ -148,7 +149,7 @@ private static Map getValuesFromSolarMax(final Strin static String getCommandString(List commandList) { String commandString = ""; for (SolarMaxCommandKey command : commandList) { - if (!"".equals(commandString)) { + if (!commandString.isEmpty()) { commandString = commandString + ";"; } commandString = commandString + command.getCommandKey(); @@ -156,7 +157,7 @@ static String getCommandString(List commandList) { return commandString; } - private static Map getValuesFromSolarMax(final Socket socket, + private static Map getValuesFromSolarMax(final Socket socket, final List commandList) throws SolarMaxException { OutputStream outputStream = null; InputStream inputStream = null; @@ -165,7 +166,6 @@ private static Map getValuesFromSolarMax(final Socke 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 { @@ -183,15 +183,13 @@ private static Map getValuesFromSolarMax(final Socke } } - private static Map getValuesFromSolarMax(final OutputStream outputStream, + private static Map getValuesFromSolarMax(final OutputStream outputStream, final InputStream inputStream, final List commandList) throws SolarMaxException { - - Map returnedValues; + Map returnedValues; String commandString = getCommandString(commandList); String request = contructRequest(commandString); try { - - logger.trace(" ==>: {}", request); + LOGGER.trace(" ==>: {}", request); outputStream.write(request.getBytes()); @@ -218,7 +216,7 @@ private static Map getValuesFromSolarMax(final Outpu } } - logger.trace(" <==: {}", response); + LOGGER.trace(" <==: {}", response); if (!validateResponse(response)) { throw new SolarMaxException("Invalid response received: " + response); @@ -227,9 +225,8 @@ private static Map getValuesFromSolarMax(final Outpu returnedValues = extractValuesFromResponse(response); return returnedValues; - } catch (IOException e) { - logger.debug("Error communicating via input/output streams: {} ", e.getMessage()); + LOGGER.debug("Error communicating via input/output streams: {} ", e.getMessage()); throw new SolarMaxException(e); } } @@ -239,13 +236,12 @@ private static Map getValuesFromSolarMax(final Outpu * "{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<>(); + 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); + LOGGER.warn("Response doesn't contain data. Response: {}", response); return responseMap; } @@ -279,14 +275,13 @@ static Map extractValuesFromResponse(String response 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.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT); socket.setSoTimeout(responseTimeout); } catch (final UnknownHostException e) { throw e; @@ -298,7 +293,6 @@ private static Socket getSocketConnection(final String host, int port) } public static boolean connectionTest(final String host, int port) throws UnknownHostException { - Socket socket = null; try { @@ -318,20 +312,6 @@ public static boolean connectionTest(final String host, int port) throws Unknown 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 */ @@ -372,7 +352,6 @@ static String contructRequest(final String questions) { * calculates the "checksum16" of the given string argument */ static String calculateChecksum16(String str) { - byte[] bytes = str.getBytes(); int sum = 0; 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 index 6d0f015e6c4d1..a65a2c1180946 100644 --- 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 @@ -16,6 +16,9 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jdt.annotation.DefaultLocation; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.types.State; @@ -26,11 +29,15 @@ * * @author Jamie Townsend - Initial contribution */ +@NonNullByDefault({ DefaultLocation.PARAMETER, DefaultLocation.FIELD, DefaultLocation.TYPE_BOUND, + DefaultLocation.TYPE_ARGUMENT }) public class SolarMaxData { - private ZonedDateTime dataDateTime; + private ZonedDateTime dataDateTime = ZonedDateTime.now(); + private boolean communicationSuccessful; - private final Map data = new HashMap<>(); + + private final Map data = new HashMap<>(); public State getDataDateTime() { return new DateTimeType(dataDateTime); @@ -199,9 +206,11 @@ public DecimalType getHeatSinkTemperature() { return getIntegerValueFrom(SolarMaxCommandKey.heatSinkTemperature); } + @Nullable 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); @@ -211,9 +220,11 @@ private DecimalType getDecimalValueFrom(SolarMaxCommandKey solarMaxCommandKey, d return null; } + @Nullable 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)); @@ -223,6 +234,7 @@ private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey, d return null; } + @Nullable private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey) { if (this.data.containsKey(solarMaxCommandKey)) { String valueString = this.data.get(solarMaxCommandKey); @@ -235,7 +247,7 @@ private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey) { return null; } - protected void setData(Map data) { + protected void setData(Map data) { this.data.putAll(data); } } 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 index 95132639b9822..10dd5bf2f11c1 100644 --- 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 @@ -82,22 +82,22 @@ Number:ElectricCurrent - - Ac Phase 1 Current in Amps + + AC Phase 1 Current in Amps Number:ElectricCurrent - - Ac Phase 2 Current in Amps + + AC Phase 2 Current in Amps Number:ElectricCurrent - - Ac Phase 3 Current in Amps + + AC Phase 3 Current in Amps @@ -166,29 +166,29 @@ Number:Frequency - + AcFrequency in Hz Number:ElectricPotential - - Ac Phase1 Voltage in V + + AC Phase1 Voltage in V Number:ElectricPotential - - Ac Phase2 Voltage in V + + AC Phase2 Voltage in V Number:ElectricPotential - - Ac Phase3 Voltage in V + + AC Phase3 Voltage in V 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 index 01d18bd319ff1..6568db03a0d65 100644 --- 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 @@ -21,6 +21,7 @@ import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.junit.jupiter.api.Test; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; @@ -35,7 +36,6 @@ public class SolarMaxDataTest { @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); @@ -51,20 +51,22 @@ public void dataDateTimeGetterSetterTest() throws Exception { @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<>(); + Map dataOrig = new HashMap<>(); dataOrig.put(SolarMaxCommandKey.softwareVersion, softwareVersionOriginal); solarMaxData.setData(dataOrig); + + @Nullable DecimalType origVersion = solarMaxData.get(SolarMaxCommandKey.softwareVersion).as(DecimalType.class); + assertNotNull(origVersion); assertEquals(Integer.parseInt(softwareVersionOriginal, 16), origVersion.intValue()); - Map dataUpdated = new HashMap<>(); + Map dataUpdated = new HashMap<>(); dataUpdated.put(SolarMaxCommandKey.softwareVersion, softwareVersionUpdated); solarMaxData.setData(dataUpdated); DecimalType updatedVersion = solarMaxData.get(SolarMaxCommandKey.softwareVersion).as(DecimalType.class); diff --git a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java index 8f7f762da3b78..ed57f48f5820a 100644 --- a/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java +++ b/bundles/org.openhab.binding.solarmax/src/test/java/org/openhab/binding/solarmax/internal/connector/SolarmaxConnectorFindCommands.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,17 +35,17 @@ * * @author Jamie Townsend - Initial contribution */ +@NonNullByDefault public class SolarmaxConnectorFindCommands { - private static final Logger logger = LoggerFactory.getLogger(SolarMaxConnector.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SolarMaxConnector.class); - private static final String host = "192.168.1.151"; - private static final int port = 12345; - private static final int connectionTimeout = 1000; // ms + private static final String HOST = "192.168.1.151"; + private static final int PORT = 12345; + private static final int CONNECTION_TIMEOUT = 1000; // ms @Test public void testForCommands() throws UnknownHostException, SolarMaxException { - List validCommands = new ArrayList<>(); List commandsToCheck = new ArrayList(); List failedCommands = new ArrayList<>(); @@ -69,7 +71,7 @@ public void testForCommands() throws UnknownHostException, SolarMaxException { while (!commandsToCheck.isEmpty()) { if (commandsToCheck.size() % 100 == 0) { - System.out.println(commandsToCheck.size() + " left to check"); + LOGGER.debug(commandsToCheck.size() + " left to check"); } try { if (checkIsValidCommand(commandsToCheck.get(0))) { @@ -79,8 +81,8 @@ public void testForCommands() throws UnknownHostException, SolarMaxException { commandsToCheck.remove(0); } } catch (Exception e) { - // TODO Auto-generated catch block - System.out.println("Sleeping after Exception: " + e.getLocalizedMessage()); + LOGGER.debug("Sleeping after Exception: " + e.getLocalizedMessage()); + if (lastFailedCommand.equals(commandsToCheck.get(0))) { failedCommandRetry = failedCommandRetry + 1; if (failedCommandRetry >= 5) { @@ -103,36 +105,32 @@ public void testForCommands() throws UnknownHostException, SolarMaxException { } catch (InterruptedException e1) { // do nothing } - } - System.out.println(); - System.out.println("Valid commands:"); + LOGGER.info("\nValid commands:"); for (String validCommand : validCommands) { - System.out.println(validCommand); + LOGGER.info(validCommand); } - System.out.println(); - System.out.println("Failed commands:"); + LOGGER.info("\nFailed commands:"); for (String failedCommand : failedCommands) { - System.out.println(failedCommand + "\", \""); + LOGGER.info(failedCommand + "\", \""); } } private boolean checkIsValidCommand(String command) throws InterruptedException, UnknownHostException, SolarMaxException { - List commands = new ArrayList(); commands.add(command); - Map responseMap = null; + Map responseMap = null; - responseMap = getValuesFromSolarMax(host, port, commands); + responseMap = getValuesFromSolarMax(HOST, PORT, commands); if (responseMap.containsKey(command)) { - System.out.println("Request: " + command + " Valid Response: " + responseMap.get(command)); + LOGGER.debug("Request: " + command + " Valid Response: " + responseMap.get(command)); return true; } return false; @@ -141,12 +139,11 @@ private boolean checkIsValidCommand(String command) /** * based on SolarMaxConnector.getValuesFromSolarMax */ - private static Map getValuesFromSolarMax(final String host, int port, + private static Map getValuesFromSolarMax(final String host, int port, final List commandList) throws SolarMaxException { - Socket socket; - Map returnMap = new HashMap<>(); + 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 @@ -156,7 +153,7 @@ private static Map getValuesFromSolarMax(final String host, int requestsRequired = requestsRequired + 1; } for (int requestNumber = 0; requestNumber < requestsRequired; requestNumber++) { - logger.debug(" Requesting data from {}:{} with timeout of {}ms", host, port, connectionTimeout); + LOGGER.debug(" Requesting data from {}:{} with timeout of {}ms", host, port, CONNECTION_TIMEOUT); int firstCommandNumber = requestNumber * maxConcurrentCommands; int lastCommandNumber = (requestNumber + 1) * maxConcurrentCommands; @@ -182,8 +179,8 @@ private static Map getValuesFromSolarMax(final String host, int return returnMap; } - private static Map getValuesFromSolarMax(final Socket socket, final List commandList) - throws SolarMaxException { + private static Map getValuesFromSolarMax(final Socket socket, + final List commandList) throws SolarMaxException { OutputStream outputStream = null; InputStream inputStream = null; try { @@ -191,7 +188,6 @@ private static Map getValuesFromSolarMax(final Socket socket, fi 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 { @@ -237,17 +233,16 @@ private List getCharacters() { private static Socket getSocketConnection(final String host, int port) throws SolarMaxConnectionException, UnknownHostException { - - port = (port == 0) ? SolarmaxConnectorFindCommands.port : port; + port = (port == 0) ? SolarmaxConnectorFindCommands.PORT : port; Socket socket; try { socket = new Socket(); - logger.debug(" Connecting to " + host + ":" + port + " with a timeout of " + connectionTimeout); - socket.connect(new InetSocketAddress(host, port), connectionTimeout); - logger.debug(" Connected."); - socket.setSoTimeout(connectionTimeout); + LOGGER.debug(" Connecting to " + host + ":" + port + " with a timeout of " + CONNECTION_TIMEOUT); + socket.connect(new InetSocketAddress(host, port), CONNECTION_TIMEOUT); + LOGGER.debug(" Connected."); + socket.setSoTimeout(CONNECTION_TIMEOUT); } catch (final UnknownHostException e) { throw e; } catch (final IOException e) { @@ -257,15 +252,13 @@ private static Socket getSocketConnection(final String host, int port) return socket; } - private static Map getValuesFromSolarMax(final OutputStream outputStream, + private static Map getValuesFromSolarMax(final OutputStream outputStream, final InputStream inputStream, final List commandList) throws SolarMaxException { - - Map returnedValues; + Map returnedValues; String commandString = getCommandString(commandList); String request = SolarMaxConnector.contructRequest(commandString); try { - - logger.trace(" ==>: {}", request); + LOGGER.trace(" ==>: {}", request); outputStream.write(request.getBytes()); @@ -292,7 +285,7 @@ private static Map getValuesFromSolarMax(final OutputStream outp } } - logger.trace(" <==: {}", response); + LOGGER.trace(" <==: {}", response); // if (!validateResponse(response)) { // throw new SolarMaxException("Invalid response received: " + response); @@ -301,9 +294,8 @@ private static Map getValuesFromSolarMax(final OutputStream outp returnedValues = extractValuesFromResponse(response); return returnedValues; - } catch (IOException e) { - logger.debug("Error communicating via input/output streams: {} ", e.getMessage()); + LOGGER.debug("Error communicating via input/output streams: {} ", e.getMessage()); throw new SolarMaxException(e); } } @@ -311,7 +303,7 @@ private static Map getValuesFromSolarMax(final OutputStream outp static String getCommandString(List commandList) { String commandString = ""; for (String command : commandList) { - if (!commandString.equals("")) { + if (!commandString.isEmpty()) { commandString = commandString + ";"; } commandString = commandString + command; @@ -324,13 +316,12 @@ static String getCommandString(List commandList) { * "{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<>(); + 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); + LOGGER.warn("Response doesn't contain data. Response: {}", response); return responseMap; } From 5a68ba91a0a30721505db9b4b5fe2f8747afc1c0 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 31 Jan 2022 15:36:43 +0100 Subject: [PATCH 15/21] Switch to using Units & move softwareVersion & buildNumber to properties Signed-off-by: Jamie Townsend --- .../org.openhab.binding.solarmax/README.md | 52 +++++++++-------- .../solarmax/internal/SolarMaxChannel.java | 56 +++++++++++-------- .../solarmax/internal/SolarMaxHandler.java | 49 +++++++++++++--- .../solarmax/internal/SolarMaxProperty.java | 41 ++++++++++++++ .../internal/connector/SolarMaxData.java | 56 +++++++++---------- .../resources/OH-INF/thing/thing-types.xml | 16 ------ .../internal/connector/SolarMaxDataTest.java | 15 +++-- 7 files changed, 180 insertions(+), 105 deletions(-) create mode 100644 bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md index 9c1d3f0664c72..d1b0f47dcd4d7 100644 --- a/bundles/org.openhab.binding.solarmax/README.md +++ b/bundles/org.openhab.binding.solarmax/README.md @@ -22,31 +22,37 @@ Each inverter requires the following configuration parameters: | refreshInterval | no | 15 | Interval (in seconds) to refresh the channel values. | +## Properties + +| property | description | +| --------------- | ------------------------------------------------------ | +| softwareVersion | Software Version installed on the SolarMax device | +| buildNumber | Firmware Build Number installed on the SolarMax device | + + ## Channels -| channel | type | description | -| ------------------------ | ------------------------ | ------------------------------------------------------ | -| lastUpdated | DateTime | Time when data was last read from the device | -| softwareVersion | Number | Software Version installed on the SolarMax device | -| buildNumber | Number | Firmware Build Number installed on the SolarMax device | -| startups | Number | Number of times the device has started | -| acPhase1Current | Number:ElectricCurrent | Ac Phase 1 Current in Amps | -| acPhase2Current | Number:ElectricCurrent | Ac Phase 2 Current in Amps | -| acPhase3Current | Number:ElectricCurrent | Ac Phase 3 Current in Amps | -| energyGeneratedToday | Number:Energy | Energy Generated Today in Wh | -| energyGeneratedTotal | Number:Energy | Energy Generated since recording began in Wh | -| operatingHours | Number | Operating Hours since recording began in h | -| energyGeneratedYesterday | Number:Energy | Energy Generated Yesterday in Wh | -| energyGeneratedLastMonth | Number:Energy | Energy Generated Last Month in Wh | -| energyGeneratedLastYear | Number:Energy | Energy Generated Last Year in Wh | -| energyGeneratedThisMonth | Number:Energy | Energy Generated This Month in Wh | -| energyGeneratedThisYear | Number:Energy | Energy Generated This Year in Wh | -| currentPowerGenerated | Number:Power | Power currently being generated in W | -| acFrequency | Number:Frequency | AcFrequency in Hz | -| acPhase1Voltage | Number:ElectricPotential | Ac Phase1 Voltage in V | -| acPhase2Voltage | Number:ElectricPotential | Ac Phase2 Voltage in V | -| acPhase3Voltage | Number:ElectricPotential | Ac Phase3 Voltage in V | -| heatSinkTemperature | Number:Temperature | Heat Sink Temperature in degrees celcius | +| channel | type | description | +| ------------------------ | ------------------------ | -------------------------------------------- | +| lastUpdated | DateTime | Time when data was last read from the device | +| startups | Number | Number of times the device has started | +| acPhase1Current | Number:ElectricCurrent | Ac Phase 1 Current in Amps | +| acPhase2Current | Number:ElectricCurrent | Ac Phase 2 Current in Amps | +| acPhase3Current | Number:ElectricCurrent | Ac Phase 3 Current in Amps | +| energyGeneratedToday | Number:Energy | Energy Generated Today in Wh | +| energyGeneratedTotal | Number:Energy | Energy Generated since recording began in Wh | +| operatingHours | Number | Operating Hours since recording began in h | +| energyGeneratedYesterday | Number:Energy | Energy Generated Yesterday in Wh | +| energyGeneratedLastMonth | Number:Energy | Energy Generated Last Month in Wh | +| energyGeneratedLastYear | Number:Energy | Energy Generated Last Year in Wh | +| energyGeneratedThisMonth | Number:Energy | Energy Generated This Month in Wh | +| energyGeneratedThisYear | Number:Energy | Energy Generated This Year in Wh | +| currentPowerGenerated | Number:Power | Power currently being generated in W | +| acFrequency | Number:Frequency | AcFrequency in Hz | +| acPhase1Voltage | Number:ElectricPotential | Ac Phase1 Voltage in V | +| acPhase2Voltage | Number:ElectricPotential | Ac Phase2 Voltage in V | +| acPhase3Voltage | Number:ElectricPotential | Ac Phase3 Voltage in V | +| heatSinkTemperature | Number:Temperature | Heat Sink Temperature in degrees celcius | ### SolarMax Commands 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 index f06843fc4b851..4931481c3a7ba 100644 --- 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 @@ -12,8 +12,13 @@ */ package org.openhab.binding.solarmax.internal; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.solarmax.internal.connector.SolarMaxCommandKey; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; /** * The {@link SolarMaxChannel} Enum defines common constants, which are @@ -24,37 +29,44 @@ @NonNullByDefault public enum SolarMaxChannel { - CHANNEL_LAST_UPDATED("lastUpdated"), // - 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()) + CHANNEL_LAST_UPDATED("lastUpdated", null), // + CHANNEL_STARTUPS(SolarMaxCommandKey.startups.name(), null), + CHANNEL_AC_PHASE1_CURRENT(SolarMaxCommandKey.acPhase1Current.name(), Units.AMPERE), + CHANNEL_AC_PHASE2_CURRENT(SolarMaxCommandKey.acPhase2Current.name(), Units.AMPERE), + CHANNEL_AC_PHASE3_CURRENT(SolarMaxCommandKey.acPhase3Current.name(), Units.AMPERE), + CHANNEL_ENERGY_GENERATED_TODAY(SolarMaxCommandKey.energyGeneratedToday.name(), Units.WATT_HOUR), + CHANNEL_ENERGY_GENERATED_TOTAL(SolarMaxCommandKey.energyGeneratedTotal.name(), Units.WATT_HOUR), + CHANNEL_OPERATING_HOURS(SolarMaxCommandKey.operatingHours.name(), Units.HOUR), + CHANNEL_ENERGY_GENERATED_YESTERDAY(SolarMaxCommandKey.energyGeneratedYesterday.name(), Units.WATT_HOUR), + CHANNEL_ENERGY_GENERATED_LAST_MONTH(SolarMaxCommandKey.energyGeneratedLastMonth.name(), Units.WATT_HOUR), + CHANNEL_ENERGY_GENERATED_LAST_YEAR(SolarMaxCommandKey.energyGeneratedLastYear.name(), Units.WATT_HOUR), + CHANNEL_ENERGY_GENERATED_THIS_MONTH(SolarMaxCommandKey.energyGeneratedThisMonth.name(), Units.WATT_HOUR), + CHANNEL_ENERGY_GENERATED_THIS_YEAR(SolarMaxCommandKey.energyGeneratedThisYear.name(), Units.WATT_HOUR), + CHANNEL_CURRENT_POWER_GENERATED(SolarMaxCommandKey.currentPowerGenerated.name(), Units.WATT_HOUR), + CHANNEL_AC_FREQUENCY(SolarMaxCommandKey.acFrequency.name(), Units.HERTZ), + CHANNEL_AC_PHASE1_VOLTAGE(SolarMaxCommandKey.acPhase1Voltage.name(), Units.VOLT), + CHANNEL_AC_PHASE2_VOLTAGE(SolarMaxCommandKey.acPhase2Voltage.name(), Units.VOLT), + CHANNEL_AC_PHASE3_VOLTAGE(SolarMaxCommandKey.acPhase3Voltage.name(), Units.VOLT), + CHANNEL_HEAT_SINK_TEMPERATUR(SolarMaxCommandKey.heatSinkTemperature.name(), SIUnits.CELSIUS) ; private final String channelId; - private SolarMaxChannel(String channelId) { + @Nullable + private Unit unit; + + private SolarMaxChannel(String channelId, @Nullable Unit unit) { this.channelId = channelId; + this.unit = unit; } public String getChannelId() { return channelId; } + + @Nullable + public Unit getUnit() { + return this.unit; + } } 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 index 1ecf3f3ef7fa1..9b9f6397cc6f6 100644 --- 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 @@ -15,12 +15,16 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.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.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -103,8 +107,9 @@ private synchronized void updateValuesFromDevice() { SolarMaxData solarMaxData = SolarMaxConnector.getAllValuesFromSolarMax(config.host, config.portNumber); if (solarMaxData.wasCommunicationSuccessful()) { - updateChannels(solarMaxData); updateStatus(ThingStatus.ONLINE); + updateProperties(solarMaxData); + updateChannels(solarMaxData); return; } } catch (SolarMaxException e) { @@ -120,13 +125,13 @@ private synchronized void updateValuesFromDevice() { */ private void updateChannels(SolarMaxData solarMaxData) { logger.debug("Updating all channels"); - for (SolarMaxChannel channelConfig : SolarMaxChannel.values()) { - String channelId = channelConfig.getChannelId(); - + for (SolarMaxChannel solarMaxChannel : SolarMaxChannel.values()) { + String channelId = solarMaxChannel.getChannelId(); Channel channel = getThing().getChannel(channelId); if (channelId.equals(SolarMaxChannel.CHANNEL_LAST_UPDATED.getChannelId())) { - // channel shows when the device was last read, so handle it specially + // CHANNEL_LAST_UPDATED shows when the device was last read and does not come from the device, so handle + // it specially State state = solarMaxData.getDataDateTime(); logger.debug("Update channel state: {} - {}", channelId, state); updateState(channel.getUID(), state); @@ -137,17 +142,47 @@ private void updateChannels(SolarMaxData solarMaxData) { if (channel == null) { logger.error("No channel found with id: {}", channelId); } - State state = solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)); + State state = convertValueToState(solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)), + solarMaxChannel.getUnit()); + + // getAcPhase1Current() 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); - } } + } + } + + private @Nullable State convertValueToState(Number value, @Nullable Unit unit) { + if (unit == null) { + return new DecimalType(value.floatValue()); + } + return new QuantityType<>(value, unit); + } + + /* + * Update the properties + */ + private void updateProperties(SolarMaxData solarMaxData) { + logger.debug("Updating properties"); + for (SolarMaxProperty solarMaxProperty : SolarMaxProperty.values()) { + String propertyId = solarMaxProperty.getPropertyId(); + Number valNumber = solarMaxData.get(SolarMaxCommandKey.valueOf(propertyId)); + if (valNumber == null) { + logger.debug("Null value returned for value of {}: {}", getThing().getUID(), propertyId); + continue; + } + // deal with properties + if (propertyId.equals(SolarMaxProperty.PROPERTY_BUILD_NUMBER.getPropertyId()) + || propertyId.equals(SolarMaxProperty.PROPERTY_SOFTWARE_VERSION.getPropertyId())) { + updateProperty(solarMaxProperty.getPropertyId(), valNumber.toString()); + continue; + } } } } diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java new file mode 100644 index 0000000000000..50ce3ded60ebd --- /dev/null +++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2022 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.binding.solarmax.internal.connector.SolarMaxCommandKey; + +/** + * The {@link SolarMaxProperty} Enum defines common constants, which are + * used across the whole binding. + * + * @author Jamie Townsend - Initial contribution + */ +@NonNullByDefault +public enum SolarMaxProperty { + + PROPERTY_SOFTWARE_VERSION(SolarMaxCommandKey.softwareVersion.name()), + PROPERTY_BUILD_NUMBER(SolarMaxCommandKey.buildNumber.name()); + + ; + + private final String propertyId; + + private SolarMaxProperty(String propertyId) { + this.propertyId = propertyId; + } + + public String getPropertyId() { + return propertyId; + } +} 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 index a65a2c1180946..ea63b1ed79fb7 100644 --- 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 @@ -20,7 +20,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.types.DateTimeType; -import org.openhab.core.library.types.DecimalType; import org.openhab.core.types.State; /** @@ -47,7 +46,7 @@ public boolean has(SolarMaxCommandKey key) { return data.containsKey(key); } - public State get(SolarMaxCommandKey key) { + public Number get(SolarMaxCommandKey key) { switch (key) { case softwareVersion: return getSoftwareVersion(); @@ -126,94 +125,94 @@ public void setCommunicationSuccessful(boolean communicationSuccessful) { this.communicationSuccessful = communicationSuccessful; } - public DecimalType getSoftwareVersion() { + public Number getSoftwareVersion() { return getIntegerValueFrom(SolarMaxCommandKey.softwareVersion); } - public DecimalType getBuildNumber() { + public Number getBuildNumber() { return getIntegerValueFrom(SolarMaxCommandKey.buildNumber); } - public DecimalType getStartups() { + public Number getStartups() { return getIntegerValueFrom(SolarMaxCommandKey.startups); } - public DecimalType getAcPhase1Current() { + public Number getAcPhase1Current() { return getDecimalValueFrom(SolarMaxCommandKey.acPhase1Current, 0.01); } - public DecimalType getAcPhase2Current() { + public Number getAcPhase2Current() { return getDecimalValueFrom(SolarMaxCommandKey.acPhase2Current, 0.01); } - public DecimalType getAcPhase3Current() { + public Number getAcPhase3Current() { return getDecimalValueFrom(SolarMaxCommandKey.acPhase3Current, 0.01); } - public DecimalType getEnergyGeneratedToday() { + public Number getEnergyGeneratedToday() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedToday, 100); } - public DecimalType getEnergyGeneratedTotal() { + public Number getEnergyGeneratedTotal() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedTotal, 1000); } - public DecimalType getOperatingHours() { + public Number getOperatingHours() { return getIntegerValueFrom(SolarMaxCommandKey.operatingHours); } - public DecimalType getEnergyGeneratedYesterday() { + public Number getEnergyGeneratedYesterday() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedYesterday, 100); } - public DecimalType getEnergyGeneratedLastMonth() { + public Number getEnergyGeneratedLastMonth() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedLastMonth, 1000); } - public DecimalType getEnergyGeneratedLastYear() { + public Number getEnergyGeneratedLastYear() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedLastYear, 1000); } - public DecimalType getEnergyGeneratedThisMonth() { + public Number getEnergyGeneratedThisMonth() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedThisMonth, 1000); } - public DecimalType getEnergyGeneratedThisYear() { + public Number getEnergyGeneratedThisYear() { return getIntegerValueFrom(SolarMaxCommandKey.energyGeneratedThisYear, 1000); } - public DecimalType getCurrentPowerGenerated() { + public Number getCurrentPowerGenerated() { return getIntegerValueFrom(SolarMaxCommandKey.currentPowerGenerated, 0.5); } - public DecimalType getAcFrequency() { + Number getAcFrequency() { return getDecimalValueFrom(SolarMaxCommandKey.acFrequency, 0.01); } - public DecimalType getAcPhase1Voltage() { + public Number getAcPhase1Voltage() { return getDecimalValueFrom(SolarMaxCommandKey.acPhase1Voltage, 0.1); } - public DecimalType getAcPhase2Voltage() { + public Number getAcPhase2Voltage() { return getDecimalValueFrom(SolarMaxCommandKey.acPhase2Voltage, 0.1); } - public DecimalType getAcPhase3Voltage() { + public Number getAcPhase3Voltage() { return getDecimalValueFrom(SolarMaxCommandKey.acPhase3Voltage, 0.1); } - public DecimalType getHeatSinkTemperature() { + public Number getHeatSinkTemperature() { return getIntegerValueFrom(SolarMaxCommandKey.heatSinkTemperature); } @Nullable - private DecimalType getDecimalValueFrom(SolarMaxCommandKey solarMaxCommandKey, double multiplyByFactor) { + private Number 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 (float) valueInt * multiplyByFactor; } return null; } @@ -221,13 +220,13 @@ private DecimalType getDecimalValueFrom(SolarMaxCommandKey solarMaxCommandKey, d } @Nullable - private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey, double multiplyByFactor) { + private Number 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 (int) (valueInt * multiplyByFactor); } return null; } @@ -235,12 +234,11 @@ private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey, d } @Nullable - private DecimalType getIntegerValueFrom(SolarMaxCommandKey solarMaxCommandKey) { + private Number 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 Integer.parseInt(valueString, 16); } return null; } 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 index 10dd5bf2f11c1..4fdda616b3e07 100644 --- 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 @@ -11,8 +11,6 @@ - - @@ -59,20 +57,6 @@ - - Number - - Software Version installed on the SolarMax device - - - - - Number - - Firmware Build Number installed on the SolarMax device - - - Number 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 index 6568db03a0d65..6aa72e1c0bc29 100644 --- 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 @@ -24,7 +24,6 @@ import org.eclipse.jdt.annotation.Nullable; 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. @@ -51,25 +50,25 @@ public void dataDateTimeGetterSetterTest() throws Exception { @Test public void valueGetterSetterTest() throws Exception { - String softwareVersionOriginal = "3B8B"; // 15243 in hex - String softwareVersionUpdated = "3B8C"; // 15244 in hex + String startupsOriginal = "3B8B"; // 15243 in hex + String startupsUpdated = "3B8C"; // 15244 in hex SolarMaxData solarMaxData = new SolarMaxData(); Map dataOrig = new HashMap<>(); - dataOrig.put(SolarMaxCommandKey.softwareVersion, softwareVersionOriginal); + dataOrig.put(SolarMaxCommandKey.startups, startupsOriginal); solarMaxData.setData(dataOrig); @Nullable - DecimalType origVersion = solarMaxData.get(SolarMaxCommandKey.softwareVersion).as(DecimalType.class); + Number origVersion = solarMaxData.get(SolarMaxCommandKey.startups); assertNotNull(origVersion); - assertEquals(Integer.parseInt(softwareVersionOriginal, 16), origVersion.intValue()); + assertEquals(Integer.parseInt(startupsOriginal, 16), origVersion.intValue()); Map dataUpdated = new HashMap<>(); - dataUpdated.put(SolarMaxCommandKey.softwareVersion, softwareVersionUpdated); + dataUpdated.put(SolarMaxCommandKey.startups, startupsUpdated); solarMaxData.setData(dataUpdated); - DecimalType updatedVersion = solarMaxData.get(SolarMaxCommandKey.softwareVersion).as(DecimalType.class); + Number updatedVersion = solarMaxData.get(SolarMaxCommandKey.startups); assertNotEquals(origVersion, updatedVersion); } } From 795803c5a03ee1642ad20e8623db6971a0f5ff15 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 31 Jan 2022 16:37:26 +0100 Subject: [PATCH 16/21] A couple of review related updates Signed-off-by: Jamie Townsend --- .../solarmax/internal/SolarMaxChannel.java | 4 +- .../solarmax/internal/SolarMaxHandler.java | 40 +++++++++---------- .../internal/SolarMaxHandlerFactory.java | 3 -- 3 files changed, 19 insertions(+), 28 deletions(-) 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 index 4931481c3a7ba..93ebacc0b8298 100644 --- 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 @@ -47,9 +47,7 @@ public enum SolarMaxChannel { CHANNEL_AC_PHASE1_VOLTAGE(SolarMaxCommandKey.acPhase1Voltage.name(), Units.VOLT), CHANNEL_AC_PHASE2_VOLTAGE(SolarMaxCommandKey.acPhase2Voltage.name(), Units.VOLT), CHANNEL_AC_PHASE3_VOLTAGE(SolarMaxCommandKey.acPhase3Voltage.name(), Units.VOLT), - CHANNEL_HEAT_SINK_TEMPERATUR(SolarMaxCommandKey.heatSinkTemperature.name(), SIUnits.CELSIUS) - - ; + CHANNEL_HEAT_SINK_TEMPERATUR(SolarMaxCommandKey.heatSinkTemperature.name(), SIUnits.CELSIUS); private final String channelId; 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 index 9b9f6397cc6f6..0501081035f2f 100644 --- 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 @@ -63,8 +63,6 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { @Override public void initialize() { - logger.debug("Initializing SolarMax"); - config = getConfigAs(SolarMaxConfiguration.class); configurePolling(); // Setup the scheduler @@ -86,7 +84,6 @@ private void configurePolling() { @Override public void dispose() { - logger.debug("Disposing SolarMax Handler Thing"); if (pollingJob != null && !pollingJob.isCancelled()) { pollingJob.cancel(true); } @@ -113,10 +110,8 @@ private synchronized void updateValuesFromDevice() { 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."); + "Communication error with the device: " + e.getMessage()); } } @@ -136,22 +131,23 @@ private void updateChannels(SolarMaxData solarMaxData) { 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 = convertValueToState(solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)), - solarMaxChannel.getUnit()); - - // getAcPhase1Current() - - 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); + } 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 = convertValueToState(solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)), + solarMaxChannel.getUnit()); + + // getAcPhase1Current() + + 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 index 029bc08070b52..5922bdfc59cfa 100644 --- 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 @@ -49,10 +49,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { 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; From 0bf67e3e04a2ccbcffd4c58cbbb6c2774c974511 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Mon, 7 Feb 2022 11:07:12 +0100 Subject: [PATCH 17/21] A couple more review related changes. Signed-off-by: Jamie Townsend --- bundles/org.openhab.binding.solarmax/README.md | 2 +- .../org/openhab/binding/solarmax/internal/SolarMaxHandler.java | 2 -- .../org/openhab/binding/solarmax/internal/SolarMaxProperty.java | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md index d1b0f47dcd4d7..c932729d9363b 100644 --- a/bundles/org.openhab.binding.solarmax/README.md +++ b/bundles/org.openhab.binding.solarmax/README.md @@ -59,6 +59,6 @@ Each inverter requires the following configuration parameters: During the implementation the SolarMax device was sent all possible 3 character commands and a number of 4 character commands, to see what it responded to. The most interesting, identifiable and useful commands were implemented as channels above. -Here is a list of other commands, which are know to return some kind of value: ADR (DeviceAddress / Device Number - only used if the devices are linked serially), AMM, CID, CPG, CPL, CP1, CP2, CP3, CP4, CP5, CYC, DIN, DMO, ETH, FH2, FQR, FWV, IAA, IED, IEE, IEM, ILM, IP4, ISL, ITS, KFS, KHS, KTS, LAN (Language), MAC (MAC Address), PAE, PAM, PDA, PDC, PFA, PIN (Power Installed), PLR, PPC, PRL (AC Power Percent, PSF, PSR, PSS, QAC, QMO, QUC, RA1, RA2, RB1, RB2, REL, RH1, RH2, RPR, RSD, SAC, SAL, SAM, SCH, SNM (IP Broadcast Address??), SPS, SRD, SRS, SYS (Operating State), TCP (probably port number - 12345), TI1, TL1, TL3, TND, TNH, TNL, TP1, TP2, TP3, TV0, TV1, TYP (Type?), UA2, UB2, UGD, UI1, UI2, UI3, ULH, ULL, UMX, UM1, UM2, UM3, UPD, UZK, VCM +Here is a list of other commands, which are known to return some kind of value: ADR (DeviceAddress / Device Number - only used if the devices are linked serially), AMM, CID, CPG, CPL, CP1, CP2, CP3, CP4, CP5, CYC, DIN, DMO, ETH, FH2, FQR, FWV, IAA, IED, IEE, IEM, ILM, IP4, ISL, ITS, KFS, KHS, KTS, LAN (Language), MAC (MAC Address), PAE, PAM, PDA, PDC, PFA, PIN (Power Installed), PLR, PPC, PRL (AC Power Percent, PSF, PSR, PSS, QAC, QMO, QUC, RA1, RA2, RB1, RB2, REL, RH1, RH2, RPR, RSD, SAC, SAL, SAM, SCH, SNM (IP Broadcast Address??), SPS, SRD, SRS, SYS (Operating State), TCP (probably port number - 12345), TI1, TL1, TL3, TND, TNH, TNL, TP1, TP2, TP3, TV0, TV1, TYP (Type?), UA2, UB2, UGD, UI1, UI2, UI3, ULH, ULL, UMX, UM1, UM2, UM3, UPD, UZK, VCM Valid commands which returned a null/empty value during testing: FFK, FRT, GCP, ITN, PLD, PLE, PLF, PLS, PPO, TV2, VLE, VLI, VLO 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 index 0501081035f2f..54e300afabcd4 100644 --- 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 @@ -140,8 +140,6 @@ private void updateChannels(SolarMaxData solarMaxData) { State state = convertValueToState(solarMaxData.get(SolarMaxCommandKey.valueOf(channelId)), solarMaxChannel.getUnit()); - // getAcPhase1Current() - if (channel != null && state != null) { logger.debug("Update channel state: {} - {}", channelId, state); updateState(channel.getUID(), state); diff --git a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java index 50ce3ded60ebd..bc9455a8972cd 100644 --- a/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java +++ b/bundles/org.openhab.binding.solarmax/src/main/java/org/openhab/binding/solarmax/internal/SolarMaxProperty.java @@ -27,8 +27,6 @@ public enum SolarMaxProperty { PROPERTY_SOFTWARE_VERSION(SolarMaxCommandKey.softwareVersion.name()), PROPERTY_BUILD_NUMBER(SolarMaxCommandKey.buildNumber.name()); - ; - private final String propertyId; private SolarMaxProperty(String propertyId) { From 7398cb1aff35311c7412beac77f69ac0d0948f82 Mon Sep 17 00:00:00 2001 From: Jamie Townsend Date: Sun, 11 Sep 2022 22:55:12 +0200 Subject: [PATCH 18/21] Added Full Example to README.md Signed-off-by: Jamie Townsend --- .../org.openhab.binding.solarmax/README.md | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/bundles/org.openhab.binding.solarmax/README.md b/bundles/org.openhab.binding.solarmax/README.md index c932729d9363b..9af6c406d01a9 100644 --- a/bundles/org.openhab.binding.solarmax/README.md +++ b/bundles/org.openhab.binding.solarmax/README.md @@ -54,6 +54,74 @@ Each inverter requires the following configuration parameters: | acPhase3Voltage | Number:ElectricPotential | Ac Phase3 Voltage in V | | heatSinkTemperature | Number:Temperature | Heat Sink Temperature in degrees celcius | +### Full Example + +Below you can find some example textual configuration for a solarmax with some basic functionallity. This can be extended/adjusted according to your needs and depending on the required channels (see list above). + +_inverter.things:_ + +``` +Thing solarmax:inverter:solarmax "SolarMax Inverter" [ + host="192.168.1.151", + port="12345", + refresh="15" +] +``` + +_inverter.items:_ + +``` +Group gInverter "SolarMax Inverter" + +DateTime lastUpdated "Last Updated" (gInverter) {channel="solarmax:inverter:solarmax:lastUpdated"} + +Number startups "Startups" (gInverter) { channel="solarmax:inverter:solarmax:startups" } + +Number:ElectricCurrent acPhase1Current "Ac Phase 1 Current in Amps" (gInverter) { channel="solarmax:inverter:solarmax:acPhase1Current" } +Number:ElectricCurrent acPhase2Current "Ac Phase 2 Current in Amps" (gInverter) { channel="solarmax:inverter:solarmax:acPhase2Current" } +Number:ElectricCurrent acPhase3Current "Ac Phase 3 Current in Amps" (gInverter) { channel="solarmax:inverter:solarmax:acPhase3Current" } + +Number:Energy energyGeneratedToday "Energy Generated Today in Wh" (gInverter) { channel="solarmax:inverter:solarmax:energyGeneratedToday" } +Number:Energy energyGeneratedTotal "Energy Generated since recording began in Wh" (gInverter) { channel="solarmax:inverter:solarmax:energyGeneratedTotal" } + +Number operatingHours "Operating Hours since recording began in h"