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

OAuth improvements and new status codes #43

Merged
merged 10 commits into from
Jun 3, 2024
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ app_password=your-app-password
### CLI usage

```
usage: myenergi [-h] [-u USERNAME] [-p PASSWORD] [-e APP_EMAIL] [-a APP_PASSWORD] [-d] [-j]
usage: myenergi [-h] [-u USERNAME] [-p PASSWORD] [-e APP_EMAIL] [-a APP_PASSWORD] [-d] [-j] [--skip-oauth]
{list,overview,zappi,eddi,harvi,libbi} ...

myenergi CLI.
Expand Down
13 changes: 8 additions & 5 deletions pymyenergi/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
async def main(args):
username = args.username or input("Please enter your hub serial number: ")
password = args.password or getpass(prompt="Password (apikey): ")
app_email = args.app_email or input(
"App email (enter to skip; only needed for libbi): "
)
if app_email:
app_password = args.app_password or getpass(prompt="App password: ")
if not args.skip_oauth:
app_email = args.app_email or input("App email (enter to skip; only needed for libbi): ")
if app_email:
app_password = args.app_password or getpass(prompt="App password: ")
else:
app_password = ""
else:
app_email = ""
app_password = ""
conn = Connection(username, password, app_password, app_email)
if app_email and app_password:
Expand Down Expand Up @@ -197,6 +199,7 @@ def cli():
dest="app_email",
default=config.get("hub", "app_email").strip('"'),
)
parser.add_argument("--skip-oauth", dest="skip_oauth", action="store_true", default=False)
parser.add_argument("-d", "--debug", dest="debug", action="store_true")
parser.add_argument("-j", "--json", dest="json", action="store_true", default=False)
parser.add_argument("--version", dest="version", action="store_true", default=False)
Expand Down
12 changes: 7 additions & 5 deletions pymyenergi/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
_CLIENT_ID = "2fup0dhufn5vurmprjkj599041"



class Connection:
"""Connection to myenergi API."""

Expand All @@ -41,7 +42,7 @@ def __init__(
self.app_email = app_email
self.auth = httpx.DigestAuth(self.username, self.password)
self.headers = {"User-Agent": "Wget/1.14 (linux-gnu)"}
if self.app_email and app_password:
if self.app_email and self.app_password:
self.oauth = Cognito(_USER_POOL_ID, _CLIENT_ID, username=self.app_email)
self.oauth.authenticate(password=self.app_password)
self.oauth_headers = {"Authorization": f"Bearer {self.oauth.access_token}"}
Expand All @@ -62,10 +63,11 @@ def _checkMyenergiServerURL(self, responseHeader):
raise WrongCredentials()

async def discoverLocations(self):
locs = await self.get("/api/Location", oauth=True)
# check if guest location - use the first location by default
if locs["content"][0]["isGuestLocation"] == True:
self.invitation_id = locs["content"][0]["invitationData"]["invitationId"]
if self.app_email and self.app_password:
locs = await self.get("/api/Location", oauth=True)
# check if guest location - use the first location by default
if locs["content"][0]["isGuestLocation"] == True:
self.invitation_id = locs["content"][0]["invitationData"]["invitationId"]

def checkAndUpdateToken(self):
# check if we have oauth credentials
Expand Down
65 changes: 39 additions & 26 deletions pymyenergi/libbi.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import logging

from pymyenergi.connection import Connection

from . import LIBBI
from .base_device import BaseDevice

_LOGGER = logging.getLogger(__name__)

STATES = {
0: "Off",
1: "On",
Expand All @@ -27,9 +23,11 @@
151: "FW Upgrade (ARM)",
156: "FW Upgrade (DSP)",
172: "BMS Charge Temperature Low",
176: "BMS Updating",
234: "Calibration Charge",
251: "FW Upgrade (DSP)",
252: "FW Upgrade (ARM)",
253: "BMS Upgrading",
}

LIBBI_MODES = ["Stopped", "Normal", "Export"]
Expand Down Expand Up @@ -83,10 +81,6 @@ def local_mode(self):
"""Get current known status"""
return self._data.get("lmo", 1)

@property
def prefix(self):
return "E"

@property
def ct_keys(self):
"""Return CT key names that are not none"""
Expand Down Expand Up @@ -201,12 +195,18 @@ def generated(self):
@property
def charge_from_grid(self):
"""Is charging from the grid enabled?"""
return self._extra_data.get("charge_from_grid")
if self._connection.app_email and self._connection.app_password:
return self._extra_data.get("charge_from_grid")
else:
return None

@property
def charge_target(self):
"""Libbi charge target"""
return self._extra_data.get("charge_target", 0) / 1000
if self._connection.app_email and self._connection.app_password:
return self._extra_data.get("charge_target", 0) / 1000
else:
return None

@property
def prefix(self):
Expand All @@ -231,12 +231,15 @@ async def set_operating_mode(self, mode: str):

async def set_charge_from_grid(self, charge_from_grid: bool):
"""Set charge from grid"""
await self._connection.put(
f"/api/AccountAccess/LibbiMode?chargeFromGrid={charge_from_grid}&serialNo={self._serialno}",
oauth=True,
)
self._extra_data["charge_from_grid"] = charge_from_grid
return True
if self._connection.app_email and self._connection.app_password:
await self._connection.put(
f"/api/AccountAccess/LibbiMode?chargeFromGrid={charge_from_grid}&serialNo={self._serialno}",
oauth=True,
)
self._extra_data["charge_from_grid"] = charge_from_grid
return True
else:
return False

async def set_priority(self, priority):
"""Set device priority"""
Expand All @@ -248,12 +251,15 @@ async def set_priority(self, priority):

async def set_charge_target(self, charge_target: float):
"""Set charge target"""
await self._connection.put(
f"/api/AccountAccess/{self._serialno}/TargetEnergy?targetEnergy={charge_target}",
oauth=True,
)
self._extra_data["charge_target"] = charge_target
return True
if self._connection.app_email and self._connection.app_password:
await self._connection.put(
f"/api/AccountAccess/{self._serialno}/TargetEnergy?targetEnergy={charge_target}",
oauth=True,
)
self._extra_data["charge_target"] = charge_target
return True
else:
return False

def show(self, short_format=False):
"""Returns a string with all data in human readable format"""
Expand All @@ -275,11 +281,18 @@ def show(self, short_format=False):
ret = ret + f"Status: {self.status}\n"
ret = ret + "Local Mode: " + self.get_mode_description(self.local_mode) + "\n"
ret = ret + "Charge from Grid: "
if self.charge_from_grid:
ret = ret + "Enabled\n"
if self.charge_from_grid is not None:
if self.charge_from_grid:
ret = ret + "Enabled\n"
else:
ret = ret + "Disabled\n"
else:
ret = ret + f"<unavailable>\n"
ret = ret + f"Charge target: "
if self.charge_target is not None:
ret = ret + f"{self.charge_target}kWh\n"
else:
ret = ret + "Disabled\n"
ret = ret + f"Charge target: {self.charge_target}kWh\n"
ret = ret + f"<unavailable>\n"
ret = ret + f"CT 1 {self.ct1.name} {self.ct1.power}W phase {self.ct1.phase}\n"
ret = ret + f"CT 2 {self.ct2.name} {self.ct2.power}W phase {self.ct2.phase}\n"
ret = ret + f"CT 3 {self.ct3.name} {self.ct3.power}W phase {self.ct3.phase}\n"
Expand Down
1 change: 1 addition & 0 deletions pymyenergi/zappi.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
}



class Zappi(BaseDevice):
"""Zappi Client for myenergi API."""

Expand Down
Loading