[solax] Initial contribution (#14880)

Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
This commit is contained in:
Konstantin Polihronov 2023-08-24 15:39:24 +03:00 committed by GitHub
parent ab135421a0
commit 17978b8625
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1349 additions and 0 deletions

View File

@ -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

View File

@ -1606,6 +1606,11 @@
<artifactId>org.openhab.binding.solarwatt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solax</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.somfymylink</artifactId>

View File

@ -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

View File

@ -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="<SERIAL NUMBER OF THE WIFI MODULE>", hostname="<local IP/hostname in the network>" ]
```
### Item Configuration
```java
Group gSolaxInverter "Solax Inverter" <energy> (boilerRoom)
Group solarPanels "Solar panels" <energy> (gSolaxInverter)
Number solaxPowerWest "West [%.0f W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:localConnectInverter:localInverter:pv1-power" }
Number solaxPowerEast "East [%.0f W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:localConnectInverter:localInverter:pv2-power" }
Number solaxBatteryPower "Battery power [%.0f W]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:battery-power" }
Number solaxBatterySoc "Battery SoC [%.0f %%]" <batterylevel> (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:battery-state-of-charge" }
Number solaxFeedInPower "Feed-in power (CEZ) [%.0f W]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:feed-in-power" }
Number solaxAcPower "Invertor output power [%.0f W]" <energy> (gsolax_inverter,EveryChangePersist){ channel="solax:localConnectInverter:localInverter:inverter-output-power" }
String solaxInverterType "Inverter Type [%s]" <energy> (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:inverter-type"}
String solaxUploadTime "Last update time [%s]" <calendar> (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:last-update-time" }
String solaxRawData "Raw data [%s]" <data> (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]
}
}
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.solax</artifactId>
<name>openHAB Add-ons :: Bundles :: Solax Binding</name>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.solax-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-solax" description="solax Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.solax/${project.version}</bundle>
</feature>
</features>

View File

@ -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<ThingTypeUID> 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";
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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.";
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon:addon id="solax" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
<type>binding</type>
<name>Solax Binding</name>
<description>This is the binding for Solax inverters.</description>
<connection>local</connection>
</addon:addon>

View File

@ -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.

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="solax"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<channel-type id="frequency">
<item-type>Number:Frequency</item-type>
<label>Electric Frequency</label>
<description>Frequency of the electricity to/from the inverter</description>
<tags>
<tag>Measurement</tag>
<tag>Frequency</tag>
</tags>
<state pattern="%d %unit%" readOnly="true"/>
</channel-type>
<channel-type id="battery-temperature">
<item-type>Number:Temperature</item-type>
<label>Battery Temperature</label>
<description>Battery Temperature</description>
<tags>
<tag>Measurement</tag>
<tag>Temperature</tag>
</tags>
<state pattern="%d %unit%" readOnly="true"/>
</channel-type>
<channel-type id="last-retrieve-time-stamp">
<item-type>DateTime</item-type>
<label>Last Retrieve Time Stamp</label>
<description>Last time with a successful retrieval of data</description>
<category>Time</category>
<state pattern="yyyy-MM-dd HH:mm:ss" readOnly="true"/>
</channel-type>
<channel-type id="raw-data-type" advanced="true">
<item-type>String</item-type>
<label>Raw Data</label>
<description>The raw JSON data retrieved from the inverter's Wi-Fi module.</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="solax"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="local-connect-inverter">
<label>Local Connect Inverter</label>
<description>The inverter representation that supports local connections via HTTP</description>
<channels>
<channel id="inverter-output-power" typeId="system.electric-power">
<label>Inverter Input/Output Power</label>
<description>Power to/from the inverter</description>
</channel>
<channel id="inverter-current" typeId="system.electric-current">
<label>Inverter Input/Output Current</label>
<description>Current to/from the inverter</description>
</channel>
<channel id="inverter-voltage" typeId="system.electric-voltage">
<label>Inverter Voltage</label>
<description>Voltage of the inverter</description>
</channel>
<channel id="inverter-frequency" typeId="frequency"/>
<channel id="pv1-voltage" typeId="system.electric-voltage">
<label>PV 1 Voltage</label>
<description>Electric voltage of PV String 1</description>
</channel>
<channel id="pv2-voltage" typeId="system.electric-voltage">
<label>PV 2 Voltage</label>
<description>Electric voltage of PV String 2</description>
</channel>
<channel id="pv1-current" typeId="system.electric-current">
<label>PV 1 Current</label>
<description>Electric current of PV String 1</description>
</channel>
<channel id="pv2-current" typeId="system.electric-current">
<label>PV 2 Current</label>
<description>Electric current of PV String 2</description>
</channel>
<channel id="pv1-power" typeId="system.electric-power">
<label>PV 1 Power</label>
<description>Electric power of PV String 1</description>
</channel>
<channel id="pv2-power" typeId="system.electric-power">
<label>PV 2 Power</label>
<description>Electric power of PV String 2</description>
</channel>
<channel id="pv-total-power" typeId="system.electric-power">
<label>PV Total Power</label>
<description>The sum of PV powers from all strings</description>
</channel>
<channel id="pv-total-current" typeId="system.electric-current">
<label>PV Total Current</label>
<description>The sum of PV currents from all strings</description>
</channel>
<channel id="battery-power" typeId="system.electric-power">
<label>Battery Power</label>
<description>Power to/from the battery</description>
</channel>
<channel id="battery-current" typeId="system.electric-current">
<label>Battery Current</label>
<description>Electric current to/from the battery</description>
</channel>
<channel id="battery-voltage" typeId="system.electric-voltage">
<label>Battery Voltage</label>
<description>Electric voltage of the battery</description>
</channel>
<channel id="battery-temperature" typeId="battery-temperature">
<label>Battery Temperature</label>
<description>Temperature of the battery</description>
</channel>
<channel id="battery-level" typeId="system.battery-level">
<label>Battery Level</label>
<description>The battery state of charge in percent</description>
</channel>
<channel id="feed-in-power" typeId="system.electric-power">
<label>Feed-in Power</label>
<description>Power to/from the electricity network.</description>
</channel>
<channel id="last-update-time" typeId="last-retrieve-time-stamp"/>
<channel id="raw-data" typeId="raw-data-type"/>
</channels>
<config-description>
<parameter name="refreshInterval" type="integer" min="1" max="600">
<label>Refresh Interval</label>
<description>Specifies the refresh interval in seconds.</description>
<default>10</default>
</parameter>
<parameter name="password" type="text" required="true">
<label>Password</label>
<description>Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module)</description>
<context>password</context>
</parameter>
<parameter name="hostname" type="text" required="true">
<label>Network Address</label>
<description>IP address or the host name of the Wi-Fi module</description>
<context>network-address</context>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@ -356,6 +356,7 @@
<module>org.openhab.binding.solarlog</module>
<module>org.openhab.binding.solarmax</module>
<module>org.openhab.binding.solarwatt</module>
<module>org.openhab.binding.solax</module>
<module>org.openhab.binding.somfymylink</module>
<module>org.openhab.binding.somfytahoma</module>
<module>org.openhab.binding.somneo</module>