diff --git a/CODEOWNERS b/CODEOWNERS index b433f9f1bda94..2a39f5c6a570d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -324,6 +324,7 @@ /bundles/org.openhab.binding.solarlog/ @johannrichard /bundles/org.openhab.binding.solarmax/ @jamietownsend /bundles/org.openhab.binding.solarwatt/ @sven-carstens +/bundles/org.openhab.binding.solax/ @theater /bundles/org.openhab.binding.somfymylink/ @loungeflyz /bundles/org.openhab.binding.somfytahoma/ @octa22 /bundles/org.openhab.binding.somneo/ @0x4d4d diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 024a15561867a..95eba8ac828c2 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1606,6 +1606,11 @@ org.openhab.binding.solarwatt ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.solax + ${project.version} + org.openhab.addons.bundles org.openhab.binding.somfymylink diff --git a/bundles/org.openhab.binding.solax/NOTICE b/bundles/org.openhab.binding.solax/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.solax/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.solax/README.md b/bundles/org.openhab.binding.solax/README.md new file mode 100644 index 0000000000000..6b35fb62ea100 --- /dev/null +++ b/bundles/org.openhab.binding.solax/README.md @@ -0,0 +1,189 @@ +# Solax Binding + +This is a binding for Solax solar power inverters. + +Solax Wi-Fi module with direct connection via HTTP is supported. +Wi-Fi module firmware version 3.x+ is required. +Please note that earlier firmware releases do not support direct connection, therefore the binding will not work in its current state. + +The binding retrieves a structured data from the inverter's Wi-Fi module, parses it and pushes it into the inverter Thing where each channel represents a specific information (inverter output power, voltage, PV1 power, etc.) + +In case the parsed information that comes with the binding out of the box differs, the raw data channel can be used with a combination of JSON Path transformation to map the proper values to the necessary items. + +## Supported Things + +| Thing | Thing Type | Description | +|------------------------|------------|-------------------------------------------------------------------------------------| +| local-connect-inverter | Thing | This is model representation of inverter with all the data available as a channels | + +## Thing Configuration + +### Local Connect Inverter Configuration + +| 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. | + +### Inverter Output Channels + +| Channel | Type | Description | +|--------------------------|----------------------------|--------------------------------------------------| +| inverter-output-power | Number:Power | The output power of the inverter [W] | +| inverter-current | Number:ElectricCurrent | The output current of the inverter [A] | +| inverter-voltage | Number:ElectricPotential | The output voltage of the inverter [V] | +| inverter-frequency | Number:Frequency | The frequency of the output voltage [Hz] | + +### Photovoltaic Panels Production Channels + +| Channel | Type | Description | +|--------------------------|----------------------------|-------------------------------------------------| +| pv1-voltage | Number:ElectricPotential | The voltage of PV1 string [V] | +| pv2-voltage | Number:ElectricPotential | The voltage of PV2 string [V] | +| pv1-current | Number:ElectricCurrent | The current of PV1 string [A] | +| pv2-current | Number:ElectricCurrent | The current of PV2 string [A] | +| pv1-power | Number:Power | The output power PV1 string [W] | +| pv2-power | Number:Power | The output power PV2 string [W] | +| pv-total-power | Number:Power | The total output power of both PV strings [W] | +| pv-total-current | Number:ElectricCurrent | The total current of both PV strings [A] | + +### Battery channels + +| Channel | Type | Description | +|---------------------------|----------------------------|------------------------------------------------------------------------------------------------| +| battery-power | Number:Power | The power to / from battery (negative means power is pulled from battery and vice-versa) [W] | +| battery-current | Number:ElectricCurrent | The current to / from battery (negative means power is pulled from battery and vice-versa) [A] | +| battery-voltage | Number:ElectricPotential | The voltage of the battery [V] | +| battery-temperature | Number:Temperature | The temperature of the battery [C/F] | +| battery-state-of-charge | Number | The state of charge of the battery [%] | + +### Grid related channels + +| Channel | Type | Description | +|--------------------------|----------------------------|------------------------------------------------------------------------------------------------| +| feed-in-power | Number:Power | The power to / from grid (negative means power is pulled from the grid and vice-versa) [W] | + +### General channels + +| Channel | Type | Description | +|--------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| last-update-time | DateTime | Last time when a call has been made to the inverter | +| raw-data | String | The raw data retrieved from inverter in JSON format. (Usable for channels not implemented. Can be consumed with the JSONpath transformation | + +### Properties + +| Property | Description | +|-------------------|-------------------------------------------| +| serialNumber | The serial number of the Wi-Fi module | +| inverterType | Inverter Type (for example X1_HYBRID_G4) | + +## Full Example + +Here are some file based examples. + +### Thing Configuration + +```java +// The local connect inverter thing +Thing solax:local-connect-inverter:localInverter [ refreshInterval=10, password="", hostname="" ] +``` + +### Item Configuration + +```java +Group gSolaxInverter "Solax Inverter" (boilerRoom) +Group solarPanels "Solar panels" (gSolaxInverter) + +Number solaxPowerWest "West [%.0f W]" (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:localConnectInverter:localInverter:pv1-power" } +Number solaxPowerEast "East [%.0f W]" (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:localConnectInverter:localInverter:pv2-power" } +Number solaxBatteryPower "Battery power [%.0f W]" (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:battery-power" } +Number solaxBatterySoc "Battery SoC [%.0f %%]" (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:battery-state-of-charge" } + +Number solaxFeedInPower "Feed-in power (CEZ) [%.0f W]" (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:feed-in-power" } +Number solaxAcPower "Invertor output power [%.0f W]" (gsolax_inverter,EveryChangePersist){ channel="solax:localConnectInverter:localInverter:inverter-output-power" } + +String solaxInverterType "Inverter Type [%s]" (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:inverter-type"} +String solaxUploadTime "Last update time [%s]" (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:last-update-time" } +String solaxRawData "Raw data [%s]" (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:raw-data" } +``` + +### Sitemap Configuration + +```perl +Frame label="Solar power strings" { + Text item=solaxPowerEast valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] { + Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4] + } + Text item=solaxPowerWest valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] { + Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4] + } + Text item=solaxGenerationTotal valuecolor=[<=100="gray",<=500="red", <2000="orange", >=2000="green"] { + Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Text item=solaxGenerationTotal icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4] + } +} +Frame label="Consumption" { + Text item=solaxAcPower valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] { + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Text item=solaxAcPower icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] + Chart item=solaxAcPower period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solaxAcPower period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solaxAcPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solaxAcPower period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solaxAcPower period=Y refresh=3600 visibility=[Chart_Period==4] + } + Text item=solaxFeedInPower valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] { + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Text item=solaxFeedInPower icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] + Chart item=solaxFeedInPower period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solaxFeedInPower period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solaxFeedInPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solaxFeedInPower period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solaxFeedInPower period=Y refresh=3600 visibility=[Chart_Period==4] + } +} +Frame label="Battery" { + Text item=solaxBatteryPower valuecolor=[<=-500="red", <0="orange", ==0="gray", >0="green"] { + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Text item=solaxBatteryPower icon="energy" valuecolor=[<-800="red", <0="orange", ==0="gray", >=0="green"] + Chart item=solaxBatteryPower period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solaxBatteryPower period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solaxBatteryPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solaxBatteryPower period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solaxBatteryPower period=Y refresh=3600 visibility=[Chart_Period==4] + } + Text item=solaxBatterySoc valuecolor=[<=30="red", <50="orange", >=50="green"] { + Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"] + Text item=solaxBatterySoc valuecolor=[<=30="red", <50="orange", >=50="green"] + Chart item=solaxBatterySoc period=h refresh=600 visibility=[Chart_Period==0] + Chart item=solaxBatterySoc period=D refresh=3600 visibility=[Chart_Period==1] + Chart item=solaxBatterySoc period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized] + Chart item=solaxBatterySoc period=M refresh=3600 visibility=[Chart_Period==3] + Chart item=solaxBatterySoc period=Y refresh=3600 visibility=[Chart_Period==4] + } +} +``` + + diff --git a/bundles/org.openhab.binding.solax/pom.xml b/bundles/org.openhab.binding.solax/pom.xml new file mode 100644 index 0000000000000..6e3d15e5973f0 --- /dev/null +++ b/bundles/org.openhab.binding.solax/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.1.0-SNAPSHOT + + + org.openhab.binding.solax + + openHAB Add-ons :: Bundles :: Solax Binding + + diff --git a/bundles/org.openhab.binding.solax/src/main/feature/feature.xml b/bundles/org.openhab.binding.solax/src/main/feature/feature.xml new file mode 100644 index 0000000000000..ae1b59e819e82 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.solax/${project.version} + + 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 new file mode 100644 index 0000000000000..74151b3aecbc7 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2023 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; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link SolaxBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class SolaxBindingConstants { + + private static final String BINDING_ID = "solax"; + private static final String THING_LOCAL_CONNECT_INVERTER_ID = "local-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 Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LOCAL_CONNECT_INVERTER); + + // List of properties + public static final String PROPERTY_INVERTER_TYPE = "inverterType"; + + // List of all Channel ids + public static final String INVERTER_OUTPUT_POWER = "inverter-output-power"; + public static final String INVERTER_OUTPUT_CURRENT = "inverter-current"; + public static final String INVERTER_OUTPUT_VOLTAGE = "inverter-voltage"; + public static final String INVERTER_OUTPUT_FREQUENCY = "inverter-frequency"; + + public static final String INVERTER_PV1_POWER = "pv1-power"; + public static final String INVERTER_PV1_VOLTAGE = "pv1-voltage"; + public static final String INVERTER_PV1_CURRENT = "pv1-current"; + + public static final String INVERTER_PV2_POWER = "pv2-power"; + public static final String INVERTER_PV2_VOLTAGE = "pv2-voltage"; + public static final String INVERTER_PV2_CURRENT = "pv2-current"; + + public static final String INVERTER_PV_TOTAL_POWER = "pv-total-power"; + public static final String INVERTER_PV_TOTAL_CURRENT = "pv-total-current"; + + public static final String BATTERY_POWER = "battery-power"; + public static final String BATTERY_VOLTAGE = "battery-voltage"; + public static final String BATTERY_CURRENT = "battery-current"; + public static final String BATTERY_TEMPERATURE = "battery-temperature"; + public static final String BATTERY_STATE_OF_CHARGE = "battery-level"; + + public static final String FEED_IN_POWER = "feed-in-power"; + + public static final String TIMESTAMP = "last-update-time"; + public static final String RAW_DATA = "raw-data"; + + // I18N Keys + protected static final String I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED = "@text/offline.communication-error.json-cannot-be-retrieved"; +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxConfiguration.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxConfiguration.java new file mode 100644 index 0000000000000..6114f746ea309 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxConfiguration.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2023 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link SolaxConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class SolaxConfiguration { + + public String hostname = ""; + public String password = ""; + public int refreshInterval = 10; +} 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 new file mode 100644 index 0000000000000..12c67ec6dd8a1 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2010-2023 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; + +import static org.openhab.binding.solax.internal.SolaxBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link SolaxHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.solax", service = ThingHandlerFactory.class) +public class SolaxHandlerFactory extends BaseThingHandlerFactory { + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + if (THING_TYPE_LOCAL_CONNECT_INVERTER.equals(thingTypeUID)) { + return new SolaxLocalAccessHandler(thing); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java new file mode 100644 index 0000000000000..7edcfaf216cf2 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2010-2023 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; + +import java.io.IOException; +import java.time.ZonedDateTime; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector; +import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean; +import org.openhab.binding.solax.internal.model.InverterData; +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.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.types.Command; +import org.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. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class SolaxLocalAccessHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessHandler.class); + + private static final int INITIAL_SCHEDULE_DELAY_SECONDS = 5; + + private @NonNullByDefault({}) LocalHttpConnector localHttpConnector; + + private @Nullable ScheduledFuture schedule; + + public SolaxLocalAccessHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + + SolaxConfiguration config = getConfigAs(SolaxConfiguration.class); + localHttpConnector = new LocalHttpConnector(config.password, config.hostname); + int refreshInterval = config.refreshInterval; + TimeUnit timeUnit = TimeUnit.SECONDS; + + logger.debug("Scheduling regular interval retrieval every {} {}", refreshInterval, timeUnit); + schedule = scheduler.scheduleWithFixedDelay(this::retrieveData, INITIAL_SCHEDULE_DELAY_SECONDS, refreshInterval, + timeUnit); + } + + private void retrieveData() { + try { + String rawJsonData = localHttpConnector.retrieveData(); + logger.debug("Raw data retrieved = {}", rawJsonData); + + if (rawJsonData != null && !rawJsonData.isEmpty()) { + updateData(rawJsonData); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + SolaxBindingConstants.I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED); + } + } catch (IOException e) { + logger.debug("Exception received while attempting to retrieve data via HTTP", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + private void updateData(String rawJsonData) { + try { + LocalConnectRawDataBean inverterParsedData = parseJson(rawJsonData); + updateThing(inverterParsedData); + } catch (JsonParseException e) { + logger.debug("Unable to deserialize from JSON.", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + private void updateThing(LocalConnectRawDataBean inverterParsedData) { + transferInverterDataToChannels(inverterParsedData); + + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + } + + private LocalConnectRawDataBean parseJson(String rawJsonData) { + LocalConnectRawDataBean inverterParsedData = LocalConnectRawDataBean.fromJson(rawJsonData); + logger.debug("Received a new inverter data object. Data = {}", inverterParsedData.toStringDetailed()); + return inverterParsedData; + } + + private void transferInverterDataToChannels(InverterData data) { + updateProperty(Thing.PROPERTY_SERIAL_NUMBER, data.getWifiSerial()); + updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, data.getInverterType().name()); + + updateState(SolaxBindingConstants.INVERTER_OUTPUT_POWER, + new QuantityType<>(data.getInverterOutputPower(), Units.WATT)); + updateState(SolaxBindingConstants.INVERTER_OUTPUT_CURRENT, + new QuantityType<>(data.getInverterCurrent(), Units.AMPERE)); + updateState(SolaxBindingConstants.INVERTER_OUTPUT_VOLTAGE, + new QuantityType<>(data.getInverterVoltage(), Units.VOLT)); + updateState(SolaxBindingConstants.INVERTER_OUTPUT_FREQUENCY, + new QuantityType<>(data.getInverterFrequency(), Units.HERTZ)); + + updateState(SolaxBindingConstants.INVERTER_PV1_POWER, new QuantityType<>(data.getPV1Power(), Units.WATT)); + updateState(SolaxBindingConstants.INVERTER_PV1_CURRENT, new QuantityType<>(data.getPV1Current(), Units.AMPERE)); + updateState(SolaxBindingConstants.INVERTER_PV1_VOLTAGE, new QuantityType<>(data.getPV1Voltage(), Units.VOLT)); + + updateState(SolaxBindingConstants.INVERTER_PV2_POWER, new QuantityType<>(data.getPV2Power(), Units.WATT)); + updateState(SolaxBindingConstants.INVERTER_PV2_CURRENT, new QuantityType<>(data.getPV2Current(), Units.AMPERE)); + updateState(SolaxBindingConstants.INVERTER_PV2_VOLTAGE, new QuantityType<>(data.getPV2Voltage(), Units.VOLT)); + + updateState(SolaxBindingConstants.INVERTER_PV_TOTAL_POWER, + new QuantityType<>(data.getPVTotalPower(), Units.WATT)); + updateState(SolaxBindingConstants.INVERTER_PV_TOTAL_CURRENT, + new QuantityType<>(data.getPVTotalCurrent(), Units.AMPERE)); + + updateState(SolaxBindingConstants.BATTERY_POWER, new QuantityType<>(data.getBatteryPower(), Units.WATT)); + updateState(SolaxBindingConstants.BATTERY_CURRENT, new QuantityType<>(data.getBatteryCurrent(), Units.AMPERE)); + updateState(SolaxBindingConstants.BATTERY_VOLTAGE, new QuantityType<>(data.getBatteryVoltage(), Units.VOLT)); + updateState(SolaxBindingConstants.BATTERY_TEMPERATURE, + new QuantityType<>(data.getBatteryTemperature(), SIUnits.CELSIUS)); + updateState(SolaxBindingConstants.BATTERY_STATE_OF_CHARGE, + new QuantityType<>(data.getBatterySoC(), Units.PERCENT)); + + updateState(SolaxBindingConstants.FEED_IN_POWER, new QuantityType<>(data.getFeedInPower(), Units.WATT)); + + updateState(SolaxBindingConstants.TIMESTAMP, new DateTimeType(ZonedDateTime.now())); + updateState(SolaxBindingConstants.RAW_DATA, new StringType(data.getRawData())); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // Nothing to do here as of now. Maybe implement a REFRESH command in the future. + } + + @Override + public void dispose() { + super.dispose(); + ScheduledFuture schedule = this.schedule; + if (schedule != null) { + schedule.cancel(true); + this.schedule = null; + } + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/LocalHttpConnector.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/LocalHttpConnector.java new file mode 100644 index 0000000000000..ab1525b714617 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/LocalHttpConnector.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2023 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.connectivity; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.http.HttpMethod; +import org.openhab.core.io.net.http.HttpUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link LocalHttpConnector} class uses HttpUtil to retrieve the raw JSON data from Inverter's Wi-Fi module. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class LocalHttpConnector implements SolaxConnector { + + private static final int HTTP_REQUEST_TIME_OUT = 5000; + + private static final String CONTENT_TYPE = "text/html; charset=utf-8"; + + private final Logger logger = LoggerFactory.getLogger(LocalHttpConnector.class); + + private static final String OPT_TYPE = "optType"; + private static final String READ_REALTIME_DATA = "ReadRealTimeData"; + + private static final String PASSWORD = "pwd"; + // The serial number of the Wifi dongle is the password for the connection (default) + private String passwordValue; + private String uri; + + public LocalHttpConnector(String passwordValue, String host) { + this.passwordValue = passwordValue; + this.uri = "http://" + host; + } + + @Override + public @Nullable String retrieveData() throws IOException { + String requestBody = createRequestBody(); + logger.trace("Uri: {}, Request body: {}", uri, requestBody); + String result = HttpUtil.executeUrl(HttpMethod.POST.name(), uri, + new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8)), CONTENT_TYPE, + HTTP_REQUEST_TIME_OUT); + logger.trace("Retrieved content = {}", result); + return result; + } + + private String createRequestBody() { + StringBuilder sb = new StringBuilder(); + + sb.append(OPT_TYPE).append("=").append(READ_REALTIME_DATA); + sb.append("&"); + sb.append(PASSWORD).append("=").append(passwordValue); + + return sb.toString(); + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/SolaxConnector.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/SolaxConnector.java new file mode 100644 index 0000000000000..25eda68ae266b --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/SolaxConnector.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2023 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.connectivity; + +import java.io.IOException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link SolaxConnector} is interface for connecting to the Solax endpoints (cloud API or local IP) + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public interface SolaxConnector { + + @Nullable + String retrieveData() throws IOException; +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java new file mode 100644 index 0000000000000..b6c978418ef35 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java @@ -0,0 +1,255 @@ +/** + * Copyright (c) 2010-2023 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.connectivity.rawdata; + +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.solax.internal.model.InverterData; +import org.openhab.binding.solax.internal.model.InverterType; +import org.openhab.binding.solax.internal.util.GsonSupplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +/** + * The {@link LocalConnectRawDataBean} collects the raw data and the specific implementation to return the parsed data. + * If there are differences between the inverters probably would be wise to split the parsing in seprate class(es) + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class LocalConnectRawDataBean implements RawDataBean, InverterData { + + private final Logger logger = LoggerFactory.getLogger(LocalConnectRawDataBean.class); + + private @Nullable String sn; + private @Nullable String ver; + private int type; + @SerializedName("Data") + private short @Nullable [] data; + @SerializedName("Information") + private String @Nullable [] information; + private @Nullable String rawData; + + @Override + public String toString() { + return "LocalConnectRawDataBean [sn=" + sn + ", ver=" + ver + ", type=" + type + ", Information=" + + Arrays.toString(information) + ", Data=" + Arrays.toString(data) + "]"; + } + + public @Nullable String getSn() { + return sn; + } + + public void setSn(@Nullable String sn) { + this.sn = sn; + } + + public @Nullable String getVer() { + return ver; + } + + public void setVer(@Nullable String ver) { + this.ver = ver; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public short @Nullable [] getData() { + return data; + } + + public void setData(short @Nullable [] data) { + this.data = data; + } + + public String @Nullable [] getInformation() { + return information; + } + + public void setInformation(String @Nullable [] information) { + this.information = information; + } + + @Override + public @Nullable String getRawData() { + return rawData; + } + + public void setRawData(String rawData) { + this.rawData = rawData; + } + + public static LocalConnectRawDataBean fromJson(String json) { + if (json.isEmpty()) { + throw new IllegalArgumentException("JSON payload should not be empty"); + } + + Gson gson = GsonSupplier.getInstance(); + LocalConnectRawDataBean deserializedObject = gson.fromJson(json, LocalConnectRawDataBean.class); + if (deserializedObject == null) { + throw new IllegalStateException("Unexpected null result when deserializing JSON"); + } + deserializedObject.setRawData(json); + return deserializedObject; + } + + // Parsed inverter data interface implementation starts here + + @Override + public @Nullable String getWifiSerial() { + return getSn(); + } + + @Override + public @Nullable String getWifiVersion() { + return getVer(); + } + + @Override + public InverterType getInverterType() { + return InverterType.fromIndex(type); + } + + @Override + public short getInverterVoltage() { + return (short) (getData(0) / 10); + } + + @Override + public short getInverterCurrent() { + return (short) (getData(1) / 10); + } + + @Override + public short getInverterOutputPower() { + return getData(2); + } + + @Override + public short getInverterFrequency() { + return (short) (getData(3) / 100); + } + + @Override + public short getPV1Voltage() { + return (short) (getData(4) / 10); + } + + @Override + public short getPV1Current() { + return (short) (getData(6) / 10); + } + + @Override + public short getPV1Power() { + return getData(8); + } + + @Override + public short getPV2Voltage() { + return (short) (getData(5) / 10); + } + + @Override + public short getPV2Current() { + return (short) (getData(7) / 10); + } + + @Override + public short getPV2Power() { + return getData(9); + } + + @Override + public short getBatteryVoltage() { + return (short) (getData(14) / 100); + } + + @Override + public short getBatteryCurrent() { + return (short) (getData(15) / 100); + } + + @Override + public short getBatteryPower() { + return getData(16); + } + + @Override + public short getBatteryTemperature() { + return getData(17); + } + + @Override + public short getBatterySoC() { + return getData(18); + } + + @Override + public long getOnGridTotalYield() { + return packU16(11, 12) / 100; + } + + @Override + public short getOnGridDailyYield() { + return (short) (getData(13) / 10); + } + + @Override + public short getFeedInPower() { + return getData(32); + } + + @Override + public long getTotalFeedInEnergy() { + return packU16(34, 35) / 100; + } + + @Override + public long getTotalConsumption() { + return packU16(36, 37) / 100; + } + + private short getData(int index) { + try { + short[] dataArray = data; + if (dataArray != null) { + return dataArray[index]; + } + } catch (IndexOutOfBoundsException e) { + logger.debug("Tried to get data out of bounds of the raw data array.", e); + } + return 0; + } + + private long packU16(int indexMajor, int indexMinor) { + short major = getData(indexMajor); + short minor = getData(indexMinor); + if (major == 0) { + return minor; + } + + return ((major << 16) & 0xFFFF0000) | minor & 0xFFFF; + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/RawDataBean.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/RawDataBean.java new file mode 100644 index 0000000000000..5edcddf4824b1 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/RawDataBean.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2023 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.connectivity.rawdata; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link RawDataBean} is interface which should be implemented by all types of raw information that is retrieved + * (the idea is to retrieve a raw data from a Solax inverter locally or their cloud API) + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public interface RawDataBean { + @Nullable + String getRawData(); +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterData.java new file mode 100644 index 0000000000000..64d8375ad4ec4 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterData.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010-2023 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.solax.internal.connectivity.rawdata.RawDataBean; + +/** + * The {@link InverterData} interface should implement the interface that returns the parsed data in human readable code + * and format. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public interface InverterData extends RawDataBean { + @Nullable + String getWifiSerial(); + + @Nullable + String getWifiVersion(); + + InverterType getInverterType(); + + short getInverterVoltage(); + + short getInverterCurrent(); + + short getInverterOutputPower(); + + short getInverterFrequency(); + + short getPV1Voltage(); + + short getPV1Current(); + + short getPV1Power(); + + short getPV2Voltage(); + + short getPV2Current(); + + short getPV2Power(); + + default short getPVTotalPower() { + return (short) (getPV1Power() + getPV2Power()); + } + + default short getPVTotalCurrent() { + return (short) (getPV1Current() + getPV2Current()); + } + + short getBatteryVoltage(); // V / 100 + + short getBatteryCurrent(); // A / 100 + + short getBatteryPower(); // W + + short getBatteryTemperature(); // temperature C + + short getBatterySoC(); // % battery SoC + + long getOnGridTotalYield(); // KWh total Yeld from the sun (to the grid?) + + short getOnGridDailyYield(); // KWh daily Yeld from the sun (to the grid?) + + long getTotalFeedInEnergy(); // KWh all times + + long getTotalConsumption(); // KWh all times + + short getFeedInPower(); + + default String toStringDetailed() { + return "WifiSerial = " + getWifiSerial() + ", WifiVersion = " + getWifiVersion() + ", InverterType = " + + getInverterType() + ", InverterVoltage = " + getInverterVoltage() + "V, InverterCurrent = " + + getInverterCurrent() + "A, InverterPower = " + getInverterOutputPower() + "W, BatteryPower = " + + getBatteryPower() + "W, Battery SoC = " + getBatterySoC() + "%, FeedIn Power = " + getFeedInPower() + + "W, Total PV Power = " + (getPV1Power() + getPV2Power()) + "W, Total Consumption = " + + getTotalConsumption() + "kWh, Total Feed-in Energy = " + getTotalFeedInEnergy() + + "kWh, Total On-Grid Yield = " + getOnGridTotalYield() + "kWh."; + } +} 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 new file mode 100644 index 0000000000000..b8131fb16baf0 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2023 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; + +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link InverterType} class is enum representing the different inverter types with a simple logic to convert from + * int(coming from the JSON) to a more meaningful enum value. + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public enum InverterType { + + X1_LX(1), + X_HYBRID(2), + X1_HYBRID_FIT(3), + X1_BOOST_AIR_MINI(4), + X3_HYBRID_FIT(5), + X3_20K_30K(6), + X3_MIC_PRO(7), + X1_SMART(8), + X1_AC(9), + A1_HYBRID(10), + A1_FIT(11), + A1_GRID(12), + J1_ESS(13), + X3_HYBRID_G4(14), + X1_HYBRID_G4(15), + UNKNOWN(-1); + + private int typeIndex; + + InverterType(int typeIndex) { + this.typeIndex = typeIndex; + } + + public static InverterType fromIndex(int index) { + InverterType[] values = InverterType.values(); + return Stream.of(values).filter(value -> value.typeIndex == index).findFirst().orElse(UNKNOWN); + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/GsonSupplier.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/GsonSupplier.java new file mode 100644 index 0000000000000..283a6ce59604b --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/GsonSupplier.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2010-2023 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; + +import com.google.gson.Gson; + +/** + * The {@link GsonSupplier} provides a singleton instance of a Gson object + * + * @author Konstantin Polihronov - Initial contribution + */ +@NonNullByDefault +public class GsonSupplier { + private static final Gson GSON = new Gson(); + + private GsonSupplier() { + }; + + public static Gson getInstance() { + return GSON; + } +} diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 0000000000000..b455cf3b43f7b --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,11 @@ + + + + binding + Solax Binding + This is the binding for Solax inverters. + local + + 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 new file mode 100644 index 0000000000000..3b132065b4704 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties @@ -0,0 +1,67 @@ +# add-on + +addon.solax.name = Solax Binding +addon.solax.description = This is the binding for Solax inverters. + +# thing types + +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 +thing-type.solax.local-connect-inverter.channel.battery-current.description = Electric current to/from the battery +thing-type.solax.local-connect-inverter.channel.battery-level.label = Battery Level +thing-type.solax.local-connect-inverter.channel.battery-level.description = The battery state of charge in percent +thing-type.solax.local-connect-inverter.channel.battery-power.label = Battery Power +thing-type.solax.local-connect-inverter.channel.battery-power.description = Power to/from the battery +thing-type.solax.local-connect-inverter.channel.battery-temperature.label = Battery Temperature +thing-type.solax.local-connect-inverter.channel.battery-temperature.description = Temperature of the battery +thing-type.solax.local-connect-inverter.channel.battery-voltage.label = Battery Voltage +thing-type.solax.local-connect-inverter.channel.battery-voltage.description = Electric voltage of the battery +thing-type.solax.local-connect-inverter.channel.feed-in-power.label = Feed-in Power +thing-type.solax.local-connect-inverter.channel.feed-in-power.description = Power to/from the electricity network. +thing-type.solax.local-connect-inverter.channel.inverter-current.label = Inverter Input/Output Current +thing-type.solax.local-connect-inverter.channel.inverter-current.description = Current to/from the inverter +thing-type.solax.local-connect-inverter.channel.inverter-output-power.label = Inverter Input/Output Power +thing-type.solax.local-connect-inverter.channel.inverter-output-power.description = Power to/from the inverter +thing-type.solax.local-connect-inverter.channel.inverter-voltage.label = Inverter Voltage +thing-type.solax.local-connect-inverter.channel.inverter-voltage.description = Voltage of the inverter +thing-type.solax.local-connect-inverter.channel.pv-total-current.label = PV Total Current +thing-type.solax.local-connect-inverter.channel.pv-total-current.description = The sum of PV currents from all strings +thing-type.solax.local-connect-inverter.channel.pv-total-power.label = PV Total Power +thing-type.solax.local-connect-inverter.channel.pv-total-power.description = The sum of PV powers from all strings +thing-type.solax.local-connect-inverter.channel.pv1-current.label = PV 1 Current +thing-type.solax.local-connect-inverter.channel.pv1-current.description = Electric current of PV String 1 +thing-type.solax.local-connect-inverter.channel.pv1-power.label = PV 1 Power +thing-type.solax.local-connect-inverter.channel.pv1-power.description = Electric power of PV String 1 +thing-type.solax.local-connect-inverter.channel.pv1-voltage.label = PV 1 Voltage +thing-type.solax.local-connect-inverter.channel.pv1-voltage.description = Electric voltage of PV String 1 +thing-type.solax.local-connect-inverter.channel.pv2-current.label = PV 2 Current +thing-type.solax.local-connect-inverter.channel.pv2-current.description = Electric current of PV String 2 +thing-type.solax.local-connect-inverter.channel.pv2-power.label = PV 2 Power +thing-type.solax.local-connect-inverter.channel.pv2-power.description = Electric power of PV String 2 +thing-type.solax.local-connect-inverter.channel.pv2-voltage.label = PV 2 Voltage +thing-type.solax.local-connect-inverter.channel.pv2-voltage.description = Electric voltage of PV String 2 + +# thing types config + +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 +thing-type.config.solax.local-connect-inverter.password.description = Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module) +thing-type.config.solax.local-connect-inverter.refreshInterval.label = Refresh Interval +thing-type.config.solax.local-connect-inverter.refreshInterval.description = Specifies the refresh interval in seconds. + +# channel types + +channel-type.solax.battery-temperature.label = Battery Temperature +channel-type.solax.battery-temperature.description = Battery Temperature +channel-type.solax.frequency.label = Electric Frequency +channel-type.solax.frequency.description = Frequency of the electricity to/from the inverter +channel-type.solax.last-retrieve-time-stamp.label = Last Retrieve Time Stamp +channel-type.solax.last-retrieve-time-stamp.description = Last time with a successful retrieval of data +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. + +# thing status descriptions + +offline.communication-error.json-cannot-be-retrieved = JSON data could not be retrieved. 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 new file mode 100644 index 0000000000000..a1a814436d56e --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml @@ -0,0 +1,41 @@ + + + + + Number:Frequency + + Frequency of the electricity to/from the inverter + + Measurement + Frequency + + + + + Number:Temperature + + Battery Temperature + + Measurement + Temperature + + + + + DateTime + + Last time with a successful retrieval of data + Time + + + + String + + The raw JSON data retrieved from the inverter's Wi-Fi module. + + + + diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectInverter.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectInverter.xml new file mode 100644 index 0000000000000..8a3c4f7982c48 --- /dev/null +++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectInverter.xml @@ -0,0 +1,109 @@ + + + + + + + The inverter representation that supports local connections via HTTP + + + + + Power to/from the inverter + + + + Current to/from the inverter + + + + Voltage of the inverter + + + + + + Electric voltage of PV String 1 + + + + Electric voltage of PV String 2 + + + + Electric current of PV String 1 + + + + Electric current of PV String 2 + + + + Electric power of PV String 1 + + + + Electric power of PV String 2 + + + + The sum of PV powers from all strings + + + + The sum of PV currents from all strings + + + + + Power to/from the battery + + + + Electric current to/from the battery + + + + Electric voltage of the battery + + + + Temperature of the battery + + + + The battery state of charge in percent + + + + + Power to/from the electricity network. + + + + + + + + + + + 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/pom.xml b/bundles/pom.xml index dbaf85bd430fd..9c099d94e55fb 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -356,6 +356,7 @@ org.openhab.binding.solarlog org.openhab.binding.solarmax org.openhab.binding.solarwatt + org.openhab.binding.solax org.openhab.binding.somfymylink org.openhab.binding.somfytahoma org.openhab.binding.somneo