Skip to content

Commit

Permalink
Add tedapi decode tool
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonacox committed Nov 22, 2023
1 parent 98682e8 commit c319ec6
Show file tree
Hide file tree
Showing 4 changed files with 1,128 additions and 0 deletions.
156 changes: 156 additions & 0 deletions tools/tedapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Decode /tedapi API Payloads

This tool is to help decode `/tedapi` API payloads (requests and responses) using a Protobuf schema definition file [tedapi.proto](tedapi.proto).

## Tool

```bash
# Build python bindings for protobuf schema - tedapi_pb2.py
protoc --python_out=. tedapi.proto

# Decode payload
python decode.py <filename>
```

## Background

* This requires using the gateway WiFi access point (for PW2/+ systems this is TEG-xxx and for PW3 it is TeslaPW_xxx) and the https://192.168.91.1 endpoint. It seems that this is required for the /tedapi endpoints (LAN or other access results in "User does not have adequate access rights" 403 Error)
* The /tedapi API calls are using binary Protocol Buffers ([protobuf](https://protobuf.dev/)) payloads.

## APIs

### GET /tedapi/din

This API fetches the Powerwall DIN (device identification number). It uses basic auth that appears to be: Tesla_Energy_Device:GW_PWD where the GW_PWD is the password near the QR code on the Powerwall that you scan with the Tesla Pros app.

```bash
curl -v -k -u "Tesla_Energy_Device:GW_PWD"" https://192.168.91.1/tedapi/din
```
It only returns a simple string that contains the DIN:
```
1232100-00-E--TG123456789012
```
### POST /tedapi/v1
This appears to be the workhorse function. It uses basic auth that appears to be: `Tesla_Energy_Device:GW_PWD` where the GW_PWD is the password near the QR code on the Powerwall that you scan with the Tesla Pros app.
```bash
curl -v -k -H 'Content-type: application/octet-string' -u "Tesla_Energy_Device:GW_PWD" --data-binary @request.bin https://192.168.91.1/tedapi/v1
```
Payloads are binary Protocol Buffers (protobufs).
* The [decode.py](decode.py) tool will help decode this using the proto file.
* Or you can use `protoc --decode_raw < v1_request` to decode the raw response.
There appear to be different types of request sent. One is for `config` which gets a payload that contains the configuration of the Powerwall. Another is for `query` that gets current data (e.g. systemStatus, realPowerW, voltages, frequencies, etc.).
#### CONFIG Example
```bash
# Request Config Data from Powerwall
curl -v -k -H 'Content-type: application/octet-string' -u "Tesla_Energy_Device:GW_PWD" --data-binary @request.bin https://192.168.91.1/tedapi/v1 > response.bin
# Decode Config Data
python3 decode.py response.bin
```
An example response shows the system config data in the JSON `text` (removed to protect the innocent) and a `code` payload (TBD).
```
message {
head: 1
response {
din: "1232100-00-E--TG123456789012"
}
request {
value: 1
}
config {
recv {
file {
name: "config.json"
text: "{...JSON Payload Removed...}"
}
code: "...Binary Data Removed..."
}
}
}
tail {
value: 1
}
```
#### QUERY Example
To get the status of the Powerwall, send a binary query.bin payload. The structure of the query payload has a `text` query string that seems to be an exhaustive list of labels. The `code` field is a binary payload.
```
message {
head: 1
response {
value: 1
}
request {
din: "1232100-00-E--TG123456789012"
}
payload {
send {
num: 2
payload {
value: 1
text: " query DeviceControllerQuery {\n control {\n systemStatus {\n nominalFullPackEnergyWh...Truncated..."
}
code: "0\201\210...Truncated..."
b {
value: "{}"
}
}
}
}
tail {
value: 1
}
```
```bash
# Request Status Data from Powerwall
curl -v -k -H 'Content-type: application/octet-string' -u "Tesla_Energy_Device:GW_PWD" --data-binary @query.bin https://192.168.91.1/tedapi/v1 > response.bin
# Decode Config Data
python3 decode.py response.bin
```
An example response shows the system status data in the JSON `text` field (truncated).
```
message {
head: 1
response {
din: "1232100-00-E--TG121048001E4G"
}
request {
value: 1
}
payload {
recv {
value: 1
text: "{\"control\":{\"alerts\":{\"active\":[\"SystemConnectedToGrid\",\"FWUpdateSucceeded\",\"GridCodesWrite\",\"PodCommissionTime\"]},\"batteryBlocks\":[{\"din\":\"2012170-25-E#TG123456789012\", ...Truncated...\"updateUrgencyCheck\":null}}"
}
}
}
tail {
value: 1
}
```
## Credit
* Thanks to [zigam](https://github.com/zigam) for starting this research and the initial discovery, [post](https://github.com/jrester/tesla_powerwall/issues/20#issuecomment-1810848383) and tips.
* Thanks to [jesaf00](https://github.com/jesaf00) for opening the [Powerwall 3 Support issue](https://github.com/jasonacox/Powerwall-Dashboard/issues/387) and help testing.
* Thanks to others helping test: [longzheng](https://github.com/longzheng) [pbburkhalter](https://github.com/pbburkhalter) [stevecastaneda](https://github.com/stevecastaneda)
34 changes: 34 additions & 0 deletions tools/tedapi/decode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
"""
Python module to decode tedapi protobuf data
Requires:
- Protobuf pip install protobuf
- Generate tedapi_pb2.py with protoc --python_out=. tedapi.proto
Author: Jason A. Cox
For more information see https://github.com/jasonacox/pypowerwall
"""

import tedapi_pb2
import sys

FILENAME = 'request.bin'

# Set filename from command line if specified
filename = FILENAME
if len(sys.argv) > 1:
filename = sys.argv[1]

# Open request or response file and read data
with open(filename, 'rb') as f:
data = f.read()

# Decode protobuf data
tedapi = tedapi_pb2.ParentMessage()
tedapi.ParseFromString(data)
print(tedapi)




180 changes: 180 additions & 0 deletions tools/tedapi/tedapi.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Tesla tedapi API Protocol Buffer definition (tedapi.proto)
//
// Create tedapi_pb2.py for use in projects using the protoc compiler:
// protoc --python_out=. tedapi.proto
//
// Author: Jason A. Cox - Date: 21 Nov 2023 - Version: 1.0
//
// For more information see https://github.com/jasonacox/pypowerwall

syntax = "proto3";

package tedapi;


// ***** Parent *****

message ParentMessage {
Message message = 1;
Tail tail = 2;
}

message Message {
int32 head = 1;
StringNumber response = 2;
StringNumber request = 3;
optional ConfigType config = 15;
optional QueryType payload = 16;
}

message StringNumber { // 2
optional string din = 1;
optional int32 value = 3;
}

message Tail {
int32 value = 1;
}

// ***** Query = 16 *****

message QueryType { // 16
optional PayloadQuerySend send = 1;
optional PayloadString recv = 2;
}

message PayloadQuerySend { // 1
optional int32 num = 1;
optional PayloadString payload = 2;
optional bytes code = 3;
optional StringValue b = 4;
}

// ***** Config = 15 *****

message ConfigType { // 15
optional PayloadConfigSend send = 1;
optional PayloadConfigRecv recv = 2;
}

message PayloadConfigSend { // 1
PayloadString file = 1;
}

message PayloadConfigRecv { // 2
ConfigString file = 1;
bytes code = 2;
}

message ConfigString {
string name = 1;
string text = 100;
}

// ***** General *****

message PayloadString {
int32 value = 1;
string text = 2;
}

message StringValue {
string value = 1;
}

message NumberValue {
int32 value = 3;
}

// ***** BASED ON RAW DECODED PAYLOADS *****
//
// REQUEST - config
// 1 {
// 1: 1
// 2 {
// 3: 1
// }
// 3 {
// 1: "1232100-00-E--TG123456789012"
// }
// 15 {
// 1 {
// 1: 1
// 2: "config.json"
// }
// }
// }
// 2 {
// 1: 1
// }
//
// RESPONSE - config
// 1 {
// 1: 1
// 2 {
// 1: "1232100-00-E--TG123456789012"
// }
// 3 {
// 3: 1
// }
// 15 {
// 2 {
// 1 {
// 1: "config.json"
// 100: "{}"
// }
// 2: "\255\177t+5\35..."
// }
// }
// }
// 2 {
// 1: 1
// }
//
//
// REQUEST - query
// 1 {
// 1: 1
// 2 {
// 3: 1
// }
// 3 {
// 1: "1232100-00-E--TG123456789012"
// }
// 16 {
// 1 {
// 1: 2
// 2 {
// 1: 1
// 2: " query DeviceControllerQuery {..."
// }
// 3: "0\201\210\002B\0026\335T\310\02..."
// 4 {
// 1: "{}"
// }
// }
// }
// }
// 2 {
// 1: 1
// }
//
// RESPONSE - query
// 1 {
// 1: 1
// 2 {
// 1: "1232100-00-E--TG123456789012"
// }
// 3 {
// 3: 1
// }
// 16 {
// 2 {
// 1: 1
// 2: "{...}"
// }
// }
// }
// 2 {
// 1: 1
// }
Loading

0 comments on commit c319ec6

Please sign in to comment.