jasonacox / pypowerwall Goto Github PK
View Code? Open in Web Editor NEWPython API for Tesla Powerwall and Solar Power Data
License: MIT License
Python API for Tesla Powerwall and Solar Power Data
License: MIT License
I have my pypowerwall.env
connection/cache settings like this:
PW_CACHE_EXPIRE=60
PW_POOL_MAXSIZE=1
I am seeing logs that look like this showing a lot of discarded connections. Why is it creating extra connections when there "should only be one"? Could this be a problem?
04/24/2024 09:54:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 09:57:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 09:59:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 10:01:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 10:01:39 AM [proxy] [INFO] pyPowerwall Proxy Stopped
CANCEL
04/24/2024 10:01:56 AM [proxy] [INFO] pyPowerwall [0.8.3] Proxy Server [t54] - HTTP Port 8675
04/24/2024 10:01:56 AM [proxy] [INFO] pyPowerwall Proxy Started
04/24/2024 10:01:56 AM [proxy] [INFO] pyPowerwall Proxy Server - Local Mode
04/24/2024 10:01:56 AM [proxy] [INFO] Connected to Energy Gateway 192.168.7.87 (Harrison)
04/24/2024 10:02:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 10:04:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 10:06:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 10:11:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
04/24/2024 10:17:00 AM [urllib3.connectionpool] [WARNING] Connection pool is full, discarding connection: 192.168.7.87. Connection pool size: 1
The alerts below are all showing for me with one defective Powerwall in the group. Gateway disabled this battery due to communication failures - possibly due to a failed firmware upgrade. Tesla said the battery needs to be replaced under warranty service.
"GridCodesWrite",
"GridCodesWrite",
"GridCodesWrite",
"GridCodesWriteError",
"FWUpdateFailed",
"BatteryDisabled",
"PodCommissionTime",
"SystemConnectedToGrid",
"PINV_a067_overvoltageNeutralChassis",
"PINV_a067_overvoltageNeutralChassis",
"PINV_a067_overvoltageNeutralChassis",
"THC_w042_POD_MIA",
"SYNC_a001_SW_App_Boot"
Setting Cache-Control in server.py here is not done correctly. This line of code
self.send_header("Set-Cookie", "Cache-Control: no-cache")
is incorrect as cache control is not a cookie, it is it's own header field. The correct code is:
self.send_header("Cache-Control", "no-cache, no-store")
Note that I added no-store
. I did this because when analyzing what the Testa Powerwall returns when I connect the browser directly to the Powerwall, I see both no-cache and no-store. I believe this is to support older browsers.
Okay, so, by default pyPowerwall should set no-cache, no-store
by fixing this bug.
But not caching has severe performance implications when reloading a page with embedded power flow animation graphics. We should provide an option that lets a user indicate that they want to permit a browser to cache resources. I propose a setting.
PW_BROWSER_CACHE=<seconds>
and if this is set to anything non-zero then
browser_cache = int(os.getenv("PW_BROWSER_CACHE", "0"))
if browser_cache > 0:
self.send_header("Cache-Control", "max-age={}".format(browser_cache))
else:
self.send_header("Cache-Control", "no-cache, no-store")
This dramatically improves page load (after the first load) until whatever number of seconds expires and then the browser will fetch the resources again (on page reload). A user can always force the reload by holding down the shift key.
Notice the resources that are now retrieved from memory.
Now, we could further refine this. Rather than setting the header before the data is retrieved from Powerwall, wait until after so that the content-type is known. If content-type is text/css
, application/javascript
or image/png
then use max-age
else do not permit caching. Specifically application/json
should never be permitted to cache. This is what appears to be happening by default, but I don't know if that is coincidence or not... It's not really coincidence (as coded right now) because that data is coming from <ipaddr>:8675/api/xxxx
that are in the ALLOWLIST
so it is not going through the proxy code.
So, it might be safer to move the header code down a few lines where we already have...
ftype = r.headers['content-type']
and then add
browser_cache = int(os.getenv("PW_BROWSER_CACHE", "0"))
# Allow browser caching, if user permits, only for CSS, JavaScript and PNG images...
if browser_cache > 0 and (ftype == 'text/css' or ftype == 'application/javascript' or ftype == 'image/png'):
self.send_header("Cache-Control", "max-age={}".format(browser_cache))
else:
self.send_header("Cache-Control", "no-cache, no-store")
And finally, I would move the first line up to where all the other environment variables are retrieved.
David
There is a bug identified in the current pypowerwall release on how 404 HTTP status codes are handled.
Firmware version 23.44.0 has eliminated /api/devices/vitals
resulting in a 404 response from the Powerwall Gateway (TEG) when this is requested. There is a bug in the pypowerwall code that will treat this 404 like an authentication failure which will result in attempts to log in over and over, eventually hitting the rate limit. This is especially impactful for those using the proxy for things like Powerwall-Dashboard as the rate limit will prohibit other data gathering.
Related issue: #57
A fix is underway...
Am I blind, or is there no way to set the reserve / change modes using the Proxy?
I've noticed API requests to the Powerwall gateway are not using HTTP persistent connections (aka keep-alive or connection re-use), which results in every request opening a new connection to the gateway and closing again.
There can be a lot of requests being sent to the gateway, even more so if the Power Flow is being viewed via the proxy as with the default Grafana dashboard of the Powerwall-Dashboard (~7 additional requests every few seconds).
I assume the constant open/close of HTTP connections might cause more load on the Powerwall, not to mention the continual requirement for new TCP sockets and such. My Powerwall has been a bit flaky sometimes where it stopped responding for a while, which could be attributed to overloading it perhaps?
So, to potentially reduce the load and make pypowerwall more efficient with API requests to the gateway, I was thinking it would be better to utilise HTTP connection re-use and pooling.
This appears to be simple enough by using a Session object of the Requests module. I will submit a PR with my code changes for this.
Using the Session object results in HTTP connections to the gateway remaining established via keep-alive and being re-used with each API request. There could be multiple connections to the gateway (pooling), but this will be limited by the max pool size (10 by default, which seemed fine in my testing).
I ended up doing a fair amount of before/after testing of this change, as I wanted to quantify and validate that it was in fact beneficial?
I also did resilience testing with Powerwall-Dashboard and the pypowerwall proxy, to ensure there were no adverse affects. i.e. Testing what happens if an established HTTP connection was purposefully broken, invalidating the auth cookie to see if a re-login would occur, disrupting the network connection to the gateway for a while, etc. All my tests passed, with the connection to the gateway re-establishing okay without any user intervention or having to restart the pypowerwall proxy. π
Anyway, I thought I would include findings from my before/after testing for anyone interested...
@jasonacox - let me know what you think? This appears to be a very worthwhile change I believe. I think I just missed the v0.5.1 release as I was typing this out when released! v0.6.0 maybe? π
And sorry for such a long post! But hopefully the below testing might also be informative to others (as it was for me).
Firstly, I wanted to see what happens regarding HTTP connections direct to the gateway using a standard web browser. As my Powerwall is in a DMZ on my network, I used my firewall to view the TCP connections.
When browsing direct to the gateway (i.e. not via pypowerwall proxy), only 1 persistent HTTP TCP connection is used. With the browsers dev tools, the network activity showed requests being sent every second or so over this connection.
By comparison, when viewing the TCP connections to the gateway coming from the Powerwall-Dashboard and pypowerwall, I was seeing constant open/established/close of connections, using different ports.
After changing pypowerwall to use persistent HTTP connections however, it then behaves like a standard web browser by using keep-alive to keep connections established and re-use them for API requests, with multiple connections up to the pool max. Nice!
I also used the example test script to check connection use and measure any performance improvements. For the test purposes, I set pwcacheexpire=0
in the script to disable the pypowerwall caching.
Before the changes, debug output shows each API request creates a new HTTP connection.
After modifying pypowerwall to use persistent HTTP connections, only 1 connection is established and used for each API request.
Regarding the performance differences for the above, I measured the elapsed time of sending the set of requests over several iterations, with a 5 second delay between each set.
Before any changes, the time taken for each set of requests to complete was ~1 second.
When re-using a single HTTP connection however, the response time was significantly reduced, taking around only 0.25 seconds.
So, there definitely appears to be a benefit here, potentially reducing the load on the Powerwall gateway, while also improving response times!
The latest Powerwall Firmware upgrade to 22.26.1-foxtrot 4d562eaf resulted in a few bugs with the pyPowerwall proxy.
/version
API does not respond. This is due to an exception caused by the unexpected "-foxtrot" suffix.@danisla Do you know if the "compliance" text in the animation graphic could be easily fixed?
I like to begin by saying thank you for everyone's time, energy, and efforts toward this project; its been phenomenal to learn and use!
At 11:38pm last night I noticed that the pypowerwall was having authentication issues, I had to use the forget password function to reset the gateway password. Once I restarted everything I noticed that pypowerwall was no longer getting vitals and that the firmware version seems to be new.
output:
Device Version:
23.44.0 eb113390
Device Vitals:
None
generated by:
import pypowerwall
password='xxx'
email='xxx'
host = "xxx"
timezone = "America/Los_Angeles"
pw = pypowerwall.Powerwall(host,password,email,timezone)
print("Device Version:\n %s\n" % pw.version())
print("Device Vitals:\n %s\n" % pw.vitals(True))
From @youzer-name jasonacox/Powerwall-Dashboard#25 (comment)
I use Grafana and InfluxDB to monitor my Pi4's vital stats. Recently I noticed that my memory used was steadily climbing. When I checked the Task Manager in the Pi4 GUI I found that Python3 was using a lot of memory in the "VM-Size" column. After rebooting the Pi the total memory use dropped down a lot.
I've been watching the memory usage since my last reboot about 6 days ago. While pypowerwall isn't the only thing using more memory, restarting my pypowerwall container immediately dropped the total memory usage by about 11%. The memory use by Python3 dropped from over 400MB to < 20MB. Based on the numbers I saw before the reboot, I expect that if I had let it go longer, the Python3 memory utilization would have just continued to climb.
Is this normal or could there be a memory leak in pypowerwall or Python3?
This image shows the last 7 days. Towards the left you can see the before/after reboot. At the very right edge you can see the restart of the pypowerwall docker container.
Originally posted by @youzer-name in jasonacox/Powerwall-Dashboard#25
Is there a reason I can't set a reserve lower than 5? I get no response from the API when I set a value lower than 5.
Ideally I want to set the reserve to 0 as 0 set by cloud is actually 5% SoC if you look at the local API's battery level. At the moment setting the value to 5 leaves me at a local 10% SoC.
I'm trying create some dashboards based on the data collected by pypowerwall and my greatest problem is a understanding of the fields and their values
i.E:
site
and load
instant_power: 62
instant_reactive_power: -253
instant_apparent_power: 260.48608408128064
energy_imported
and energy_exported
fields?Thanks for clarification
Dakky
Provide a call to /api/system_status/grid_status
from https://github.com/vloschiavo/powerwall2 the return values would be:
{"grid_status":"SystemGridConnected","grid_services_active":false}
{"grid_status":"SystemGridConnected"} = grid is up
{"grid_status":"SystemIslandedActive"} = grid is down
{"grid_status":"SystemTransitionToGrid"} = grid is restored but not yet in sync.
if verbose=True
then return the full json which would include grid_services_active
Two things related to poll()
If I make a pull request and make these changes, would you be interested in accepting them?
Raised by user:
The script set_mode.py has been working but in the past few days it has failed. Here is the me what I see.
Traceback (most recent call last):
File "C:\Users\dgpet\Documents\Projects\Python\Powerwall\set-mode.py", line 299, in
mode = data["operation"]
KeyError: 'operation'I have dumped the βdataβ though I donβt have a copy of successful one to compare too.. Anything I can do to help?
Confirmed that the data payload no longer contains 'operation' key.
Pypowerwall proxy stops working when the pypowerwall.env
file has PW_CONTROL_SECRET
set. If the value is empty things work on. This happens for me on both the latest container and jasonacox/pypowerwall:0.8.4t54-beta2
[email protected]
PW_PASSWORD=password
PW_HOST=192.168.7.87
PW_TIMEZONE=America/Chicago
PW_STYLE=grafana-dark
TZ=America/Chicago
PW_DEBUG=no
PW_CACHE_EXPIRE=30
PW_POOL_MAXSIZE=1
PW_CONTROL_SECRET=password.here
2024-04-18 16:14:08 NameError: name 'pw_control' is not defined
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [proxy] [INFO] pyPowerwall [0.8.4] Proxy Server [t54] - HTTP Port 8675
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [proxy] [INFO] pyPowerwall Proxy Started
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [proxy] [INFO] pyPowerwall Proxy Server - Local Mode
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [proxy] [INFO] Connected to Energy Gateway 192.168.7.87 (Harrison)
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [proxy] [INFO] Control Commands Activating - WARNING: Use with caution!
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [pypowerwall.cloud.pypowerwall_cloud] [WARNING] Missing auth file .auth/.pypowerwall.auth - run setup
2024-04-18 16:14:09 04/18/2024 04:14:09 PM [proxy] [ERROR] Control Mode Failed: Unable to connect to cloud - Run Setup
2024-04-18 16:14:09 Traceback (most recent call last):
2024-04-18 16:14:09 File "/app/server.py", line 205, in <module>
2024-04-18 16:14:09 if pw_control:
2024-04-18 16:14:09 NameError: name 'pw_control' is not defined
Host: 192.168.7.87 ... OPEN - Found Powerwall 1232100-00-H--GF2231140002H0
[Firmware 23.44.0 eb113390]
The tools set-reserve.py
and set-mode.py
started producing a 410 Client Error message:
requests.exceptions.HTTPError: 410 Client Error: https://powergate.prd.sn.tesla.services:443/api/powerwalls/xx/fullstatus => Gone for url: https://owner-api.teslamotors.com/api/1/powerwalls/xx
It appears that Tesla changed their API. These tools use TeslaPy and the issue was identified here: tdorssers/TeslaPy#145
Thanks to John Banks for raising this issue.
Hi @jasonacox,
I'm not quite sure what is happening here, but the code in the server modules is doing something odd. Using server.py as the example:
from http.server import BaseHTTPRequestHandler, HTTPServer, ThreadingHTTPServer
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
daemon_threads = True
pass
From a quick object inspection, line 347 creates server with base classes of HTTPServer and ThreadingMixIn (from line 110) - which is not helpful, because these are the base classes we would expect from the redefinition and also the base classes for ThreadingHTTPServer ...
Is this what you intended or should lines 110-112 be removed and line be modified to:
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
I'm guessing the latter?
(This is an aside triggered by the ToU stuff I'm putting together - which unfortunately could be made thread unsafe very easily!).
Back when you could set reserve on the local PowerWall gateway API it would always charge from the grid at the full 5kw.
When that broke I switched to using ToU in the Tesla app, and it always pulled 5kw from the grid when charging.
Now I've switched to pypowerwall and am setting the reserve, it only seems to be charging at ~1850W from the grid.
This may be coincidental of course and I have something else going on.
Any thoughts?
Hi,
python3 set-reserve.py --read
fails with this error:
ERROR: Failed to retrieve PRODUCT_LIST - (missing_token) Missing access token parameter.
I guess the problem is that the auth token has expired as I have the following in the set-reserve.auth file:
... "expires_in": 28800, "token_type": "Bearer", "expires_at": 1700028409.1826518}}}
which can be converted as expires_in 8 hours and expires_at Wednesday, November 15, 2023 6:06:49 AM GMT .
Is there a way to automatically refresh the token without having to login again?
Can it be embedded in set-reserve.py and the other tools?
Thanks
Tesla's firmware 23.28.2 no longer supports the old way of getting metrics as far as I can see. Going to my Powerwall's IP address I get "Switch to Tesla Pros for a better commissioning experience. The current experience is no longer supported"
The pypowerwall app says it connects but there are not any metrics:
10/13/2023 05:13:32 PM [proxy] [INFO] pyPowerwall [0.6.2] Proxy Server [t27] - HTTP Port 8675
10/13/2023 05:13:32 PM [proxy] [INFO] pyPowerwall Proxy Started
Will pypowerwall support the newer Tesla Pros application?
Should have posted to Powerwall-Dashboard repo. No action required.
I'm maintaining the FreeBSD port for pypowerwall and have noticed that the new Powerwall-Dashboards require the /alerts/pw call to exist.
Would you be able to tag a new release so I can update the port and get this functionality out to the world?
Thanks!
You can get detailed battery information from the 'system_status' api call. In particular, you get
"Type": "",
"PackagePartNumber": "3012170-10-B",
"PackageSerialNumber": "TGxxxxx",
"disabled_reasons": [],
"pinv_state": "PINV_GridFollowing",
"pinv_grid_state": "Grid_Compliant",
"nominal_energy_remaining": 13803,
"nominal_full_pack_energy": 13803,
"p_out": 0,
"q_out": 0,
"v_out": 248.60000000000002,
"f_out": 60.007999999999996,
"i_out": -0.4,
"energy_charged": 12780,
"energy_discharged": 740,
"off_grid": false,
"vf_mode": false,
"wobble_detected": false,
"charge_power_clamped": false,
"backup_ready": true,
"OpSeqState": "Active",
"version": "b0ec24329c08e4"
What do you think of having a detailed_battery()
call that returns this information plus adds a temperature
value from the vitals()
data?
The parameters would follow the the verbose=True to return the JSON. Otherwise, return a dict structure per battery with the two nominal energy readings and the temperature.
Currently, the root path on the proxy only shows the animation. It would be nice to have links to all the proxy and api endpoints as a hyperlink on that page too.
Firstly, another outstanding bit of coding.
The documentation says: This python module can be used to monitor and control Tesla Energy Powerwalls.
I imagine this has been discussed many times but there is a train of thought that forcing the backup level to 100% forces the Powerwall to charge no matter what option is chosen and thus override the Tesla "algorithm". I know people who wait until the off peak time (10 PM or perhaps 11 PM for daylight savings) to change their battery backup reserve to 100% and then get up before the peak time (usually 7 AM) to ensure they are not charging during peak hours.
So can this code do that? I can't (probably not looking hard enough) find anything to say set backup reserve to 100%. Then something could poll the battery to ensure it reaches 100% before peak time, set the battery reserve back to say 15%. Importing from the grid seems to be set to 3.5 kWh so a 13.5 kWh battery needs around 4 hours to charge from 10%.
I generally just leave my battery alone but it would be interesting to see how this could work. A couple of wet cloudy days really screws with charging and more often than not, the "algorithm" doesn't pick the weather and my battery will not charge.
Regards
Stephen
These aren't unique to 0.8.2, but thought you'd like to see them regardless with all the new error handling.
2024-04-12 22:01:05 04/12/2024 10:01:05 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:01:05 04/12/2024 10:01:05 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:01:06 04/12/2024 10:01:06 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:01:08 04/12/2024 10:01:08 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:01:08 04/12/2024 10:01:08 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:31:10 04/12/2024 10:31:10 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:31:10 04/12/2024 10:31:10 PM [proxy] [ERROR] Socket broken sending API response to client [doGET]: [Errno 32] Broken pipe
2024-04-12 22:31:11 ----------------------------------------
2024-04-12 22:31:11 Exception occurred during processing of request from ('172.18.0.6', 42768)
2024-04-12 22:31:11 Traceback (most recent call last):
2024-04-12 22:31:11 File "/usr/local/lib/python3.10/socketserver.py", line 683, in process_request_thread
2024-04-12 22:31:11 self.finish_request(request, client_address)
2024-04-12 22:31:11 File "/usr/local/lib/python3.10/socketserver.py", line 360, in finish_request
2024-04-12 22:31:11 self.RequestHandlerClass(request, client_address, self)
2024-04-12 22:31:11 File "/usr/local/lib/python3.10/socketserver.py", line 747, in __init__
2024-04-12 22:31:11 self.handle()
2024-04-12 22:31:11 File "/usr/local/lib/python3.10/http/server.py", line 433, in handle
2024-04-12 22:31:11 self.handle_one_request()
2024-04-12 22:31:11 File "/usr/local/lib/python3.10/http/server.py", line 421, in handle_one_request
2024-04-12 22:31:11 method()
2024-04-12 22:31:11 File "/app/server.py", line 321, in do_GET
2024-04-12 22:31:11 fcv["grid_status"] = pw.grid_status(type="numeric")
2024-04-12 22:31:11 File "/app/pypowerwall/__init__.py", line 616, in grid_status
2024-04-12 22:31:11 grid_status = payload['grid_status']
2024-04-12 22:31:11 TypeError: 'NoneType' object is not subscriptable
2024-04-12 22:31:11 ----------------------------------------
I'm running pypowerwall in an Unraid docker, but am having to re-perform the setup process each time I make a change to the docker and the image is refreshed.
It would be great to be able to define a custom path for .pypowerwall.auth and other volatile config related files so I can store them outside of the root file structure. Then in Unraid I can redirect a container path to a host path.
How would I achieve this?
Adding some feedback on alert codes, and my understanding of what some of them mean:
BackfeedLimited - I believe this controls backfeed before PTO, under the "Permission to Operate" option under the settings menu. Prior to PTO the backfeed rate is limited, as the system needs to produce over the load, but by the minimum possible. The system seems to regulate this by switching MCI's open/closed, to minimize the overage that must be backfed, as the power has to go somewhere, and at this point the batteries are 100%. This seems to toggle that setting per inverter. I was told by Tesla that one of my two powerwalls were still stuck in this mode, post PTO, because of some software glitch that doesn't always toggle all devices to on, when selecting Yes to operate from the app.
RealPowerAvailableLimited - Seems related to the above
POD_w031_SW_Brick_OV
POD_w044_SW_Brick_UV_Warning
POD_w045_SW_Brick_OV_Warning
I believe all the Brick warnings are related to preventing the condition where the powerwall doesn't have the minimum amount of power it needs to turn back on. When this happens, a third party charger is needed to get the powerwall back to it's minimum operating battery requirement to turn back on, or it's "bricked". Solar cannot return it to this state, because it needs power to make power.
Greetings,
Version v0.7.7 Tesla Firmware 23.44.0
I just installed the latest version and I have the right set-reserve.auth and set-reserve.conf. However, when I tried to run the following command, it produces this:
python3 set-reserve.py --read
Traceback (most recent call last):
File "/home/pi/pypowerwall/tools/set-reserve.py", line 313, in
data = get_level()
File "/home/pi/pypowerwall/tools/set-reserve.py", line 264, in get_level
data = battery.get_site_info()
AttributeError: 'Battery' object has no attribute 'get_site_info'
I can get info out of the battery by using the example.py so it seems the local account is good. This used to work with an older version but I haven't tried recently.
Thanks
Stephen
Is it possible to add the following to READ and SET
https://owner-api.teslamotors.com/api/1/energy_sites/{}/grid_import_export
Payload options:
"customer_preferred_export_rule": "battery_ok"
"customer_preferred_export_rule": "pv_only"
"disallow_charge_from_grid_with_solar_installed": false
"disallow_charge_from_grid_with_solar_installed": true
Things have stopped working for me since you implemented POST.
I can set the reserve correctly using POST using curl from your example, but when I set the method in my node-red function to POST it says my token is bad.
"{"unauthorized": "Control Command Token Invalid"}"
I'm not sure what I'm doing wrong. It should be as simple as changing msg.method in my custom function:
if (powerwall_reserve!=charge_level && overnight_charge===true) {
msg.method = "POST"
msg.url = "http://192.168.78.40:8675/control/reserve?token=MY_TOKEN&value=" + charge_level
return msg;
The only thing I can see different is your example sets the value before the token. Should this matter?
Any ideas?
Open issue for Greg Rhan - See jasonacox/Powerwall-Dashboard#1
Thanks for putting together your [Powerwall Monitor] - looks great!
I'm trying to get it set up and it looks like your pypowerwall container may not be built for arm (I'm on a M1 Mac and Raspberry Pi v3b+). Any chance you could update the docker image to support linux/arm64 & linux/arm/v7 in addition to amd64? Thanks in advance.From my Pi
$ docker logs pypowerwall
standard_init_linux.go:228: exec user process caused: exec format error
standard_init_linux.go:228: exec user process caused: exec format error
standard_init_linux.go:228: exec user process caused: exec format errorFrom my M1 Mac
battery_blocks() merges battery specific information from /api/system_status and /api/device/vitals
Currently, it is only pulling data from the TETHC block but more information is available in the TPOD and TINV blocks.
Pulling data from those blocks is a little harder since we'd have to match against the componentParentDin to find the battery serial number.
There should also be some thought on which design path to take when pulling in the info from the other blocks as, at first glance, there may be the same data already exposed via system_status. As such, we'd want to consider whether battery_blocks() should provide a merged view of all data or present the TPOD/TINV data in a separate block. I'd probably lean towards providing more data fidelity and show the data provenance, over ease of access.
When I access x.x.x.x:8675/ for the powerflow animation, the background of the page is transparent, and I can't see the current battery percentage without highlighting the text on the page.
Did I miss a configuration step?
If I look at the page source and change the CSS background to black, it looks great, but then I think it autorefreshes and goes back to background=transparent.
Why does the PW_HOST setting in the env file have to be an IP address? I tried setting it to the hostname of the Gateway, but the container complains that it can't connect. Why would I want to use the hostname? If my Access Point reboots for an update, the gateway disconnects and falls back to the ethernet connection...which has a DIFFERENT IP address. Which means that the dashboard isn't getting any data. By specifying a hostname instead of an IP address, the proxy will ALWAYS be able to connect to the gateway, regardless of if it's connected via WiFi or ethernet.
Ran the upgrade.sh script to update to latest version, expected tessolarcharge.py script to be available under 'tools' folder but could not find this script after the update.
These are what can be found under tools after running the update:
-rw-r--r-- 1 pi pi 3532 Oct 4 20:36 DOCKER.md
drwxr-xr-x 2 pi pi 4096 May 8 2023 ecowitt-weather-history
drwxr-xr-x 2 pi pi 4096 Mar 28 2023 fixmonthtags
drwxr-xr-x 5 pi pi 4096 May 8 2023 influxdb2
drwxr-xr-x 2 pi pi 4096 Mar 28 2023 mysql
drwxr-xr-x 2 pi pi 4096 Mar 28 2023 pvoutput
drwxr-xr-x 2 pi pi 4096 Jul 3 17:11 pwstatus
-rw-r--r-- 1 pi pi 4540 Dec 4 23:12 README.md
drwxr-xr-x 2 pi pi 4096 Dec 4 23:12 solar-only
drwxr-xr-x 2 pi pi 4096 Dec 4 23:12 tesla-history
drwxr-xr-x 2 pi pi 4096 Oct 16 22:09 usage-service
drwxr-xr-x 2 pi pi 4096 Jul 3 17:11 weather-history
I noticed that you added a control surface for the battery reserve. I am very excited about this for my needs. Thank you all for working on this. π―
I was curious why you made the request a GET and not a POST? Maybe this has already been debated. My history with creating web APIs points to avoiding using GETs in front of anything that changes the state of the world.
Feel free to close this if I am being too pedantic.
Thanks again.
This is a cross-reference link to a discussion at vloschiavo/powerwall2#51 on the Tesla Powerwall Gateway /api/devices/vitals
endpoint. This issue is to troubleshoot and help provide help getting real time string data to trend and identify system issues.
By "string" I mean each string of solar panels that are connected together and terminated at the Powerwall solar inverter. The Powerwall+ allows for 4 strings each, identified as A, B, C and D. If you have multiple inverters, the pyPowerwall pw.strings()
function will append a number to the letter to accommodate multiple inverters (A, B, C, D, A1, B1, C1, D1, A2, ...).
/api/devices/vitals
endpoint responds with a protobuf binary payload. Thanks to the great work by @brianhealey, we have a tesla.proto file that can be used to create python interfaces to the data. pyPowerwall now uses this to decode the vitals payload through two simple functions, vitals()
and strings()
:# Install pyPowerwall package
pip install pypowerwall
import pypowerwall
# Credentials for your Powerwall - Customer Login Data
password='password'
email='[email protected]'
host = "localhost" # Change to the IP of your Powerwall
timezone = "America/Los_Angeles" # Change to your local timezone/tz
# Connect to Powerwall
pw = pypowerwall.Powerwall(host,password,email,timezone)
# Display Vitals (returns details as dictionary or JSON)
vitals = pw.vitals(jsonformat=False)
print(vitals)
# Display String Data
strings = pw.strings(jsonformat=False)
print(strings)
I wanted to be able to capture string data to see if my system was performing correctly. If you want to see it in human readable format, just specify jsonformat as True like this:
strings = pw.strings(jsonformat=True)
print(strings)
Output:
{
"A": {
"Connected": true,
"Current": 0.48,
"Power": 115.0,
"State": "PV_Active",
"Voltage": 251.8
},
"B": {
"Connected": false,
"Current": 0.0,
"Power": 0.0,
"State": "PV_Active",
"Voltage": -2.3999999999999995
},
"C": {
"Connected": true,
"Current": 1.82,
"Power": 526.0,
"State": "PV_Active",
"Voltage": 293.6
},
"D": {
"Connected": true,
"Current": 1.81,
"Power": 521.0,
"State": "PV_Active_Parallel",
"Voltage": 294.0
}
}
I push that into a tiny installation of Splunk (see TinySplunk) and get a graph like this:
Here is an example script I used to pull the string data and stuff it into Splunk:
# pyPowerwall - Powerwall Solar String data to Splunk
from splunk_http_event_collector import http_event_collector
import logging
import sys
import pypowerwall
# Credentials for your Powerwall - Customer Login Data
password='password'
email='[email protected]'
host = "localhost" # Change to the IP of your Powerwall
timezone = "America/Los_Angeles" # Change to your local timezone/tz
# Connect to Powerwall
pw = pypowerwall.Powerwall(host,password,email,timezone)
# Grab strings data from Powerwall
strings = pw.strings(jsonformat=False)
# init logging config, this would be job of your main code using this class.
logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S %z')
# Create event collector object, default SSL and HTTP Event Collector Port
http_event_collector_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
http_event_collector_host = "10.1.1.101"
# Setup Splunk HEC Connector
splunk = http_event_collector(http_event_collector_key, http_event_collector_host)
splunk.log.setLevel(logging.ERROR)
# perform a HEC reachable check
hec_reachable = splunk.check_connectivity()
if not hec_reachable:
sys.exit(1)
# break apart JSON into event items
event = {}
try:
for k, v in strings.items():
# key and value are payload event items
event.update({k:v})
# Build payload with metadata information
payload = {}
payload.update({"index":"main"})
payload.update({"sourcetype":"solar-strings"})
payload.update({"source":"http-stream"})
payload.update({"host":"pypowerwall"})
payload.update({"event":event})
splunk.sendEvent(payload)
splunk.flushBatch()
except:
print("ERROR: Unable to send")
I created a dashboard graphs with Splunk queries like this:
* sourcetype="solar-strings" | timechart span=5m avg(A.Power) as "A" avg(B.Power) as "B" avg(C.Power) as "C" avg(D.Power) as "D"
I also have occasional trouble with the Neruio solar meter that Tesla installed in my Powerwall+ so I also have scripts to watch that. It shows up in the vitals
payload as a device that I monitor and capture to create alerts (e.g. if it doesn't show up in the vitals
payload that means it has lost connection with the gateway and shut down solar production):
"NEURIO--VAHxxxxxxxxxx": {
"NEURIO_CT0_InstRealPower": 1333.5606370817025,
"NEURIO_CT0_Location": "solar",
"Parent": "STSTSM--xxxxxxx-00-E--TGxxxxxxxxxxxx",
"firmwareVersion": "1.6.1-Tesla",
"lastCommunicationTime": "1638720520",
"manufacturer": "NEURIO",
"partNumber": "",
"serialNumber": "VAHxxxxxxxxxx"
}
I have a script that pulls this out of the vitals payload and stuffs it into Splunk as well. Below is an example showing a dropout where the Neurio loses connection and reconnects:
Splunk queries:
sourcetype="solar-meter"
| eval connected=state*(-1000)
| timechart span=5m avg(power) as Power avg(connected) as Connected
sourcetype="solar-meter"
| eval tsmax=if(ts == 0, _time, ts)
| eval delay=_time-tsmax
| timechart span=5m avg(delay) as Delay
The latest firmware upgrade from Tesla Powerwall has changed the powerflow animation javascript on the gateway. JavaScript error are now being produced for anyone using the pypowerwall proxy to render the animation.
@danisla Let me know if you see the same thing or have any suggestions for a fix. I'm taking a look as well.
Errors:
Issue also raised in Powerwall-Dashboard: jasonacox/Powerwall-Dashboard#88
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.