Tesla Powerwall 3 Integration using PyPowerwall Proxy & HTTP Binding

Update: This has now been edited to use the pypowerwall ‘convenience’ (/pw) endpoints

So after having a play with the OH Powerwall binding, it looks like at this stage it doesn’t support the Powerwall 3. I needed a fairly quick solution to integrate the PW3 into OpenHAB, so I could monitor status and persist key values.

My requirements don’t include controlling the PW3 - Happy to let it do its own thing there, but arguably you could use the same approach for the controllable values, only thing then, is that requires the cloud API connection from the PyPowerwall Proxy

For others who are looking to do the same, this is a rough how-to guide I have collated from various sources, to get PW3 values back into OpenHAB, with the following high-level steps:

  • Install/Configure WIFI connection on OH server, as client to PW3 WiFi AP
  • Install Docker & Run pypowerwallproxy container
  • Add HTTP Things & Items, to retrieve values from pypowerwall proxy

I have done this all on my local openHAB server, running Ubuntu, but arguably the first 3 steps could be done on a separate device such as a PI Zero 2W (with Ethernet HAT), giving you a dedicated proxying device. You may need to do this, if you are already using, or don’t have a WiFi adapter in your OpenHAB server, and don’t want to add another one in there.

Obviously you will need to interpret and apply your own site/device-specific commands/config.

First things first, you are going to need the WIFI SSID and Wifi Password for the TEDAPI access - There is no access via the local LAN ports to the PW3 API, which was taken away in a recent release (25.10). Take a picture of the WiFi sticker in the main PW3 unit (Not the Gateway Unit), while your installer is connecting things up…

1) Setup WiFi Network Adapter

#install network manager (if not already installed)
sudo apt install network-manager

#list wifi interfaces - In my example below, the wlan interface is wlp3s0
sudo nmcli d

#turn on WiFi
sudo nmcli r wifi on

#List Wifi Networks (Just to check its working, however noting the Powerwall 3 Wifi Network is hidden, so you will not see it in the list)
sudo nmcli d wifi list

#replace <ssid> below with SSID from Powerwall 3 - Will be something like TealsaPW_XXXX, and also replace the ifname with the actual wifi interface name on your system
sudo nmcli c add type wifi con-name powerwall ifname wlp3s0 ssid <ssid>

#replace <password> with password from Powerwall 3
sudo nmcli c modify powerwall wifi-sec.key-mgmt wpa-psk wifi-sec.psk <password>

# The following is optional, to set a fixed IP address, as the PW3 will provide a DCHP IP Address
sudo nmcli con mod "powerwall" \
  ipv4.addresses "192.168.91.50/24" \
  ipv4.gateway "" \
  ipv4.dns "" \
  ipv4.dns-search "" \
  ipv4.method "manual"

#bring up the network
sudo nmcli c up powerwall

#Ping the powerwall - If it responds, you are ready to move onto the next steps
ping 192.168.91.1

2) Install and run pypowerwall proxy in Docker

#If docker is not already installed, execute the following command:
sudo apt install docker.io docker-compose-v2 docker-doc

#'Install' & run the pypowerwall container. Replace the gateway_password, with the one from your PW3, and the timezone with that appropriate to your location

sudo docker run \
        -d \
        -p 8675:8675 \
        -e PW_PORT='8675' \
        -e PW_HOST='192.168.91.1' \
        -e PW_GW_PWD='Gateway_Password' \
        -e PW_TIMEZONE='Pacific/Auckland' \
        -e TZ='Pacific/Auckland' \
        -e PW_CACHE_EXPIRE='5' \
        -e PW_DEBUG='no' \
        -e PW_HTTPS='no' \
        -e PW_STYLE='clear' \
        --name pypowerwall \
        --restart unless-stopped \
        jasonacox/pypowerwall

The container will restart following reboots. Documentation for the proxy can be found at pypowerwall/proxy at main · jasonacox/pypowerwall · GitHub

Browse to your openhab server, on port 8675 (http://openhabserveraddress:8675/ ), and you should then see a animated graphic similar to the following:

This means you should be ready to move onto the next step, of setting up OpenHAB itself.

  1. Setup OpenHAB Things
    This assumes you have got the HTTP binding installed already, and the baseURL below assumes you are using your OpenHAB server to run the pypowerwall proxy container:

The below is a sample of a few channels

UID: http:url:powerwall3_http
label: Powerwall 3 via pypowerwall proxy
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://localhost:8675
  delay: 1000
  stateMethod: GET
  refresh: 10
  commandMethod: GET
  timeout: 10000
  bufferSize: 2048
channels:
  - id: last-failure
    channelTypeUID: http:request-date-time
    label: Last Failure
    configuration: {}
  - id: last-success
    channelTypeUID: http:request-date-time
    label: Last Success
    configuration: {}
  - id: pw3BatteryLevel
    channelTypeUID: http:string
    label: Powerwall 3 Battery Level
    description: Powerwall 3 Battery Level via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/level
  - id: pw3BatteryPower
    channelTypeUID: http:string
    label: Powerwall 3 Battery Power
    description: Powerwall 3 BatteryPower via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/power
  - id: pw3SitePower
    channelTypeUID: http:string
    label: Powerwall 3 Site Power
    description: Powerwall 3 Site Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/power
  - id: pw3LoadPower
    channelTypeUID: http:string
    label: Powerwall 3 Load Power
    description: Powerwall 3 Load Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/power
  - id: pw3SolarTotalPower
    channelTypeUID: http:string
    label: Powerwall 3 Total Solar Power
    description: Powerwall 3 Total Solar Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/power
  - id: pw3MPPTAPower
    channelTypeUID: http:string
    label: Powerwall 3 MPPT A Power
    description: Powerwall 3 MPPT A Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/strings
  - id: pw3MPPTBPower
    channelTypeUID: http:string
    label: Powerwall 3 MPPT B Power
    description: Powerwall 3 MPPT B Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/strings
  - id: pw3MPPTCPower
    channelTypeUID: http:string
    label: Powerwall 3 MPPT C Power
    description: Powerwall 3 MPPT C Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/strings
  - id: pw3MPPTDPower
    channelTypeUID: http:string
    label: Powerwall 3 MPPT D Power
    description: Powerwall 3 MPPT D Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/strings
  - id: pw3MPPTEPower
    channelTypeUID: http:string
    label: Powerwall 3 MPPT E Power
    description: Powerwall 3 MPPT E Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/strings
  - id: pw3MPPTFPower
    channelTypeUID: http:string
    label: Powerwall 3 MPPT F Power
    description: Powerwall 3 MPPT F Power via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /pw/strings
  - id: pw3GridStatus
    channelTypeUID: http:string
    label: Powerwall 3 Grid Status
    description: Powerwall 3 Grid Status via HTTP pypowerwall proxy
    configuration:
      mode: READONLY
      stateExtension: /freq
  - id: pw3ProxyStatus
    channelTypeUID: http:string
    label: Powerwall 3 Proxy connection to PW3
    description: Powerwall 3 Proxy connection to PW3 status
    configuration:
      mode: READONLY
      stateExtension: /pw/is_connected

Then from there, you will need link the channels to items, and apply some JSONpath expressions to pull out the specific values related to the items, e.g. for the above channel examples, these would be (Pick whatever names UID’s you want - These are just as examples):

Description Channel ID Item UID JSONpath Proxy Source
Powerwall 3 Battery Level pw3BatteryLevel PW3_Battery_Level $.level /pw/level
Powerwall 3 Battery Power pw3BatteryPower PW3_Battery_Power $.battery /pw/power
Powerwall 3 Load Power pw3LoadPower PW3_Site_Power $.site /pw/power
Powerwall 3 Total Solar Power pw3SolarTotalPower PW3_Total_Solar_Power $.solar /pw/power
Powerwall 3 MPPT A Power pw3MPPTAPower Powerwall_3_MPPT_A_Power $.*.PVAC_PVMeasuredPower_A /pw/strings
Powerwall 3 MPPT B Power pw3MPPTBPower Powerwall_3_MPPT_B_Power $.*.PVAC_PVMeasuredPower_B /pw/strings
Powerwall 3 MPPT C Power pw3MPPTCPower Powerwall_3_MPPT_C_Power $.*.PVAC_PVMeasuredPower_C /pw/strings
Powerwall 3 MPPT D Power pw3MPPTDPower Powerwall_3_MPPT_D_Power $.*.PVAC_PVMeasuredPower_D /pw/strings
Powerwall 3 MPPT E Power pw3MPPTEPower Powerwall_3_MPPT_E_Power $.*.PVAC_PVMeasuredPower_E /pw/strings
Powerwall 3 MPPT F Power pw3MPPTFPower Powerwall_3_MPPT_F_Power $.*.PVAC_PVMeasuredPower_F /pw/strings
Powerwall 3 Grid Status pw3GridStatus Powerwall_3_Grid_Status config:js:1on0offtransform /freq
Powerwall 3 Grid Status pw3ProxyStatus Powerwall_3_Proxy_Status config:js:PW3_proxy_trueOnfalseOff /pw/is_connected

For the grid status the following is the config:js:1on0offtransform contents:

(function(data) {
  var returnValue = JSON.parse(input).grid_status
  if (returnValue == '0') {
        return 'OFF';
    } else 
      if (returnValue == "1") {
        return 'ON';
    } else
      return ""
})(input)

And for the proxy status, the following is the config:js:PW3_proxy_trueOnfalseOff contents:

(function(data) {
  var returnValue = JSON.parse(input).is_connected
  if (returnValue == false) {
        return 'OFF';
    } else 
      if (returnValue == true) {
        return 'ON';
    } else
      return "OFF"
})(input)

Whilst the status of the ‘thing’ will change if the Proxy stops responding, the is_connected channel is useful to inform OpenHAB if the Proxy has lost connection to the Powerwall itself.

Note that the 10000ms timeout is required for the HTTP Thing for this to work however - Most requests complete in subsecond time, but if the Proxy loses connection to the Powerwall, all Proxy responses take longer, (most will return a null), however the is_connected will also take longer, and we want it to return a false, so we can tell OpenHab that the Powerwall network connection is offline, rather than it just timing out.

There are a ton of other stats available via the Proxy, but also refer to the proxy documentation for some cases where data is not available for the PW3 (The proxy also supports predecessor versions).

Note for the MPPT Power Readings, I use a wildcard in the JSONpath - The powerwall unit ID/serial number forms part of the path, and the wildcard works just fine…. if you only have one Powerwall !! I assume this section may be repeated, if you have multiple units on your site, in which case you will just need to replace the wildcard with the specific ID’s for each unit.

A note for those in New Zealand & Australia:

  • There are 6 logical string values in the PW3 interface
  • For those in North America etc, they can use the 6 string values individually, as this matches the 6 physical MPPT inputs on the PW3
  • For those of us at the bottom of the world (NZ/Aus), the PW3 ships with only 3 physical MPPT inputs, but still displays 6 string values in the interface
  • In essence, it looks like the inputs are paired (physically jumpered together) to provide higher current capacity, so to get the readings for the 3 physical MPPT inputs, you will need to add A+B, C+D,E+F to get the correct total current and power for each string. Obviously you don’t need to do that for voltage, and can just take that value for A,C,E which will/should be identical to their paired inputs.
        "PVAC_PVCurrent_A": 0,
        "PVAC_PVCurrent_B": 0,
        "PVAC_PVCurrent_C": 0,
        "PVAC_PVCurrent_D": 0,
        "PVAC_PVCurrent_E": 0,
        "PVAC_PVCurrent_F": 0,
        "PVAC_PVMeasuredPower_A": 0,
        "PVAC_PVMeasuredPower_B": 0,
        "PVAC_PVMeasuredPower_C": 0,
        "PVAC_PVMeasuredPower_D": 0,
        "PVAC_PVMeasuredPower_E": 0,
        "PVAC_PVMeasuredPower_F": 0,
        "PVAC_PVMeasuredVoltage_A": 2,
        "PVAC_PVMeasuredVoltage_B": 2,
        "PVAC_PVMeasuredVoltage_C": 6,
        "PVAC_PVMeasuredVoltage_D": 6,
        "PVAC_PVMeasuredVoltage_E": 22,
        "PVAC_PVMeasuredVoltage_F": 22,

I will continue to add to this post (Including my final findings/setup on the string values), as I expand on my usage of the statistics, but really, once you have the above data coming into OpenHAB, you can apply your usual persistence, and presentation approaches, including use of some of the ‘Energy’ widgets others have so kindly created.

Hope this helps

1 Like

I don’t have a PW3 here to test with - but if you can increase logging to Trace with the Powerwall binding and send me the logs, I can take a look.

Hi Paul,
Cheers for the response - I’m guessing it would be a reasonable change for the binding to support the PW3, given the research and analysis in the below thread (With the PW3 using tedapi)

I just put this workaround post using the powerwallproxy & http binding in the forum solutions category, in case anyone else needed a quick workaround.

I would be keen to assist as best I can, extension of the OH binding to include PW3 support, but for the moment I need to leave the workaround in place probably for a few months to monitor/capture performance, as our PW3 was doing some crazy-dumb things around the ‘OptiCast’ optimisation. I’m also keen to persist individual string performance in the near term, for analysis as well

This (‘crazy optimisation’) seems to have been fixed via the lastest firmware release, but want to monitor and see that it settles down. Unfortunately that was the same firmware release which took away access to TEDAPI via lan, so had to order/setup WiFi card for direct client connection to the PW3, so have a big gap in the stats.

Probably the best idea, is to pickup this discussion via GIT enhancement request to progress this in future, if you are still keen to have a look at PW3 support in future (as well as the extended metrics available for your PW2 etc). [teslapowerwall] Add support for Powerwall TEDAPI (Powerwall 3 support, and Powerwall 2/+ extended metrics) · Issue #18766 · openhab/openhab-addons · GitHub
In all honesty, its probably mainly related to the authentication/token etc, once that is in place, it’s mostly going to just be different end-points, and JSONpath to pull out the values.

But, for now, as requested, the TRACE log is below:

2025-06-07 10:41:30.031 [DEBUG] [ll.internal.TeslaPowerwallWebTargets] - logonjson = {"username":"customer","password":"XXXXXX","email":"xxxxx@yyyyy.com","force_sm_off":false}
2025-06-07 10:41:30.031 [DEBUG] [ll.internal.TeslaPowerwallWebTargets] - Calling url: https://192.168.91.1/api/login/Basic
2025-06-07 10:41:30.032 [TRACE] [ll.internal.TeslaPowerwallWebTargets] - POST request for https://192.168.91.1/api/login/Basic
2025-06-07 10:41:32.525 [DEBUG] [rwall.internal.TeslaPowerwallHandler] - Unexpected error connecting to Tesla Powerwall
org.openhab.binding.teslapowerwall.internal.TeslaPowerwallCommunicationException: {}
	at org.openhab.binding.teslapowerwall.internal.TeslaPowerwallWebTargets.invoke(TeslaPowerwallWebTargets.java:154) ~[?:?]
	at org.openhab.binding.teslapowerwall.internal.TeslaPowerwallWebTargets.getToken(TeslaPowerwallWebTargets.java:111) ~[?:?]
	at org.openhab.binding.teslapowerwall.internal.TeslaPowerwallWebTargets.invoke(TeslaPowerwallWebTargets.java:122) ~[?:?]
	at org.openhab.binding.teslapowerwall.internal.TeslaPowerwallWebTargets.getOperations(TeslaPowerwallWebTargets.java:98) ~[?:?]
	at org.openhab.binding.teslapowerwall.internal.TeslaPowerwallHandler.pollStatus(TeslaPowerwallHandler.java:111) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[?:?]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
	at java.lang.Thread.run(Thread.java:1583) [?:?]

Cheers - Glen

Hey Glen, I’ll do some reading of those links.

1 Like