From 104a1e537956809e7f777fc049914d8bcc5a93f0 Mon Sep 17 00:00:00 2001 From: Konstantin Polihronov Date: Tue, 23 Jul 2024 10:23:03 +0300 Subject: [PATCH] [solax] Support for Solax EV charger via direct(local) data retrieval (#17055) * Implementation of Solax EV Charger support Signed-off-by: Konstantin Polihronov --- bundles/org.openhab.binding.solax/README.md | 49 +++++- .../solax/internal/SolaxBindingConstants.java | 31 +++- .../solax/internal/SolaxHandlerFactory.java | 7 +- .../local/LocalConnectRawDataBean.java | 21 +++ .../handlers/AbstractSolaxHandler.java | 14 ++ .../SolaxLocalAccessAbstractHandler.java | 101 +++++++++++ .../SolaxLocalAccessChargerHandler.java | 129 ++++++++++++++ ...a => SolaxLocalAccessInverterHandler.java} | 84 ++------- .../solax/internal/model/InverterType.java | 10 +- ...erData.java => CommonLocalDeviceData.java} | 21 ++- .../internal/model/local/EvChargerData.java | 161 ++++++++++++++++++ ...{LocalInverterData.java => LocalData.java} | 19 ++- .../local/{parsers => }/RawDataParser.java | 5 +- ...erterData.java => X1BoostAirMiniData.java} | 32 ++-- ...4InverterData.java => X1HybridG4Data.java} | 40 ++--- ...4InverterData.java => X3HybridG4Data.java} | 84 ++++----- ...nverterData.java => X3MicOrProG2Data.java} | 54 +++--- .../local/evchargers/EvChargerDataParser.java | 53 ++++++ .../X1BoostAirMiniDataParser.java | 13 +- .../X1HybridG4DataParser.java | 13 +- .../X3HybridG4DataParser.java | 13 +- .../X3MicOrProG2DataParser.java | 11 +- .../binding/solax/internal/util/ByteUtil.java | 31 ++++ .../resources/OH-INF/i18n/solax.properties | 99 ++++++++++- .../resources/OH-INF/thing/channel_types.xml | 41 +++++ .../OH-INF/thing/localConnectEVCharger.xml | 129 ++++++++++++++ .../internal/local/AbstractParserTest.java | 8 +- .../local/parsers/TestEVChargerParser.java | 101 +++++++++++ .../TestX1BoostAirMiniDataParser.java | 7 +- .../{ => parsers}/TestX1HybridG4Parser.java | 7 +- .../{ => parsers}/TestX3HybridG4Parser.java | 7 +- .../{ => parsers}/TestX3MicOrProG2Parser.java | 8 +- .../solax/internal/util/TestByteUtil.java | 52 ++++++ 33 files changed, 1206 insertions(+), 249 deletions(-) create mode 100644 bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java create mode 100644 bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/{SolaxLocalAccessHandler.java => SolaxLocalAccessInverterHandler.java} (75%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{CommonLocalInverterData.java => CommonLocalDeviceData.java} (78%) create mode 100644 bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{LocalInverterData.java => LocalData.java} (91%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{parsers => }/RawDataParser.java (81%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{X1BoostAirMiniInverterData.java => X1BoostAirMiniData.java} (64%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{X1HybridG4InverterData.java => X1HybridG4Data.java} (64%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{X3HybridG4InverterData.java => X3HybridG4Data.java} (61%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{X3MicOrProG2InverterData.java => X3MicOrProG2Data.java} (63%) create mode 100644 bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{parsers => inverters}/X1BoostAirMiniDataParser.java (82%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{parsers => inverters}/X1HybridG4DataParser.java (83%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{parsers => inverters}/X3HybridG4DataParser.java (87%) rename bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/{parsers => inverters}/X3MicOrProG2DataParser.java (87%) create mode 100644 bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java create mode 100644 bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml create mode 100644 bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java rename bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/{ => parsers}/TestX1BoostAirMiniDataParser.java (91%) rename bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/{ => parsers}/TestX1HybridG4Parser.java (94%) rename bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/{ => parsers}/TestX3HybridG4Parser.java (95%) rename bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/{ => parsers}/TestX3MicOrProG2Parser.java (93%) create mode 100644 bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java diff --git a/bundles/org.openhab.binding.solax/README.md b/bundles/org.openhab.binding.solax/README.md index 08b6cd8973..a928d872df 100644 --- a/bundles/org.openhab.binding.solax/README.md +++ b/bundles/org.openhab.binding.solax/README.md @@ -12,10 +12,11 @@ In case the parsed information that comes with the binding out of the box differ ## Supported Things -| Thing | Thing Type | Description | -|------------------------|------------|-------------------------------------------------------------------------------------| -| local-connect-inverter | Thing | An inverter representation with all the data available as a channels (directly retrieved from the wi-fi module | -| cloud-connect-inverter | Thing | An inverter representation with all the data available as a channels (retrieved from the Solax cloud API) | +| Thing | Thing Type | Description | +|------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------| +| local-connect-inverter | Thing | An inverter representation with all the data available as a channels (directly retrieved from the wi-fi module) | +| local-connect-charger | Thing | An electric vehicle charger representation with all the data available as a channels (directly retrieved from the wi-fi module) | +| cloud-connect-inverter | Thing | An inverter representation with all the data available as a channels (retrieved from the Solax cloud API) | Note: Channels may vary depending on the inverter type and the availability of information for parsing the raw data. If you're missing a channel this means that it's not supported for your inverter type. @@ -113,6 +114,46 @@ If you're missing a channel this means that it's not supported for your inverter | inverterType | Inverter Type (for example X1_HYBRID_G4) | +### Local Connect EV Charger Configuration + +### Parameters + +| Parameter | Description | +|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| refreshInterval | Defines the refresh interval when the binding polls from the inverter's Wi-Fi module (in seconds). Optional parameter. Default 10 seconds. | +| password | Password for accessing the Wi-Fi module (the serial number of the wifi). Mandatory parameter. | +| hostname | IP address or hostname of your Wi-Fi module. If hostname is used must be resolvable by OpenHAB. Mandatory parameter. | + +### Channels + +| Channel ID | Type | Description | +|------------------------------------------|-----------------------------|---------------------------------------------------------------| +| eq-single-session | Number:Energy | Energy charged for the current session. [kWh] | +| eq-total | Number:Energy | Total energy charged for all sessions. [kWh] | +| charger-output-power-phase1 | Number:Power | Power to/from the charger phase 1. [W] | +| charger-output-power-phase2 | Number:Power | Power to/from the charger phase 2. [W] | +| charger-output-power-phase3 | Number:Power | Power to/from the charger phase 3. [W] | +| charger-total-output-power | Number:Power | Power from the charger on all phases. [W] | +| charger-current-phase1 | Number:ElectricCurrent | Current from the charger phase 1. [A] | +| charger-current-phase2 | Number:ElectricCurrent | Current from the charger phase 2. [A] | +| charger-current-phase3 | Number:ElectricCurrent | Current from the charger phase 3. [A] | +| charger-voltage-phase1 | Number:ElectricPotential | Voltage of the charger's phase 1. [V] | +| charger-voltage-phase2 | Number:ElectricPotential | Voltage of the charger's phase 2. [V] | +| charger-voltage-phase3 | Number:ElectricPotential | Voltage of the charger's phase 3. [V] | +| charger-external-current-phase1 | Number:ElectricCurrent | Current from the provider phase 1. [A] | +| charger-external-current-phase2 | Number:ElectricCurrent | Current from the provider phase 2. [A] | +| charger-external-current-phase3 | Number:ElectricCurrent | Current from the provider phase 3. [A] | +| charger-external-power-phase1 | Number:Power | Power from the provider phase 1. [W] | +| charger-external-power-phase2 | Number:Power | Power from the provider phase 2. [W] | +| charger-external-power-phase3 | Number:Power | Power from the provider phase 3. [W] | +| charger-external-total-power | Number:Power | Total power from the provider. [W] | +| charger-plug-temperature | Number:Temperature | Temperature of the charger's plug. [°C] | +| charger-internal-temperature | Number:Temperature | Internal temperature on the board of the charger. [°C] | +| charger-mode | String | Charger Workmode. | +| charger-state | String | Charger State. | +| last-update-time | DateTime | Last time with a successful retrieval of data. | + + ### Cloud Connect Inverter Configuration | Parameter | Description | diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java index 4d2f18fb49..16d2f2c4c5 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java @@ -28,16 +28,19 @@ public class SolaxBindingConstants { public static final String BINDING_ID = "solax"; private static final String THING_LOCAL_CONNECT_INVERTER_ID = "local-connect-inverter"; + private static final String THING_LOCAL_CONNECT_CHARGER_ID = "local-connect-charger"; private static final String THING_CLOUD_CONNECT_INVERTER_ID = "cloud-connect-inverter"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_LOCAL_CONNECT_INVERTER = new ThingTypeUID(BINDING_ID, THING_LOCAL_CONNECT_INVERTER_ID); + public static final ThingTypeUID THING_TYPE_LOCAL_CONNECT_CHARGER = new ThingTypeUID(BINDING_ID, + THING_LOCAL_CONNECT_CHARGER_ID); public static final ThingTypeUID THING_TYPE_CLOUD_CONNECT_INVERTER = new ThingTypeUID(BINDING_ID, THING_CLOUD_CONNECT_INVERTER_ID); public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LOCAL_CONNECT_INVERTER, - THING_TYPE_CLOUD_CONNECT_INVERTER); + THING_TYPE_LOCAL_CONNECT_CHARGER, THING_TYPE_CLOUD_CONNECT_INVERTER); // List of properties public static final String PROPERTY_INVERTER_TYPE = "inverterType"; @@ -116,6 +119,32 @@ public class SolaxBindingConstants { public static final String CHANNEL_INVERTER_EPS_POWER_S = "inverter-eps-power-s"; public static final String CHANNEL_INVERTER_EPS_POWER_T = "inverter-eps-power-t"; + // EV Charger channels + public static final String CHANNEL_CHARGER_MODE = "charger-mode"; + public static final String CHANNEL_CHARGER_STATE = "charger-state"; + public static final String CHANNEL_CHARGER_EQ_SINGLE_SESSION = "eq-single-session"; + public static final String CHANNEL_CHARGER_EQ_TOTAL = "eq-total"; + public static final String CHANNEL_CHARGER_OUTPUT_POWER_PHASE1 = "charger-output-power-phase1"; + public static final String CHANNEL_CHARGER_OUTPUT_POWER_PHASE2 = "charger-output-power-phase2"; + public static final String CHANNEL_CHARGER_OUTPUT_POWER_PHASE3 = "charger-output-power-phase3"; + public static final String CHANNEL_CHARGER_TOTAL_OUTPUT_POWER = "charger-total-output-power"; + public static final String CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE1 = "charger-current-phase1"; + public static final String CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE2 = "charger-current-phase2"; + public static final String CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE3 = "charger-current-phase3"; + public static final String CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE1 = "charger-voltage-phase1"; + public static final String CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE2 = "charger-voltage-phase2"; + public static final String CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE3 = "charger-voltage-phase3"; + public static final String CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE1 = "charger-external-current-phase1"; + public static final String CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE2 = "charger-external-current-phase2"; + public static final String CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE3 = "charger-external-current-phase3"; + public static final String CHANNEL_CHARGER_EXTERNAL_POWER_PHASE1 = "charger-external-power-phase1"; + public static final String CHANNEL_CHARGER_EXTERNAL_POWER_PHASE2 = "charger-external-power-phase2"; + public static final String CHANNEL_CHARGER_EXTERNAL_POWER_PHASE3 = "charger-external-power-phase3"; + public static final String CHANNEL_CHARGER_TOTAL_EXTERNAL_POWER = "charger-external-total-power"; + public static final String CHANNEL_CHARGER_PLUG_TEMPERATURE = "charger-plug-temperature"; + public static final String CHANNEL_CHARGER_INTERNAL_TEMPERATURE = "charger-internal-temperature"; + // I18N Keys public static final String I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED = "@text/offline.communication-error.json-cannot-be-retrieved"; + public static final String I18N_KEY_OFFLINE_CONFIGURATION_ERROR_JSON_CANNOT_BE_PARSED = "@text/offline.configuration-error.json-cannot-be-parsed"; } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java index c0c5a2b103..1a2fe28d4f 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java @@ -17,7 +17,8 @@ import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.solax.internal.handlers.SolaxCloudHandler; -import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessHandler; +import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessChargerHandler; +import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessInverterHandler; import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TranslationProvider; @@ -61,9 +62,11 @@ public class SolaxHandlerFactory extends BaseThingHandlerFactory { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_LOCAL_CONNECT_INVERTER.equals(thingTypeUID)) { - return new SolaxLocalAccessHandler(thing, i18nProvider, timeZoneProvider); + return new SolaxLocalAccessInverterHandler(thing, i18nProvider, timeZoneProvider); } else if (THING_TYPE_CLOUD_CONNECT_INVERTER.equals(thingTypeUID)) { return new SolaxCloudHandler(thing, i18nProvider, timeZoneProvider, localeProvider); + } else if (THING_TYPE_LOCAL_CONNECT_CHARGER.equals(thingTypeUID)) { + return new SolaxLocalAccessChargerHandler(thing, i18nProvider, timeZoneProvider); } return null; } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/local/LocalConnectRawDataBean.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/local/LocalConnectRawDataBean.java index 876648493c..576853d321 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/local/LocalConnectRawDataBean.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/local/LocalConnectRawDataBean.java @@ -30,6 +30,7 @@ import com.google.gson.annotations.SerializedName; @NonNullByDefault public class LocalConnectRawDataBean implements RawDataBean { + @SerializedName(value = "sn", alternate = { "SN" }) private @Nullable String sn; private @Nullable String ver; private int type; @@ -38,6 +39,10 @@ public class LocalConnectRawDataBean implements RawDataBean { @SerializedName("Information") private String @Nullable [] information; private @Nullable String rawData; + @SerializedName("OCPPServer") + private @Nullable String ocppServer; + @SerializedName("OCPPChargerId") + private @Nullable String ocppChargerId; @Override public String toString() { @@ -95,6 +100,22 @@ public class LocalConnectRawDataBean implements RawDataBean { this.rawData = rawData; } + public @Nullable String getOcppServer() { + return ocppServer; + } + + public void setOcppServer(@Nullable String ocppServer) { + this.ocppServer = ocppServer; + } + + public @Nullable String getOcppChargerId() { + return ocppChargerId; + } + + public void setOcppChargerId(String ocppChargerId) { + this.ocppChargerId = ocppChargerId; + } + public static LocalConnectRawDataBean fromJson(String json) { if (json.isEmpty()) { throw new IllegalArgumentException("JSON payload should not be empty"); diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/AbstractSolaxHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/AbstractSolaxHandler.java index 037efa896b..cfcb9d8143 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/AbstractSolaxHandler.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/AbstractSolaxHandler.java @@ -36,6 +36,8 @@ import org.openhab.core.types.RefreshType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonSyntaxException; + /** * The {@link SolaxCloudHandler} is responsible for handling commands, which are * sent to one of the channels. @@ -99,6 +101,10 @@ public abstract class AbstractSolaxHandler extends BaseThingHandler { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } catch (SolaxUpdateException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } catch (JsonSyntaxException jse) { + logger.debug("JsonSyntaxException received.", jse); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + SolaxBindingConstants.I18N_KEY_OFFLINE_CONFIGURATION_ERROR_JSON_CANNOT_BE_PARSED); } finally { retrieveDataCallLock.unlock(); } @@ -116,6 +122,14 @@ public abstract class AbstractSolaxHandler extends BaseThingHandler { } } + @Override + protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) { + super.updateStatus(status, statusDetail, description); + if (status == ThingStatus.OFFLINE && statusDetail == ThingStatusDetail.CONFIGURATION_ERROR) { + cancelSchedule(); + } + } + @Override public void dispose() { super.dispose(); diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java new file mode 100644 index 0000000000..39d55a5706 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.handlers; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.measure.Quantity; +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.SolaxConfiguration; +import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector; +import org.openhab.binding.solax.internal.connectivity.SolaxConnector; +import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; +import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SolaxLocalAccessAbstractHandler} abstract handler combining the common logic for an inverter and a charger + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public abstract class SolaxLocalAccessAbstractHandler extends AbstractSolaxHandler { + private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessAbstractHandler.class); + + protected final Set unsupportedExistingChannels = new HashSet<>(); + protected boolean alreadyRemovedUnsupportedChannels; + + public SolaxLocalAccessAbstractHandler(Thing thing, TranslationProvider i18nProvider, + TimeZoneProvider timeZoneProvider) { + super(thing, i18nProvider, timeZoneProvider); + } + + @Override + protected SolaxConnector createConnector(SolaxConfiguration config) { + return new LocalHttpConnector(config.password, config.hostname); + } + + protected LocalConnectRawDataBean parseJson(String rawJsonData) { + LocalConnectRawDataBean fromJson = LocalConnectRawDataBean.fromJson(rawJsonData); + logger.debug("Received a new inverter JSON object. Data = {}", fromJson.toString()); + return fromJson; + } + + protected void removeUnsupportedChannels(Set supportedChannels) { + if (supportedChannels.isEmpty()) { + return; + } + List channels = getThing().getChannels(); + List channelsToRemove = channels.stream() + .filter(channel -> !supportedChannels.contains(channel.getUID().getId())).toList(); + + if (!channelsToRemove.isEmpty()) { + if (logger.isDebugEnabled()) { + logRemovedChannels(channelsToRemove); + } + updateThing(editThing().withoutChannels(channelsToRemove).build()); + } + } + + private void logRemovedChannels(List channelsToRemove) { + List channelsToRemoveForLog = channelsToRemove.stream().map(channel -> channel.getUID().getId()) + .toList(); + logger.debug("Detected unsupported channels for the current inverter. Channels to be removed: {}", + channelsToRemoveForLog); + } + + protected > void updateChannel(String channelID, double value, Unit unit, + Set supportedChannels) { + if (supportedChannels.contains(channelID)) { + if (value > Short.MIN_VALUE) { + updateState(channelID, new QuantityType<>(value, unit)); + } else if (!unsupportedExistingChannels.contains(channelID)) { + updateState(channelID, UnDefType.UNDEF); + unsupportedExistingChannels.add(channelID); + logger.warn( + "Channel {} is marked as supported, but its value is out of the defined range. Value = {}. This is unexpected behaviour. Please file a bug.", + channelID, value); + } + } + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java new file mode 100644 index 0000000000..5609c4318a --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.handlers; + +import java.time.ZonedDateTime; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.SolaxBindingConstants; +import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; +import org.openhab.binding.solax.internal.exceptions.SolaxUpdateException; +import org.openhab.binding.solax.internal.model.local.EvChargerData; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; +import org.openhab.binding.solax.internal.model.local.evchargers.EvChargerDataParser; +import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Thing; + +/** + * The {@link SolaxLocalAccessChargerHandler} the handler for the charger + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class SolaxLocalAccessChargerHandler extends SolaxLocalAccessAbstractHandler { + + private static final EvChargerDataParser parser = new EvChargerDataParser(); + + public SolaxLocalAccessChargerHandler(Thing thing, TranslationProvider i18nProvider, + TimeZoneProvider timeZoneProvider) { + super(thing, i18nProvider, timeZoneProvider); + } + + @Override + public void initialize() { + removeUnsupportedChannels(parser.getSupportedChannels()); + super.initialize(); + } + + @Override + protected void updateFromData(String rawJsonData) throws SolaxUpdateException { + LocalConnectRawDataBean rawDataBean = parseJson(rawJsonData); + EvChargerData data = parser.getData(rawDataBean); + updateChannels(parser, data); + updateProperties(data); + } + + private void updateProperties(LocalData data) { + } + + private void updateChannels(RawDataParser parser, EvChargerData data) { + Set supportedChannels = parser.getSupportedChannels(); + + // States/modes + updateState(SolaxBindingConstants.CHANNEL_CHARGER_MODE, new StringType(data.getDeviceMode())); + updateState(SolaxBindingConstants.CHANNEL_CHARGER_STATE, new StringType(data.getDeviceState())); + + // Energy + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EQ_SINGLE_SESSION, data.getEqSingle(), Units.KILOWATT_HOUR, + supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EQ_TOTAL, data.getEqTotal(), Units.KILOWATT_HOUR, + supportedChannels); + + // Output data + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE1, data.getCurrentPhase1(), + Units.AMPERE, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE2, data.getCurrentPhase2(), + Units.AMPERE, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE3, data.getCurrentPhase3(), + Units.AMPERE, supportedChannels); + + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE1, data.getVoltagePhase1(), Units.VOLT, + supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE2, data.getVoltagePhase2(), Units.VOLT, + supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE3, data.getVoltagePhase3(), Units.VOLT, + supportedChannels); + + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_POWER_PHASE1, data.getOutputPowerPhase1(), + Units.WATT, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_POWER_PHASE2, data.getOutputPowerPhase2(), + Units.WATT, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_POWER_PHASE3, data.getOutputPowerPhase3(), + Units.WATT, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_TOTAL_OUTPUT_POWER, data.getTotalChargePower(), Units.WATT, + supportedChannels); + + // Provider data + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE1, data.getExternalCurrentPhase1(), + Units.AMPERE, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE2, data.getExternalCurrentPhase2(), + Units.AMPERE, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE3, data.getExternalCurrentPhase3(), + Units.AMPERE, supportedChannels); + + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_POWER_PHASE1, data.getExternalPowerPhase1(), + Units.WATT, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_POWER_PHASE2, data.getExternalPowerPhase2(), + Units.WATT, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_POWER_PHASE3, data.getExternalPowerPhase3(), + Units.WATT, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_TOTAL_EXTERNAL_POWER, data.getExternalTotalPower(), + Units.WATT, supportedChannels); + + // Temperatures + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_PLUG_TEMPERATURE, data.getPlugTemperature(), + SIUnits.CELSIUS, supportedChannels); + updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_INTERNAL_TEMPERATURE, data.getInternalTemperature(), + SIUnits.CELSIUS, supportedChannels); + + // Binding provided data + updateState(SolaxBindingConstants.CHANNEL_TIMESTAMP, new DateTimeType(ZonedDateTime.now())); + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessInverterHandler.java similarity index 75% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessHandler.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessInverterHandler.java index 60f59e74eb..b496cc09a6 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessHandler.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessInverterHandler.java @@ -13,63 +13,43 @@ package org.openhab.binding.solax.internal.handlers; import java.time.ZonedDateTime; -import java.util.HashSet; -import java.util.List; import java.util.Set; -import javax.measure.Quantity; -import javax.measure.Unit; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.SolaxBindingConstants; -import org.openhab.binding.solax.internal.SolaxConfiguration; -import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector; -import org.openhab.binding.solax.internal.connectivity.SolaxConnector; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; import org.openhab.binding.solax.internal.model.InverterType; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.library.types.DateTimeType; -import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; -import org.openhab.core.thing.Channel; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; -import org.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.JsonParseException; /** - * The {@link SolaxLocalAccessHandler} is responsible for handling commands, which are - * sent to one of the channels. + * The {@link SolaxLocalAccessInverterHandler} the handler for the inverter * * @author Konstantin Polihronov - Initial contribution */ @NonNullByDefault -public class SolaxLocalAccessHandler extends AbstractSolaxHandler { - - private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessHandler.class); - - private boolean alreadyRemovedUnsupportedChannels; +public class SolaxLocalAccessInverterHandler extends SolaxLocalAccessAbstractHandler { - private final Set unsupportedExistingChannels = new HashSet<>(); + private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessInverterHandler.class); - public SolaxLocalAccessHandler(Thing thing, TranslationProvider i18nProvider, TimeZoneProvider timeZoneProvider) { + public SolaxLocalAccessInverterHandler(Thing thing, TranslationProvider i18nProvider, + TimeZoneProvider timeZoneProvider) { super(thing, i18nProvider, timeZoneProvider); } - @Override - protected SolaxConnector createConnector(SolaxConfiguration config) { - return new LocalHttpConnector(config.password, config.hostname); - } - @Override protected void updateFromData(String rawJsonData) { try { @@ -82,7 +62,7 @@ public class SolaxLocalAccessHandler extends AbstractSolaxHandler { alreadyRemovedUnsupportedChannels = true; } - LocalInverterData genericInverterData = parser.getData(rawDataBean); + LocalData genericInverterData = parser.getData(rawDataBean); updateChannels(parser, genericInverterData); updateProperties(genericInverterData); } else { @@ -96,23 +76,17 @@ public class SolaxLocalAccessHandler extends AbstractSolaxHandler { } } - private LocalConnectRawDataBean parseJson(String rawJsonData) { - LocalConnectRawDataBean fromJson = LocalConnectRawDataBean.fromJson(rawJsonData); - logger.debug("Received a new inverter JSON object. Data = {}", fromJson.toString()); - return fromJson; - } - private InverterType calculateInverterType(LocalConnectRawDataBean rawDataBean) { int type = rawDataBean.getType(); return InverterType.fromIndex(type); } - private void updateProperties(LocalInverterData genericInverterData) { + private void updateProperties(LocalData genericInverterData) { updateProperty(Thing.PROPERTY_SERIAL_NUMBER, genericInverterData.getWifiSerial()); updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, genericInverterData.getInverterType().name()); } - private void updateChannels(RawDataParser parser, LocalInverterData inverterData) { + private void updateChannels(RawDataParser parser, LocalData inverterData) { updateState(SolaxBindingConstants.CHANNEL_RAW_DATA, new StringType(inverterData.getRawData())); Set supportedChannels = parser.getSupportedChannels(); @@ -222,42 +196,4 @@ public class SolaxLocalAccessHandler extends AbstractSolaxHandler { // Binding provided data updateState(SolaxBindingConstants.CHANNEL_TIMESTAMP, new DateTimeType(ZonedDateTime.now())); } - - private void removeUnsupportedChannels(Set supportedChannels) { - if (supportedChannels.isEmpty()) { - return; - } - List channels = getThing().getChannels(); - List channelsToRemove = channels.stream() - .filter(channel -> !supportedChannels.contains(channel.getUID().getId())).toList(); - - if (!channelsToRemove.isEmpty()) { - if (logger.isDebugEnabled()) { - logRemovedChannels(channelsToRemove); - } - updateThing(editThing().withoutChannels(channelsToRemove).build()); - } - } - - private void logRemovedChannels(List channelsToRemove) { - List channelsToRemoveForLog = channelsToRemove.stream().map(channel -> channel.getUID().getId()) - .toList(); - logger.debug("Detected unsupported channels for the current inverter. Channels to be removed: {}", - channelsToRemoveForLog); - } - - private > void updateChannel(String channelID, double value, Unit unit, - Set supportedChannels) { - if (supportedChannels.contains(channelID)) { - if (value > Short.MIN_VALUE) { - updateState(channelID, new QuantityType<>(value, unit)); - } else if (!unsupportedExistingChannels.contains(channelID)) { - updateState(channelID, UnDefType.UNDEF); - unsupportedExistingChannels.add(channelID); - logger.warn( - "Channel {} is marked as supported, but its value is out of the defined range. Value = {}. This is unexpected behaviour. Please file a bug.", - channelID, value); - } - } - } } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java index 17a88adecb..09996a0759 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java @@ -19,11 +19,11 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser; -import org.openhab.binding.solax.internal.model.local.parsers.X1BoostAirMiniDataParser; -import org.openhab.binding.solax.internal.model.local.parsers.X1HybridG4DataParser; -import org.openhab.binding.solax.internal.model.local.parsers.X3HybridG4DataParser; -import org.openhab.binding.solax.internal.model.local.parsers.X3MicOrProG2DataParser; +import org.openhab.binding.solax.internal.model.local.RawDataParser; +import org.openhab.binding.solax.internal.model.local.inverters.X1BoostAirMiniDataParser; +import org.openhab.binding.solax.internal.model.local.inverters.X1HybridG4DataParser; +import org.openhab.binding.solax.internal.model.local.inverters.X3HybridG4DataParser; +import org.openhab.binding.solax.internal.model.local.inverters.X3MicOrProG2DataParser; /** * The {@link InverterType} class is enum representing the different inverter types with a simple logic to convert from diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalInverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalDeviceData.java similarity index 78% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalInverterData.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalDeviceData.java index 6d7f23f5ad..4a6b16949c 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalInverterData.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalDeviceData.java @@ -20,19 +20,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The {@link CommonLocalInverterData} is an abstract class that contains the common information, applicable for all - * inverters. + * The {@link CommonLocalDeviceData} is an abstract class that contains the common information, applicable for all + * devices. * * @author Konstantin Polihronov - Initial contribution */ @NonNullByDefault -public abstract class CommonLocalInverterData implements LocalInverterData { +public abstract class CommonLocalDeviceData implements LocalData { - private final Logger logger = LoggerFactory.getLogger(CommonLocalInverterData.class); + private final Logger logger = LoggerFactory.getLogger(CommonLocalDeviceData.class); private LocalConnectRawDataBean data; - public CommonLocalInverterData(LocalConnectRawDataBean data) { + public CommonLocalDeviceData(LocalConnectRawDataBean data) { this.data = data; } @@ -56,7 +56,7 @@ public abstract class CommonLocalInverterData implements LocalInverterData { return InverterType.fromIndex(data.getType()); } - protected short getData(int index) { + protected Short getFromRawData(int index) { try { short[] dataArray = data.getData(); if (dataArray != null) { @@ -69,12 +69,17 @@ public abstract class CommonLocalInverterData implements LocalInverterData { } public long packU16(int indexMajor, int indexMinor) { - short major = getData(indexMajor); - short minor = getData(indexMinor); + short major = getFromRawData(indexMajor); + short minor = getFromRawData(indexMinor); if (major == 0) { return minor; } return Integer.toUnsignedLong(major << 16 | minor & 0xFFFF); } + + @Override + public LocalConnectRawDataBean getData() { + return data; + } } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java new file mode 100644 index 0000000000..41962e662a --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.model.local; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; +import org.openhab.binding.solax.internal.model.InverterType; +import org.openhab.binding.solax.internal.util.ByteUtil; + +/** + * The {@link EvChargerData} is the data representation of the EV charger, retrieved from the raw data array. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class EvChargerData extends CommonLocalDeviceData { + + public EvChargerData(LocalConnectRawDataBean bean) { + super(bean); + } + + @Override + public InverterType getInverterType() { + return InverterType.UNKNOWN; + } + + public String getDeviceState() { + return String.valueOf(getFromRawData(0)); + } + + public String getDeviceMode() { + return String.valueOf(getFromRawData(1)); + } + + public double getEqSingle() { + return getFromRawData(12).doubleValue() / 10; + } + + public double getEqTotal() { + return ((double) ByteUtil.read32BitSigned(getFromRawData(14), getFromRawData(15))) / 10; + } + + public short getTotalChargePower() { + return getFromRawData(11); + } + + @Override + public double getVoltagePhase1() { + return getFromRawData(2).doubleValue() / 100; + } + + @Override + public double getVoltagePhase2() { + return getFromRawData(3).doubleValue() / 100; + } + + @Override + public double getVoltagePhase3() { + return getFromRawData(4).doubleValue() / 100; + } + + @Override + public double getCurrentPhase1() { + return getFromRawData(5).doubleValue() / 100; + } + + @Override + public double getCurrentPhase2() { + return getFromRawData(6).doubleValue() / 100; + } + + @Override + public double getCurrentPhase3() { + return getFromRawData(7).doubleValue() / 100; + } + + @Override + public short getOutputPowerPhase1() { + return getFromRawData(8); + } + + @Override + public short getOutputPowerPhase2() { + return getFromRawData(9); + } + + @Override + public short getOutputPowerPhase3() { + return getFromRawData(10); + } + + public double getExternalCurrentPhase1() { + return getFromRawData(16).doubleValue() / 100; + } + + public double getExternalCurrentPhase2() { + return getFromRawData(17).doubleValue() / 100; + } + + public double getExternalCurrentPhase3() { + return getFromRawData(18).doubleValue() / 100; + } + + public double getExternalPowerPhase1() { + return getFromRawData(19).intValue(); + } + + public double getExternalPowerPhase2() { + return getFromRawData(20).intValue(); + } + + public double getExternalPowerPhase3() { + return getFromRawData(21).intValue(); + } + + public double getExternalTotalPower() { + return getFromRawData(22).intValue(); + } + + public short getPlugTemperature() { + return getFromRawData(23); + } + + public short getInternalTemperature() { + return getFromRawData(24); + } + + public short getCPState() { + return getFromRawData(26); + } + + public int getChargingDuration() { + return ByteUtil.read32BitSigned(getFromRawData(80), getFromRawData(81)); + } + + public short getOccpOfflineMode() { + return getFromRawData(85); + } + + public short getTypePower() { + return getFromRawData(87); + } + + public short getTypePhase() { + return getFromRawData(88); + } + + public short getTypeCharger() { + return getFromRawData(89); + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalInverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalData.java similarity index 91% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalInverterData.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalData.java index 106aaee772..f9c6ac7b1e 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalInverterData.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalData.java @@ -14,26 +14,35 @@ package org.openhab.binding.solax.internal.model.local; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; import org.openhab.binding.solax.internal.model.InverterType; /** - * The {@link LocalInverterData} Interface for the parsed inverter data in meaningful format + * The {@link LocalData} Interface for the parsed inverter data in meaningful format * * @author Konstantin Polihronov - Initial contribution */ @NonNullByDefault -public interface LocalInverterData { +public interface LocalData { @Nullable - String getWifiSerial(); + default String getWifiSerial() { + return getData().getSn(); + } @Nullable - String getWifiVersion(); + default String getWifiVersion() { + return getData().getVer(); + } InverterType getInverterType(); @Nullable - String getRawData(); + default String getRawData() { + return getData().getRawData(); + } + + LocalConnectRawDataBean getData(); default double getPV1Voltage() { return Short.MIN_VALUE; diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/RawDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/RawDataParser.java similarity index 81% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/RawDataParser.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/RawDataParser.java index d45cc088eb..7214d30bf2 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/RawDataParser.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/RawDataParser.java @@ -10,13 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.model.local.parsers; +package org.openhab.binding.solax.internal.model.local; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; /** * The {@link RawDataParser} declares generic parser implementation that parses raw data to generic inverter data which @@ -27,7 +26,7 @@ import org.openhab.binding.solax.internal.model.local.LocalInverterData; @NonNullByDefault public interface RawDataParser { - LocalInverterData getData(LocalConnectRawDataBean bean); + LocalData getData(LocalConnectRawDataBean bean); Set getSupportedChannels(); } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniInverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniData.java similarity index 64% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniInverterData.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniData.java index 9c4e951180..104ac87ec6 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniInverterData.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniData.java @@ -16,80 +16,80 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; /** - * The {@link X1BoostAirMiniInverterData} is an implementation of the single phased inverter data interface for X1 Mini + * The {@link X1BoostAirMiniData} is an implementation of the single phased inverter data interface for X1 Mini * / X1 Air Mini or X1 Boost Mini inverter. * * @author Konstantin Polihronov - Initial contribution */ @NonNullByDefault -public class X1BoostAirMiniInverterData extends CommonLocalInverterData { +public class X1BoostAirMiniData extends CommonLocalDeviceData { - public X1BoostAirMiniInverterData(LocalConnectRawDataBean data) { + public X1BoostAirMiniData(LocalConnectRawDataBean data) { super(data); } @Override public double getInverterVoltage() { - return (double) getData(0) / 10; + return (double) getFromRawData(0) / 10; } @Override public double getInverterCurrent() { - return (double) getData(1) / 10; + return (double) getFromRawData(1) / 10; } @Override public short getInverterOutputPower() { - return getData(2); + return getFromRawData(2); } @Override public double getPV1Voltage() { - return (double) getData(3) / 10; + return (double) getFromRawData(3) / 10; } @Override public double getPV2Voltage() { - return (double) getData(4) / 10; + return (double) getFromRawData(4) / 10; } @Override public double getPV1Current() { - return (double) getData(5) / 10; + return (double) getFromRawData(5) / 10; } @Override public double getPV2Current() { - return (double) getData(6) / 10; + return (double) getFromRawData(6) / 10; } @Override public short getPV1Power() { - return getData(7); + return getFromRawData(7); } @Override public short getPV2Power() { - return getData(8); + return getFromRawData(8); } @Override public double getInverterFrequency() { - return (double) getData(9) / 100; + return (double) getFromRawData(9) / 100; } @Override public double getTotalEnergy() { - return (double) getData(11) / 10; + return (double) getFromRawData(11) / 10; } @Override public double getTodayEnergy() { - return (double) getData(13) / 10; + return (double) getFromRawData(13) / 10; } @Override public short getPowerUsage() { - return (short) Math.round((double) getData(43) / 10); + return (short) Math.round((double) getFromRawData(43) / 10); } } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4Data.java similarity index 64% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4InverterData.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4Data.java index b5364595c2..ea59e4bb96 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4InverterData.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4Data.java @@ -16,100 +16,100 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; /** - * The {@link X1HybridG4InverterData} is an implementation of the single phased inverter data interface for X1 Hybrid G4 + * The {@link X1HybridG4Data} is an implementation of the single phased inverter data interface for X1 Hybrid G4 * inverter. * * @author Konstantin Polihronov - Initial contribution */ @NonNullByDefault -public class X1HybridG4InverterData extends CommonLocalInverterData { +public class X1HybridG4Data extends CommonLocalDeviceData { - public X1HybridG4InverterData(LocalConnectRawDataBean data) { + public X1HybridG4Data(LocalConnectRawDataBean data) { super(data); } @Override public double getInverterVoltage() { - return ((double) getData(0)) / 10; + return ((double) getFromRawData(0)) / 10; } @Override public double getInverterCurrent() { - return ((double) getData(1)) / 10; + return ((double) getFromRawData(1)) / 10; } @Override public short getInverterOutputPower() { - return getData(2); + return getFromRawData(2); } @Override public double getInverterFrequency() { - return ((double) getData(3)) / 100; + return ((double) getFromRawData(3)) / 100; } @Override public short getFeedInPower() { - return getData(32); + return getFromRawData(32); } @Override public double getPV1Voltage() { - return ((double) getData(4)) / 10; + return ((double) getFromRawData(4)) / 10; } @Override public double getPV2Voltage() { - return ((double) getData(5)) / 10; + return ((double) getFromRawData(5)) / 10; } @Override public double getPV1Current() { - return ((double) getData(6)) / 10; + return ((double) getFromRawData(6)) / 10; } @Override public double getPV2Current() { - return ((double) getData(7)) / 10; + return ((double) getFromRawData(7)) / 10; } @Override public short getPV1Power() { - return getData(8); + return getFromRawData(8); } @Override public short getPV2Power() { - return getData(9); + return getFromRawData(9); } @Override public double getBatteryVoltage() { - return ((double) getData(14)) / 100; + return ((double) getFromRawData(14)) / 100; } @Override public double getBatteryCurrent() { - return ((double) getData(15)) / 100; + return ((double) getFromRawData(15)) / 100; } @Override public short getBatteryPower() { - return getData(16); + return getFromRawData(16); } @Override public short getBatteryTemperature() { - return getData(17); + return getFromRawData(17); } @Override public short getBatteryLevel() { - return getData(18); + return getFromRawData(18); } @Override public short getInverterWorkModeCode() { - return getData(10); + return getFromRawData(10); } } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4Data.java similarity index 61% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4InverterData.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4Data.java index b613fb4487..a1f1b16519 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4InverterData.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4Data.java @@ -16,15 +16,15 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; /** - * The {@link X3HybridG4InverterData} is responsible for handling commands, which are - * sent to one of the channels. + * The {@link X3HybridG4Data} is an implementation of the single phased inverter data interface for X3 Hybrid G4 + * inverter. * * @author Konstantin Polihronov - Initial contribution */ @NonNullByDefault -public class X3HybridG4InverterData extends CommonLocalInverterData { +public class X3HybridG4Data extends CommonLocalDeviceData { - public X3HybridG4InverterData(LocalConnectRawDataBean data) { + public X3HybridG4Data(LocalConnectRawDataBean data) { super(data); } @@ -32,197 +32,197 @@ public class X3HybridG4InverterData extends CommonLocalInverterData { @Override public double getVoltagePhase1() { - return ((double) getData(0)) / 10; + return ((double) getFromRawData(0)) / 10; } @Override public double getVoltagePhase2() { - return ((double) getData(1)) / 10; + return ((double) getFromRawData(1)) / 10; } @Override public double getVoltagePhase3() { - return ((double) getData(2)) / 10; + return ((double) getFromRawData(2)) / 10; } @Override public double getCurrentPhase1() { - return ((double) getData(3)) / 10; + return ((double) getFromRawData(3)) / 10; } @Override public double getCurrentPhase2() { - return ((double) getData(4)) / 10; + return ((double) getFromRawData(4)) / 10; } @Override public double getCurrentPhase3() { - return ((double) getData(5)) / 10; + return ((double) getFromRawData(5)) / 10; } @Override public short getOutputPowerPhase1() { - return getData(6); + return getFromRawData(6); } @Override public short getOutputPowerPhase2() { - return getData(7); + return getFromRawData(7); } @Override public short getOutputPowerPhase3() { - return getData(8); + return getFromRawData(8); } @Override public short getTotalOutputPower() { - return getData(9); + return getFromRawData(9); } @Override public double getPV1Voltage() { - return ((double) getData(10)) / 10; + return ((double) getFromRawData(10)) / 10; } @Override public double getPV2Voltage() { - return ((double) getData(11)) / 10; + return ((double) getFromRawData(11)) / 10; } @Override public double getPV1Current() { - return ((double) getData(12)) / 10; + return ((double) getFromRawData(12)) / 10; } @Override public double getPV2Current() { - return ((double) getData(13)) / 10; + return ((double) getFromRawData(13)) / 10; } @Override public short getPV1Power() { - return getData(14); + return getFromRawData(14); } @Override public short getPV2Power() { - return getData(15); + return getFromRawData(15); } @Override public double getFrequencyPhase1() { - return ((double) getData(16)) / 100; + return ((double) getFromRawData(16)) / 100; } @Override public double getFrequencyPhase2() { - return ((double) getData(17)) / 100; + return ((double) getFromRawData(17)) / 100; } @Override public double getFrequencyPhase3() { - return ((double) getData(18)) / 100; + return ((double) getFromRawData(18)) / 100; } // Battery @Override public double getBatteryVoltage() { - return ((double) getData(39)) / 100; + return ((double) getFromRawData(39)) / 100; } @Override public double getBatteryCurrent() { - return ((double) getData(40)) / 100; + return ((double) getFromRawData(40)) / 100; } @Override public short getBatteryPower() { - return getData(41); + return getFromRawData(41); } @Override public short getBatteryTemperature() { - return getData(105); + return getFromRawData(105); } @Override public short getBatteryLevel() { - return getData(103); + return getFromRawData(103); } // Feed in power @Override public short getFeedInPower() { - return (short) (getData(34) - getData(35)); + return (short) (getFromRawData(34) - getFromRawData(35)); } // Totals @Override public short getPowerUsage() { - return getData(47); + return getFromRawData(47); } @Override public double getTotalEnergy() { - return ((double) getData(68)) / 10; + return ((double) getFromRawData(68)) / 10; } @Override public short getTotalBatteryDischargeEnergy() { - return getData(74); + return getFromRawData(74); } @Override public short getTotalBatteryChargeEnergy() { - return getData(76); + return getFromRawData(76); } @Override public double getTotalPVEnergy() { - return ((double) getData(80)) / 10; + return ((double) getFromRawData(80)) / 10; } @Override public short getTotalFeedInEnergy() { - return getData(86); + return getFromRawData(86); } @Override public double getTotalConsumption() { - return ((double) getData(88)) / 10; + return ((double) getFromRawData(88)) / 10; } @Override public double getTodayEnergy() { - return ((double) getData(82)) / 10; + return ((double) getFromRawData(82)) / 10; } @Override public double getTodayFeedInEnergy() { - return ((double) getData(90)) / 100; + return ((double) getFromRawData(90)) / 100; } @Override public double getTodayConsumption() { - return ((double) getData(92)) / 100; + return ((double) getFromRawData(92)) / 100; } @Override public double getTodayBatteryDischargeEnergy() { - return ((double) getData(78)) / 10; + return ((double) getFromRawData(78)) / 10; } @Override public double getTodayBatteryChargeEnergy() { - return ((double) getData(79)) / 10; + return ((double) getFromRawData(79)) / 10; } @Override public short getInverterWorkModeCode() { - return getData(19); + return getFromRawData(19); } } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2Data.java similarity index 63% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2InverterData.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2Data.java index bd0e03c7a4..b56fba937a 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2InverterData.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2Data.java @@ -16,16 +16,16 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; /** - * The {@link X3HybridG4InverterData} is responsible for handling commands, which are + * The {@link X3HybridG4Data} is responsible for handling commands, which are * sent to one of the channels. * * @author Henrik Tóth - Initial contribution * (based on X1/X3 G4 parser from Konstantin Polihronov) */ @NonNullByDefault -public class X3MicOrProG2InverterData extends CommonLocalInverterData { +public class X3MicOrProG2Data extends CommonLocalDeviceData { - public X3MicOrProG2InverterData(LocalConnectRawDataBean data) { + public X3MicOrProG2Data(LocalConnectRawDataBean data) { super(data); } @@ -33,121 +33,121 @@ public class X3MicOrProG2InverterData extends CommonLocalInverterData { @Override public double getVoltagePhase1() { - return ((double) getData(0)) / 10; + return ((double) getFromRawData(0)) / 10; } @Override public double getVoltagePhase2() { - return ((double) getData(1)) / 10; + return ((double) getFromRawData(1)) / 10; } @Override public double getVoltagePhase3() { - return ((double) getData(2)) / 10; + return ((double) getFromRawData(2)) / 10; } @Override public double getCurrentPhase1() { - return ((double) getData(3)) / 10; + return ((double) getFromRawData(3)) / 10; } @Override public double getCurrentPhase2() { - return ((double) getData(4)) / 10; + return ((double) getFromRawData(4)) / 10; } @Override public double getCurrentPhase3() { - return ((double) getData(5)) / 10; + return ((double) getFromRawData(5)) / 10; } @Override public short getOutputPowerPhase1() { - return getData(6); + return getFromRawData(6); } @Override public short getOutputPowerPhase2() { - return getData(7); + return getFromRawData(7); } @Override public short getOutputPowerPhase3() { - return getData(8); + return getFromRawData(8); } @Override public double getPV1Voltage() { - return ((double) getData(9)) / 10; + return ((double) getFromRawData(9)) / 10; } @Override public double getPV2Voltage() { - return ((double) getData(10)) / 10; + return ((double) getFromRawData(10)) / 10; } @Override public double getPV1Current() { - return ((double) getData(12)) / 10; + return ((double) getFromRawData(12)) / 10; } @Override public double getPV2Current() { - return ((double) getData(13)) / 10; + return ((double) getFromRawData(13)) / 10; } @Override public short getPV1Power() { - return getData(15); + return getFromRawData(15); } @Override public short getPV2Power() { - return getData(16); + return getFromRawData(16); } @Override public double getFrequencyPhase1() { - return ((double) getData(18)) / 100; + return ((double) getFromRawData(18)) / 100; } @Override public double getFrequencyPhase2() { - return ((double) getData(19)) / 100; + return ((double) getFromRawData(19)) / 100; } @Override public double getFrequencyPhase3() { - return ((double) getData(20)) / 100; + return ((double) getFromRawData(20)) / 100; } @Override public short getInverterWorkModeCode() { - return getData(21); + return getFromRawData(21); } @Override public double getTotalEnergy() { - return ((double) getData(22)) / 10; + return ((double) getFromRawData(22)) / 10; } @Override public double getTodayEnergy() { - return ((double) getData(24)) / 10; + return ((double) getFromRawData(24)) / 10; } @Override public short getInverterTemperature1() { - return getData(26); + return getFromRawData(26); } @Override public short getInverterTemperature2() { - return getData(27); + return getFromRawData(27); } @Override public short getTotalOutputPower() { - return getData(78); + return getFromRawData(78); } } diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java new file mode 100644 index 0000000000..bd08d99224 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.model.local.evchargers; + +import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; +import org.openhab.binding.solax.internal.model.local.EvChargerData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; + +/** + * The {@link EvChargerDataParser} is the implementation that parses raw data into a EvChargerData for the EV charger. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class EvChargerDataParser implements RawDataParser { + + private static final Set EV_CHARGER_SUPPORTED_CHANNELS = Set.of(CHANNEL_CHARGER_MODE, CHANNEL_CHARGER_STATE, + CHANNEL_CHARGER_EQ_SINGLE_SESSION, CHANNEL_CHARGER_EQ_TOTAL, CHANNEL_CHARGER_OUTPUT_POWER_PHASE1, + CHANNEL_CHARGER_OUTPUT_POWER_PHASE2, CHANNEL_CHARGER_OUTPUT_POWER_PHASE3, + CHANNEL_CHARGER_TOTAL_OUTPUT_POWER, CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE1, + CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE2, CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE3, + CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE1, CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE2, + CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE3, CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE1, + CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE2, CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE3, + CHANNEL_CHARGER_EXTERNAL_POWER_PHASE1, CHANNEL_CHARGER_EXTERNAL_POWER_PHASE2, + CHANNEL_CHARGER_EXTERNAL_POWER_PHASE3, CHANNEL_CHARGER_TOTAL_EXTERNAL_POWER, + CHANNEL_CHARGER_PLUG_TEMPERATURE, CHANNEL_CHARGER_INTERNAL_TEMPERATURE); + + @Override + public EvChargerData getData(LocalConnectRawDataBean bean) { + return new EvChargerData(bean); + } + + @Override + public Set getSupportedChannels() { + return EV_CHARGER_SUPPORTED_CHANNELS; + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1BoostAirMiniDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1BoostAirMiniDataParser.java similarity index 82% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1BoostAirMiniDataParser.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1BoostAirMiniDataParser.java index 4b680b609f..d79bb23cda 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1BoostAirMiniDataParser.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1BoostAirMiniDataParser.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.model.local.parsers; +package org.openhab.binding.solax.internal.model.local.inverters; import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; @@ -18,11 +18,12 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.X1BoostAirMiniInverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; +import org.openhab.binding.solax.internal.model.local.X1BoostAirMiniData; /** - * The {@link X1BoostAirMiniDataParser} is the implementation that parses raw data into a LocalInverterData for the + * The {@link X1BoostAirMiniDataParser} is the implementation that parses raw data into a {@link LocalData} for the * X1 Mini / X1 Air Mini or X1 Boost Mini inverter. * * @author Konstantin Polihronov - Initial contribution @@ -38,8 +39,8 @@ public class X1BoostAirMiniDataParser implements RawDataParser { CHANNEL_TOTAL_ENERGY, CHANNEL_TODAY_ENERGY, CHANNEL_POWER_USAGE); @Override - public LocalInverterData getData(LocalConnectRawDataBean bean) { - return new X1BoostAirMiniInverterData(bean); + public LocalData getData(LocalConnectRawDataBean bean) { + return new X1BoostAirMiniData(bean); } @Override diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1HybridG4DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1HybridG4DataParser.java similarity index 83% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1HybridG4DataParser.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1HybridG4DataParser.java index 2804f1dbbf..59abad6a4a 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1HybridG4DataParser.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1HybridG4DataParser.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.model.local.parsers; +package org.openhab.binding.solax.internal.model.local.inverters; import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; @@ -18,11 +18,12 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.X1HybridG4InverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; +import org.openhab.binding.solax.internal.model.local.X1HybridG4Data; /** - * The {@link X1HybridG4DataParser} is the implementation that parses raw data into a LocalInverterData for the + * The {@link X1HybridG4DataParser} is the implementation that parses raw data into a {@link LocalData} for the * X1 Hybrid G4 inverter. * * @author Konstantin Polihronov - Initial contribution @@ -39,8 +40,8 @@ public class X1HybridG4DataParser implements RawDataParser { CHANNEL_INVERTER_OUTPUT_VOLTAGE, CHANNEL_INVERTER_OUTPUT_FREQUENCY, CHANNEL_INVERTER_WORKMODE); @Override - public LocalInverterData getData(LocalConnectRawDataBean rawData) { - return new X1HybridG4InverterData(rawData); + public LocalData getData(LocalConnectRawDataBean rawData) { + return new X1HybridG4Data(rawData); } @Override diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3HybridG4DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3HybridG4DataParser.java similarity index 87% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3HybridG4DataParser.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3HybridG4DataParser.java index 3cc4f11f5e..d023596593 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3HybridG4DataParser.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3HybridG4DataParser.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.model.local.parsers; +package org.openhab.binding.solax.internal.model.local.inverters; import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; @@ -18,11 +18,12 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.X3HybridG4InverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; +import org.openhab.binding.solax.internal.model.local.X3HybridG4Data; /** - * The {@link X3HybridG4DataParser} is the implementation that parses raw data into a LocalInverterData for the + * The {@link X3HybridG4DataParser} is the implementation that parses raw data into a {@link LocalData} for the * X3 Hybrid G4 inverter. * * @author Konstantin Polihronov - Initial contribution @@ -47,8 +48,8 @@ public class X3HybridG4DataParser implements RawDataParser { CHANNEL_TODAY_BATTERY_CHARGE_ENERGY, CHANNEL_TODAY_BATTERY_DISCHARGE_ENERGY, CHANNEL_INVERTER_WORKMODE); @Override - public LocalInverterData getData(LocalConnectRawDataBean rawData) { - return new X3HybridG4InverterData(rawData); + public LocalData getData(LocalConnectRawDataBean rawData) { + return new X3HybridG4Data(rawData); } @Override diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3MicOrProG2DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3MicOrProG2DataParser.java similarity index 87% rename from bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3MicOrProG2DataParser.java rename to bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3MicOrProG2DataParser.java index 4dec8566be..f63c70c23c 100644 --- a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3MicOrProG2DataParser.java +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3MicOrProG2DataParser.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.model.local.parsers; +package org.openhab.binding.solax.internal.model.local.inverters; import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; @@ -18,8 +18,9 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.X3MicOrProG2InverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; +import org.openhab.binding.solax.internal.model.local.X3MicOrProG2Data; /** * The {@link X3MicOrProG2DataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the @@ -44,8 +45,8 @@ public class X3MicOrProG2DataParser implements RawDataParser { CHANNEL_INVERTER_TEMPERATURE2, CHANNEL_INVERTER_WORKMODE, CHANNEL_RAW_DATA); @Override - public LocalInverterData getData(LocalConnectRawDataBean rawData) { - return new X3MicOrProG2InverterData(rawData); + public LocalData getData(LocalConnectRawDataBean rawData) { + return new X3MicOrProG2Data(rawData); } @Override diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java new file mode 100644 index 0000000000..dc5b5981d3 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ByteUtil} Utility method for manipulating byte-level data + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class ByteUtil { + + public static final int MASK = 0xFFFF; + public static final int SHIFT_VALUE = 16; + + public static int read32BitSigned(short low, short high) { + return (high << SHIFT_VALUE) | (low & MASK); + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties index e5f3ee6b0c..bd1c910650 100644 --- a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties @@ -23,6 +23,8 @@ thing-type.solax.cloud-connect-inverter.channel.inverter-meter2-power.label = Me thing-type.solax.cloud-connect-inverter.channel.inverter-meter2-power.description = Inverter power on meter2. thing-type.solax.cloud-connect-inverter.channel.inverter-output-power.label = Inverter Input/Output Power thing-type.solax.cloud-connect-inverter.channel.inverter-output-power.description = Power to/from the inverter +thing-type.solax.cloud-connect-inverter.channel.inverter-workmode.label = Inverter Workmode +thing-type.solax.cloud-connect-inverter.channel.inverter-workmode.description = Inverter Workmode thing-type.solax.cloud-connect-inverter.channel.pv-total-power.label = PV Total Power thing-type.solax.cloud-connect-inverter.channel.pv-total-power.description = The sum of PV powers from all PV strings thing-type.solax.cloud-connect-inverter.channel.pv1-power.label = PV 1 Power @@ -41,6 +43,54 @@ thing-type.solax.cloud-connect-inverter.channel.total-energy.label = Yield total thing-type.solax.cloud-connect-inverter.channel.total-energy.description = Total inverter output energy thing-type.solax.cloud-connect-inverter.channel.total-feed-in-energy.label = Total Feed-In Energy thing-type.solax.cloud-connect-inverter.channel.total-feed-in-energy.description = Total energy feed-in to the electricity network. +thing-type.solax.local-connect-charger.label = Local Connect Charger +thing-type.solax.local-connect-charger.description = The charger representation that supports local connections via HTTP +thing-type.solax.local-connect-charger.channel.charger-current-phase1.label = Charger Output Current Phase 1 +thing-type.solax.local-connect-charger.channel.charger-current-phase1.description = Current from the charger phase 1 +thing-type.solax.local-connect-charger.channel.charger-current-phase2.label = Charger Output Current Phase 2 +thing-type.solax.local-connect-charger.channel.charger-current-phase2.description = Current from the charger phase 2 +thing-type.solax.local-connect-charger.channel.charger-current-phase3.label = Charger Output Current Phase 3 +thing-type.solax.local-connect-charger.channel.charger-current-phase3.description = Current from the charger phase 3 +thing-type.solax.local-connect-charger.channel.charger-external-current-phase1.label = Charger External Current Phase 1 +thing-type.solax.local-connect-charger.channel.charger-external-current-phase1.description = Current from the provider phase 1 +thing-type.solax.local-connect-charger.channel.charger-external-current-phase2.label = Charger External Current Phase 2 +thing-type.solax.local-connect-charger.channel.charger-external-current-phase2.description = Current from the provider phase 2 +thing-type.solax.local-connect-charger.channel.charger-external-current-phase3.label = Charger External Current Phase 3 +thing-type.solax.local-connect-charger.channel.charger-external-current-phase3.description = Current from the provider phase 3 +thing-type.solax.local-connect-charger.channel.charger-external-power-phase1.label = Charger External Power Phase 1 +thing-type.solax.local-connect-charger.channel.charger-external-power-phase1.description = Power from the provider phase 1 +thing-type.solax.local-connect-charger.channel.charger-external-power-phase2.label = Charger External Power Phase 2 +thing-type.solax.local-connect-charger.channel.charger-external-power-phase2.description = Power from the provider phase 2 +thing-type.solax.local-connect-charger.channel.charger-external-power-phase3.label = Charger External Power Phase 3 +thing-type.solax.local-connect-charger.channel.charger-external-power-phase3.description = Power from the provider phase 3 +thing-type.solax.local-connect-charger.channel.charger-external-total-power.label = Charger External Total Power +thing-type.solax.local-connect-charger.channel.charger-external-total-power.description = Total power from the provider +thing-type.solax.local-connect-charger.channel.charger-internal-temperature.label = Charger Internal Temperature +thing-type.solax.local-connect-charger.channel.charger-internal-temperature.description = Internal temperature on the board of the charger +thing-type.solax.local-connect-charger.channel.charger-mode.label = Charger Workmode +thing-type.solax.local-connect-charger.channel.charger-mode.description = Charger Workmode +thing-type.solax.local-connect-charger.channel.charger-output-power-phase1.label = Charger Output Power Phase 1 +thing-type.solax.local-connect-charger.channel.charger-output-power-phase1.description = Power to/from the charger phase 1 +thing-type.solax.local-connect-charger.channel.charger-output-power-phase2.label = Charger Output Power Phase 2 +thing-type.solax.local-connect-charger.channel.charger-output-power-phase2.description = Power to/from the charger phase 2 +thing-type.solax.local-connect-charger.channel.charger-output-power-phase3.label = Charger Output Power Phase 3 +thing-type.solax.local-connect-charger.channel.charger-output-power-phase3.description = Power to/from the charger phase 3 +thing-type.solax.local-connect-charger.channel.charger-plug-temperature.label = Charger Plug Temperature +thing-type.solax.local-connect-charger.channel.charger-plug-temperature.description = Temperature of the charger's plug +thing-type.solax.local-connect-charger.channel.charger-state.label = Charger State +thing-type.solax.local-connect-charger.channel.charger-state.description = Charger State +thing-type.solax.local-connect-charger.channel.charger-total-output-power.label = Charger Output Total Power +thing-type.solax.local-connect-charger.channel.charger-total-output-power.description = Power from the charger on all phases +thing-type.solax.local-connect-charger.channel.charger-voltage-phase1.label = Charger Voltage Phase 1 +thing-type.solax.local-connect-charger.channel.charger-voltage-phase1.description = Voltage of the charger's phase 1 +thing-type.solax.local-connect-charger.channel.charger-voltage-phase2.label = Charger Voltage Phase 2 +thing-type.solax.local-connect-charger.channel.charger-voltage-phase2.description = Voltage of the charger's phase 2 +thing-type.solax.local-connect-charger.channel.charger-voltage-phase3.label = Charger Voltage Phase 3 +thing-type.solax.local-connect-charger.channel.charger-voltage-phase3.description = Voltage of the charger's phase 3 +thing-type.solax.local-connect-charger.channel.eq-single-session.label = Energy charged this session +thing-type.solax.local-connect-charger.channel.eq-single-session.description = The energy charged for the current session +thing-type.solax.local-connect-charger.channel.eq-total.label = Total energy charged +thing-type.solax.local-connect-charger.channel.eq-total.description = The total energy charged for all sessions thing-type.solax.local-connect-inverter.label = Local Connect Inverter thing-type.solax.local-connect-inverter.description = The inverter representation that supports local connections via HTTP thing-type.solax.local-connect-inverter.channel.battery-current.label = Battery Current @@ -142,6 +192,12 @@ thing-type.config.solax.cloud-connect-inverter.refreshInterval.label = Refresh I thing-type.config.solax.cloud-connect-inverter.refreshInterval.description = Refresh interval in seconds. (Cloud API is limited to max 10 calls per minute and 10000 times per day) thing-type.config.solax.cloud-connect-inverter.token.label = Token thing-type.config.solax.cloud-connect-inverter.token.description = Token to access the Solax cloud API +thing-type.config.solax.local-connect-charger.hostname.label = Network Address +thing-type.config.solax.local-connect-charger.hostname.description = IP address or the host name of the Wi-Fi module +thing-type.config.solax.local-connect-charger.password.label = Password +thing-type.config.solax.local-connect-charger.password.description = Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module) +thing-type.config.solax.local-connect-charger.refreshInterval.label = Refresh Interval +thing-type.config.solax.local-connect-charger.refreshInterval.description = Specifies the refresh interval in seconds. thing-type.config.solax.local-connect-inverter.hostname.label = Network Address thing-type.config.solax.local-connect-inverter.hostname.description = IP address or the host name of the Wi-Fi module thing-type.config.solax.local-connect-inverter.password.label = Password @@ -153,12 +209,45 @@ thing-type.config.solax.local-connect-inverter.refreshInterval.description = Spe channel-type.solax.battery-temperature.label = Battery Temperature channel-type.solax.battery-temperature.description = Battery Temperature +channel-type.solax.charger-mode.label = Charger Mode +channel-type.solax.charger-mode.description = Charger Mode +channel-type.solax.charger-mode.state.option.0 = Stop +channel-type.solax.charger-mode.state.option.1 = Fast +channel-type.solax.charger-mode.state.option.2 = Eco +channel-type.solax.charger-mode.state.option.3 = Green +channel-type.solax.charger-state.label = Charger State +channel-type.solax.charger-state.description = Charger State +channel-type.solax.charger-state.state.option.0 = Preparing +channel-type.solax.charger-state.state.option.1 = Preparing +channel-type.solax.charger-state.state.option.2 = Charging +channel-type.solax.charger-state.state.option.3 = Finishing +channel-type.solax.charger-state.state.option.4 = Fault +channel-type.solax.charger-state.state.option.5 = Unavailable +channel-type.solax.charger-state.state.option.6 = Reserved +channel-type.solax.charger-state.state.option.7 = Suspended EV +channel-type.solax.charger-state.state.option.8 = Suspended EV SE +channel-type.solax.charger-temperature.label = Charger Temperature +channel-type.solax.charger-temperature.description = Charger Temperature channel-type.solax.frequency.label = Electric Frequency channel-type.solax.frequency.description = Frequency of the electricity to/from the inverter -channel-type.solax.inverter-status-type.label = Inverter Status -channel-type.solax.inverter-status-type.description = The status of the inverter. channel-type.solax.inverter-temperature.label = Inverter Temperature channel-type.solax.inverter-temperature.description = Inverter Temperature +channel-type.solax.inverter-workmode-cloud.label = Inverter Workmode +channel-type.solax.inverter-workmode-cloud.description = Inverter Workmode +channel-type.solax.inverter-workmode-cloud.state.option.100 = Waiting +channel-type.solax.inverter-workmode-cloud.state.option.101 = Checking +channel-type.solax.inverter-workmode-cloud.state.option.102 = Normal +channel-type.solax.inverter-workmode-cloud.state.option.103 = Fault +channel-type.solax.inverter-workmode-cloud.state.option.104 = Permanent Fault +channel-type.solax.inverter-workmode-cloud.state.option.105 = Updating +channel-type.solax.inverter-workmode-cloud.state.option.106 = EPS Check +channel-type.solax.inverter-workmode-cloud.state.option.107 = EPS Normal +channel-type.solax.inverter-workmode-cloud.state.option.108 = Self Test +channel-type.solax.inverter-workmode-cloud.state.option.109 = Idle +channel-type.solax.inverter-workmode-cloud.state.option.110 = Standby +channel-type.solax.inverter-workmode-cloud.state.option.111 = PV Wake-up Battery +channel-type.solax.inverter-workmode-cloud.state.option.112 = GEN Check +channel-type.solax.inverter-workmode-cloud.state.option.113 = GEN Run channel-type.solax.inverter-workmode.label = Inverter Workmode channel-type.solax.inverter-workmode.description = Inverter Workmode channel-type.solax.inverter-workmode.state.option.0 = Waiting @@ -180,6 +269,11 @@ channel-type.solax.last-retrieve-time-stamp.description = Last time with a succe channel-type.solax.raw-data-type.label = Raw Data channel-type.solax.raw-data-type.description = The raw JSON data retrieved from the inverter's Wi-Fi module. +# channel types + +channel-type.solax.inverter-status-type.label = Inverter Status +channel-type.solax.inverter-status-type.description = The status of the inverter. + # thing status descriptions cloud.inverter.status.wait = Wait @@ -198,3 +292,4 @@ cloud.inverter.status.gen-run = Gen Run cloud.inverter.status.unknown = Unknown offline.communication-error.json-cannot-be-retrieved = JSON data could not be retrieved. offline.configuration-error.parser-not-implemented = Parser for inverter of type {0} is not implemented. +offline.configuration-error.json-cannot-be-parsed = Json cannot be parsed. Check your connection configuration for a wrong password. diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml index 2267461b2e..20af98eb43 100644 --- a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml @@ -93,5 +93,46 @@ The raw JSON data retrieved from the inverter's Wi-Fi module. + + String + + Charger State + + + + + + + + + + + + + + + + String + + Charger Mode + + + + + + + + + + + Number:Temperature + + Charger Temperature + + Measurement + Temperature + + + diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml new file mode 100644 index 0000000000..51325c97d4 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml @@ -0,0 +1,129 @@ + + + + + + + The charger representation that supports local connections via HTTP + + + + + The energy charged for the current session + + + + The total energy charged for all sessions + + + + Power to/from the charger phase 1 + + + + Power to/from the charger phase 2 + + + + Power to/from the charger phase 3 + + + + Power from the charger on all phases + + + + Current from the charger phase 1 + + + + Current from the charger phase 2 + + + + Current from the charger phase 3 + + + + Voltage of the charger's phase 1 + + + + Voltage of the charger's phase 2 + + + + Voltage of the charger's phase 3 + + + + + Current from the provider phase 1 + + + + Current from the provider phase 2 + + + + Current from the provider phase 3 + + + + Power from the provider phase 1 + + + + Power from the provider phase 2 + + + + Power from the provider phase 3 + + + + Total power from the provider + + + + + Temperature of the charger's plug + + + + Internal temperature on the board of the charger + + + + Charger Workmode + + + + Charger State + + + + + + + + + Specifies the refresh interval in seconds. + 10 + + + + Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module) + password + + + + IP address or the host name of the Wi-Fi module + network-address + + + + diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/AbstractParserTest.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/AbstractParserTest.java index 791e304ba4..3b840a8972 100644 --- a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/AbstractParserTest.java +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/AbstractParserTest.java @@ -20,8 +20,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; import org.openhab.binding.solax.internal.model.InverterType; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; /** * The {@link AbstractParserTest} Abstract class defining the common logic for testing local connections to the various @@ -44,7 +44,7 @@ public abstract class AbstractParserTest { Set supportedChannels = parser.getSupportedChannels(); assertFalse(supportedChannels.isEmpty()); - LocalInverterData data = parser.getData(bean); + LocalData data = parser.getData(bean); assertParserSpecific(data); } @@ -52,5 +52,5 @@ public abstract class AbstractParserTest { protected abstract String getRawData(); - protected abstract void assertParserSpecific(LocalInverterData data); + protected abstract void assertParserSpecific(LocalData data); } diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java new file mode 100644 index 0000000000..6bbce4ec52 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.local.parsers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; +import org.openhab.binding.solax.internal.model.local.EvChargerData; + +/** + * The {@link TestEVChargerParser} Simple test that tests for proper parsing against a real data from the inverter + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class TestEVChargerParser { + + private static final String RAW_DATA = """ + { + "SN":"SQBLABLA", + "ver":"3.004.11", + "type":1, + "Data":[ + 2,2,23914,23991,23895,1517,1513,1519,3654,3657, + 3656,10968,44,0,346,0,65434,35463,65459,65508, + 65513,27,402,0,43,0,2,15,0,0, + 0,0,0,5004,5000,4996,10518,1547,6150,4, + 0,0,0,0,0,0,0,0,1,100, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 1717,0,3114,1547,6150,0,1,1,1,0, + 0,121,584,266,0,50,0,0,1,1,0], + "Information":[11.000,1,"CXXXXXXXXXX",1,1.13,1.01,0.00,0.00,0.00,1], + "OCPPServer":"", + "OCPPChargerId":"" + } + """; + + @Test + public void testParser() { + LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(RAW_DATA); + assertNotNull(bean); + + EvChargerData data = new EvChargerData(bean); + assertEquals("SQBLABLA", data.getWifiSerial()); // 0 + assertEquals("3.004.11", data.getWifiVersion()); // 1 + + assertEquals(239.14, data.getVoltagePhase1()); // 2 + assertEquals(239.91, data.getVoltagePhase2()); // 3 + assertEquals(238.95, data.getVoltagePhase3()); // 4 + + assertEquals(15.17, data.getCurrentPhase1()); // 5 + assertEquals(15.13, data.getCurrentPhase2()); // 6 + assertEquals(15.19, data.getCurrentPhase3()); // 7 + + assertEquals(3654, data.getOutputPowerPhase1()); // 8 + assertEquals(3657, data.getOutputPowerPhase2()); // 9 + assertEquals(3656, data.getOutputPowerPhase3()); // 10 + + assertEquals(10968, data.getTotalChargePower()); // 11 + + assertEquals(4.4, data.getEqSingle()); // 12 + assertEquals(34.6, data.getEqTotal()); // 14 and 15 + + assertEquals(-1.02, data.getExternalCurrentPhase1()); // 16 + assertEquals(-300.73, data.getExternalCurrentPhase2()); // 17 + assertEquals(-0.77, data.getExternalCurrentPhase3()); // 18 + + assertEquals(-28, data.getExternalPowerPhase1()); // 19 + assertEquals(-23, data.getExternalPowerPhase2()); // 20 + assertEquals(27, data.getExternalPowerPhase3()); // 21 + assertEquals(402, data.getExternalTotalPower()); // 22 + + assertEquals(0, data.getPlugTemperature()); // 23 + assertEquals(43, data.getInternalTemperature()); // 24 + + assertEquals(2, data.getCPState()); // 26 + + assertEquals(1717, data.getChargingDuration()); // 80 and 81 + + assertEquals(0, data.getOccpOfflineMode()); // 85 + + assertEquals(1, data.getTypePower()); // 87 + assertEquals(1, data.getTypePhase()); // 88 + assertEquals(0, data.getTypeCharger()); // 89 + } +} diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1BoostAirMiniDataParser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1BoostAirMiniDataParser.java similarity index 91% rename from bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1BoostAirMiniDataParser.java rename to bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1BoostAirMiniDataParser.java index 66f6f4bcff..395085d3e0 100644 --- a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1BoostAirMiniDataParser.java +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1BoostAirMiniDataParser.java @@ -10,13 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.local; +package org.openhab.binding.solax.internal.local.parsers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.local.AbstractParserTest; import org.openhab.binding.solax.internal.model.InverterType; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; /** * The {@link TestX1BoostAirMiniDataParser} Simple test that tests for proper parsing against a real data from the @@ -49,7 +50,7 @@ public class TestX1BoostAirMiniDataParser extends AbstractParserTest { } @Override - protected void assertParserSpecific(LocalInverterData data) { + protected void assertParserSpecific(LocalData data) { assertEquals("SR***", data.getWifiSerial()); assertEquals("3.006.04", data.getWifiVersion()); diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1HybridG4Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1HybridG4Parser.java similarity index 94% rename from bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1HybridG4Parser.java rename to bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1HybridG4Parser.java index f2d2032469..90ea895246 100644 --- a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1HybridG4Parser.java +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1HybridG4Parser.java @@ -10,13 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.local; +package org.openhab.binding.solax.internal.local.parsers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.local.AbstractParserTest; import org.openhab.binding.solax.internal.model.InverterType; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; /** * The {@link TestX1HybridG4Parser} Simple test that tests for proper parsing against a real data from the inverter @@ -46,7 +47,7 @@ public class TestX1HybridG4Parser extends AbstractParserTest { } @Override - protected void assertParserSpecific(LocalInverterData data) { + protected void assertParserSpecific(LocalData data) { assertEquals("SOME_SERIAL_NUMBER", data.getWifiSerial()); assertEquals("3.008.10", data.getWifiVersion()); diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3HybridG4Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3HybridG4Parser.java similarity index 95% rename from bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3HybridG4Parser.java rename to bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3HybridG4Parser.java index a30172c981..056d2bc64b 100644 --- a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3HybridG4Parser.java +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3HybridG4Parser.java @@ -10,13 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.local; +package org.openhab.binding.solax.internal.local.parsers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.solax.internal.local.AbstractParserTest; import org.openhab.binding.solax.internal.model.InverterType; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; +import org.openhab.binding.solax.internal.model.local.LocalData; /** * The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter @@ -53,7 +54,7 @@ public class TestX3HybridG4Parser extends AbstractParserTest { } @Override - protected void assertParserSpecific(LocalInverterData data) { + protected void assertParserSpecific(LocalData data) { assertEquals("XYZ", data.getWifiSerial()); assertEquals("3.005.01", data.getWifiVersion()); diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3MicOrProG2Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3MicOrProG2Parser.java similarity index 93% rename from bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3MicOrProG2Parser.java rename to bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3MicOrProG2Parser.java index 29846be0de..f983227abf 100644 --- a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3MicOrProG2Parser.java +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3MicOrProG2Parser.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.solax.internal.local; +package org.openhab.binding.solax.internal.local.parsers; import static org.junit.jupiter.api.Assertions.*; @@ -18,8 +18,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean; import org.openhab.binding.solax.internal.model.InverterType; -import org.openhab.binding.solax.internal.model.local.LocalInverterData; -import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser; +import org.openhab.binding.solax.internal.model.local.LocalData; +import org.openhab.binding.solax.internal.model.local.RawDataParser; /** * The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter @@ -56,7 +56,7 @@ public class TestX3MicOrProG2Parser { RawDataParser parser = inverterType.getParser(); assertNotNull(parser); - LocalInverterData data = parser.getData(bean); + LocalData data = parser.getData(bean); assertEquals("XYZ", data.getWifiSerial()); assertEquals("3.003.02", data.getWifiVersion()); diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java new file mode 100644 index 0000000000..a66a4bddce --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.solax.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * The {@link TestByteUtil} Simple test that tests the methods of the ByteUtil + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class TestByteUtil { + + @Test + public void testRead32BitSignedWithNegativeInputs() { + assertEquals(-65536, ByteUtil.read32BitSigned((short) 0, (short) -1)); + assertEquals(-1, ByteUtil.read32BitSigned((short) -1, (short) -1)); + assertEquals(-2, ByteUtil.read32BitSigned((short) -2, (short) -1)); + } + + @Test + public void testRead32BitSignedWithBoundaryValues() { + assertEquals(Short.MAX_VALUE, ByteUtil.read32BitSigned((short) Short.MAX_VALUE, (short) 0)); + assertEquals((Short.MIN_VALUE & 0xFFFF) | (Short.MAX_VALUE << 16), + ByteUtil.read32BitSigned((short) Short.MIN_VALUE, (short) Short.MAX_VALUE)); + assertEquals(Integer.MIN_VALUE, ByteUtil.read32BitSigned((short) 0, (short) (Integer.MIN_VALUE >> 16))); + } + + @Test + public void testRead32BitSignedResultingInNegative() { + assertEquals(-131072, ByteUtil.read32BitSigned((short) 0, (short) -2)); + } + + @Test + public void testRead32BitSignedResultingInMaxInteger() { + assertEquals(Integer.MAX_VALUE, ByteUtil.read32BitSigned((short) 0xFFFF, (short) 0x7FFF)); + } +} -- 2.47.3