Tesla Powerwall 2 Integration

Hello,
I’ve got my Tesla powerwall 2 recently installed and tried to integrate it into openhab. I’d like to share my solution to integrated the PW2 into openhab by using the api documented here https://github.com/vloschiavo/powerwall2

It seems there is an issues regardings the ssl certifcates on the Powerwall, so i used the exec binding to issue CURL commands to the powerwall gateway with the -k option

 -k, --insecure      Allow connections to SSL sites without certs (H)

The openhab config looks as follow

.thing

Thing exec:command:pw_aggregates [command="curl https://192.168.X.XXX/api/meters/aggregates -k", interval=1, timeout=5]
Thing exec:command:pw_soe [command="curl https://192.168.X.XXX/api/system_status/soe -k", interval=15, timeout=5]

.items

String PWAggregates_JSON "[%s]" {channel="exec:command:pw_aggregates:output"} // liest JSON String, wird in Rule in folgende Items geschrieben

Number:Power PW_InstPower_Site "InstPower Site [%.1f W]" (gPowerwall, gInfluxDefault)
Number:Power PW_InstPower_Battery "InstPower Battery [%.1f W]" (gPowerwall, gInfluxDefault)
Number:Power PW_InstPower_Load "InstPower Load [%.1f W]" (gPowerwall, gInfluxDefault)
Number:Power PW_InstPower_Solar "InstPower Solar [%.1f W]" (gPowerwall, gInfluxDefault)

Number:Energy PW_EnergyExported_Site "EnergyExported Site [%.1f Wh]" (gPowerwall, gInfluxDefault)
Number:Energy PW_EnergyImported_Site "EnergyImported Site [%.1f Wh]" (gPowerwall, gInfluxDefault)     
Number:Energy PW_EnergyExported_Battery "EnergyExported Battery [%.1f Wh]" (gPowerwall, gInfluxDefault)
Number:Energy PW_EnergyImported_Battery "EnergyImported Battery [%.1f Wh]" (gPowerwall, gInfluxDefault)     
Number:Energy PW_EnergyExported_Load "EnergyExported Load [%.1f Wh]" (gPowerwall, gInfluxDefault)
Number:Energy PW_EnergyImported_Load "EnergyImported Load [%.1f Wh]" (gPowerwall, gInfluxDefault)     
Number:Energy PW_EnergyExported_Solar "EnergyExported Solar [%.1f Wh]" (gPowerwall, gInfluxDefault)
Number:Energy PW_EnergyImported_Solar "EnergyImported Solar [%.1f Wh]" (gPowerwall, gInfluxDefault)     

String PWSoe_JSON "[%s]" {channel="exec:command:pw_soe:output"} // liest JSON String, wird in Rule in folgende Items geschrieben
Number PW_StateofCharge "State of Charge [%.1f]" (gPowerwall, gInfluxDefault)  

.rules

// Powerwall JSON Conversion Aggregates
rule "Convert JSON to Item Type Number"
  when
    Item PWAggregates_JSON changed
 then
    // use the transformation service to retrieve the value
    //val newValue = transform("JSONPATH", "$.site.instant_power", PWAggregates_JSON.state.toString)
    PW_InstPower_Site.postUpdate( transform("JSONPATH", "$.site.instant_power", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_InstPower_Battery.postUpdate( transform("JSONPATH", "$.battery.instant_power", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_InstPower_Load.postUpdate( transform("JSONPATH", "$.load.instant_power", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_InstPower_Solar.postUpdate( transform("JSONPATH", "$.solar.instant_power", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyExported_Site.postUpdate( transform("JSONPATH", "$.site.energy_exported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyImported_Site.postUpdate( transform("JSONPATH", "$.site.energy_imported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyExported_Battery.postUpdate( transform("JSONPATH", "$.battery.energy_exported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyImported_Battery .postUpdate( transform("JSONPATH", "$.battery.energy_imported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyExported_Load.postUpdate( transform("JSONPATH", "$.load.energy_exported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyImported_Load.postUpdate( transform("JSONPATH", "$.load.energy_imported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyExported_Solar.postUpdate( transform("JSONPATH", "$.solar.energy_exported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item
    PW_EnergyImported_Solar.postUpdate( transform("JSONPATH", "$.solar.energy_imported", PWAggregates_JSON.state.toString) ) // post the new value to the Number Item

 end

 // Powerwall JSON Conversion Soe
rule "Convert JSON to Item Type Number"
  when
    Item PWSoe_JSON changed
 then
    // use the transformation service to retrieve the value
    PW_StateofCharge.postUpdate( transform("JSONPATH", "$.percentage", PWSoe_JSON.state.toString) ) // post the new value to the Number Item
 end

any suggestions for improvements or a more efficient solution by using the http binding are highly welcome

6 Likes

how i configure the sitemap?

Hi,

I’m running Openhab 2 on QNAP and I have the same issue with http binding and Testa Powerwall2, this is the error i got:

2019-02-06 23:25:50.178 [ERROR] [org.openhab.io.net.http.HttpUtil    ] - Fatal transport error: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: signature check failed

Typical issue when into the Java keystore (cacerts) is missing the root certificate, I tried to add to this cacerts the certificates exposed by Powerwall (end-certificate and ESET certificate) without success.

Also tried to add this line to “setenv”:

-Djavax.net.ssl.trustStore=/share/CACHEDEV1_DATA/.qpkg/JRE/jre/lib/security/cacerts

No way to solve the issue at the moment, if someone have some suggestion, please share!

Thanks for posting this, helped me to get openHAB to read my PW2 data.

Is there any way to suppress the extra event log activity? The PWAggregates_JSON ItemStateChangedEvents pretty much floods the log.

I’ve tried adding the following to org.ops4j.pax.logging.cfg and it works for the items with names starting with PW_Inst & PW_Energy but not for the JSON data containing items - PWAggregates_JSON & PWSoe_JSON… special characters in the JSON returned data causing issues maybe?

log4j2.appender.event.filter.PWFilter.type = RegexFilter
log4j2.appender.event.filter.PWFilter.regex = .*(PWAggregates_JSON|PWSoe_JSON|PW_Inst|PW_Energy).*
log4j2.appender.event.filter.PWFilter.onMatch = DENY
log4j2.appender.event.filter.PWFilter.onMisMatch = ACCEPT

I simplified this a little.

First, I downloaded the SSL root certificate for my PW2 using openssl:

openssl s_client -showcerts -connect 192.168.1.30:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem

I then imported it into Java’s cacert file:

sudo keytool -import -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias PW2 -import -file mycertfile.pem

I then restarted openhab to make sure the new cacert was loaded.

I added the following to services/http.cfg:

http:pw2grid.url=https://192.168.1.30/api/system_status/grid_status
http:pw2grid.updateInterval=60000
http:pw2soe.url=https://192.168.1.30/api/system_status/soe
http:pw2soe.updateInterval=60000
http:pw2aggregates.url=https://192.168.1.30/api/meters/aggregates
http:pw2aggregates.updateInterval=60000

And the following to items:

String Grid_Status "PW2 Grid Status" {http="<[pw2grid:10000:JSONPATH($.grid_status)]" }
Number Battery_SOE "PW2 Battery State" {http="<[pw2soe:10000:JSONPATH(.percentage)]" }
Number:Power PW_InstPower_Site "InstPower Site [%.1f W]" {http="<[pw2aggregates:10000:JSONPATH($.site.instant_power)]" }
Number:Power PW_InstPower_Battery "InstPower Battery [%.1f W]" {http="<[pw2aggregates:10000:JSONPATH($.battery.instant_power)]" }
Number:Power PW_InstPower_Load "InstPower Load [%.1f W]" {http="<[pw2aggregates:10000:JSONPATH($.load.instant_power)]" }
Number:Power PW_InstPower_Solar "InstPower Solar [%.1f W]" {http="<[pw2aggregates:10000:JSONPATH($.solar.instant_power)]" }

Number:Energy PW_EnergyExported_Site "EnergyExported Site [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.site.energy_exported)]" }
Number:Energy PW_EnergyImported_Site "EnergyImported Site [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.site.energy_imported)]" }
Number:Energy PW_EnergyExported_Battery "EnergyExported Battery [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.battery.energy_exported)]" }
Number:Energy PW_EnergyImported_Battery "EnergyImported Battery [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.battery.energy_imported)]" }
Number:Energy PW_EnergyExported_Load "EnergyExported Load [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.load.energy_exported)]" }
Number:Energy PW_EnergyImported_Load "EnergyImported Load [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.load.energy_imported)]" }
Number:Energy PW_EnergyExported_Solar "EnergyExported Solar [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.solar.energy_exported)]" }
Number:Energy PW_EnergyImported_Solar "EnergyImported Solar [%.1f Wh]" {http="<[pw2aggregates:10000:JSONPATH($.solar.energy_imported)]" }

…and voila :slight_smile:

Cheers,

Paul

I’m thinking about writing a binding for the PW2 - it would need the certificate import per my previous post. This would avoid the need to manually edit http.cfg/items files/sitemaps.

I likely wouldn’t bother supporting autodiscovery given it will fail to communicate anyway without importing the java ssl certificate, so some level of manual configuration will always be required.

Is there much interest in this?

2 Likes

Nice work Paul, thanks for sharing.

A binding sounds interesting but your method above is pretty slick anyway - think I’ll implement it this weekend.

Thanks! the above works OK, but it’s a PITA to initially configure, a binding would be ‘neater’ and easier for end users. This is on my todo list for a rainy day :slight_smile:

OK… rough around the edges still, but http://smedley.id.au/tmp/org.openhab.binding.teslapowerwall-2.5.0-SNAPSHOT.jar is working here.

Several caveats, due to the self-signed SSL certificate that Tesla chose to use.

  1. the SSL cert must be imported into the Java keystore per my post above

  2. for reasons I don’t understand, whilst requests to an IP address worked find with the http binding, when called from within the powerwall binding, the requests fail as the hostname (ie the IP) isn’t found in the certificate. For this reason, you need to use a hostname ‘powerwall’ in the Host/IP field; and make sure that this host can be looked up to an IP. I run my own local DNS server, using hosts file might work too.

  3. mode/reserve settings are not implemented yet - but the channels are created for when I get time to do it.

  4. Units/decimal places are inconsistent for me in PaperUI - not sure why, as all items are configured the same :frowning:

The solution to 2) will likely be to implement some kind of "trust’ for SSL certificates and disable hostname validation - I need to study some similar code in other bindings.

For 3) I need to work out how to send a json string with the authentication to the PW2 and get an authentication token to request the current mode and reserve settings - it should also be possible to change the reserve setting from Openhab :slight_smile:

Source is at https://github.com/psmedley/openhab2-addons/tree/teslapowerwall

Feedback appreciated.

Cheers,

Paul

1 Like

For item 3 above, I can now retrieve the token based on supplying the customer email and password :slight_smile: should be easy enough to obtain the reserve % and operating mode. Updating these should also be easy enough.

For item 4, looking at events.log, seems the units and decimal places are good, so may just be a PaperUI thing

Paul, thanks for the Resolution with certificate Import. I’m using know the HTTP binding in my intel NUC with Debian Buster running in a kvm virtual machine. I also tried tour Tesla binding but couldn’t get it running, but I didn’t spent much effort on this.

Hi @Paul_Smedley & all - This looks really good - but I’m having a few problems setting up the Tesla self signed certificate. In my log file I keep getting the following errors:

2019-10-10 10:17:46.842 [ERROR] [org.openhab.io.net.http.HttpUtil    ] - Fatal transport error: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

I think it’s because Tesla is not a CA on my Pi. But I have no clue how to add it as one - have tried a few ways to do this but no change. Any ideas of my next step?

Hi @PeteW - agree that looks like the error I was getting before adding the PW2 certificate to the Java cacert file.

Did you run the example commands I posted? change 192.168.1.30 for the IP of your PW2.

post the output here so we can have a look, and note that after running the ‘keytool -import…’ command you’ll have to restart openhab so that java can ‘see’ the new certificate.

Cheers,

Paul

Hi, when running the above i get the option to add to the keystore ok, but when i say ‘Y’ i get the following error:

Certificate was added to keystore
keytool error: java.io.FileNotFoundException: /jre/lib/security/cacerts (No such file or directory)

I then tried the command:

sudo keytool -import -trustcacerts -keystore securit -storepass changeit -alias PW2 -import -file cacert.pem

(ie, removed the path for the ‘securit’ comand)

and that said ‘Certificate added to keystore’ ok

Have restarted openhab, but am still getting the same error in the openhab logs ‘unable to find valid certificate’

I have also tried a direct command as suggested in the GIT Powerwall documentation you suggested above, but i get the error:

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: http://curl.haxx.se/docs/sslcerts.html

However, when i issue the curl command with -k it fetches the data from the Powerwall ok.

I’m not quite there yet! Is there a way to check if the cert has been loaded into the keystore?

Pete

Just reran the keystore command and i get:

keytool error: java.lang.Exception: Certificate not imported, alias <PW2> already exists

So it must have loaded into the keystore ok.

I think maybe its because the cert is self signed? And i need to tell my keystore to trust certs issued by Tesla?

It was my $JAVA_HOME

mine needs to be:
/usr/lib/jvm/zulu-embedded-8-armhf

it was blank, then I stupidly set it to the oracle one, not knowing i was on zulu jvm !

Thanks!
Pete

Great! I had a similar issue when I updated Java and $JAVA_HOME was still pointing to the old java version. Glad it’s ok now.

1 Like

Oh one other thing that might help others…

I hadn’t installed the JSON transformation. Once i did that all the powerwall data is appearing on my sitemap :wink:

Thanks Paul - an excellent implementation, pretty neat I think

Hi @PeteW
Are you using the binding or the manual configuration of http.cfg / items file?

Cheers,

Paul

I’m using the manual config of http.cfg and an items file

My Sitemap is pretty basic at the moment, but its working pretty well. I’ve also updated my Grafana charts to use the Powerwall info rather than an EmonPi I was using to monitor the house power.

Not much sun today though ;-(

Now i just need to add a feed from my SolaX inverters, so i can include their perfornace in the mix. Sounds like i need to use http.cfg again - which seems pretty easy - they have similar API calls with JSON returns.

Powerwall gives some great stats itself though - so only need the inverter info if i suspect something is going wrong up on my roof!

1 Like