]> git.basschouten.com Git - openhab-addons.git/commitdiff
[teslapowerwall] Initial contribution (#16876)
authorPaul Smedley <paul@smedley.id.au>
Fri, 28 Jun 2024 11:33:54 +0000 (21:03 +0930)
committerGitHub <noreply@github.com>
Fri, 28 Jun 2024 11:33:54 +0000 (13:33 +0200)
* Latest fixes

Signed-off-by: Paul Smedley <paul@smedley.id.au>
23 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.teslapowerwall/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/README.md [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallAuthenticationException.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallCommunicationException.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallTlsTrustManagerProvider.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallWebTargets.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/BatterySOE.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/GridStatus.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/MeterAggregates.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/Operations.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/SystemStatus.java [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/addon/addon.xml [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/i18n/teslapowerwall.properties [new file with mode: 0644]
bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/pom.xml

index 292a42062bff8ed40d5b0c3836819d0a5cff1c24..a2d901c962635bc974649d15ce4dd25e00ddc8fc 100755 (executable)
 /bundles/org.openhab.binding.teleinfo/ @Nokyyz @olivierkeke
 /bundles/org.openhab.binding.tellstick/ @openhab/add-ons-maintainers
 /bundles/org.openhab.binding.tesla/ @kgoderis
+/bundles/org.openhab.binding.teslapowerwall/ @psmedley
 /bundles/org.openhab.binding.tibber/ @kjoglum
 /bundles/org.openhab.binding.tivo/ @mlobstein
 /bundles/org.openhab.binding.touchwand/ @roieg
index 1bcffbae2836704784372e664428339e6ca4d640..631c9f7a82f75c7eea06f6409c42a951ac682b61 100644 (file)
       <artifactId>org.openhab.binding.tesla</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.teslapowerwall</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.tibber</artifactId>
diff --git a/bundles/org.openhab.binding.teslapowerwall/NOTICE b/bundles/org.openhab.binding.teslapowerwall/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.teslapowerwall/README.md b/bundles/org.openhab.binding.teslapowerwall/README.md
new file mode 100644 (file)
index 0000000..fa0c471
--- /dev/null
@@ -0,0 +1,100 @@
+# TeslaPowerwall Binding
+
+This binding enables the capture of key data from a Tesla Powerwall 2 into openHAB.
+
+## Supported Things
+
+- `powerwall` Tesla Powerwall 2
+
+## Discovery
+
+The binding does not support auto discovery.
+
+## Thing Configuration
+
+As a minimum, the hostname is needed:
+
+* hostname - The hostname of the Tesla Powerwall 2. Defaults to powerwall to avoid SSL certificate issues
+* email - the email of the local account on the Powerwall that the installer provided
+* password - the password of the local account on the Powerwall that the installer provided
+* refresh - The frequency with which to refresh information from the Tesla Powerwall2 specified in seconds. Defaults to 10 seconds.
+
+## Channels
+
+| channel id                | type                 | description                                                  |
+|---------------------------|----------------------|--------------------------------------------------------------|
+| grid-status               | String               | Current status of the Power Grid                             |
+| battery-soe               | Number:Dimensionless | Current battery state of charge                              |
+| mode                      | String               | Current operating mode                                       |
+| reserve                   | Number:Dimensionless | Current battery reserve %                                    |
+| grid-inst-power           | Number:Power         | Instantaneous Grid Power Supply                              |
+| battery-inst-power        | Number:Power         | Instantaneous Battery Power Supply                           |
+| home-inst-power           | Number:Power         | Instantaneous Home Power Supply                              |
+| solar-inst-power          | Number:Power         | Instantaneous Solar Power Supply                             |
+| grid-energy-exported      | Number:Energy        | Total Grid Energy Exported                                   |
+| battery-energy-exported   | Number:Energy        | Total Battery Energy Exported                                |
+| home-energy-exported      | Number:Energy        | Total Home Energy Exported                                   |
+| solar-energy-exported     | Number:Energy        | Total Solar Energy Exported                                  |
+| grid-energy-imported      | Number:Energy        | Total Grid Energy Imported                                   |
+| battery-energy-imported   | Number:Energy        | Total Battery Energy Imported                                |
+| home-energy-imported      | Number:Energy        | Total Home Energy Imported                                   |
+| solar-energy-imported     | Number:Energy        | Total Solar Energy Imported                                  |
+| degradation               | Number:Dimensionless | Current battery degradation % (Based on single battery)      |
+| full-pack-energy          | Number:Energy        | Reported battery capacity at full                            |
+
+## Full Example
+
+### `teslapowerwall.things`:
+
+```java
+teslapowerwall:tesla-powerwall:TeslaPowerwall [ hostname="192.168.0.5" ]
+```
+
+### `teslapowerwall.items`:
+
+```java
+String TeslaPowerwall_grid-status { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:grid-status" }
+Switch TeslaPowerwall_grid-services { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:grid-services" }
+Number:Dimensionless TeslaPowerwall_battery-soe { channel="tesla-powerwall:teslapowerwall:TeslaPowerwall:battery-soe", unit="%" }
+String TeslaPowerwall_mode { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:mode" }
+Number:Dimensionless TeslaPowerwall_reserve { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:reserve", unit="%" }
+Number:Power TeslaPowerwall_grid-inst-power { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:grid-inst-power" }
+Number:Power TeslaPowerwall_battery-inst-power { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:battery-inst-power" }
+Number:Power TeslaPowerwall_home-inst-power { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:home-inst-power" }
+Number:Power TeslaPowerwall_solar-inst-power { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:solar-inst-power" }
+Number:Energy TeslaPowerwall_grid-energy-exported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:grid-energy-exported" }
+Number:Energy TeslaPowerwall_grid-energy-imported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:grid-energy-imported" }
+Number:Energy TeslaPowerwall_battery-energy-exported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:battery-energy-exported" }
+Number:Energy TeslaPowerwall_battery-energy-imported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:battery-energy-imported" }
+Number:Energy TeslaPowerwall_home-energy-exported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:home-energy-exported" }
+Number:Energy TeslaPowerwall_home-energy-imported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:home-energy-imported" }
+Number:Energy TeslaPowerwall_solar-energy-exported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:solar-energy-exported" }
+Number:Energy TeslaPowerwall_solar-energy-imported { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:solar-energy-imported" }
+Number:Dimensionless TeslaPowerwall_degradation { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:degradation", unit="%" }
+Number:Energy TeslaPowerwall_full-pack-energy { channel="teslapowerwall:tesla-powerwall:TeslaPowerwall:full-pack-energy" }
+```
+
+### `teslapowerwall.sitemap`:
+
+```perl
+Text item=TeslaPowerwall_grid-status label="Grid Status [%s]"
+Text item=TeslaPowerwall_grid-services label="Grid Services Status [%s]"
+Text item=TeslaPowerwall_battery-soe label="Battery Charge"
+Text item=TeslaPowerwall_mode label="Battery Mode"
+Text item=TeslaPowerwall_reserve label="Battery Reserve"
+Text item=TeslaPowerwall_grid-inst-power label="Grid Power [%.1f W]"
+Text item=TeslaPowerwall_battery-inst-power label="Battery Power [%.1f W]"
+Text item=TeslaPowerwall_home-inst-power label="Home Power [%.1f W]"
+Text item=TeslaPowerwall_solar-inst-power label="Solar Power [%.1f W]"
+Text item=TeslaPowerwall_grid-energy-exported label="Grid Energy Exported [%.1f kWh]"
+Text item=TeslaPowerwall_grid-energy-imported label="Grid Energy Imported [%.1f kWh]"
+Text item=TeslaPowerwall_battery-energy-exported label="Battery Energy Exported [%.1f kWh]"
+Text item=TeslaPowerwall_battery-energyi-mported label="Battery Energy Imported [%.1f kWh]"
+Text item=TeslaPowerwall_home-energy-exported label="Home Energy Exported [%.1f kWh]"
+Text item=TeslaPowerwall_home-energy-imported label="Home Energy Imported [%.1f kWh]"
+Text item=TeslaPowerwall_solar-energy-exported label="Solar Energy Exported [%.1f kWh]"
+Text item=TeslaPowerwall_solar-energy-imported label="Solar Energy Imported [%.1f kWh]"
+Text item=TeslaPowerwall_full-pack-energy label="Full Pack Energy"
+Text item=TeslaPowerwall_degradation label="Degradation level"
+```
+
diff --git a/bundles/org.openhab.binding.teslapowerwall/pom.xml b/bundles/org.openhab.binding.teslapowerwall/pom.xml
new file mode 100644 (file)
index 0000000..5746fde
--- /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>4.2.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.openhab.binding.teslapowerwall</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: Tesla Powerwall Binding</name>
+
+</project>
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/feature/feature.xml b/bundles/org.openhab.binding.teslapowerwall/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..99d637a
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.teslapowerwall-${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-teslapowerwall" description="Tesla Powerwall Binding" version="${project.version}">
+               <feature>openhab-runtime-base</feature>
+               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.teslapowerwall/${project.version}</bundle>
+       </feature>
+</features>
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallAuthenticationException.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallAuthenticationException.java
new file mode 100644 (file)
index 0000000..c658c6c
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Exception for when an unexpected response is received from the TeslaPowerwall controller.
+ *
+ * @author Paul Smedley - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class TeslaPowerwallAuthenticationException extends Exception {
+    private static final long serialVersionUID = 529232811860854017L;
+
+    public TeslaPowerwallAuthenticationException(String message) {
+        super(message);
+    }
+
+    public TeslaPowerwallAuthenticationException(Throwable ex) {
+        super(ex);
+    }
+
+    public TeslaPowerwallAuthenticationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallBindingConstants.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallBindingConstants.java
new file mode 100644 (file)
index 0000000..98be26a
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link TeslaPowerwallBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Paul Smedley - Initial contribution
+ */
+@NonNullByDefault
+public class TeslaPowerwallBindingConstants {
+
+    private static final String BINDING_ID = "teslapowerwall";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID TESLAPOWERWALL_THING = new ThingTypeUID(BINDING_ID, "tesla-powerwall");
+
+    // List of all Channel ids
+    public static final String CHANNEL_TESLAPOWERWALL_GRID_STATUS = "grid-status";
+    public static final String CHANNEL_TESLAPOWERWALL_GRID_SERVICES = "grid-services";
+    public static final String CHANNEL_TESLAPOWERWALL_BATTERY_SOE = "battery-soe";
+    public static final String CHANNEL_TESLAPOWERWALL_MODE = "mode";
+    public static final String CHANNEL_TESLAPOWERWALL_RESERVE = "reserve";
+    public static final String CHANNEL_TESLAPOWERWALL_GRID_INST_POWER = "grid-inst-power";
+    public static final String CHANNEL_TESLAPOWERWALL_GRID_ENERGY_EXPORTED = "grid-energy-exported";
+    public static final String CHANNEL_TESLAPOWERWALL_GRID_ENERGY_IMPORTED = "grid-energy-imported";
+    public static final String CHANNEL_TESLAPOWERWALL_BATTERY_INST_POWER = "battery-inst-power";
+    public static final String CHANNEL_TESLAPOWERWALL_BATTERY_ENERGY_IMPORTED = "battery-energy-imported";
+    public static final String CHANNEL_TESLAPOWERWALL_BATTERY_ENERGY_EXPORTED = "battery-energy-exported";
+    public static final String CHANNEL_TESLAPOWERWALL_HOME_INST_POWER = "home-inst-power";
+    public static final String CHANNEL_TESLAPOWERWALL_HOME_ENERGY_EXPORTED = "home-energy-exported";
+    public static final String CHANNEL_TESLAPOWERWALL_HOME_ENERGY_IMPORTED = "home-energy-imported";
+    public static final String CHANNEL_TESLAPOWERWALL_SOLAR_INST_POWER = "solar-inst-power";
+    public static final String CHANNEL_TESLAPOWERWALL_SOLAR_ENERGY_EXPORTED = "solar-energy-exported";
+    public static final String CHANNEL_TESLAPOWERWALL_SOLAR_ENERGY_IMPORTED = "solar-energy-imported";
+    public static final String CHANNEL_TESLAPOWERWALL_FULL_PACK_ENERGY = "full-pack-energy";
+    public static final String CHANNEL_TESLAPOWERWALL_DEGRADATION = "degradation";
+
+    public static final int TESLA_POWERWALL_CAPACITY = 13500;
+
+    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(TESLAPOWERWALL_THING);
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallCommunicationException.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallCommunicationException.java
new file mode 100644 (file)
index 0000000..d4f1f1a
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Exception for when an unexpected response is received from the TeslaPowerwall controller.
+ *
+ * @author Paul Smedley - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class TeslaPowerwallCommunicationException extends Exception {
+    private static final long serialVersionUID = 529232811860854017L;
+
+    public TeslaPowerwallCommunicationException(String message) {
+        super(message);
+    }
+
+    public TeslaPowerwallCommunicationException(Throwable ex) {
+        super(ex);
+    }
+
+    public TeslaPowerwallCommunicationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallConfiguration.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallConfiguration.java
new file mode 100644 (file)
index 0000000..d7dbf8a
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The TeslaPowerwallConfiguration class contains fields mapping thing configuration parameters.
+ *
+ * @author Paul Smedley - Initial contribution
+ */
+@NonNullByDefault
+public class TeslaPowerwallConfiguration {
+    public String hostname = "";
+    public long refresh = 10;
+    public String email = "";
+    public String password = "";
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallHandler.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallHandler.java
new file mode 100644 (file)
index 0000000..9a25621
--- /dev/null
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.teslapowerwall.internal.api.BatterySOE;
+import org.openhab.binding.teslapowerwall.internal.api.GridStatus;
+import org.openhab.binding.teslapowerwall.internal.api.MeterAggregates;
+import org.openhab.binding.teslapowerwall.internal.api.Operations;
+import org.openhab.binding.teslapowerwall.internal.api.SystemStatus;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.MetricPrefix;
+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;
+
+/**
+ * The {@link TeslaPowerwallHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Paul Smedley - Initial contribution
+ */
+@NonNullByDefault
+public class TeslaPowerwallHandler extends BaseThingHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(TeslaPowerwallHandler.class);
+
+    private @NonNullByDefault({}) TeslaPowerwallConfiguration config;
+    private @NonNullByDefault({}) TeslaPowerwallWebTargets webTargets;
+    private @Nullable ScheduledFuture<?> pollFuture;
+
+    public TeslaPowerwallHandler(Thing thing, HttpClient httpClient) {
+        super(thing);
+        config = getConfigAs(TeslaPowerwallConfiguration.class);
+        webTargets = new TeslaPowerwallWebTargets(config.hostname, httpClient);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        logger.warn("This binding is read only");
+    }
+
+    @Override
+    public void initialize() {
+        config = getConfigAs(TeslaPowerwallConfiguration.class);
+        logger.debug("config.hostname = {}, refresh = {}", config.hostname, config.refresh);
+        if (config.hostname.isBlank() || config.email.isBlank() || config.password.isBlank()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                    "@text/offline.conf-error.missing-config-key");
+            return;
+        } else {
+            updateStatus(ThingStatus.UNKNOWN);
+            schedulePoll();
+        }
+    }
+
+    @Override
+    public void dispose() {
+        super.dispose();
+        stopPoll();
+    }
+
+    private void schedulePoll() {
+        ScheduledFuture<?> pollFuture = this.pollFuture;
+        if (pollFuture != null) {
+            pollFuture.cancel(false);
+        }
+        logger.debug("Scheduling poll for every {} s", config.refresh);
+        this.pollFuture = scheduler.scheduleWithFixedDelay(this::pollStatus, 0, config.refresh, TimeUnit.SECONDS);
+    }
+
+    private void stopPoll() {
+        final Future<?> future = pollFuture;
+        if (future != null) {
+            future.cancel(true);
+            pollFuture = null;
+        }
+    }
+
+    private void pollStatus() {
+        Operations operations = null;
+        BatterySOE batterySOE = null;
+        GridStatus gridStatus = null;
+        SystemStatus systemStatus = null;
+        MeterAggregates meterAggregates = null;
+        try {
+            operations = webTargets.getOperations(config.email, config.password);
+            batterySOE = webTargets.getBatterySOE(config.email, config.password);
+            gridStatus = webTargets.getGridStatus(config.email, config.password);
+            systemStatus = webTargets.getSystemStatus(config.email, config.password);
+            meterAggregates = webTargets.getMeterAggregates(config.email, config.password);
+            updateStatus(ThingStatus.ONLINE);
+        } catch (TeslaPowerwallAuthenticationException e) {
+            logger.debug("Unexpected authentication error connecting to Tesla Powerwall", e);
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
+            return;
+        } catch (TeslaPowerwallCommunicationException e) {
+            logger.debug("Unexpected error connecting to Tesla Powerwall", e);
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+            return;
+        }
+
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_MODE, new StringType(operations.mode));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_RESERVE,
+                new QuantityType<>(((operations.reserve / 0.95) - (5 / 0.95)), Units.PERCENT));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_BATTERY_SOE,
+                new QuantityType<>(((batterySOE.soe / 0.95) - (5 / 0.95)), Units.PERCENT));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_GRID_STATUS,
+                new StringType(gridStatus.gridStatus));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_GRID_SERVICES,
+                (gridStatus.gridServices ? OnOffType.ON : OnOffType.OFF));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_FULL_PACK_ENERGY,
+                new QuantityType<>(systemStatus.fullPackEnergy, Units.WATT_HOUR));
+        if (systemStatus.fullPackEnergy < TeslaPowerwallBindingConstants.TESLA_POWERWALL_CAPACITY) {
+            updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_DEGRADATION,
+                    new QuantityType<>(
+                            (TeslaPowerwallBindingConstants.TESLA_POWERWALL_CAPACITY - systemStatus.fullPackEnergy)
+                                    / TeslaPowerwallBindingConstants.TESLA_POWERWALL_CAPACITY * 100,
+                            Units.PERCENT));
+        } else {
+            updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_DEGRADATION,
+                    new QuantityType<>(0, Units.PERCENT));
+        }
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_GRID_INST_POWER,
+                new QuantityType<>(meterAggregates.gridInstpower, MetricPrefix.KILO(Units.WATT)));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_GRID_ENERGY_EXPORTED,
+                new QuantityType<>(meterAggregates.gridEnergyexported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_GRID_ENERGY_IMPORTED,
+                new QuantityType<>(meterAggregates.gridEnergyimported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_BATTERY_INST_POWER,
+                new QuantityType<>(meterAggregates.batteryInstpower, MetricPrefix.KILO(Units.WATT)));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_BATTERY_ENERGY_EXPORTED,
+                new QuantityType<>(meterAggregates.batteryEnergyexported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_BATTERY_ENERGY_IMPORTED,
+                new QuantityType<>(meterAggregates.batteryEnergyimported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_HOME_INST_POWER,
+                new QuantityType<>(meterAggregates.homeInstpower, MetricPrefix.KILO(Units.WATT)));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_HOME_ENERGY_EXPORTED,
+                new QuantityType<>(meterAggregates.homeEnergyexported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_HOME_ENERGY_IMPORTED,
+                new QuantityType<>(meterAggregates.homeEnergyimported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_SOLAR_INST_POWER,
+                new QuantityType<>(meterAggregates.solarInstpower, MetricPrefix.KILO(Units.WATT)));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_SOLAR_ENERGY_EXPORTED,
+                new QuantityType<>(meterAggregates.solarEnergyexported, Units.KILOWATT_HOUR));
+        updateState(TeslaPowerwallBindingConstants.CHANNEL_TESLAPOWERWALL_SOLAR_ENERGY_IMPORTED,
+                new QuantityType<>(meterAggregates.solarEnergyimported, Units.KILOWATT_HOUR));
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallHandlerFactory.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallHandlerFactory.java
new file mode 100644 (file)
index 0000000..96344db
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.core.io.net.http.HttpClientFactory;
+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.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link TeslaPowerwallHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Paul Smedley - Initial contribution
+ */
+@Component(service = ThingHandlerFactory.class, configurationPid = "binding.tesla-powerwall")
+@NonNullByDefault
+public class TeslaPowerwallHandlerFactory extends BaseThingHandlerFactory {
+
+    private final HttpClient httpClient;
+
+    @Activate
+    public TeslaPowerwallHandlerFactory(@Reference HttpClientFactory httpClientFactory,
+            ComponentContext componentContext) {
+        super.activate(componentContext);
+        this.httpClient = httpClientFactory.getCommonHttpClient();
+    }
+
+    @Override
+    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+        return TeslaPowerwallBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+    }
+
+    @Override
+    protected @Nullable ThingHandler createHandler(Thing thing) {
+        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+        if (thingTypeUID.equals(TeslaPowerwallBindingConstants.TESLAPOWERWALL_THING)) {
+            return new TeslaPowerwallHandler(thing, httpClient);
+        }
+
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallTlsTrustManagerProvider.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallTlsTrustManagerProvider.java
new file mode 100644 (file)
index 0000000..2ae4941
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import javax.net.ssl.X509ExtendedTrustManager;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.net.http.TlsTrustManagerProvider;
+import org.openhab.core.io.net.http.TrustAllTrustManager;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * Provides a TrustManager to allow secure connections to any Tesla Powerwall
+ *
+ * @author Paul Smedley - Initial Contribution
+ */
+@Component
+@NonNullByDefault
+public class TeslaPowerwallTlsTrustManagerProvider implements TlsTrustManagerProvider {
+
+    @Override
+    public String getHostName() {
+        return "powerwall";
+    }
+
+    @Override
+    public X509ExtendedTrustManager getTrustManager() {
+        return TrustAllTrustManager.getInstance();
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallWebTargets.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/TeslaPowerwallWebTargets.java
new file mode 100644 (file)
index 0000000..eba46fa
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.openhab.binding.teslapowerwall.internal.api.BatterySOE;
+import org.openhab.binding.teslapowerwall.internal.api.GridStatus;
+import org.openhab.binding.teslapowerwall.internal.api.MeterAggregates;
+import org.openhab.binding.teslapowerwall.internal.api.Operations;
+import org.openhab.binding.teslapowerwall.internal.api.SystemStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Handles performing the actual HTTP requests for communicating with Tesla Powerwall units.
+ *
+ * @author Paul Smedley - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class TeslaPowerwallWebTargets {
+    private static final int TIMEOUT_MS = 30000;
+
+    private String getBatterySOEUri;
+    private String getGridStatusUri;
+    private String getSystemStatusUri;
+    private String getMeterAggregatesUri;
+    private String getTokenUri;
+    private String getOperationUri;
+    private String token = "";
+    private final Logger logger = LoggerFactory.getLogger(TeslaPowerwallWebTargets.class);
+    private HttpClient httpClient;
+
+    public TeslaPowerwallWebTargets(String hostname, HttpClient httpClient) {
+        this.httpClient = httpClient;
+        String baseUri = "https://" + hostname + "/";
+        getBatterySOEUri = baseUri + "api/system_status/soe";
+        getGridStatusUri = baseUri + "api/system_status/grid_status";
+        getSystemStatusUri = baseUri + "api/system_status";
+        getMeterAggregatesUri = baseUri + "api/meters/aggregates";
+        getTokenUri = baseUri + "api/login/Basic";
+        getOperationUri = baseUri + "api/operation";
+    }
+
+    public BatterySOE getBatterySOE(String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        String response = invoke(getBatterySOEUri, email, password);
+        logger.trace("getBatterySOE response = {}", response);
+        return BatterySOE.parse(response);
+    }
+
+    public GridStatus getGridStatus(String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        String response = invoke(getGridStatusUri, email, password);
+        logger.trace("getGridStatus response = {}", response);
+        return GridStatus.parse(response);
+    }
+
+    public SystemStatus getSystemStatus(String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        String response = invoke(getSystemStatusUri, email, password);
+        logger.trace("getSystemStatus response = {}", response);
+        return SystemStatus.parse(response);
+    }
+
+    public MeterAggregates getMeterAggregates(String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        String response = invoke(getMeterAggregatesUri, email, password);
+        logger.trace("getMeterAggregates response = {}", response);
+        return MeterAggregates.parse(response);
+    }
+
+    public Operations getOperations(String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        String response = invoke(getOperationUri, email, password);
+        logger.trace("getOperations response = {}", response);
+        return Operations.parse(response);
+    }
+
+    public String getToken(String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        JsonObject jsonObject = new JsonObject();
+        jsonObject.addProperty("username", "customer");
+        jsonObject.addProperty("password", password);
+        jsonObject.addProperty("email", email);
+        jsonObject.addProperty("force_sm_off", false);
+        logger.debug("logonjson = {}", jsonObject.toString());
+        String response = invoke(getTokenUri, HttpMethod.POST, "Content-Type", "application/json",
+                jsonObject.toString());
+        JsonObject jsonResponse = JsonParser.parseString(response).getAsJsonObject();
+        String token = jsonResponse.get("token").getAsString();
+        logger.debug("Token: {}", token);
+        return token;
+    }
+
+    private String invoke(String uri, String email, String password)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        if (token.isEmpty()) {
+            token = getToken(email, password);
+        }
+        return invoke(uri, HttpMethod.GET, "Authorization", "Bearer " + token, "");
+    }
+
+    private String invoke(String uri, HttpMethod method, String headerKey, String headerValue, String params)
+            throws TeslaPowerwallCommunicationException, TeslaPowerwallAuthenticationException {
+        logger.debug("Calling url: {}", uri);
+        int status = 0;
+        String jsonResponse = "";
+        synchronized (this) {
+            try {
+                Request request = httpClient.newRequest(uri).method(method).header(headerKey, headerValue)
+                        .timeout(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+                        .content(new StringContentProvider(params), "application/json");
+                if (logger.isTraceEnabled()) {
+                    logger.trace("{} request for {}", method, uri);
+                }
+                ContentResponse response = request.send();
+                status = response.getStatus();
+                jsonResponse = response.getContentAsString();
+                if (!jsonResponse.isEmpty()) {
+                    logger.trace("JSON response: '{}'", jsonResponse);
+                }
+                if (status == HttpStatus.UNAUTHORIZED_401) {
+                    throw new TeslaPowerwallAuthenticationException("Unauthorized");
+                }
+                if (!HttpStatus.isSuccess(status)) {
+                    throw new TeslaPowerwallCommunicationException(
+                            String.format("Tesla Powerwall returned error <%d> while invoking %s", status, uri));
+                }
+            } catch (TimeoutException | ExecutionException | InterruptedException ex) {
+                throw new TeslaPowerwallCommunicationException(String.format("{}", ex.getLocalizedMessage(), ex));
+            }
+        }
+
+        return jsonResponse;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/BatterySOE.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/BatterySOE.java
new file mode 100644 (file)
index 0000000..dc59719
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Class for holding the set of parameters used to read the battery soe.
+ *
+ * @author Paul Smedley - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class BatterySOE {
+    public double soe;
+
+    private BatterySOE() {
+    }
+
+    public static BatterySOE parse(String response) {
+        /* parse json string */
+        JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
+        BatterySOE info = new BatterySOE();
+        info.soe = jsonObject.get("percentage").getAsDouble();
+        return info;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/GridStatus.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/GridStatus.java
new file mode 100644 (file)
index 0000000..9b36719
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Class for holding the set of parameters used to read the battery soe.
+ *
+ * @author Paul Smedley - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class GridStatus {
+
+    public String gridStatus = "";
+    public Boolean gridServices = false;
+
+    private GridStatus() {
+    }
+
+    public static GridStatus parse(String response) {
+        /* parse json string */
+        JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
+        GridStatus info = new GridStatus();
+        info.gridStatus = jsonObject.get("grid_status").getAsString();
+        info.gridServices = jsonObject.get("grid_services_active").getAsString().equalsIgnoreCase("true");
+        return info;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/MeterAggregates.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/MeterAggregates.java
new file mode 100644 (file)
index 0000000..07af2bf
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Class for holding the set of parameters used to read the battery soe.
+ *
+ * @author Paul Smedley - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class MeterAggregates {
+    public double gridInstpower;
+    public double batteryInstpower;
+    public double homeInstpower;
+    public double solarInstpower;
+    public double gridEnergyexported;
+    public double batteryEnergyexported;
+    public double homeEnergyexported;
+    public double solarEnergyexported;
+    public double gridEnergyimported;
+    public double batteryEnergyimported;
+    public double homeEnergyimported;
+    public double solarEnergyimported;
+
+    private MeterAggregates() {
+    }
+
+    public static MeterAggregates parse(String response) {
+        /* parse json string */
+        JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
+        MeterAggregates info = new MeterAggregates();
+
+        JsonObject sitejson = jsonObject.get("site").getAsJsonObject();
+        info.gridInstpower = sitejson.get("instant_power").getAsDouble() / 1000;
+        info.gridEnergyexported = sitejson.get("energy_exported").getAsDouble() / 1000;
+        info.gridEnergyimported = sitejson.get("energy_imported").getAsDouble() / 1000;
+
+        JsonObject batteryjson = jsonObject.get("battery").getAsJsonObject();
+        info.batteryInstpower = batteryjson.get("instant_power").getAsDouble() / 1000;
+        info.batteryEnergyexported = batteryjson.get("energy_exported").getAsDouble() / 1000;
+        info.batteryEnergyimported = batteryjson.get("energy_imported").getAsDouble() / 1000;
+
+        JsonObject loadjson = jsonObject.get("load").getAsJsonObject();
+        info.homeInstpower = loadjson.get("instant_power").getAsDouble() / 1000;
+        info.homeEnergyexported = loadjson.get("energy_exported").getAsDouble() / 1000;
+        info.homeEnergyimported = loadjson.get("energy_imported").getAsDouble() / 1000;
+
+        JsonObject solarjson = jsonObject.get("solar").getAsJsonObject();
+        info.solarInstpower = solarjson.get("instant_power").getAsDouble() / 1000;
+        info.solarEnergyexported = solarjson.get("energy_exported").getAsDouble() / 1000;
+        info.solarEnergyimported = solarjson.get("energy_imported").getAsDouble() / 1000;
+
+        return info;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/Operations.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/Operations.java
new file mode 100644 (file)
index 0000000..7d10171
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Class for holding the set of parameters used to read the battery mode/reserver.
+ *
+ * @author Paul Smedley - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class Operations {
+
+    public String mode = "";
+    public double reserve;
+
+    private Operations() {
+    }
+
+    public static Operations parse(String response) {
+        /* parse json string */
+        JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
+        Operations info = new Operations();
+        info.mode = jsonObject.get("real_mode").getAsString();
+        info.reserve = jsonObject.get("backup_reserve_percent").getAsDouble();
+        return info;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/SystemStatus.java b/bundles/org.openhab.binding.teslapowerwall/src/main/java/org/openhab/binding/teslapowerwall/internal/api/SystemStatus.java
new file mode 100644 (file)
index 0000000..90ec3be
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.teslapowerwall.internal.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Class for holding the set of parameters used to read the battery soe.
+ *
+ * @author Paul Smedley - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class SystemStatus {
+    public double fullPackEnergy;
+
+    private SystemStatus() {
+    }
+
+    public static SystemStatus parse(String response) {
+        /* parse json string */
+        JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
+        SystemStatus info = new SystemStatus();
+        info.fullPackEnergy = jsonObject.get("nominal_full_pack_energy").getAsDouble();
+        return info;
+    }
+}
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644 (file)
index 0000000..38be32c
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon:addon id="teslapowerwall" 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>Tesla Powerwall Binding</name>
+       <description>This is the binding for Tesla Powerwall.</description>
+       <connection>local</connection>
+
+</addon:addon>
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/i18n/teslapowerwall.properties b/bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/i18n/teslapowerwall.properties
new file mode 100644 (file)
index 0000000..564a967
--- /dev/null
@@ -0,0 +1,181 @@
+# add-on
+
+addon.teslapowerwall.name = Tesla Powerwall Binding
+addon.teslapowerwall.description = This is the binding for Tesla Powerwall.
+
+# thing types
+
+thing-type.teslapowerwall.tesla-powerwall.label = Tesla Powerwall
+thing-type.teslapowerwall.tesla-powerwall.description = Tesla Powerwall
+
+# thing types config
+
+thing-type.config.teslapowerwall.tesla-powerwall.email.label = Email
+thing-type.config.teslapowerwall.tesla-powerwall.email.description = Local Powerwall account email
+thing-type.config.teslapowerwall.tesla-powerwall.hostname.label = Hostname/IP Address
+thing-type.config.teslapowerwall.tesla-powerwall.hostname.description = The host name or IP address of the Tesla Powerwall.
+thing-type.config.teslapowerwall.tesla-powerwall.password.label = Password
+thing-type.config.teslapowerwall.tesla-powerwall.password.description = Local Powerwall account password
+thing-type.config.teslapowerwall.tesla-powerwall.refresh.label = Refresh Interval
+thing-type.config.teslapowerwall.tesla-powerwall.refresh.description = Specifies the refresh interval in seconds.
+
+# channel types
+
+channel-type.teslapowerwall.battery-energy-exported.label = Battery Energy Exported
+channel-type.teslapowerwall.battery-energy-exported.description = Total battery energy exported
+channel-type.teslapowerwall.battery-energy-imported.label = Battery Energy Imported
+channel-type.teslapowerwall.battery-energy-imported.description = Total battery energy imported
+channel-type.teslapowerwall.battery-inst-power.label = Instant Battery Power
+channel-type.teslapowerwall.battery-inst-power.description = Instantaneous battery power supply
+channel-type.teslapowerwall.battery-soe.label = Battery SOE
+channel-type.teslapowerwall.battery-soe.description = Current battery state of charge
+channel-type.teslapowerwall.degradation.label = Battery Degradation
+channel-type.teslapowerwall.degradation.description = Current battery degradation %, based on 13.5kW full capacity
+channel-type.teslapowerwall.full-pack-energy.label = Battery Full Pack Energy
+channel-type.teslapowerwall.full-pack-energy.description = Battery full pack energy
+channel-type.teslapowerwall.grid-energy-exported.label = Grid Energy Exported
+channel-type.teslapowerwall.grid-energy-exported.description = Total grid energy exported
+channel-type.teslapowerwall.grid-energy-imported.label = Grid Energy Imported
+channel-type.teslapowerwall.grid-energy-imported.description = Total grid energy imported
+channel-type.teslapowerwall.grid-inst-power.label = Instant Grid Power
+channel-type.teslapowerwall.grid-inst-power.description = Instantaneous grid power supply
+channel-type.teslapowerwall.grid-services.label = Grid Services
+channel-type.teslapowerwall.grid-services.description = Grid services activation status
+channel-type.teslapowerwall.grid-status.label = Grid Status
+channel-type.teslapowerwall.grid-status.description = Current status of the power grid
+channel-type.teslapowerwall.home-energy-exported.label = Home Energy Exported
+channel-type.teslapowerwall.home-energy-exported.description = Total home energy exported
+channel-type.teslapowerwall.home-energy-imported.label = Home Energy Imported
+channel-type.teslapowerwall.home-energy-imported.description = Total home energy emported
+channel-type.teslapowerwall.home-inst-power.label = Instant Home Power
+channel-type.teslapowerwall.home-inst-power.description = Instantaneous home power supply
+channel-type.teslapowerwall.mode.label = Operating Mode
+channel-type.teslapowerwall.mode.description = Current operating mode
+channel-type.teslapowerwall.reserve.label = Battery Reserve
+channel-type.teslapowerwall.reserve.description = Current battery reserve %
+channel-type.teslapowerwall.solar-energy-exported.label = Solar Energy Exported
+channel-type.teslapowerwall.solar-energy-exported.description = Total solar energy exported
+channel-type.teslapowerwall.solar-energy-imported.label = Solar Energy Imported
+channel-type.teslapowerwall.solar-energy-imported.description = Total solar energy imported
+channel-type.teslapowerwall.solar-inst-power.label = Instant Solar Power
+channel-type.teslapowerwall.solar-inst-power.description = Instantaneous solar power supply
+
+# channel types
+
+channel-type.teslapowerwall.tesla-powerwall-battery-energyexported.label = Battery Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-battery-energyexported.description = Total Battery Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-battery-energyimported.label = Battery Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-battery-energyimported.description = Total Battery Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-battery-instpower.label = Instant Battery Power
+channel-type.teslapowerwall.tesla-powerwall-battery-instpower.description = Instantaneous Battery Power Supply
+channel-type.teslapowerwall.tesla-powerwall-batterysoe.label = Battery SOE
+channel-type.teslapowerwall.tesla-powerwall-batterysoe.description = Current battery state of charge
+channel-type.teslapowerwall.tesla-powerwall-degradation.label = Battery Degradation
+channel-type.teslapowerwall.tesla-powerwall-degradation.description = Current battery degradation %, based on 13.5kW full capacity
+channel-type.teslapowerwall.tesla-powerwall-full-pack-energy.label = Battery Full Pack Energy
+channel-type.teslapowerwall.tesla-powerwall-full-pack-energy.description = Battery Full Pack Energy
+channel-type.teslapowerwall.tesla-powerwall-grid-energyexported.label = Grid Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-grid-energyexported.description = Total Grid Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-grid-energyimported.label = Grid Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-grid-energyimported.description = Total Grid Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-grid-instpower.label = Instant Grid Power
+channel-type.teslapowerwall.tesla-powerwall-grid-instpower.description = Instantaneous Grid Power Supply
+channel-type.teslapowerwall.tesla-powerwall-gridservices.label = Grid Services
+channel-type.teslapowerwall.tesla-powerwall-gridservices.description = Grid Services Activation Status
+channel-type.teslapowerwall.tesla-powerwall-gridstatus.label = Grid Status
+channel-type.teslapowerwall.tesla-powerwall-gridstatus.description = Current status of the Power Grid
+channel-type.teslapowerwall.tesla-powerwall-home-energyexported.label = Home Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-home-energyexported.description = Total Home Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-home-energyimported.label = Home Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-home-energyimported.description = Total Home Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-home-instpower.label = Instant Home Power
+channel-type.teslapowerwall.tesla-powerwall-home-instpower.description = Instantaneous Home Power Supply
+channel-type.teslapowerwall.tesla-powerwall-mode.label = Operating Mode
+channel-type.teslapowerwall.tesla-powerwall-mode.description = Current operating mode
+channel-type.teslapowerwall.tesla-powerwall-reserve.label = Battery Reserve
+channel-type.teslapowerwall.tesla-powerwall-reserve.description = Current battery reserve %
+channel-type.teslapowerwall.tesla-powerwall-solar-energyexported.label = Solar Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-solar-energyexported.description = Total Solar Energy Exported
+channel-type.teslapowerwall.tesla-powerwall-solar-energyimported.label = Solar Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-solar-energyimported.description = Total Solar Energy Imported
+channel-type.teslapowerwall.tesla-powerwall-solar-instpower.label = Instant Solar Power
+channel-type.teslapowerwall.tesla-powerwall-solar-instpower.description = Instantaneous Solar Power Supply
+
+# thing types config
+
+thing-type.config.tesla-powerwall.tesla-powerwall.email.label = Local Powerwall Login Email
+thing-type.config.tesla-powerwall.tesla-powerwall.email.description = Local Powerwall Account Email
+thing-type.config.tesla-powerwall.tesla-powerwall.hostname.label = Hostname/IP Address
+thing-type.config.tesla-powerwall.tesla-powerwall.hostname.description = The host name or IP address of the Tesla Powerwall.
+thing-type.config.tesla-powerwall.tesla-powerwall.password.label = Local Powerwall Login Password
+thing-type.config.tesla-powerwall.tesla-powerwall.password.description = Local Powerwall Account Password
+thing-type.config.tesla-powerwall.tesla-powerwall.refresh.label = Refresh Interval
+thing-type.config.tesla-powerwall.tesla-powerwall.refresh.description = Specifies the refresh interval in seconds.
+
+# channel types
+
+channel-type.teslapowerwall.teslapowerwall-battery-energyexported.label = Battery Energy Exported
+channel-type.teslapowerwall.teslapowerwall-battery-energyexported.description = Total Battery Energy Exported
+channel-type.teslapowerwall.teslapowerwall-battery-energyimported.label = Battery Energy Imported
+channel-type.teslapowerwall.teslapowerwall-battery-energyimported.description = Total Battery Energy Imported
+channel-type.teslapowerwall.teslapowerwall-degradation.label = Battery Degradation
+channel-type.teslapowerwall.teslapowerwall-degradation.description = Current battery degradation %, based on 13.5kW full capacity
+channel-type.teslapowerwall.teslapowerwall-full-pack-energy.label = Battery Full Pack Energy
+channel-type.teslapowerwall.teslapowerwall-full-pack-energy.description = Battery Full Pack Energy
+channel-type.teslapowerwall.teslapowerwall-grid-energyexported.label = Grid Energy Exported
+channel-type.teslapowerwall.teslapowerwall-grid-energyexported.description = Total Grid Energy Exported
+channel-type.teslapowerwall.teslapowerwall-grid-energyimported.label = Grid Energy Imported
+channel-type.teslapowerwall.teslapowerwall-grid-energyimported.description = Total Grid Energy Imported
+channel-type.teslapowerwall.teslapowerwall-home-energyexported.label = Home Energy Exported
+channel-type.teslapowerwall.teslapowerwall-home-energyexported.description = Total Home Energy Exported
+channel-type.teslapowerwall.teslapowerwall-home-energyimported.label = Home Energy Imported
+channel-type.teslapowerwall.teslapowerwall-home-energyimported.description = Total Home Energy Imported
+channel-type.teslapowerwall.teslapowerwall-solar-energyexported.label = Solar Energy Exported
+channel-type.teslapowerwall.teslapowerwall-solar-energyexported.description = Total Solar Energy Exported
+channel-type.teslapowerwall.teslapowerwall-solar-energyimported.label = Solar Energy Imported
+channel-type.teslapowerwall.teslapowerwall-solar-energyimported.description = Total Solar Energy Imported
+channel-type.teslapowerwall.teslapowerwall-solar-instpower.label = Instant Solar Power
+channel-type.teslapowerwall.teslapowerwall-solar-instpower.description = Instantaneous Solar Power Supply
+
+# channel types
+
+channel-type.teslapowerwall.teslapowerwall-battery_energyexported.label = Battery Energy Exported
+channel-type.teslapowerwall.teslapowerwall-battery_energyexported.description = Total Battery Energy Exported
+channel-type.teslapowerwall.teslapowerwall-battery_energyimported.label = Battery Energy Imported
+channel-type.teslapowerwall.teslapowerwall-battery_energyimported.description = Total Battery Energy Imported
+channel-type.teslapowerwall.teslapowerwall-battery_instpower.label = Instant Battery Power
+channel-type.teslapowerwall.teslapowerwall-battery_instpower.description = Instantaneous Battery Power Supply
+channel-type.teslapowerwall.teslapowerwall-batterysoe.label = Battery SOE
+channel-type.teslapowerwall.teslapowerwall-batterysoe.description = Current battery state of charge
+channel-type.teslapowerwall.teslapowerwall-full_pack_energy.label = Battery Full Pack Energy
+channel-type.teslapowerwall.teslapowerwall-full_pack_energy.description = Battery Full Pack Energy
+channel-type.teslapowerwall.teslapowerwall-grid_energyexported.label = Grid Energy Exported
+channel-type.teslapowerwall.teslapowerwall-grid_energyexported.description = Total Grid Energy Exported
+channel-type.teslapowerwall.teslapowerwall-grid_energyimported.label = Grid Energy Imported
+channel-type.teslapowerwall.teslapowerwall-grid_energyimported.description = Total Grid Energy Imported
+channel-type.teslapowerwall.teslapowerwall-grid_instpower.label = Instant Grid Power
+channel-type.teslapowerwall.teslapowerwall-grid_instpower.description = Instantaneous Grid Power Supply
+channel-type.teslapowerwall.teslapowerwall-gridservices.label = Grid Services
+channel-type.teslapowerwall.teslapowerwall-gridservices.description = Grid Services Activation Status
+channel-type.teslapowerwall.teslapowerwall-gridstatus.label = Grid Status
+channel-type.teslapowerwall.teslapowerwall-gridstatus.description = Current status of the Power Grid
+channel-type.teslapowerwall.teslapowerwall-home_energyexported.label = Home Energy Exported
+channel-type.teslapowerwall.teslapowerwall-home_energyexported.description = Total Home Energy Exported
+channel-type.teslapowerwall.teslapowerwall-home_energyimported.label = Home Energy Imported
+channel-type.teslapowerwall.teslapowerwall-home_energyimported.description = Total Home Energy Imported
+channel-type.teslapowerwall.teslapowerwall-home_instpower.label = Instant Home Power
+channel-type.teslapowerwall.teslapowerwall-home_instpower.description = Instantaneous Home Power Supply
+channel-type.teslapowerwall.teslapowerwall-mode.label = Operating Mode
+channel-type.teslapowerwall.teslapowerwall-mode.description = Current operating mode
+channel-type.teslapowerwall.teslapowerwall-reserve.label = Battery Reserve
+channel-type.teslapowerwall.teslapowerwall-reserve.description = Current battery reserve %
+channel-type.teslapowerwall.teslapowerwall-solar_energyexported.label = Solar Energy Exported
+channel-type.teslapowerwall.teslapowerwall-solar_energyexported.description = Total Solar Energy Exported
+channel-type.teslapowerwall.teslapowerwall-solar_energyimported.label = Solar Energy Imported
+channel-type.teslapowerwall.teslapowerwall-solar_energyimported.description = Total Solar Energy Imported
+channel-type.teslapowerwall.teslapowerwall-solar_instpower.label = Instant Solar Power
+channel-type.teslapowerwall.teslapowerwall-solar_instpower.description = Instantaneous Solar Power Supply
+
+# thing status descriptions
+
+offline.conf-error.missing-config-key = Mandatory configuration items must be set
diff --git a/bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.teslapowerwall/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644 (file)
index 0000000..be3c006
--- /dev/null
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="teslapowerwall"
+       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="tesla-powerwall">
+               <label>Tesla Powerwall</label>
+               <description>Tesla Powerwall</description>
+
+               <channels>
+                       <channel id="grid-status" typeId="grid-status"/>
+                       <channel id="grid-services" typeId="grid-services"/>
+                       <channel id="battery-soe" typeId="battery-soe"/>
+                       <channel id="mode" typeId="mode"/>
+                       <channel id="reserve" typeId="reserve"/>
+                       <channel id="grid-inst-power" typeId="grid-inst-power"/>
+                       <channel id="battery-inst-power" typeId="battery-inst-power"/>
+                       <channel id="home-inst-power" typeId="home-inst-power"/>
+                       <channel id="solar-inst-power" typeId="solar-inst-power"/>
+                       <channel id="grid-energy-exported" typeId="grid-energy-exported"/>
+                       <channel id="battery-energy-exported" typeId="battery-energy-exported"/>
+                       <channel id="home-energy-exported" typeId="home-energy-exported"/>
+                       <channel id="solar-energy-exported" typeId="solar-energy-exported"/>
+                       <channel id="grid-energy-imported" typeId="grid-energy-imported"/>
+                       <channel id="battery-energy-imported" typeId="battery-energy-imported"/>
+                       <channel id="home-energy-imported" typeId="home-energy-imported"/>
+                       <channel id="solar-energy-imported" typeId="solar-energy-imported"/>
+                       <channel id="full-pack-energy" typeId="full-pack-energy"/>
+                       <channel id="degradation" typeId="degradation"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="hostname" type="text" required="true">
+                               <label>Hostname/IP Address</label>
+                               <description>The host name or IP address of the Tesla Powerwall.</description>
+                       </parameter>
+                       <parameter name="email" type="text" required="true">
+                               <label>Email</label>
+                               <description>Local Powerwall account email</description>
+                       </parameter>
+                       <parameter name="password" type="text" required="true">
+                               <label>Password</label>
+                               <description>Local Powerwall account password</description>
+                               <context>password</context>
+                       </parameter>
+                       <parameter name="refresh" type="integer" required="false">
+                               <label>Refresh Interval</label>
+                               <description>Specifies the refresh interval in seconds.</description>
+                               <default>10</default>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+
+       <channel-type id="grid-status">
+               <item-type>String</item-type>
+               <label>Grid Status</label>
+               <description>Current status of the power grid</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="grid-services">
+               <item-type>Switch</item-type>
+               <label>Grid Services</label>
+               <description>Grid services activation status</description>
+               <state readOnly="true"/>
+       </channel-type>
+       <channel-type id="battery-soe">
+               <item-type unitHint="%">Number:Dimensionless</item-type>
+               <label>Battery SOE</label>
+               <description>Current battery state of charge</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="mode">
+               <item-type>String</item-type>
+               <label>Operating Mode</label>
+               <description>Current operating mode</description>
+       </channel-type>
+       <channel-type id="reserve">
+               <item-type unitHint="%">Number:Dimensionless</item-type>
+               <label>Battery Reserve</label>
+               <description>Current battery reserve %</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="grid-inst-power">
+               <item-type>Number:Power</item-type>
+               <label>Instant Grid Power</label>
+               <description>Instantaneous grid power supply</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="battery-inst-power">
+               <item-type>Number:Power</item-type>
+               <label>Instant Battery Power</label>
+               <description>Instantaneous battery power supply</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="home-inst-power">
+               <item-type>Number:Power</item-type>
+               <label>Instant Home Power</label>
+               <description>Instantaneous home power supply</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="solar-inst-power">
+               <item-type>Number:Power</item-type>
+               <label>Instant Solar Power</label>
+               <description>Instantaneous solar power supply</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="grid-energy-exported">
+               <item-type>Number:Energy</item-type>
+               <label>Grid Energy Exported</label>
+               <description>Total grid energy exported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="battery-energy-exported">
+               <item-type>Number:Energy</item-type>
+               <label>Battery Energy Exported</label>
+               <description>Total battery energy exported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="home-energy-exported">
+               <item-type>Number:Energy</item-type>
+               <label>Home Energy Exported</label>
+               <description>Total home energy exported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="solar-energy-exported">
+               <item-type>Number:Energy</item-type>
+               <label>Solar Energy Exported</label>
+               <description>Total solar energy exported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="grid-energy-imported">
+               <item-type>Number:Energy</item-type>
+               <label>Grid Energy Imported</label>
+               <description>Total grid energy imported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="battery-energy-imported">
+               <item-type>Number:Energy</item-type>
+               <label>Battery Energy Imported</label>
+               <description>Total battery energy imported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="home-energy-imported">
+               <item-type>Number:Energy</item-type>
+               <label>Home Energy Imported</label>
+               <description>Total home energy emported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="solar-energy-imported">
+               <item-type>Number:Energy</item-type>
+               <label>Solar Energy Imported</label>
+               <description>Total solar energy imported</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="full-pack-energy">
+               <item-type>Number:Energy</item-type>
+               <label>Battery Full Pack Energy</label>
+               <description>Battery full pack energy</description>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="degradation">
+               <item-type unitHint="%">Number:Dimensionless</item-type>
+               <label>Battery Degradation</label>
+               <description>Current battery degradation %, based on 13.5kW full capacity</description>
+               <state pattern="%.1f %unit%"/>
+       </channel-type>
+
+</thing:thing-descriptions>
index 8eb9e1456cd3ce3cb9e16548c4b1f40646fbc592..b51354c2c24d87076d9ab1d56b0c6603407d44a0 100644 (file)
     <module>org.openhab.binding.teleinfo</module>
     <module>org.openhab.binding.tellstick</module>
     <module>org.openhab.binding.tesla</module>
+    <module>org.openhab.binding.teslapowerwall</module>
     <module>org.openhab.binding.tibber</module>
     <module>org.openhab.binding.tivo</module>
     <module>org.openhab.binding.touchwand</module>