Skip to content

Commit c47cfc3

Browse files
authored
add more helper scripts (#92)
1 parent af96ae2 commit c47cfc3

6 files changed

Lines changed: 297 additions & 45 deletions

File tree

setup.cfg

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ install_requires =
3232
websocket-client
3333
packages = e3dc
3434

35+
[options.extras_require]
36+
develop =
37+
jsbeautifier
38+
argparse
39+
3540
[isort]
3641
profile = black
3742

tools/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Helper Scripts
2+
3+
## install requirements
4+
5+
`pip install pye3dc[develop]`
6+
7+
## Convert RSCP Tags
8+
9+
The script `convert_rscp_tags.py` can be used to generate the `RscpTag(Enum)` class if a new version of the Javascript rscp library (`https://s10.e3dc.com/s10/js/rscpLibV0.9.3.min.js`) is available.
10+
11+
### usage
12+
13+
```
14+
usage: convert_rscp_tags.py [-h] [-r RSCPLIB]
15+
16+
E3DC rscp tags convert
17+
18+
options:
19+
-h, --help show this help message and exit
20+
-r RSCPLIB, --rscpLib RSCPLIB
21+
rscp library file
22+
```
23+
24+
## Run tests
25+
26+
The script `test.py` will run all non altering methods for `pye3dc` for testing or sharing the output.
27+
28+
### usage
29+
30+
```
31+
usage: tests.py [-h] [-c CONFIG] -i IPADDRESS -u USERNAME -p PASSWORD -k KEY
32+
33+
E3DC tests
34+
35+
options:
36+
-h, --help show this help message and exit
37+
-c CONFIG, --config CONFIG
38+
config of E3DC
39+
40+
required named arguments:
41+
-i IPADDRESS, --ipaddress IPADDRESS
42+
IP address of E3DC
43+
-u USERNAME, --username USERNAME
44+
username of E3DC
45+
-p PASSWORD, --password PASSWORD
46+
password of E3DC
47+
-k KEY, --key KEY key of E3DC
48+
```
49+
50+
## Run tests for different python versions
51+
52+
The script `testcontainers.py` wil run the `tests`, using docker, for multiple Python versions supported by this library.
53+
54+
### usage
55+
56+
```
57+
usage: testcontainers.py [-h] [-l LIST] [-c CONFIG] -i IPADDRESS -u USERNAME -p PASSWORD -k KEY
58+
59+
E3DC testcontainers
60+
61+
options:
62+
-h, --help show this help message and exit
63+
-l LIST, --list LIST list of Python versions to test with
64+
-c CONFIG, --config CONFIG
65+
config of E3DC
66+
67+
required named arguments:
68+
-i IPADDRESS, --ipaddress IPADDRESS
69+
IP address of E3DC
70+
-u USERNAME, --username USERNAME
71+
username of E3DC
72+
-p PASSWORD, --password PASSWORD
73+
password of E3DC
74+
-k KEY, --key KEY key of E3DC
75+
```

tools/_convert_rscp_tags.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

tools/convert_rscp_tags.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""This is a helper script to convert the enumerations from the "official" RSCP implementation into a format that can be used in the Python implementation.
2+
3+
Requirements (just for this script):
4+
pip install pye3dc[develop]
5+
"""
6+
7+
import argparse
8+
import re
9+
import sys
10+
11+
import jsbeautifier
12+
import requests
13+
14+
parser = argparse.ArgumentParser(description="E3DC rscp tags convert")
15+
parser.add_argument(
16+
"-r", "--rscpLib", help="rscp library file", default="rscpLibV0.9.3.min.js"
17+
)
18+
args = vars(parser.parse_args())
19+
20+
rscpLib = args["rscpLib"]
21+
source = requests.get(
22+
"https://s10.e3dc.com/s10/js/{}".format(rscpLib), allow_redirects=True
23+
)
24+
if source.status_code != 200:
25+
print("Can't download {}".format(rscpLib), file=sys.stderr)
26+
exit(1)
27+
input = jsbeautifier.beautify(str(source.content, encoding="utf-8"))
28+
29+
rscpTagsFromJS = re.search(
30+
r"var rscpTags = {(.*?)getHexTag.*?}", input, re.DOTALL
31+
).group(1)
32+
33+
lines = rscpTagsFromJS.splitlines()
34+
35+
print("class RscpTag(Enum):")
36+
print(
37+
' """All available RSCP tags. Generated from https://s10.e3dc.com/s10/js/{}."""\n'.format(
38+
rscpLib
39+
)
40+
)
41+
42+
for line in lines:
43+
line = line.strip()
44+
if not line:
45+
continue
46+
number, string = line.split(": ")
47+
number = number.strip()
48+
hex_number = hex(int(float(number))).upper()[2:]
49+
padded_hex_number = hex_number.zfill(8)
50+
name = string.removeprefix('"').removesuffix('",').strip()
51+
enum_entry = f" {name} = 0x{padded_hex_number}"
52+
print(enum_entry)

tools/testcontainers.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""A simple python script to test the e3dc module with various python versions in docker."""
2+
3+
import argparse
4+
import json
5+
from pathlib import Path
6+
7+
import docker
8+
9+
10+
class Testcontainers:
11+
"""A class to run commands in a python container."""
12+
13+
container = ""
14+
version = ""
15+
16+
def __init__(
17+
self,
18+
ipAddress,
19+
username,
20+
password,
21+
key,
22+
configuration,
23+
version="3.8",
24+
):
25+
"""The init method for Testcontainers."""
26+
client = docker.from_env()
27+
self.version = version
28+
image_name = "python:" + version
29+
container_name = "python-e3dc-" + version
30+
client.images.pull(image_name)
31+
try:
32+
client.containers.get(container_name).remove(force=True)
33+
except docker.errors.NotFound:
34+
pass
35+
self.container = client.containers.run(
36+
image_name,
37+
name=container_name,
38+
stdin_open=True,
39+
remove=True,
40+
detach=True,
41+
volumes=[str(Path(__file__).resolve().parents[1]) + ":/pye3dc"],
42+
working_dir="/pye3dc",
43+
environment={
44+
"IPADDRESS": ipAddress,
45+
"USERNAME": username,
46+
"PASSWORD": password,
47+
"KEY": key,
48+
"CONFIG": configuration,
49+
},
50+
)
51+
52+
def exec_cmd_stream(self, command):
53+
"""Execute a command and stream output."""
54+
_, stream = self.container.exec_run(cmd=command, stream=True)
55+
for data in stream:
56+
print(data.decode(), end="")
57+
58+
def exec_cmd(self, command):
59+
"""Execute a command and validate return code."""
60+
result, output = self.container.exec_run(cmd=command)
61+
print(output.decode())
62+
if result != 0:
63+
exit(1)
64+
65+
def remove(self):
66+
"""Remove the test container."""
67+
self.container.remove(force=True)
68+
69+
70+
parser = argparse.ArgumentParser(description="E3DC testcontainers")
71+
parser.add_argument(
72+
"-l",
73+
"--list",
74+
help="list of Python versions to test with",
75+
default='["3.8", "3.9", "3.10", "3.11", "3.12"]',
76+
)
77+
parser.add_argument("-c", "--config", help="config of E3DC", default="{}")
78+
requiredNamed = parser.add_argument_group("required named arguments")
79+
requiredNamed.add_argument(
80+
"-i", "--ipaddress", help="IP address of E3DC", required=True
81+
)
82+
requiredNamed.add_argument("-u", "--username", help="username of E3DC", required=True)
83+
requiredNamed.add_argument("-p", "--password", help="password of E3DC", required=True)
84+
requiredNamed.add_argument("-k", "--key", help="key of E3DC", required=True)
85+
args = vars(parser.parse_args())
86+
87+
for version in json.loads(args["list"]):
88+
print("Starting test on Python " + version + ":")
89+
testcontainers = Testcontainers(
90+
ipAddress=args["ipaddress"],
91+
username=args["username"],
92+
password=args["password"],
93+
key=args["key"],
94+
configuration=args["config"],
95+
version=version,
96+
)
97+
testcontainers.exec_cmd("pip install .")
98+
testcontainers.exec_cmd(
99+
"sh -c 'python tools/tests.py -i $IPADDRESS -u $USERNAME -p $PASSWORD -k $KEY -c $CONFIG'"
100+
)
101+
testcontainers.remove()
102+
print()

tools/tests.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""A simple python script to test the e3dc module."""
2+
3+
import argparse
4+
import json
5+
from datetime import date, datetime
6+
7+
from e3dc import E3DC
8+
9+
10+
def json_serial(obj):
11+
"""JSON serializer for objects not serializable by default json code."""
12+
if isinstance(obj, (datetime, date)):
13+
return obj.isoformat()
14+
raise TypeError("Type %s not serializable" % type(obj))
15+
16+
17+
def printJson(obj):
18+
"""Print a json object with a datetime obect."""
19+
print(json.dumps(obj, indent=2, default=json_serial))
20+
21+
22+
parser = argparse.ArgumentParser(description="E3DC tests")
23+
parser.add_argument("-c", "--config", help="config of E3DC", default="{}")
24+
requiredNamed = parser.add_argument_group("required named arguments")
25+
requiredNamed.add_argument(
26+
"-i", "--ipaddress", help="IP address of E3DC", required=True
27+
)
28+
requiredNamed.add_argument("-u", "--username", help="username of E3DC", required=True)
29+
requiredNamed.add_argument("-p", "--password", help="password of E3DC", required=True)
30+
requiredNamed.add_argument("-k", "--key", help="key of E3DC", required=True)
31+
args = vars(parser.parse_args())
32+
33+
e3dc = E3DC(
34+
E3DC.CONNECT_LOCAL,
35+
ipAddress=args["ipaddress"],
36+
username=args["username"],
37+
password=args["password"],
38+
key=args["key"],
39+
configuration=json.loads(args["config"]),
40+
)
41+
42+
methods = [
43+
"poll",
44+
"poll_switches",
45+
"get_idle_periods",
46+
"get_db_data",
47+
"get_system_info",
48+
"get_system_status",
49+
"get_battery_data",
50+
"get_batteries_data",
51+
"get_pvi_data",
52+
"get_pvis_data",
53+
"get_powermeters",
54+
"get_powermeter_data",
55+
"get_powermeters_data",
56+
"get_power_settings",
57+
]
58+
59+
for method in methods:
60+
print(method + "():")
61+
method = getattr(e3dc, method)
62+
printJson(method(keepAlive=True))
63+
print()

0 commit comments

Comments
 (0)