Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Grid Control Functions #109

Merged
merged 2 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ and call function to poll data. Here is an example:
set_mode(mode) # Set Current Battery Operation Mode
get_time_remaining() # Get the backup time remaining on the battery
set_operation(level, mode, json) # Set Battery Reserve Percentage and/or Operation Mode
set_grid_charging(mode) # Enable or disable grid charging (mode = True or False)
set_grid_export(mode) # Set grid export mode (mode = battery_ok, pv_only, never)
get_grid_charging() # Get the current grid charging mode
get_grid_export() # Get the current grid export mode
```

## Tools
Expand All @@ -243,16 +247,18 @@ Options:

Commands (run <command> -h to see usage information):
{setup,scan,set,get,version}
setup Setup Tesla Login for Cloud Mode access
scan Scan local network for Powerwall gateway
set Set Powerwall Mode and Reserve Level
get Get Powerwall Settings and Power Levels
version Print version information
setup Setup Tesla Login for Cloud Mode access
scan Scan local network for Powerwall gateway
set Set Powerwall Mode and Reserve Level
get Get Powerwall Settings and Power Levels
version Print version information

set options:
-mode MODE Powerwall Mode: self_consumption, backup, or autonomous
-reserve RESERVE Set Battery Reserve Level [Default=20]
-current Set Battery Reserve Level to Current Charge
-mode MODE Powerwall Mode: self_consumption, backup, or autonomous
-reserve RESERVE Set Battery Reserve Level [Default=20]
-current Set Battery Reserve Level to Current Charge
-gridcharging MODE Set Grid Charging (allow) Mode ("on" or "off")
-gridexport MODE Set Export to Grid Mode ("battery_ok", "pv_only", or "never")

get options:
-format FORMAT Output format: text, json, csv
Expand Down
49 changes: 49 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,54 @@
# RELEASE NOTES

## v0.10.10 - Add Grid Control

* Add a function and command line options to allow user to get and set grid charging and exporting modes (see https://github.com/jasonacox/pypowerwall/issues/108).
* Supports FleetAPI and Cloud modes only (not Local mode)

#### Command Line Examples

```bash
# Connect to Cloud
python3 -m pypowerwall setup # or fleetapi

# Get Current Settings
python3 -m pypowerwall get

# Turn on Grid charging
python3 -m pypowerwall set -gridcharging on

# Turn off Grid charging
python3 -m pypowerwall set -gridcharging off

# Set Grid Export to Solar (PV) energy only
python3 -m pypowerwall set -gridexport pv_only

# Set Grid Export to Battery and Solar energy
python3 -m pypowerwall set -gridexport battery_ok

# Disable export of all energy to grid
python3 -m pypowerwall set -gridexport never
```

#### Programming Examples

```python
import pypowerwall

# FleetAPI Mode
PW_HOST=""
PW_EMAIL="my@example.com"
pw = pypowerwall.Powerwall(host=PW_HOST, email=PW_EMAIL, fleetapi=True)

# Get modes
pw.get_grid_charging()
pw.get_grid_export()

# Set modes
pw.set_grid_charging("on") # set grid charging mode (on or off)
pw.set_grid_export("pv_only") # set grid export mode (battery_ok, pv_only, or never)
```

## v0.10.9 - TEDAPI Voltage & Current

* Add computed voltage and current to `/api/meters/aggregates` from TEDAPI status data.
Expand Down
51 changes: 50 additions & 1 deletion pypowerwall/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@
set_mode(mode) # Set Current Battery Operation Mode
get_time_remaining() # Get the backup time remaining on the battery
set_operation(level, mode, json) # Set Battery Reserve Percentage and/or Operation Mode
set_grid_charging(mode) # Enable or disable grid charging (mode = True or False)
set_grid_export(mode) # Set grid export mode (mode = battery_ok, pv_only, never)
get_grid_charging() # Get the current grid charging mode
get_grid_export() # Get the current grid export mode

Requirements
This module requires the following modules: requests, protobuf, teslapy
Expand All @@ -84,7 +88,7 @@
from typing import Union, Optional
import time

version_tuple = (0, 10, 9)
version_tuple = (0, 10, 10)
version = __version__ = '%d.%d.%d' % version_tuple
__author__ = 'jasonacox'

Expand Down Expand Up @@ -835,6 +839,51 @@ def get_time_remaining(self) -> Optional[float]:
"""
return self.client.get_time_remaining()

def set_grid_charging(self, mode) -> Optional[dict]:
"""
Enable or disable grid charging

Args:
mode: Set to True to enable grid charging, False to disable it

Returns:
Dictionary with operation results.
"""
return self.client.set_grid_charging(mode)

def get_grid_charging(self) -> Optional[bool]:
"""
Get the current grid charging mode

Returns:
True if grid charging is enabled, False if it is disabled
"""
return self.client.get_grid_charging()

def set_grid_export(self, mode: str) -> Optional[dict]:
"""
Set grid export mode

Args:
mode: Set grid export mode (battery_ok, pv_only, or never)

Returns:
Dictionary with operation results.
"""
# Check for valid mode
if mode not in ['battery_ok', 'pv_only', 'never']:
raise ValueError(f"Invalid value for parameter 'mode': {mode}")
return self.client.set_grid_export(mode)

def get_grid_export(self) -> Optional[str]:
"""
Get the current grid export mode

Returns:
The current grid export mode
"""
return self.client.get_grid_export()

def _validate_init_configuration(self):

# Basic user input validation for starters. Can be expanded to limit other parameters such as cache
Expand Down
27 changes: 24 additions & 3 deletions pypowerwall/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
help="Set Battery Reserve Level [Default=20]")
set_mode_args.add_argument("-current", action="store_true", default=False,
help="Set Battery Reserve Level to Current Charge")
set_mode_args.add_argument("-gridcharging", type=str, default=None,
help="Enable Grid Charging Mode: on or off")
set_mode_args.add_argument("-gridexport", type=str, default=None,
help="Grid Export Mode: battery_ok, pv_only, or never")

get_mode_args = subparsers.add_parser("get", help='Get Powerwall Settings and Power Levels')
get_mode_args.add_argument("-format", type=str, default="text",
Expand Down Expand Up @@ -129,8 +133,8 @@
# Set Powerwall Mode
elif command == 'set':
# If no arguments, print usage
if not args.mode and not args.reserve and not args.current:
print("usage: pypowerwall set [-h] [-mode MODE] [-reserve RESERVE] [-current]")
if not args.mode and not args.reserve and not args.current and not args.gridcharging and not args.gridexport:
print("usage: pypowerwall set [-h] [-mode MODE] [-reserve RESERVE] [-current] [-gridcharging MODE] [-gridexport MODE]")
sys.exit(1)
import pypowerwall
# Determine which cloud mode to use
Expand All @@ -154,6 +158,20 @@
current = float(pw.level())
print("Setting Powerwall Reserve to Current Charge Level %s" % current)
pw.set_reserve(current)
if args.gridcharging:
gridcharging = args.gridcharging.lower()
if gridcharging not in ['on', 'off']:
print("ERROR: Invalid Grid Charging Mode [%s] - must be on or off" % gridcharging)
sys.exit(1)
print("Setting Grid Charging Mode to %s" % gridcharging)
pw.set_grid_charging(gridcharging)
if args.gridexport:
gridexport = args.gridexport.lower()
if gridexport not in ['battery_ok', 'pv_only', 'never']:
print("ERROR: Invalid Grid Export Mode [%s] - must be battery_ok, pv_only, or never" % gridexport)
sys.exit(1)
print("Setting Grid Export Mode to %s" % gridexport)
pw.set_grid_export(gridexport)

# Get Powerwall Mode
elif command == 'get':
Expand All @@ -177,6 +195,8 @@
'home': pw.home(),
'battery': pw.battery(),
'solar': pw.solar(),
'grid_charging': pw.get_grid_charging(),
'grid_export_mode': pw.get_grid_export(),
}
if args.format == 'json':
print(json.dumps(output, indent=2))
Expand All @@ -190,7 +210,8 @@
# Table Output
for item in output:
name = item.replace("_", " ").title()
print(" {:<15}{}".format(name, output[item]))
print(" {:<18}{}".format(name, output[item]))
print("")

# Print Version
elif command == 'version':
Expand Down
58 changes: 57 additions & 1 deletion pypowerwall/cloud/pypowerwall_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ def get_site_config(self, force: bool = False):
"vpp_backup_reserve_percent": 80
}
}

"""
# GET api/1/energy_sites/{site_id}/site_info
(response, _) = self._site_api("SITE_CONFIG", SITE_CONFIG_TTL, language="en", force=force)
Expand Down Expand Up @@ -756,6 +755,63 @@ def get_api_system_status(self, **kwargs) -> Optional[Union[dict, list, str, byt

return data

def set_grid_charging(self, mode: str) -> bool:
"""
Enable/Disable grid charging mode (mode: "on" or "off")
"""
if mode in ["on", "yes"] or mode is True:
mode = False
elif mode in ["off", "no"] or mode is False:
mode = True
else:
log.debug(f"Invalid mode: {mode}")
return False
response = self._site_api("ENERGY_SITE_IMPORT_EXPORT_CONFIG", ttl=SITE_CONFIG_TTL, force=True,
disallow_charge_from_grid_with_solar_installed = mode)
# invalidate cache
super()._invalidate_cache("SITE_CONFIG")
self.pwcachetime["SITE_CONFIG"] = 0
return response

def set_grid_export(self, mode: str) -> bool:
"""
Set grid export mode (battery_ok, pv_only, or never)

Mode will show up in get_site_info() under components:
* never
"non_export_configured": true,
"customer_preferred_export_rule": "never",
* pv_only
"customer_preferred_export_rule": "pv_only"
* battery_ok
"customer_preferred_export_rule": "battery_ok"
or not set
"""
if mode not in ["battery_ok", "pv_only", "never"]:
log.debug(f"Invalid mode: {mode} - must be battery_ok, pv_only, or never")
# POST api/1/energy_sites/{site_id}/grid_import_export
response = self._site_api("ENERGY_SITE_IMPORT_EXPORT_CONFIG", ttl=SITE_CONFIG_TTL, force=True,
customer_preferred_export_rule = mode)
# invalidate cache
super()._invalidate_cache("SITE_CONFIG")
self.pwcachetime["SITE_CONFIG"] = 0
return response

def get_grid_charging(self, force=False):
""" Get allow grid charging allowed mode (True or False) """
components = self.get_site_config(force=force).get("response").get("components") or {}
state = components.get("disallow_charge_from_grid_with_solar_installed")
return not state

def get_grid_export(self, force=False):
""" Get grid export mode (battery_ok, pv_only, or never) """
components = self.get_site_config(force=force).get("response").get("components") or {}
# Check to see if non_export_configured - pre-PTO setting
if components.get("non_export_configured"):
return "never"
mode = components.get("customer_preferred_export_rule") or "battery_ok"
return mode

# noinspection PyUnusedLocal
@not_implemented_mock_data
def api_logout(self, **kwargs) -> Optional[Union[dict, list, str, bytes]]:
Expand Down
Loading
Loading