Tesla Wall Connector Gen 3 RESTful

I wrote a Munin plugin for graphing Tesla Wall Connector data fetched from the HTTP API: https://github.com/munin-monitoring/contrib/pull/1318

I’m seeing that the Tesla Wall Connector stops responding to HTTP queries during charging sessions. Munin just shows this as a gap in data. But that may explain why your graphs show these discontinuities when charging.

I installed one of these a few weeks ago and I am also seeing the unit sending back incomplete responses while charging. We charge at the same time every night (between midnight and 6 am) and the behavior I see is that it starts to return incomplete responses intermittently and then around 2:30 every response is incomplete. Around 4:30, it resumes sending complete responses.

I am using curl in a bash script to collect my data.

Interestingly, it appears that only the final close curly is missing from the incomplete responses. Here is an example:

{"contactor_closed":true,"vehicle_connected":true,"session_s":38999,"grid_v":234.9,"grid_hz":60.025,
"vehicle_current_a":47.3,"currentA_a":22.0,"currentB_a":25.3,"currentC_a":25.0,"currentN_a":22.3,
"voltageA_v":239.8,"voltageB_v":240.1,"voltageC_v":118.3,"relay_coil_v":6.1,"pcba_temp_c":71.0,
"handle_temp_c":38.0,"mcu_temp_c":52.8,"uptime_s":1435563,"input_thermopile_uv":-2501,
"prox_v":1.5,"pilot_high_v":0.1,"pilot_low_v":0.1,"session_energy_wh":22950.801,"config_status":5,
"evse_state":11,"current_alerts":[]

If a ‘}’ were added to the above response, it would be complete, valid json. It is the only difference when comparing to good responses.

I suspected the unit was timing out on the response, but I set the timeout to 30 seconds and timed how long it was taking to complete the request, and found that the request completes in a second or less. I also used the “ignore content length” flag to ensure curl was not cutting the connection due to a low value in the content length header.

So far, I’ve not been able to figure out why the unit has this behavior. I could add a curly to the end of the file if it is not there but I am resisting doing that because the thing should work correctly, all the time.

Hi @s23,
good catch.

String length here is 512. So some buffer is 512 bytes, and the size of the string is not handled properly… The bug could be in TWC or HA…

The json-string content grows with session_s, uptime_s, and session_energy_wh. You could probably confirm this by checking the first failing response (one of the numbers would have just increased over the next decimal limit).

The string length here is a little bit location dependent. In most European installations we have 3-phase input, so currentN_a should be one digit less. Second thing that could change with location is input_thermopile_uv.

so we finally had some guests use one of the chargers - the ‘state’ sensor seems to go through the following - can anyone confirm what they are?

  • 1 - standby / nothing connected
  • 6 - plugged in? pilot?
  • 10 - charging
  • 8 - end of charge? float?
  • 13 - unplugged?
  • 1 - idle / not connected

I don’t have a tesla app to see what their terminology for these states is, but if it’s common with other systems it might be nice to have a lookup table in the integration.

Because the ‘session_energy_wh’ isn’t included by default, I’d also like to set up an automation so that when charging finishes (or when vehicle is unplugged) it can notify with session_energy_wh and session_s for potential billing

Are we solving the problem at the right place here?

Is it really the TWC gen 3 that determines the output to the car? -isn’t it just setup once to what is the MAX amp that can be delivered?
I’m not claiming to know what goes on in the TWC and especially not in the power-sharing mode, but the Tesla car API also has a method “set_charging_amps” - Tesla API - charging.
Perhaps there’s a possibility by instead regulating the amps in the Tesla API instead of in the TWC API? (of course this limits the solution to only Tesla vehicles, but…)

Needless to say that I would have tried the above by now, if Tesla had delivered both my Y according to schedule and not only the TWC… :wink:

Seems another workaround has been made called Suncatcher.

Is there a way we could integrate the charging controls from suncatcher into the existing TWC integration?

This suncatcher just adjusts the amps in the car. We can already do this through the Tesla integration.

The problem is that it doesn’t work for non Teslas and is very tricky for multiple Teslas and/or multiple wall connectors. You have to know which car is plugged in where.

(Probably) because I had to reboot the Wifi AP, my TWC 3 is now on a different IP address then before. HA or the integration isn’t picking it up though. (was.124 is now .119 and can be accessed there). Does anybody know how to “re-discover” the TWC without deleting the integration? (don’t wanna mess up the energy stats)

(Integration shows “Retrying setup: Timeout while connecting to Wall Connector at 192.168.222.124” error)
EDIT: never mind, fixed by re-adding

Same issue here. I have 3 TWCs and I’ve connected them to HA immediately after turning them on (they just showed up and it was really easy). I used them in this way for a few days and then decided that I wanted to try to connect them together: now I only see the “master” and the other two disappeared from the network completely.

The only option right now seems to turn the power sharing option off :frowning:

I have the same question. I also see it in various states from what you have listed as well. Currently ours is sitting in State 9, its plugged in and was charging, but not currently. I have seen others as well. We have a 22 M3LR.

I spent the better part of an evening digging into /tedapi/v1, and after glancing over the main.js I realised it is simply based on Protocol Buffers, i.e. similar to the VCSEC or Powerwall.

If you load up the TWC3 web UI, and look at the responses from /tedapi/v1, you’ll see binary data. However, I found copy-pasting the binary responses from either Firefox or Chrome corrupts, but using Wireshark to export the data bytes (nested inside HTTP) worked fine.

You can decode the saved requests and responses with protoc --decode_raw. Because we don’t have the corresponding .proto, there are no field names, and we must figure out what they are.

What I found was the main.js actually contained JavaScript routines for encoding/decoding these protobufs, from which we can infer the original message definitions. For example, this routine (which I’ve abbreviated):

t.EcuId = {
  decode(e, t) {
      switch (e >>> 3) {
        case 1:
          i.partNumber = n.string();
          break;
        case 2:
          i.serialNumber = n.string();
          break;
      }
    }
  }
}

We can infer that the corresponding message (as defined in a .proto file) should be:

message EcuId {
  string part_number = 1;
  string serial_number = 2;
}

Which you could then decode with:

protoc twc3.proto --decode=EcuId < EcuId.pb

Obviously there are a lot more protos to decode, but for the purposes of limiting the charge current, you could focus solely on those.

The other interesting thing I noticed about main.js is the load sharing protocol itself appears to be based on protobufs. This suggests it may be possible to implement a corresponding load sharing participant (eventually).

It looks like all /tedapi/v1 requests/responses use an AuthEnvelope, which wraps a MessageEnvelope, which in turn wraps pretty much all the other protos. So AuthEnvelope is the top-level proto you want to be decoding.

To access the /tedapi/v1, the internal wi-fi access point needs to be activated, and it times out after a couple of hours, limiting its utility. But if there’s a way to implement the load sharing protocol it might be willing to keep the AP activated.

There are plenty of people who have reverse engineered the .proto definitions from various Tesla products, e.g. VCSEC here: GitHub - trifinite/vcsec-archive

But to my knowledge, nobody has yet (publicly) reversed the protobufs corresponding to the TWC3. This would be extraordinarily useful.

5 Likes

Please keep digging! Would love to be able to control load management from the Tesla wall charger :slight_smile: Sorry to say I’m not that smart to be at any help! :joy:

I’ve uploaded a minimal example of interacting with the TWC3: GitHub - jeremyvisser/wc3magic

I was able to successfully decode the same config structure that the web interface displays.

In addition, I was able to update the charge current from a script. I’ve included a sample script in the above repository that does this.

3 Likes

@jeremy23 Do you have any plans to implement this into HA somehow?

I think I would be fairly reliant on others to help with that.

An unsolved problem is how to reliably maintain administrative connection. To use the web interface (or send protos), you need to manually hold the charger button for 5 seconds to activate the internal AP.

After a period of inactivity, it times out and deactivates. Constantly polling it (e.g. requesting config) keeps it awake, but this is unreliable.

Using the load sharing protocol for providing solar input is an interesting possibility, since it is (electrically) safer than setting the max charge current. But I’m still unclear on how this protocol works.

I don’t have a second TWC3 to play with. When I create a hotspot named TeslaWallConnector_000001 with password AAAAAAAAAAAA, it connects and attempts to open a TLS connection to the gateway IP on TCP port 34578, presenting a client certificate signed by

For example, when you pair your TWC3 with another for load sharing, it connects to the SSID of the other TWC3, and tries to connect to its IP (assumed to be the gateway IP) on TCP port 34578 and establish a Mutual TLS connection, presenting a certificate issued by the “Tesla Energy Product Issuing CA”.

Or if you try to connect yourself, it tells you these are the accepted CAs:

% openssl s_client -connect 192.168.92.1:34578
---
Certificate chain
 0 s:CN = 1529455-02-D--PGTxxxxxxxxxxx, OU = Gen3 Wall Connector, OU = Energy, O = Tesla Inc., L = Palo Alto, ST = California, C = US
   i:CN = Tesla Energy Product Issuing CA, OU = Energy, OU = PKI, O = Tesla, C = US
   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256
   v:NotBefore: Nov 1 00:00:00 2021 GMT; NotAfter: Nov 1 00:00:00 2023 GMT
---
---
Acceptable client certificate CA names
CN = Tesla Energy Product Issuing CA, OU = Energy, OU = PKI, O = Tesla, C = US
CN = Tesla Powerwall Products CA
CN = Tesla Powerwall Products CA
OU = NXP Plug Trust CA, CN = Tesla Inc.-471c4e0b81d78ffc66028b985e22f2d9vE200
Client Certificate Types: RSA sign, ECDSA sign

Now obviously I don’t have access to this CA. If I present a self-signed certificate, it simply closes the connection with an “Unknown CA” error. I don’t know of a way to overwrite this CA with my own, nor mint any new certificates.

Since the cert has a 2 year expiry, possibly there’s a way to renew it. And if there’s a way to renew it, perhaps there’s a way to generate a new one. Or maybe it just ignores the validity period.

Maybe the whole load sharing thing is a dead end and the right answer is to just poll the device a lot, and poke the max charge rate setting. That seems like the simplest option, but it does require active polling.

If somebody can figure out a reliable way to convince the TWC3 to keep its internal AP alive (or whether there are older firmware versions that have this behaviour), that would be really great knowledge to have.

1 Like

Sounds like the load sharing could be an issue if one don’t have the cert then, and I guess Tesla would not be super happy to hand that over to us :rofl: I guess the only thing people want is to be able to control the amp’s or that’s what I would love to do. Today I do it via the Tesla API and that works fine but if I charge another vehicle I have no way to control the max amps…

Thanks for investigating and hopefully there is someone else that could help in some way to get this feature integrated into HA.

Since Home Assistant is monitoring the handle temperature from the wall connector, one funny anecdote I’ve discovered is the handle temperature gets erroneously recorded as 127°C while the button is held down.

I accidentally triggered an over–temperature alert on the wall connector because I held down the charger handle button for around 30 seconds one time, and it erroneously read the temperature as 127°C. :rofl:

On a more serious note, I’ve not made any further progress in figuring out the load sharing protocol, in particular how to get past the Mutual TLS authentication.

While I can set the overall max charge rate no problem, I’ve still not made further progress in reliably keeping the internal AP awake, to make repeatedly setting the charge rate reliable.

Keeping the browser page open seems to keep it awake, but sending a config settings request via curl does not seem to do the same thing. Perhaps I need keepalives, or some particular combination of requests, or something else. Or maybe it’s just not possible.

I could have sworn some old firmware versions kept the internal AP alive, so it’s possible downgrading to a version known to have this behaviour might be an option. While I found some old firmware versions here, and http://192.168.92.1/service lets you upload a firmware binary, I found that uploading old versions took no effect. So downgrading might not be supported.

Any useful findings from others would be most appreciated.

1 Like

Hi Jeremy,

many thanks for your investigation and the protocol buffer examples on GitHub!

It seems that the TWC Gen3 “prohibits” downgrades by checking the version number in the filename. So if you change the filename of the old version to a version number of a newer than the already installed version it should work (see also this post: Software Gen3 Wall Connector - #1112 von DerFizi - Weitere Ladestationen - TFF Forum - Tesla Fahrer & Freunde). Note: If your TWC has an internet connection, it will automatically update itself after some time.

I have tested your example successfully and could change the ampere settings. Unfortunately also in my case the service/installation WiFi access point stops working after some time, even after permanently changing the setting once every 5 minutes.

Furthermore I observed that the last setting is still active after a reboot of the TWC Gen3, so the change is stored internally to an EEPROM / EERAM / FRAM. I am not sure how many writing cycles are fine for the corresponding IC, but I have some doubts that this could be a problem.

One more point: It is possible to change the setting starting from 6 A. If I change it to a value between 1…5 A then an error message is returned (and the value stays at 6 A). If a value of 0 A is sent to the TWC Gen3, then there is no error message, but the value still stays at 6 A.
But what I initially wanted is to be able to stop charging by setting the value to 0 A (and then restart it again with a value >= 6 A), which is not working.

So I still think that the current way is not as optimal, as I hope the load sharing option could be.

Is there anyway how I can support you for further investigations? For your information: I have 2 TWCs (which are not yet setup for load sharing, but I could do it for tests).

Best regards,
Michael

1 Like

Agreed, setting the max charge is persisted to flash, so I agree this is a longevity problem. But the load sharing is probably runtime information, so is better suited to this task.

One thing that would be good to know is exactly what the load sharing traffic uses. I’m guessing the followers connect to the leader on 192.168.92.1:34578, performing Mutual TLS auth, and then exchanging WCLoadSharingFollowerState, WCLoadSharingLeaderState, WCLoadSharingFollowerState, and similar messages. But that’s just my guess and it’d be nice to know for sure.

While it’s unlikely a simple TLS MITM on TCP port 34578 would actually work, it’s still worth trying just to rule it out.

Or if the initial authentication happens via TLS, but the actual load sharing happens via UDP (e.g. unauthenticated), then it would be interesting to know whether anything can be injected there.

When load sharing is enabled, I’m interested to know whether the internal AP stays active all the time, or whether it times out like before, which might add further information on keeping the AP alive for other reasons.

1 Like

I found a forum thread where users stated continually broadcasting the SSID was “fixed” in 22.7.0, so I downgraded to firmware version 21.36.6 (pro tip: if it looks like it didn’t downgrade, power cycle it at the breaker).

So after downgrading to 21.36.6, I can confirm the internal AP is now continually awake even several hours later.

Unfortunately though, after a timeout period, the API disallows talking to it, requiring a physical handle button press, even when the AP is still active.

Even directly sending protos throws an error_response{ status{ code: 16 }} in this state.

So this may be a dead end, and finding ways to keep the API awake (including on newer firmware versions) is likely to be a better use of time.

1 Like