Skip to content

Commit

Permalink
Rename repo in files. Update for new unique name.
Browse files Browse the repository at this point in the history
  • Loading branch information
stegm committed May 4, 2021
1 parent 6d113e7 commit d9e49ec
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 56 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ without activating the virtual environment it.

```shell
# Install with command line support
$ pip install kostal_plenticore[CLI]
$ pip install pykoplenti[CLI]

# Install without command line support
$ pip install kostal_plenticore
$ pip install pykoplenti
```

### Using the command line interface
Expand All @@ -54,8 +54,8 @@ Usage: pykoplenti [OPTIONS] COMMAND [ARGS]...
Handling of global arguments with click

Options:
--host TEXT hostname or ip of plenticore inverter
--port INTEGER port of plenticore inverter (default 80)
--host TEXT hostname or ip of the inverter
--port INTEGER port of the inverter (default 80)
--password TEXT the password
--password-file TEXT password file (default "secrets" in the current
working directory)
Expand All @@ -81,14 +81,14 @@ example directory for full code.
Import the client module:

```python
from kostal.plenticore import PlenticoreApiClient
from pykoplenti import ApiClient
```

To communicate with the inverter you need to instantiate the client:

```python
# session is a aiohttp ClientSession
client = PlenticoreApiClient(session, '192.168.1.100')
client = ApiClient(session, '192.168.1.100')
```

Login to gain full access to process data and settings:
Expand Down
2 changes: 1 addition & 1 deletion doc/command_line.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ $ pykoplenti --host 192.168.1.100 --password verysecret write-settings devices:l

### REPL

A REPL is provided for simple interactive tests. All methods of the `PlenticoreApiClient` class can be called. The
A REPL is provided for simple interactive tests. All methods of the `ApiClient` class can be called. The
arguments must be given separated by spaces by using python literals.

```shell script
Expand Down
4 changes: 2 additions & 2 deletions doc/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## Code Format

```shell script
isort kostal
black --fast kostal
isort pykoplenti
black --fast pykoplenti
```

## Install Package editable
Expand Down
4 changes: 2 additions & 2 deletions examples/read_process_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
from aiohttp import ClientSession
from kostal.plenticore import PlenticoreApiClient
from pykoplenti import ApiClient
import sys

"""
Expand All @@ -13,7 +13,7 @@

async def async_main(host, passwd):
async with ClientSession() as session:
client = PlenticoreApiClient(session, host)
client = ApiClient(session, host)
await client.login(passwd)

data = await client.get_process_data_values('devices:local', ['Inverter:State', 'Home_P'])
Expand Down
Empty file removed kostal/__init__.py
Empty file.
42 changes: 21 additions & 21 deletions kostal/plenticore/__init__.py → pykoplenti/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def __repr__(self):


class EventData:
"""Represents a Plenticore event."""
"""Represents an event of the inverter."""

def __init__(self, raw):
self._raw = raw
Expand Down Expand Up @@ -272,17 +272,17 @@ def __repr__(self):
return dumps(self._raw)


class PlenticoreApiException(Exception):
"""Base exception for Plenticore API calls."""
class ApiException(Exception):
"""Base exception for API calls."""

def __init__(self, msg):
self.msg = msg

def __str__(self):
return f"Plenticore API Error: {self.msg}"
return f"API Error: {self.msg}"


class PlenticoreInternalCommunicationException(PlenticoreApiException):
class InternalCommunicationException(ApiException):
"""Exception for internal communication error response."""

def __init__(self, status_code: int, error: str):
Expand All @@ -291,8 +291,8 @@ def __init__(self, status_code: int, error: str):
self.error = error


class PlenticoreAuthenticationException(PlenticoreApiException):
"""Exception for authentiation or user error response."""
class AuthenticationException(ApiException):
"""Exception for authentication or user error response."""

def __init__(self, status_code: int, error: str):
super().__init__(
Expand All @@ -302,7 +302,7 @@ def __init__(self, status_code: int, error: str):
self.error = error


class PlenticoreUserLockedException(PlenticoreApiException):
class UserLockedException(ApiException):
"""Exception for user locked error response."""

def __init__(self, status_code: int, error: str):
Expand All @@ -311,7 +311,7 @@ def __init__(self, status_code: int, error: str):
self.error = error


class PlenticoreModuleNotFoundException(PlenticoreApiException):
class ModuleNotFoundException(ApiException):
"""Exception for module or setting not found response."""

def __init__(self, status_code: int, error: str):
Expand All @@ -320,7 +320,7 @@ def __init__(self, status_code: int, error: str):
self.error = error


class PlenticoreApiClient(contextlib.AbstractAsyncContextManager):
class ApiClient(contextlib.AbstractAsyncContextManager):
"""Client for the REST-API of Kostal Plenticore inverters.
The RESP-API provides several scopes of information. Each scope provides a
Expand Down Expand Up @@ -372,7 +372,7 @@ def __init__(self, websession: ClientSession, host: str, port: int = 80):
"""Create a new client.
:param websession: A aiohttp ClientSession for all requests
:param host: The hostname or ip of the plenticore inverter
:param host: The hostname or ip of the inverter
:param port: The port of the API interface (default 80)
"""
self.websession = websession
Expand All @@ -397,15 +397,15 @@ def _create_url(self, path: str) -> URL:
scheme="http",
host=self.host,
port=self.port,
path=PlenticoreApiClient.BASE_URL,
path=ApiClient.BASE_URL,
)
return base.join(URL(path))

async def login(self, password: str, user: str = "user"):
"""Login the given user (default is 'user') with the given
password.
:raises PlenticoreAuthenticationException: if authentication failed
:raises AuthenticationException: if authentication failed
:raises aiohttp.client_exceptions.ClientConnectorError: if host is not reachable
:raises asyncio.exceptions.TimeoutError: if a timeout occurs
"""
Expand Down Expand Up @@ -539,19 +539,19 @@ async def _check_response(self, resp: ClientResponse):
error = None

if resp.status == 400:
raise PlenticoreAuthenticationException(resp.status, error)
raise AuthenticationException(resp.status, error)

if resp.status == 403:
raise PlenticoreUserLockedException(resp.status, error)
raise UserLockedException(resp.status, error)

if resp.status == 404:
raise PlenticoreModuleNotFoundException(resp.status, error)
raise ModuleNotFoundException(resp.status, error)

if resp.status == 503:
raise PlenticoreInternalCommunicationException(resp.status, error)
raise InternalCommunicationException(resp.status, error)

# we got an undocumented status code
raise PlenticoreApiException(
raise ApiException(
f"Unknown API response [{resp.status}] - {error}"
)

Expand All @@ -562,7 +562,7 @@ def _relogin(fn):
async def _wrapper(self, *args, **kwargs):
try:
return await fn(self, *args, **kwargs)
except PlenticoreAuthenticationException:
except AuthenticationException:
pass

logger.debug("Request failed - try to re-login")
Expand Down Expand Up @@ -609,12 +609,12 @@ async def get_events(self, max_count=10, lang=None) -> Iterable[EventData]:

language = lang[0:2].lower()
variant = lang[3:5].lower()
if language not in PlenticoreApiClient.SUPPORTED_LANGUAGES.keys():
if language not in ApiClient.SUPPORTED_LANGUAGES.keys():
# Fallback to default
language = "en"
variant = "gb"
else:
variants = PlenticoreApiClient.SUPPORTED_LANGUAGES[language]
variants = ApiClient.SUPPORTED_LANGUAGES[language]
if variant not in variants:
variant = variants[0]

Expand Down
40 changes: 20 additions & 20 deletions kostal/plenticore/cli.py → pykoplenti/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import click
from prompt_toolkit import PromptSession, print_formatted_text

from kostal.plenticore import PlenticoreApiClient
from pykoplenti import ApiClient


class SessionCache:
Expand All @@ -23,26 +23,26 @@ def __init__(self, host):
self.host = host

def read_session_id(self) -> str:
file = os.path.join(tempfile.gettempdir(), f"plenticore-session-{self.host}")
file = os.path.join(tempfile.gettempdir(), f"pykoplenti-session-{self.host}")
if os.path.isfile(file):
with open(file, "rt") as f:
return f.readline(256)
else:
return None

def write_session_id(self, id: str):
file = os.path.join(tempfile.gettempdir(), f"plenticore-session-{self.host}")
file = os.path.join(tempfile.gettempdir(), f"pykoplenti-session-{self.host}")
f = os.open(file, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode=0o600)
try:
os.write(f, id.encode("ascii"))
finally:
os.close(f)


class PlenticoreShell:
"""Provides a shell-like access to the plenticore client."""
class ApiShell:
"""Provides a shell-like access to the inverter."""

def __init__(self, client: PlenticoreApiClient):
def __init__(self, client: ApiClient):
super().__init__()
self.client = client
self._session_cache = SessionCache(self.client.host)
Expand Down Expand Up @@ -152,17 +152,17 @@ async def _execute(self, method, args):

async def repl_main(host, port, passwd):
async with ClientSession(timeout=ClientTimeout(total=10)) as session:
client = PlenticoreApiClient(session, host=host, port=port)
client = ApiClient(session, host=host, port=port)

shell = PlenticoreShell(client)
shell = ApiShell(client)
await shell.run(passwd)


async def command_main(
host: str, port: int, passwd: str, fn: Callable[[PlenticoreApiClient], None]
host: str, port: int, passwd: str, fn: Callable[[ApiClient], None]
):
async with ClientSession(timeout=ClientTimeout(total=10)) as session:
client = PlenticoreApiClient(session, host=host, port=port)
client = ApiClient(session, host=host, port=port)
session_cache = SessionCache(host)

# Try to reuse an existing session
Expand Down Expand Up @@ -190,8 +190,8 @@ def __init__(self):


@click.group()
@click.option("--host", help="hostname or ip of plenticore inverter")
@click.option("--port", default=80, help="port of plenticore (default 80)")
@click.option("--host", help="hostname or ip of the inverter")
@click.option("--port", default=80, help="port of the inverter (default 80)")
@click.option("--password", default=None, help="the password")
@click.option(
"--password-file",
Expand All @@ -216,7 +216,7 @@ def cli(global_args, host, port, password, password_file):
@cli.command()
@pass_global_args
def repl(global_args):
"""Provides a simple REPL for executing API requests to plenticore inverters."""
"""Provides a simple REPL for executing API requests to the inverter."""
asyncio.run(repl_main(global_args.host, global_args.port, global_args.passwd))


Expand All @@ -227,7 +227,7 @@ def repl(global_args):
def read_events(global_args, lang, count):
"""Returns the last events"""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
data = await client.get_events(lang=lang, max_count=count)
for event in data:
print(
Expand All @@ -252,7 +252,7 @@ async def fn(client: PlenticoreApiClient):
def download_log(global_args, out, begin, end):
"""Download the log data from the inverter to a file."""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
data = await client.download_logdata(writer=out, begin=begin, end=end)

asyncio.run(
Expand All @@ -265,7 +265,7 @@ async def fn(client: PlenticoreApiClient):
def all_processdata(global_args):
"""Returns a list of all available process data."""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
data = await client.get_process_data()
for k, v in data.items():
for x in v:
Expand All @@ -290,7 +290,7 @@ def read_processdata(global_args, ids):
read-processdata devices:local/Inverter:State
"""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
if len(ids) == 1 and "/" not in ids[0]:
# all process data ids of a moudle
values = await client.get_process_data_values(ids[0])
Expand Down Expand Up @@ -325,7 +325,7 @@ async def fn(client: PlenticoreApiClient):
def all_settings(global_args, rw):
"""Returns the ids of all settings."""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
settings = await client.get_settings()
for k, v in settings.items():
for x in v:
Expand All @@ -351,7 +351,7 @@ def read_settings(global_args, ids):
read-settings devices:local/Battery:MinSoc devices:local/Battery:MinHomeComsumption
"""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
query = defaultdict(list)
for id in ids:
m = re.match(r"(?P<module_id>.+)/(?P<setting_id>.+)", id)
Expand Down Expand Up @@ -387,7 +387,7 @@ def write_settings(global_args, id_values):
write-settings devices:local/Battery:MinSoc=15
"""

async def fn(client: PlenticoreApiClient):
async def fn(client: ApiClient):
query = defaultdict(dict)
for id_value in id_values:
m = re.match(
Expand Down
7 changes: 3 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[metadata]
name = kostal-plenticore
name = pykoplenti
version = unreleased
description = Python REST-Client for Kostal Plenticore Solar Inverters
long_description = file: README.md
Expand All @@ -19,8 +19,7 @@ classifiers =

[options]
packages =
kostal
kostal.plenticore
pykoplenti
install_requires =
aiohttp >= 3.6
pycryptodome >= 3.9
Expand All @@ -32,4 +31,4 @@ CLI =

[options.entry_points]
console_scripts =
pykoplenti = kostal.plenticore.cli:cli [CLI]
pykoplenti = pykoplenti.cli:cli [CLI]

0 comments on commit d9e49ec

Please sign in to comment.