]> git.basschouten.com Git - openhab-addons.git/commitdiff
[sonnen] Initial contribution of new binding for solar battery (#11915)
authorchingon007 <76529461+chingon007@users.noreply.github.com>
Sun, 9 Jan 2022 10:31:23 +0000 (11:31 +0100)
committerGitHub <noreply@github.com>
Sun, 9 Jan 2022 10:31:23 +0000 (11:31 +0100)
* Initial contribution of sonnen binding

Signed-off-by: chingon007 <tron81@gmx.de>
16 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.sonnen/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/README.md [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/communication/SonnenJSONCommunication.java [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/communication/SonnenJsonDataDTO.java [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/binding/binding.xml [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/i18n/sonnen.properties [new file with mode: 0644]
bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/pom.xml

index 2393601417bccf5c4db369722f031f29eaf44b7c..dc0594108b33a31bddb0084749e4ad1fdfa01f93 100644 (file)
 /bundles/org.openhab.binding.solarwatt/ @sven-carstens
 /bundles/org.openhab.binding.somfymylink/ @loungeflyz
 /bundles/org.openhab.binding.somfytahoma/ @octa22
+/bundles/org.openhab.binding.sonnen/ @chingon007
 /bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo
 /bundles/org.openhab.binding.sonyaudio/ @freke
 /bundles/org.openhab.binding.sonyprojector/ @lolodomo
index 4e06be806049933235e81937757bcd732ac2668f..109c762a36fe380406c2ce515c625bb6c8c7e055 100644 (file)
       <artifactId>org.openhab.binding.somfytahoma</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.sonnen</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.sonos</artifactId>
diff --git a/bundles/org.openhab.binding.sonnen/NOTICE b/bundles/org.openhab.binding.sonnen/NOTICE
new file mode 100644 (file)
index 0000000..38d625e
--- /dev/null
@@ -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.sonnen/README.md b/bundles/org.openhab.binding.sonnen/README.md
new file mode 100644 (file)
index 0000000..0e30d5f
--- /dev/null
@@ -0,0 +1,64 @@
+# Sonnen Binding
+
+The binding for sonnen communicates with a sonnen battery.
+More information about the sonnen battery can be found [here](https://sonnen.de/).
+
+## Supported Things
+
+| Thing Type    | Description                    |
+|---------------|--------------------------------|
+| sonnenbattery | Monitoring of a sonnen battery |
+
+
+## Thing Configuration
+
+Only the parameter `hostIP` is required; this is the IP address of the sonnen battery in your local network.
+
+
+## Channels
+
+The following channels are yet supported:
+
+
+| Channel | Type  | Access| Description|
+|---------|-------|-------|------------|
+|batteryChargingState|Switch|read|Indicates if the Battery is charging at that moment|
+|batteryCharging|Number:Energy|read|Indicates the actual current charging the Battery. Otherwise 0.|
+|batteryDischargingState|Switch|read|Indicates if the Battery is discharging at that moment|
+|batteryDischarging|Number:Energy|read|Indicates the actual current discharging the Battery. Otherwise 0.|
+|batteryFeedIn|Number:Energy|read|Indicates the actual charging current of the Battery in watt|
+|batteryDischarging|Number:Energy|read|Indicates the actual current discharging the Battery in watt|
+|consumption|Number:Energy|read|Indicates the actual consumption of the consumer in watt|
+|gridFeedIn|Number:Energy|read|Indicates the actual current feeding to the Grid in watt.0 if nothing is feeded|
+|gridConsumption|Number:Energy|read|Indicates the actual current consumption from the Grid in watt.0 if nothing is received|
+|solarProduction|Number:Energy|read|Indicates the actual production of the Solar system in watt|
+|batteryLevel|Number|read|Indicates the actual Battery Level in % from 0 - 100|
+|flowConsumptionBatteryState|Switch|read|Indicates if there is a current flow from Battery towards Consumption|
+|flowConsumptionGridState|Switch|read|Indicates if there is a current flow from Grid towards Consumption|
+|flowConsumptionProductionState|Switch|read|Indicates if there is a current flow from Solar Production towards Consumption|
+|flowGridBatteryState|Switch|read|Indicates if there is a current flow from Grid towards Battery|
+|flowProductionBatteryState|Switch|read|Indicates if there is a current flow from Production towards Battery|
+|flowProductionGridState|Switch|read|Indicates if there is a current flow from Production towards Grid|
+
+## Full Example
+
+example.things:
+
+```
+Thing sonnen:sonnenbattery:myBattery "Sonnen Battery"  [ hostIP="192.168.0.10"]
+```
+
+example.items:
+
+```
+Number:Energy Consumption { channel="sonnen:sonnenbattery:myBattery:consumption" }
+Number:Energy GridFeeding { channel="sonnen:sonnenbattery:myBattery:gridFeedIn" }
+Number BatteryLevel { channel="sonnen:sonnenbattery:myBattery:batteryLevel" }
+Switch FlowConsumptionBattery { channel="sonnen:sonnenbattery:myBattery:flowConsumptionBattery" }
+```
+
+## Tested Hardware
+
+The binding was successfully tested with the following sonnen battery:
+
+- sonnnen eco 8.0 SW Version: 1.6.10.1221979
diff --git a/bundles/org.openhab.binding.sonnen/pom.xml b/bundles/org.openhab.binding.sonnen/pom.xml
new file mode 100644 (file)
index 0000000..baffc8c
--- /dev/null
@@ -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 http://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>3.3.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.openhab.binding.sonnen</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: Sonnen Binding</name>
+
+</project>
diff --git a/bundles/org.openhab.binding.sonnen/src/main/feature/feature.xml b/bundles/org.openhab.binding.sonnen/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..3a439f4
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.sonnen-${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-sonnen" description="Sonnen Binding" version="${project.version}">
+               <feature>openhab-runtime-base</feature>
+               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sonnen/${project.version}</bundle>
+       </feature>
+</features>
diff --git a/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenBindingConstants.java b/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenBindingConstants.java
new file mode 100644 (file)
index 0000000..09b80a1
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonnen.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link SonnenBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Christian Feininger - Initial contribution
+ */
+@NonNullByDefault
+public class SonnenBindingConstants {
+
+    private static final String BINDING_ID = "sonnen";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID THING_TYPE_BATTERY = new ThingTypeUID(BINDING_ID, "sonnenbattery");
+
+    // List of all Channel ids
+    public static final String CHANNELBATTERYCHARGINGSTATE = "batteryChargingState";
+    public static final String CHANNELBATTERYDISCHARGINGSTATE = "batteryDischargingState";
+    public static final String CHANNELBATTERYCHARGING = "batteryCharging";
+    public static final String CHANNELBATTERYDISCHARGING = "batteryDischarging";
+    public static final String CHANNELCONSUMPTION = "consumption";
+    public static final String CHANNELGRIDFEEDIN = "gridFeedIn";
+    public static final String CHANNELGRIDCONSUMPTION = "gridConsumption";
+    public static final String CHANNELSOLARPRODUCTION = "solarProduction";
+    public static final String CHANNELBATTERYLEVEL = "batteryLevel";
+    public static final String CHANNELFLOWCONSUMPTIONBATTERYSTATE = "flowConsumptionBatteryState";
+    public static final String CHANNELFLOWCONSUMPTIONGRIDSTATE = "flowConsumptionGridState";
+    public static final String CHANNELFLOWCONSUMPTIONPRODUCTIONSTATE = "flowConsumptionProductionState";
+    public static final String CHANNELFLOWGRIDBATTERYSTATE = "flowGridBatteryState";
+    public static final String CHANNELFLOWPRODUCTIONBATTERYSTATE = "flowProductionBatteryState";
+    public static final String CHANNELFLOWPRODUCTIONGRIDSTATE = "flowProductionGridState";
+}
diff --git a/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenConfiguration.java b/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenConfiguration.java
new file mode 100644 (file)
index 0000000..ffcdf1b
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonnen.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link SonnenConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Christian Feininger - Initial contribution
+ */
+@NonNullByDefault
+public class SonnenConfiguration {
+
+    public @Nullable String hostIP = null;
+    public int refreshInterval = 30;
+}
diff --git a/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenHandler.java b/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenHandler.java
new file mode 100644 (file)
index 0000000..657a5f9
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonnen.internal;
+
+import static org.openhab.binding.sonnen.internal.SonnenBindingConstants.*;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.measure.quantity.Dimensionless;
+import javax.measure.quantity.Power;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sonnen.internal.communication.SonnenJSONCommunication;
+import org.openhab.binding.sonnen.internal.communication.SonnenJsonDataDTO;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SonnenHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Christian Feininger - Initial contribution
+ */
+@NonNullByDefault
+public class SonnenHandler extends BaseThingHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(SonnenHandler.class);
+
+    private SonnenConfiguration config = new SonnenConfiguration();
+
+    private @Nullable ScheduledFuture<?> refreshJob;
+
+    private SonnenJSONCommunication serviceCommunication;
+
+    private boolean automaticRefreshing = false;
+
+    private Map<String, Boolean> linkedChannels = new HashMap<>();
+
+    public SonnenHandler(Thing thing) {
+        super(thing);
+        serviceCommunication = new SonnenJSONCommunication();
+    }
+
+    @Override
+    public void initialize() {
+        logger.debug("Initializing sonnen handler for thing {}", getThing().getUID());
+        config = getConfigAs(SonnenConfiguration.class);
+        if (config.refreshInterval < 0 || config.refreshInterval > 1000) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "Parameter 'refresh Rate' msut be in the range 0-1000!");
+            return;
+        }
+        if (config.hostIP == null) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IP Address must be configured!");
+            return;
+        }
+
+        serviceCommunication.setConfig(config);
+        updateStatus(ThingStatus.UNKNOWN);
+        scheduler.submit(() -> {
+            if (updateBatteryData()) {
+                for (Channel channel : getThing().getChannels()) {
+                    if (isLinked(channel.getUID().getId())) {
+                        channelLinked(channel.getUID());
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Calls the service to update the battery data
+     *
+     * @return true if the update succeeded, false otherwise
+     */
+    private boolean updateBatteryData() {
+        String error = serviceCommunication.refreshBatteryConnection();
+        if (error.isEmpty()) {
+            if (!ThingStatus.ONLINE.equals(getThing().getStatus())) {
+                updateStatus(ThingStatus.ONLINE);
+            }
+        } else {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error);
+        }
+        return error.isEmpty();
+    }
+
+    private void verifyLinkedChannel(String channelID) {
+        if (isLinked(channelID) && !linkedChannels.containsKey(channelID)) {
+            linkedChannels.put(channelID, true);
+        }
+    }
+
+    @Override
+    public void dispose() {
+        stopAutomaticRefresh();
+        linkedChannels.clear();
+        automaticRefreshing = false;
+    }
+
+    private void stopAutomaticRefresh() {
+        ScheduledFuture<?> job = refreshJob;
+        if (job != null) {
+            job.cancel(true);
+        }
+        refreshJob = null;
+    }
+
+    /**
+     * Start the job refreshing the oven status
+     */
+    private void startAutomaticRefresh() {
+        ScheduledFuture<?> job = refreshJob;
+        if (job == null || job.isCancelled()) {
+            refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, 0, config.refreshInterval,
+                    TimeUnit.SECONDS);
+        }
+    }
+
+    private void refreshChannels() {
+        updateBatteryData();
+        for (Channel channel : getThing().getChannels()) {
+            updateChannel(channel.getUID().getId());
+        }
+    }
+
+    @Override
+    public void channelLinked(ChannelUID channelUID) {
+        if (!automaticRefreshing) {
+            logger.debug("Start automatic refreshing");
+            startAutomaticRefresh();
+            automaticRefreshing = true;
+        }
+        verifyLinkedChannel(channelUID.getId());
+        updateChannel(channelUID.getId());
+    }
+
+    @Override
+    public void channelUnlinked(ChannelUID channelUID) {
+        linkedChannels.remove(channelUID.getId());
+        if (linkedChannels.isEmpty()) {
+            automaticRefreshing = false;
+            stopAutomaticRefresh();
+            logger.debug("Stop automatic refreshing");
+        }
+    }
+
+    private void updateChannel(String channelId) {
+        if (isLinked(channelId)) {
+            State state = null;
+            SonnenJsonDataDTO data = serviceCommunication.getBatteryData();
+            if (data != null) {
+                switch (channelId) {
+                    case CHANNELBATTERYDISCHARGINGSTATE:
+                        update(OnOffType.from(data.isBatteryDischarging()), channelId);
+                        break;
+                    case CHANNELBATTERYCHARGINGSTATE:
+                        update(OnOffType.from(data.isBatteryCharging()), channelId);
+                        break;
+                    case CHANNELCONSUMPTION:
+                        state = new QuantityType<Power>(data.getConsumptionHouse(), Units.WATT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELBATTERYDISCHARGING:
+                        state = new QuantityType<Power>(data.getbatteryCurrent() > 0 ? data.getbatteryCurrent() : 0,
+                                Units.WATT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELBATTERYCHARGING:
+                        state = new QuantityType<Power>(
+                                data.getbatteryCurrent() <= 0 ? (data.getbatteryCurrent() * -1) : 0, Units.WATT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELGRIDFEEDIN:
+                        state = new QuantityType<Power>(data.getGridValue() > 0 ? data.getGridValue() : 0, Units.WATT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELGRIDCONSUMPTION:
+                        state = new QuantityType<Power>(data.getGridValue() <= 0 ? (data.getGridValue() * -1) : 0,
+                                Units.WATT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELSOLARPRODUCTION:
+                        state = new QuantityType<Power>(data.getSolarProduction(), Units.WATT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELBATTERYLEVEL:
+                        state = new QuantityType<Dimensionless>(data.getBatteryChargingLevel(), Units.PERCENT);
+                        update(state, channelId);
+                        break;
+                    case CHANNELFLOWCONSUMPTIONBATTERYSTATE:
+                        update(OnOffType.from(data.isFlowConsumptionBattery()), channelId);
+                        break;
+                    case CHANNELFLOWCONSUMPTIONGRIDSTATE:
+                        update(OnOffType.from(data.isFlowConsumptionGrid()), channelId);
+                        break;
+                    case CHANNELFLOWCONSUMPTIONPRODUCTIONSTATE:
+                        update(OnOffType.from(data.isFlowConsumptionProduction()), channelId);
+                        break;
+                    case CHANNELFLOWGRIDBATTERYSTATE:
+                        update(OnOffType.from(data.isFlowGridBattery()), channelId);
+                        break;
+                    case CHANNELFLOWPRODUCTIONBATTERYSTATE:
+                        update(OnOffType.from(data.isFlowProductionBattery()), channelId);
+                        break;
+                    case CHANNELFLOWPRODUCTIONGRIDSTATE:
+                        update(OnOffType.from(data.isFlowProductionGrid()), channelId);
+                        break;
+                }
+            } else {
+                update(null, channelId);
+            }
+        }
+    }
+
+    /**
+     * Updates the State of the given channel
+     *
+     * @param state Given state
+     * @param channelId the refereed channelID
+     */
+    private void update(@Nullable State state, String channelId) {
+        logger.debug("Update channel {} with state {}", channelId, (state == null) ? "null" : state.toString());
+        updateState(channelId, state != null ? state : UnDefType.UNDEF);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if (command == RefreshType.REFRESH) {
+            updateBatteryData();
+            updateChannel(channelUID.getId());
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenHandlerFactory.java b/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/SonnenHandlerFactory.java
new file mode 100644 (file)
index 0000000..ab8c968
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonnen.internal;
+
+import static org.openhab.binding.sonnen.internal.SonnenBindingConstants.THING_TYPE_BATTERY;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link SonnenHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Christian Feininger - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.sonnen", service = ThingHandlerFactory.class)
+public class SonnenHandlerFactory extends BaseThingHandlerFactory {
+
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BATTERY);
+
+    @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_BATTERY.equals(thingTypeUID)) {
+            return new SonnenHandler(thing);
+        }
+
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/communication/SonnenJSONCommunication.java b/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/communication/SonnenJSONCommunication.java
new file mode 100644 (file)
index 0000000..0d8482d
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonnen.internal.communication;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.sonnen.internal.SonnenConfiguration;
+import org.openhab.core.io.net.http.HttpUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * This class handles the JSON communication with the sonnen battery
+ *
+ * @author Christian Feininger - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class SonnenJSONCommunication {
+
+    private final Logger logger = LoggerFactory.getLogger(SonnenJSONCommunication.class);
+    private SonnenConfiguration config;
+
+    private Gson gson;
+    private @Nullable SonnenJsonDataDTO batteryData;
+
+    public SonnenJSONCommunication() {
+        gson = new Gson();
+        config = new SonnenConfiguration();
+    }
+
+    /**
+     * Refreshes the battery connection.
+     *
+     * @return an empty string if no error occurred, the error message otherwise.
+     */
+    public String refreshBatteryConnection() {
+        String result = "";
+        String urlStr = "http://" + config.hostIP + "/api/v1/status";
+
+        try {
+            String response = HttpUtil.executeUrl("GET", urlStr, 10000);
+            logger.debug("BatteryData = {}", response);
+            if (response == null) {
+                throw new IOException("HttpUtil.executeUrl returned null");
+            }
+            batteryData = gson.fromJson(response, SonnenJsonDataDTO.class);
+        } catch (IOException | JsonSyntaxException e) {
+            logger.debug("Error processiong Get request {}:  {}", urlStr, e.getMessage());
+            result = "Cannot find service on given IP " + config.hostIP + ". Please verify the IP address!";
+            batteryData = null;
+        }
+        return result;
+    }
+
+    /**
+     * Set the config for service to communicate
+     *
+     * @param config2
+     */
+    public void setConfig(SonnenConfiguration config2) {
+        this.config = config2;
+    }
+
+    /**
+     * Returns the actual stored Battery Data
+     *
+     * @return JSON Data from the Battery or null if request failed
+     */
+    public @Nullable SonnenJsonDataDTO getBatteryData() {
+        return this.batteryData;
+    }
+}
diff --git a/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/communication/SonnenJsonDataDTO.java b/bundles/org.openhab.binding.sonnen/src/main/java/org/openhab/binding/sonnen/internal/communication/SonnenJsonDataDTO.java
new file mode 100644 (file)
index 0000000..84c2dae
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonnen.internal.communication;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link SonnenJsonDataDTO} is the Java class used to map the JSON
+ * response to a Oven request.
+ *
+ * @author Christian Feininger - Initial contribution
+ */
+public class SonnenJsonDataDTO {
+    @SerializedName("BatteryCharging")
+    boolean batteryCharging;
+    @SerializedName("BatteryDischarging")
+    boolean batteryDischarging;
+    @SerializedName("Consumption_W")
+    int consumptionHouse;
+    @SerializedName("GridFeedIn_W")
+    int gridValue;
+    @SerializedName("Production_W")
+    int solarProduction;
+    @SerializedName("USOC")
+    int batteryChargingLevel;
+    @SerializedName("FlowConsumptionBattery")
+    boolean flowConsumptionBattery;
+    @SerializedName("FlowConsumptionGrid")
+    boolean flowConsumptionGrid;
+    @SerializedName("FlowConsumptionProduction")
+    boolean flowConsumptionProduction;
+    @SerializedName("FlowGridBattery")
+    boolean flowGridBattery;
+    @SerializedName("FlowProductionBattery")
+    boolean flowProductionBattery;
+    @SerializedName("FlowProductionGrid")
+    boolean flowProductionGrid;
+    @SerializedName("Pac_total_W")
+    int batteryCurrent;
+
+    /**
+     * @return the batteryCurrent
+     */
+    public int getbatteryCurrent() {
+        return batteryCurrent;
+    }
+
+    /**
+     * @return the batteryCharging
+     */
+    public boolean isBatteryCharging() {
+        return batteryCharging;
+    }
+
+    /**
+     * @return the batteryDischarging
+     */
+    public boolean isBatteryDischarging() {
+        return batteryDischarging;
+    }
+
+    /**
+     * @return the consumptionHouse
+     */
+    public int getConsumptionHouse() {
+        return consumptionHouse;
+    }
+
+    /**
+     * @return the gridValue. Negative value indicates receiving from Grid. Positive value indicates feeding to Grid.
+     */
+    public int getGridValue() {
+        return gridValue;
+    }
+
+    /**
+     * @return the solarProduction
+     */
+    public int getSolarProduction() {
+        return solarProduction;
+    }
+
+    /**
+     * @return the batteryChargingLevel
+     */
+    public int getBatteryChargingLevel() {
+        return batteryChargingLevel;
+    }
+
+    /**
+     * @return the flowConsumptionBattery
+     */
+    public boolean isFlowConsumptionBattery() {
+        return flowConsumptionBattery;
+    }
+
+    /**
+     * @return the flowConsumptionGrid
+     */
+    public boolean isFlowConsumptionGrid() {
+        return flowConsumptionGrid;
+    }
+
+    /**
+     * @return the flowConsumptionProduction
+     */
+    public boolean isFlowConsumptionProduction() {
+        return flowConsumptionProduction;
+    }
+
+    /**
+     * @return the flowGridBattery
+     */
+    public boolean isFlowGridBattery() {
+        return flowGridBattery;
+    }
+
+    /**
+     * @return the flowProductionBattery
+     */
+    public boolean isFlowProductionBattery() {
+        return flowProductionBattery;
+    }
+
+    /**
+     * @return the flowProductionGrid
+     */
+    public boolean isFlowProductionGrid() {
+        return flowProductionGrid;
+    }
+}
diff --git a/bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644 (file)
index 0000000..a24ce4e
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<binding:binding id="sonnen" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
+
+       <name>Sonnen Binding</name>
+       <description>This binding communicates with a solar battery from sonnen.</description>
+
+</binding:binding>
diff --git a/bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/i18n/sonnen.properties b/bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/i18n/sonnen.properties
new file mode 100644 (file)
index 0000000..d7687f6
--- /dev/null
@@ -0,0 +1,47 @@
+# binding
+
+binding.sonnen.name = Sonnen Binding
+binding.sonnen.description = This binding communicates with a solar battery from sonnen.
+
+# thing types
+
+thing-type.sonnen.sonnenbattery.label = Sonnen Battery
+thing-type.sonnen.sonnenbattery.description = Monitoring of a sonnen battery.
+
+# thing types config
+
+thing-type.config.sonnen.sonnenbattery.hostIP.label = IP Address
+thing-type.config.sonnen.sonnenbattery.hostIP.description = Please add the IP Address of your sonnen battery.
+thing-type.config.sonnen.sonnenbattery.refreshInterval.label = Refresh Interval
+thing-type.config.sonnen.sonnenbattery.refreshInterval.description = How often in seconds the sonnen battery should schedule a refresh after a channel is linked to an item. Valid input is 0 - 1000.
+
+# channel types
+
+channel-type.sonnen.batteryCharging.label = Battery Charging
+channel-type.sonnen.batteryCharging.description = Indicates the actual current charging the Battery. Otherwise 0.
+channel-type.sonnen.batteryChargingState.label = Battery Charging State
+channel-type.sonnen.batteryChargingState.description = Indicates if the Battery is charging at that moment.
+channel-type.sonnen.batteryDischarging.label = Battery Discharging
+channel-type.sonnen.batteryDischarging.description = Indicates the actual current discharging the Battery. Otherwise 0.
+channel-type.sonnen.batteryDischargingState.label = Battery Discharging State
+channel-type.sonnen.batteryDischargingState.description = Indicates if the Battery is discharging at that moment.
+channel-type.sonnen.consumption.label = Consumption
+channel-type.sonnen.consumption.description = Indicates the actual consumption of the House.
+channel-type.sonnen.flowConsumptionBatteryState.label = Flow Battery Towards Consumption State
+channel-type.sonnen.flowConsumptionBatteryState.description = Indicates if there is a current flow from battery towards consumption.
+channel-type.sonnen.flowConsumptionGridState.label = Flow Grid Towards Consumption State
+channel-type.sonnen.flowConsumptionGridState.description = Indicates if there is a current flow from grid towards consumption.
+channel-type.sonnen.flowConsumptionProductionState.label = Flow Production Towards Consumption State
+channel-type.sonnen.flowConsumptionProductionState.description = Indicates if there is a current flow from solar production towards consumption.
+channel-type.sonnen.flowGridBatteryState.label = Flow Grid Towards Battery State
+channel-type.sonnen.flowGridBatteryState.description = Indicates if there is a current flow from grid towards battery.
+channel-type.sonnen.flowProductionBatteryState.label = Flow Production Towards Battery State
+channel-type.sonnen.flowProductionBatteryState.description = Indicates if there is a current flow from production towards battery.
+channel-type.sonnen.flowProductionGridState.label = Flow Production Towards Grid State
+channel-type.sonnen.flowProductionGridState.description = Indicates if there is a current flow from production towards grid.
+channel-type.sonnen.gridConsumption.label = Grid Consumption
+channel-type.sonnen.gridConsumption.description = Indicates the actual current consumption from the the Grid. Otherwise 0.
+channel-type.sonnen.gridFeedIn.label = Grid Feed In
+channel-type.sonnen.gridFeedIn.description = Indicates the actual current feeding to the Grid. Otherwise 0.
+channel-type.sonnen.solarProduction.label = Solar Production
+channel-type.sonnen.solarProduction.description = Indicates the actual production of the Solar system.
diff --git a/bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.sonnen/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644 (file)
index 0000000..5821dcf
--- /dev/null
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="sonnen"
+       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">
+
+       <!-- Sonnen battery Thing Type -->
+       <thing-type id="sonnenbattery">
+               <label>Sonnen Battery</label>
+               <description>Monitoring of a sonnen battery.</description>
+
+               <channels>
+                       <channel id="batteryChargingState" typeId="batteryChargingState"/>
+                       <channel id="batteryDischargingState" typeId="batteryDischargingState"/>
+                       <channel id="batteryCharging" typeId="batteryCharging"/>
+                       <channel id="batteryDischarging" typeId="batteryDischarging"/>
+                       <channel id="consumption" typeId="consumption"/>
+                       <channel id="gridFeedIn" typeId="gridFeedIn"/>
+                       <channel id="gridConsumption" typeId="gridConsumption"/>
+                       <channel id="solarProduction" typeId="solarProduction"/>
+                       <channel id="batteryLevel" typeId="system.battery-level"/>
+                       <channel id="flowConsumptionBatteryState" typeId="flowConsumptionBatteryState"/>
+                       <channel id="flowConsumptionGridState" typeId="flowConsumptionGridState"/>
+                       <channel id="flowConsumptionProductionState" typeId="flowConsumptionProductionState"/>
+                       <channel id="flowGridBatteryState" typeId="flowGridBatteryState"/>
+                       <channel id="flowProductionBatteryState" typeId="flowProductionBatteryState"/>
+                       <channel id="flowProductionGridState" typeId="flowProductionGridState"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="hostIP" type="text" required="true">
+                               <context>network-address</context>
+                               <label>IP Address</label>
+                               <description>Please add the IP Address of your sonnen battery.</description>
+                       </parameter>
+                       <parameter name="refreshInterval" type="integer" unit="s" min="0" max="1000">
+                               <label>Refresh Interval</label>
+                               <description>How often in seconds the sonnen battery should schedule a refresh after a channel is linked to an item.
+                                       Valid input is 0 - 1000.</description>
+                               <advanced>true</advanced>
+                               <default>30</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <!-- sonnen Channel Type -->
+       <channel-type id="batteryChargingState">
+               <item-type>Switch</item-type>
+               <label>Battery Charging State</label>
+               <description>Indicates if the Battery is charging at that moment.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="batteryDischargingState">
+               <item-type>Switch</item-type>
+               <label>Battery Discharging State</label>
+               <description>Indicates if the Battery is discharging at that moment.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="batteryCharging">
+               <item-type>Number:Energy</item-type>
+               <label>Battery Charging</label>
+               <description>Indicates the actual current charging the Battery. Otherwise 0.</description>
+               <state readOnly="true" pattern="%.0f %unit%"/>
+       </channel-type>
+       <channel-type id="batteryDischarging">
+               <item-type>Number:Energy</item-type>
+               <label>Battery Discharging</label>
+               <description>Indicates the actual current discharging the Battery. Otherwise 0.</description>
+               <state readOnly="true" pattern="%.0f %unit%"/>
+       </channel-type>
+       <channel-type id="consumption">
+               <item-type>Number:Energy</item-type>
+               <label>Consumption</label>
+               <description>Indicates the actual consumption of the House.</description>
+               <state readOnly="true" pattern="%.0f %unit%"/>
+       </channel-type>
+       <channel-type id="gridFeedIn">
+               <item-type>Number:Energy</item-type>
+               <label>Grid Feed In</label>
+               <description>Indicates the actual current feeding to the Grid. Otherwise 0.</description>
+               <state readOnly="true" pattern="%.0f %unit%"/>
+       </channel-type>
+       <channel-type id="gridConsumption">
+               <item-type>Number:Energy</item-type>
+               <label>Grid Consumption</label>
+               <description>Indicates the actual current consumption from the the Grid. Otherwise 0.</description>
+               <state readOnly="true" pattern="%.0f %unit%"/>
+       </channel-type>
+       <channel-type id="solarProduction">
+               <item-type>Number:Energy</item-type>
+               <label>Solar Production</label>
+               <description>Indicates the actual production of the Solar system.</description>
+               <state readOnly="true" pattern="%.0f %unit%"/>
+       </channel-type>
+       <channel-type id="flowConsumptionBatteryState">
+               <item-type>Switch</item-type>
+               <label>Flow Battery Towards Consumption State</label>
+               <description>Indicates if there is a current flow from battery towards consumption.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="flowConsumptionGridState">
+               <item-type>Switch</item-type>
+               <label>Flow Grid Towards Consumption State</label>
+               <description>Indicates if there is a current flow from grid towards consumption.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="flowConsumptionProductionState">
+               <item-type>Switch</item-type>
+               <label>Flow Production Towards Consumption State</label>
+               <description>Indicates if there is a current flow from solar production towards consumption.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="flowGridBatteryState">
+               <item-type>Switch</item-type>
+               <label>Flow Grid Towards Battery State</label>
+               <description>Indicates if there is a current flow from grid towards battery.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="flowProductionBatteryState">
+               <item-type>Switch</item-type>
+               <label>Flow Production Towards Battery State</label>
+               <description>Indicates if there is a current flow from production towards battery.</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="flowProductionGridState">
+               <item-type>Switch</item-type>
+               <label>Flow Production Towards Grid State</label>
+               <description>Indicates if there is a current flow from production towards grid.</description>
+               <state readOnly="true"/>
+       </channel-type>
+</thing:thing-descriptions>
index 536eef33390ec63b98645c8747bac1603b8121b7..c45ac553505aa4a896bb05ef405ebb192c405891 100644 (file)
     <module>org.openhab.binding.solarwatt</module>
     <module>org.openhab.binding.somfymylink</module>
     <module>org.openhab.binding.somfytahoma</module>
+    <module>org.openhab.binding.sonnen</module>
     <module>org.openhab.binding.sonos</module>
     <module>org.openhab.binding.sonyaudio</module>
     <module>org.openhab.binding.sonyprojector</module>