From 94609cffa93669b5542380e39fbc24ae186d473d Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Fri, 30 Oct 2020 09:48:24 +0100 Subject: [PATCH 01/24] get values safely --- kostalpyko/kostalpyko.py | 124 +++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 23 deletions(-) diff --git a/kostalpyko/kostalpyko.py b/kostalpyko/kostalpyko.py index e8e0c6a..3b10294 100644 --- a/kostalpyko/kostalpyko.py +++ b/kostalpyko/kostalpyko.py @@ -20,28 +20,28 @@ def __init__(self, host=None, username="pvserver", password="pvwr"): def get_solar_generator_power(self): """returns the current power of the solar generator in W""" if self._get_content_of_own_consumption(): - return self._get_content_of_own_consumption()[5] + return self.safe_list_get(self._get_content_of_own_consumption(), 5) else: return "No BA sensor installed" def get_consumption_phase_1(self): """returns the current consumption of phase 1 in W""" if self._get_content_of_own_consumption(): - return self._get_content_of_own_consumption()[8] + return self.safe_list_get(self._get_content_of_own_consumption(), 8) else: return "No BA sensor installed" def get_consumption_phase_2(self): """returns the current consumption of phase 2 in W""" if self._get_content_of_own_consumption(): - return self._get_content_of_own_consumption()[9] + return self.safe_list_get(self._get_content_of_own_consumption(), 9) else: return "No BA sensor installed" def get_consumption_phase_3(self): """returns the current consumption of phase 3 in W""" if self._get_content_of_own_consumption(): - return self._get_content_of_own_consumption()[10] + return self.safe_list_get(self._get_content_of_own_consumption(), 10) else: return "No BA sensor installed" @@ -80,37 +80,65 @@ def get_logdaten_dat(self): def get_current_power(self): """returns the current power in W""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[0]) + value = self.safe_list_get(self._get_raw_content(), 0) + if value is not None: + return int(value) + else: + return None def get_total_energy(self): """returns the total energy in kWh""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[1]) + value = self.safe_list_get(self._get_raw_content(), 1) + if value is not None: + return int(value) + else: + return None def get_daily_energy(self): """returns the daily energy in kWh""" if self._get_raw_content() is not None: - return float(self._get_raw_content()[2]) + value = self.safe_list_get(self._get_raw_content(), 2) + if value is not None: + return float(value) + else: + return None def get_string1_voltage(self): """returns the voltage from string 1 in V""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[3]) + value = self.safe_list_get(self._get_raw_content(), 3) + if value is not None: + return int(value) + else: + return None def get_string1_current(self): """returns the current from string 1 in A""" if self._get_raw_content() is not None: - return float(self._get_raw_content()[5]) + value = self.safe_list_get(self._get_raw_content(), 5) + if value is not None: + return float(value) + else: + return None def get_string2_voltage(self): """returns the voltage from string 2 in V""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[7]) + value = self.safe_list_get(self._get_raw_content(), 7) + if value is not None: + return int(value) + else: + return None def get_string2_current(self): """returns the current from string 2 in A""" if self._get_raw_content() is not None: - return float(self._get_raw_content()[9]) + value = self.safe_list_get(self._get_raw_content(), 9) + if value is not None: + return float(value) + else: + return None def get_string3_voltage(self): """returns the voltage from string 3 in V""" @@ -119,7 +147,11 @@ def get_string3_voltage(self): # String 3 not installed return None else: - return int(raw_content[11]) + value = self.safe_list_get(raw_content, 11) + if value is not None: + return int(value) + else: + return None def get_string3_current(self): """returns the current from string 3 in A""" @@ -128,57 +160,97 @@ def get_string3_current(self): # String 3 not installed return None else: - return float(raw_content[13]) + value = self.safe_list_get(raw_content, 13) + if value is not None: + return float(value) + else: + return None def get_l1_voltage(self): """returns the voltage from line 1 in V""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[4]) + value = self.safe_list_get(self._get_raw_content(), 4) + if value is not None: + return int(value) + else: + return None def get_l1_power(self): """returns the power from line 1 in W""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[6]) + value = self.safe_list_get(self._get_raw_content(), 6) + if value is not None: + return int(value) + else: + return None def get_l2_voltage(self): """returns the voltage from line 2 in V""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[8]) + value = self.safe_list_get(self._get_raw_content(), 8) + if value is not None: + return int(value) + else: + return None def get_l2_power(self): """returns the power from line 1 in W""" if self._get_raw_content() is not None: - return int(self._get_raw_content()[10]) + value = self.safe_list_get(self._get_raw_content(), 10) + if value is not None: + return int(value) + else: + return None def get_l3_voltage(self): """returns the voltage from line 3 in V""" raw_content = self._get_raw_content() if len(raw_content) < 15: # 2 Strings - return int(raw_content[11]) + value = self.safe_list_get(raw_content, 11) + if value is not None: + return int(value) + else: + return None else: # 3 Strings - return int(raw_content[12]) + value = self.safe_list_get(raw_content, 12) + if value is not None: + return int(value) + else: + return None def get_l3_power(self): """returns the power from line 3 in W""" raw_content = self._get_raw_content() if len(raw_content) < 15: # 2 Strings - return int(raw_content[12]) + value = self.safe_list_get(raw_content, 12) + if value is not None: + return int(value) + else: + return None else: # 3 Strings - return int(raw_content[14]) + value = self.safe_list_get(raw_content, 14) + if value is not None: + return int(value) + else: + return None def get_piko_status(self): """returns the power from line 3 in W""" raw_content = self._get_raw_content() if len(raw_content) < 15: # 2 Strings - return raw_content[13] + return self.safe_list_get(raw_content, 13) else: # 3 Strings - return int(raw_content[15]) + value = self.safe_list_get(raw_content, 15) + if value is not None: + return int(value) + else: + return None def _get_raw_content(self): """returns all values as a list""" @@ -238,3 +310,9 @@ def _get_info(self): except requests.exceptions.Timeout as errt: LOG.debug(errt) return None + + def safe_list_get(self, l, idx, default = None): + try: + return l[idx] + except IndexError: + return default \ No newline at end of file From 251b7634e5c16604dbf5e1dbc177b47bc44dae3a Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Fri, 30 Oct 2020 12:49:10 +0100 Subject: [PATCH 02/24] rebranding --- {kostalpyko => kostalpiko}/__init__.py | 0 .../kostalpyko.py => kostalpiko/kostalpiko.py | 288 ++++++++++-------- setup.py | 12 +- ...{test_kostalpyko.py => test_kostalpiko.py} | 0 4 files changed, 173 insertions(+), 127 deletions(-) rename {kostalpyko => kostalpiko}/__init__.py (100%) rename kostalpyko/kostalpyko.py => kostalpiko/kostalpiko.py (72%) rename tests/{test_kostalpyko.py => test_kostalpiko.py} (100%) diff --git a/kostalpyko/__init__.py b/kostalpiko/__init__.py similarity index 100% rename from kostalpyko/__init__.py rename to kostalpiko/__init__.py diff --git a/kostalpyko/kostalpyko.py b/kostalpiko/kostalpiko.py similarity index 72% rename from kostalpyko/kostalpyko.py rename to kostalpiko/kostalpiko.py index 3b10294..15bc186 100644 --- a/kostalpyko/kostalpyko.py +++ b/kostalpiko/kostalpiko.py @@ -11,39 +11,75 @@ LOG = logging.getLogger(__name__) +def safe_list_get(self, l, idx, default=None): + try: + return l[idx] + except IndexError: + return default + + class Piko: def __init__(self, host=None, username="pvserver", password="pvwr"): self.host = host self.username = username self.password = password + self.data = None + self.ba_data = None - def get_solar_generator_power(self): - """returns the current power of the solar generator in W""" - if self._get_content_of_own_consumption(): - return self.safe_list_get(self._get_content_of_own_consumption(), 5) - else: - return "No BA sensor installed" + def update(self): + """updates all data""" + self.update_data() + self.update_ba_data() - def get_consumption_phase_1(self): - """returns the current consumption of phase 1 in W""" - if self._get_content_of_own_consumption(): - return self.safe_list_get(self._get_content_of_own_consumption(), 8) - else: - return "No BA sensor installed" + def update_data(self): + """updates data""" + try: + data = self._get_raw_content() + if data is not None: + self.data = PikoData(data) + except Exception as err: + LOG.debug(err) + pass - def get_consumption_phase_2(self): - """returns the current consumption of phase 2 in W""" - if self._get_content_of_own_consumption(): - return self.safe_list_get(self._get_content_of_own_consumption(), 9) - else: - return "No BA sensor installed" + def update_ba_data(self): + """updates ba data""" + try: + data = self._get_content_of_own_consumption() + if data is not None: + self.ba_data = PikoBAData(data) + except Exception as err: + LOG.debug(err) + pass - def get_consumption_phase_3(self): - """returns the current consumption of phase 3 in W""" - if self._get_content_of_own_consumption(): - return self.safe_list_get(self._get_content_of_own_consumption(), 10) - else: - return "No BA sensor installed" + def _get_raw_content(self): + """returns all values as a list""" + url = self.host + "/index.fhtml" + login = self.username + pwd = self.password + try: + r = requests.get(url, auth=(login, pwd), timeout=15) + if r.status_code == 200: + response = html.fromstring(r.content) + data = [] + for v in response.xpath("//td[@bgcolor='#FFFFFF']"): + raw = v.text.strip() + if "x x x" in raw: + raw = 0 + data.append(raw) + status = response.xpath("/html/body/form/font/table[2]/tr[8]/td[3]")[ + 0 + ].text.strip() + data.append(status) + LOG.debug(data) + return data + else: + raise ConnectionError + except requests.exceptions.ConnectionError as errc: + LOG.debug(errc) + return None + except requests.exceptions.Timeout as errt: + LOG.debug(errt) + return def _get_content_of_own_consumption(self): """returns all values as a list""" @@ -74,13 +110,51 @@ def _get_content_of_own_consumption(self): LOG.debug(errt) return None + def _get_info(self): + """returns the info about the inverter""" + url = self.host + "/Solar2.fhtml" + login = self.username + pwd = self.password + try: + r = requests.get(url, auth=(login, pwd), timeout=15) + if r.status_code == 200: + response = html.fromstring(r.content) + data = [] + serial = response.xpath("/html/body/form/font/table/tr[2]/td[3]")[ + 0 + ].text.strip() + data.append(serial) + model = response.xpath("/html/body/form/table/tr[2]/td[2]/font[1]")[ + 0 + ].text.strip() + data.append(model) + LOG.debug(data) + return data + else: + raise ConnectionError + except requests.exceptions.ConnectionError as errc: + LOG.debug(errc) + return None + except requests.exceptions.Timeout as errt: + LOG.debug(errt) + return None + def get_logdaten_dat(self): pass + +class PikoData(object): + """ + PIKO Data + """ + + def __init__(self, raw_data): + self._raw_data = raw_data + def get_current_power(self): """returns the current power in W""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 0) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 0) if value is not None: return int(value) else: @@ -88,8 +162,8 @@ def get_current_power(self): def get_total_energy(self): """returns the total energy in kWh""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 1) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 1) if value is not None: return int(value) else: @@ -97,8 +171,8 @@ def get_total_energy(self): def get_daily_energy(self): """returns the daily energy in kWh""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 2) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 2) if value is not None: return float(value) else: @@ -106,8 +180,8 @@ def get_daily_energy(self): def get_string1_voltage(self): """returns the voltage from string 1 in V""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 3) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 3) if value is not None: return int(value) else: @@ -115,8 +189,8 @@ def get_string1_voltage(self): def get_string1_current(self): """returns the current from string 1 in A""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 5) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 5) if value is not None: return float(value) else: @@ -124,8 +198,8 @@ def get_string1_current(self): def get_string2_voltage(self): """returns the voltage from string 2 in V""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 7) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 7) if value is not None: return int(value) else: @@ -133,8 +207,8 @@ def get_string2_voltage(self): def get_string2_current(self): """returns the current from string 2 in A""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 9) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 9) if value is not None: return float(value) else: @@ -142,12 +216,12 @@ def get_string2_current(self): def get_string3_voltage(self): """returns the voltage from string 3 in V""" - raw_content = self._get_raw_content() + raw_content = self._raw_data if len(raw_content) < 15: # String 3 not installed return None else: - value = self.safe_list_get(raw_content, 11) + value = safe_list_get(raw_content, 11) if value is not None: return int(value) else: @@ -155,12 +229,12 @@ def get_string3_voltage(self): def get_string3_current(self): """returns the current from string 3 in A""" - raw_content = self._get_raw_content() + raw_content = self._raw_data if len(raw_content) < 15: # String 3 not installed return None else: - value = self.safe_list_get(raw_content, 13) + value = safe_list_get(raw_content, 13) if value is not None: return float(value) else: @@ -168,8 +242,8 @@ def get_string3_current(self): def get_l1_voltage(self): """returns the voltage from line 1 in V""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 4) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 4) if value is not None: return int(value) else: @@ -177,8 +251,8 @@ def get_l1_voltage(self): def get_l1_power(self): """returns the power from line 1 in W""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 6) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 6) if value is not None: return int(value) else: @@ -186,8 +260,8 @@ def get_l1_power(self): def get_l2_voltage(self): """returns the voltage from line 2 in V""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 8) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 8) if value is not None: return int(value) else: @@ -195,8 +269,8 @@ def get_l2_voltage(self): def get_l2_power(self): """returns the power from line 1 in W""" - if self._get_raw_content() is not None: - value = self.safe_list_get(self._get_raw_content(), 10) + if self._raw_data is not None: + value = safe_list_get(self._raw_data, 10) if value is not None: return int(value) else: @@ -204,17 +278,17 @@ def get_l2_power(self): def get_l3_voltage(self): """returns the voltage from line 3 in V""" - raw_content = self._get_raw_content() + raw_content = self._raw_data if len(raw_content) < 15: # 2 Strings - value = self.safe_list_get(raw_content, 11) + value = safe_list_get(raw_content, 11) if value is not None: return int(value) else: return None else: # 3 Strings - value = self.safe_list_get(raw_content, 12) + value = safe_list_get(raw_content, 12) if value is not None: return int(value) else: @@ -222,17 +296,17 @@ def get_l3_voltage(self): def get_l3_power(self): """returns the power from line 3 in W""" - raw_content = self._get_raw_content() + raw_content = self._raw_data if len(raw_content) < 15: # 2 Strings - value = self.safe_list_get(raw_content, 12) + value = safe_list_get(raw_content, 12) if value is not None: return int(value) else: return None else: # 3 Strings - value = self.safe_list_get(raw_content, 14) + value = safe_list_get(raw_content, 14) if value is not None: return int(value) else: @@ -240,79 +314,51 @@ def get_l3_power(self): def get_piko_status(self): """returns the power from line 3 in W""" - raw_content = self._get_raw_content() + raw_content = self._raw_data if len(raw_content) < 15: # 2 Strings - return self.safe_list_get(raw_content, 13) + return safe_list_get(raw_content, 13) else: # 3 Strings - value = self.safe_list_get(raw_content, 15) + value = safe_list_get(raw_content, 15) if value is not None: return int(value) else: return None - def _get_raw_content(self): - """returns all values as a list""" - url = self.host + "/index.fhtml" - login = self.username - pwd = self.password - try: - r = requests.get(url, auth=(login, pwd), timeout=15) - if r.status_code == 200: - response = html.fromstring(r.content) - data = [] - for v in response.xpath("//td[@bgcolor='#FFFFFF']"): - raw = v.text.strip() - if "x x x" in raw: - raw = 0 - data.append(raw) - status = response.xpath("/html/body/form/font/table[2]/tr[8]/td[3]")[ - 0 - ].text.strip() - data.append(status) - LOG.debug(data) - return data - else: - raise ConnectionError - except requests.exceptions.ConnectionError as errc: - LOG.debug(errc) - return None - except requests.exceptions.Timeout as errt: - LOG.debug(errt) - return None - def _get_info(self): - """returns the info about the inverter""" - url = self.host + "/Solar2.fhtml" - login = self.username - pwd = self.password - try: - r = requests.get(url, auth=(login, pwd), timeout=15) - if r.status_code == 200: - response = html.fromstring(r.content) - data = [] - serial = response.xpath("/html/body/form/font/table/tr[2]/td[3]")[ - 0 - ].text.strip() - data.append(serial) - model = response.xpath("/html/body/form/table/tr[2]/td[2]/font[1]")[ - 0 - ].text.strip() - data.append(model) - LOG.debug(data) - return data - else: - raise ConnectionError - except requests.exceptions.ConnectionError as errc: - LOG.debug(errc) - return None - except requests.exceptions.Timeout as errt: - LOG.debug(errt) - return None +class PikoBAData(object): + """ + PIKO BA Data + """ - def safe_list_get(self, l, idx, default = None): - try: - return l[idx] - except IndexError: - return default \ No newline at end of file + def __init__(self, raw_data): + self._raw_data = raw_data + + def get_solar_generator_power(self): + """returns the current power of the solar generator in W""" + if self._raw_data: + return safe_list_get(self._raw_data, 5) + else: + return "No BA sensor installed" + + def get_consumption_phase_1(self): + """returns the current consumption of phase 1 in W""" + if self._raw_data: + return safe_list_get(self._raw_data, 8) + else: + return "No BA sensor installed" + + def get_consumption_phase_2(self): + """returns the current consumption of phase 2 in W""" + if self._raw_data: + return safe_list_get(self._raw_data, 9) + else: + return "No BA sensor installed" + + def get_consumption_phase_3(self): + """returns the current consumption of phase 3 in W""" + if self._raw_data: + return safe_list_get(self._raw_data, 10) + else: + return "No BA sensor installed" diff --git a/setup.py b/setup.py index 9ad0041..56ba270 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,13 @@ from setuptools import setup setup( - name="kostalpyko", - version="v0.5", - packages=["tests", "kostalpyko"], + name="kostalpiko", + version="v0.1", + packages=["tests", "kostalpiko"], install_requires=["lxml",], - url="https://github.com/gieljnssns/KostalPikoPy", + url="https://github.com/rcasula/kostalpiko", license="MIT", - author="Giel Janssens", - author_email="gieljnssns@vivaldi.net", + author="Roberto Casula", + author_email="roberto@casula.dev", description="A package for working with a Piko Inverter from Kostal", ) diff --git a/tests/test_kostalpyko.py b/tests/test_kostalpiko.py similarity index 100% rename from tests/test_kostalpyko.py rename to tests/test_kostalpiko.py From a2446400b586c53de145f61bed26881b01f931e7 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Fri, 30 Oct 2020 14:10:14 +0100 Subject: [PATCH 03/24] wip --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 9b45e92..6ecc45d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -PYTHON=`which python` -NAME=`python setup.py --name` -VERSION=`python setup.py --version` +PYTHON=`which python3` +NAME=`python3 setup.py --name` +VERSION=`python3 setup.py --version` SDIST=dist/$(NAME)-$(VERSION).tar.gz VENV=/tmp/venv @@ -19,7 +19,7 @@ rpm: $(PYTHON) setup.py bdist_rpm --post-install=rpm/postinstall --pre-uninstall=rpm/preuninstall install: - $(PYTHON) setup.py install --install-layout=deb + $(PYTHON) setup.py install test: unit2 discover -s tests -t . @@ -37,7 +37,7 @@ upload: $(PYTHON) setup.py bdist_wininst upload init: - pip install -r requirements.txt --use-mirrors + pip3 install -r requirements.txt update: rm ez_setup.py From bcf8eac9e6fa3ae0b275ed00afff7a76cdf1e3b2 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Mon, 16 Nov 2020 08:27:05 +0100 Subject: [PATCH 04/24] wip --- CHANGES.txt | 1 + Makefile | 2 +- kostalpiko-0.1.tar.gz | Bin 0 -> 3589 bytes kostalpiko/kostalpiko.py | 9 ++------- kostalpiko/utils.py | 5 +++++ setup.py | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 kostalpiko-0.1.tar.gz create mode 100644 kostalpiko/utils.py diff --git a/CHANGES.txt b/CHANGES.txt index 94b7de1..e6d0d34 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1 +1,2 @@ v0.1, 2014-07-31 -- Initial release. +v0.2, 2020-11-16 -- Fixed some bugs. diff --git a/Makefile b/Makefile index 6ecc45d..8bcaa87 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ deploy: # setup venv rm -rf $(VENV) - virtualenv --no-site-packages $(VENV) + virtualenv $(VENV) $(VENV)/bin/pip install $(SDIST) clean: diff --git a/kostalpiko-0.1.tar.gz b/kostalpiko-0.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..875ff5b8b9128e73873040f68075377c0c120c2f GIT binary patch literal 3589 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 L>2gTk0LTCUks&|b literal 0 HcmV?d00001 diff --git a/kostalpiko/kostalpiko.py b/kostalpiko/kostalpiko.py index 15bc186..87949ae 100644 --- a/kostalpiko/kostalpiko.py +++ b/kostalpiko/kostalpiko.py @@ -8,14 +8,9 @@ import requests from lxml import html -LOG = logging.getLogger(__name__) - +from .utils import safe_list_get -def safe_list_get(self, l, idx, default=None): - try: - return l[idx] - except IndexError: - return default +LOG = logging.getLogger(__name__) class Piko: diff --git a/kostalpiko/utils.py b/kostalpiko/utils.py new file mode 100644 index 0000000..c37d020 --- /dev/null +++ b/kostalpiko/utils.py @@ -0,0 +1,5 @@ +def safe_list_get(l, idx, default=None): + try: + return l[idx] + except IndexError: + return default \ No newline at end of file diff --git a/setup.py b/setup.py index 56ba270..9fce6d3 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="kostalpiko", - version="v0.1", + version="v0.2", packages=["tests", "kostalpiko"], install_requires=["lxml",], url="https://github.com/rcasula/kostalpiko", From 3f257bc84c94c379f2d261ab78aaf161ebd31fde Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Mon, 29 Nov 2021 11:33:20 +0100 Subject: [PATCH 05/24] Fix indices --- kostalpiko/const.py | 36 +++++++++++++ kostalpiko/kostalpiko.py | 114 +++++++++++++++------------------------ 2 files changed, 79 insertions(+), 71 deletions(-) create mode 100644 kostalpiko/const.py diff --git a/kostalpiko/const.py b/kostalpiko/const.py new file mode 100644 index 0000000..557c65e --- /dev/null +++ b/kostalpiko/const.py @@ -0,0 +1,36 @@ +BASE_INDICES = { + "current_power": 0, + "total_energy": 1, + "daily_energy": 2, + "string1_voltage": 3, + "l1_voltage": 4, + "string1_current": 5, + "l1_power": 6, +} + +SINGLE_STRING_INDICES = { + **BASE_INDICES, + "status": 7 +} + +DOUBLE_STRING_INDICES = { + **BASE_INDICES, + "string2_voltage": 7, + "l2_voltage": 8, + "string2_current": 9, + "l2_power": 10, + "status": 11 +} + +TRIPLE_STRING_INDICES = { + **BASE_INDICES, + "string2_voltage": 7, + "l2_voltage": 8, + "string2_current": 9, + "l2_power": 10, + "string3_voltage": 11, + "l3_voltage": 12, + "string3_current": 13, + "l3_power": 14, + "status": 15 +} \ No newline at end of file diff --git a/kostalpiko/kostalpiko.py b/kostalpiko/kostalpiko.py index 87949ae..e75f79d 100644 --- a/kostalpiko/kostalpiko.py +++ b/kostalpiko/kostalpiko.py @@ -3,16 +3,13 @@ """Library to work with a Piko inverter from Kostal.""" import logging - -# HTTP libraries depends upon Python 2 or 3 import requests from lxml import html - from .utils import safe_list_get +from .const import SINGLE_STRING_INDICES, DOUBLE_STRING_INDICES, TRIPLE_STRING_INDICES LOG = logging.getLogger(__name__) - class Piko: def __init__(self, host=None, username="pvserver", password="pvwr"): self.host = host @@ -70,10 +67,10 @@ def _get_raw_content(self): else: raise ConnectionError except requests.exceptions.ConnectionError as errc: - LOG.debug(errc) + LOG.error(errc) return None except requests.exceptions.Timeout as errt: - LOG.debug(errt) + LOG.error(errt) return def _get_content_of_own_consumption(self): @@ -145,11 +142,18 @@ class PikoData(object): def __init__(self, raw_data): self._raw_data = raw_data + n_values = len(raw_data) + if n_values == 8: + self.indices = SINGLE_STRING_INDICES + elif n_values == 12: + self.indices = DOUBLE_STRING_INDICES + elif n_values == 16: + self.indices = TRIPLE_STRING_INDICES def get_current_power(self): """returns the current power in W""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 0) + if self._raw_data is not None and self.indices['current_power'] is not None: + value = safe_list_get(self._raw_data, self.indices['current_power']) if value is not None: return int(value) else: @@ -157,8 +161,8 @@ def get_current_power(self): def get_total_energy(self): """returns the total energy in kWh""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 1) + if self._raw_data is not None and self.indices['total_energy'] is not None: + value = safe_list_get(self._raw_data, self.indices['total_energy']) if value is not None: return int(value) else: @@ -166,8 +170,8 @@ def get_total_energy(self): def get_daily_energy(self): """returns the daily energy in kWh""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 2) + if self._raw_data is not None and self.indices['daily_energy'] is not None: + value = safe_list_get(self._raw_data, self.indices['daily_energy']) if value is not None: return float(value) else: @@ -175,8 +179,8 @@ def get_daily_energy(self): def get_string1_voltage(self): """returns the voltage from string 1 in V""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 3) + if self._raw_data is not None and self.indices['string1_voltage'] is not None: + value = safe_list_get(self._raw_data, self.indices['string1_voltage']) if value is not None: return int(value) else: @@ -184,8 +188,8 @@ def get_string1_voltage(self): def get_string1_current(self): """returns the current from string 1 in A""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 5) + if self._raw_data is not None and self.indices['string1_current'] is not None: + value = safe_list_get(self._raw_data, self.indices['string1_current']) if value is not None: return float(value) else: @@ -193,8 +197,8 @@ def get_string1_current(self): def get_string2_voltage(self): """returns the voltage from string 2 in V""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 7) + if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['string2_voltage'] is not None: + value = safe_list_get(self._raw_data, index) if value is not None: return int(value) else: @@ -202,8 +206,8 @@ def get_string2_voltage(self): def get_string2_current(self): """returns the current from string 2 in A""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 9) + if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['string2_current'] is not None: + value = safe_list_get(self._raw_data, index) if value is not None: return float(value) else: @@ -211,12 +215,8 @@ def get_string2_current(self): def get_string3_voltage(self): """returns the voltage from string 3 in V""" - raw_content = self._raw_data - if len(raw_content) < 15: - # String 3 not installed - return None - else: - value = safe_list_get(raw_content, 11) + if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['string3_voltage'] is not None: + value = safe_list_get(self._raw_data, index) if value is not None: return int(value) else: @@ -224,12 +224,8 @@ def get_string3_voltage(self): def get_string3_current(self): """returns the current from string 3 in A""" - raw_content = self._raw_data - if len(raw_content) < 15: - # String 3 not installed - return None - else: - value = safe_list_get(raw_content, 13) + if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['string3_current'] is not None: + value = safe_list_get(self._raw_data, index) if value is not None: return float(value) else: @@ -237,8 +233,8 @@ def get_string3_current(self): def get_l1_voltage(self): """returns the voltage from line 1 in V""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 4) + if self._raw_data is not None and self.indices['l1_voltage'] is not None: + value = safe_list_get(self._raw_data, self.indices['l1_voltage']) if value is not None: return int(value) else: @@ -246,8 +242,8 @@ def get_l1_voltage(self): def get_l1_power(self): """returns the power from line 1 in W""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 6) + if self._raw_data is not None and self.indices['l1_power'] is not None: + value = safe_list_get(self._raw_data, self.indices['l1_power']) if value is not None: return int(value) else: @@ -255,8 +251,8 @@ def get_l1_power(self): def get_l2_voltage(self): """returns the voltage from line 2 in V""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 8) + if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['l2_voltage'] is not None: + value = safe_list_get(self._raw_data, self.indices['l2_voltage']) if value is not None: return int(value) else: @@ -264,8 +260,8 @@ def get_l2_voltage(self): def get_l2_power(self): """returns the power from line 1 in W""" - if self._raw_data is not None: - value = safe_list_get(self._raw_data, 10) + if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['l2_power'] is not None: + value = safe_list_get(self._raw_data, self.indices['l2_power']) if value is not None: return int(value) else: @@ -273,35 +269,16 @@ def get_l2_power(self): def get_l3_voltage(self): """returns the voltage from line 3 in V""" - raw_content = self._raw_data - if len(raw_content) < 15: - # 2 Strings - value = safe_list_get(raw_content, 11) - if value is not None: - return int(value) - else: - return None - else: - # 3 Strings - value = safe_list_get(raw_content, 12) + if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['l3_voltage'] is not None: + value = safe_list_get(self._raw_data, self.indices['l3_voltage']) if value is not None: return int(value) else: return None def get_l3_power(self): - """returns the power from line 3 in W""" - raw_content = self._raw_data - if len(raw_content) < 15: - # 2 Strings - value = safe_list_get(raw_content, 12) - if value is not None: - return int(value) - else: - return None - else: - # 3 Strings - value = safe_list_get(raw_content, 14) + if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['l3_power'] is not None: + value = safe_list_get(self._raw_data, self.indices['l3_power']) if value is not None: return int(value) else: @@ -309,15 +286,10 @@ def get_l3_power(self): def get_piko_status(self): """returns the power from line 3 in W""" - raw_content = self._raw_data - if len(raw_content) < 15: - # 2 Strings - return safe_list_get(raw_content, 13) - else: - # 3 Strings - value = safe_list_get(raw_content, 15) + if self._raw_data is not None and self.indices['status'] is not None: + value = safe_list_get(self._raw_data, self.indices['status']) if value is not None: - return int(value) + return value else: return None From 01c29530bc053011365b295f167523aaf196fd78 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Mon, 29 Nov 2021 11:53:36 +0100 Subject: [PATCH 06/24] update version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9fce6d3..8c860c3 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="kostalpiko", - version="v0.2", + version="v0.3", packages=["tests", "kostalpiko"], install_requires=["lxml",], url="https://github.com/rcasula/kostalpiko", From fc4b67873d29580c5e6a392816fcffc5f32b5490 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Mon, 29 Nov 2021 11:53:42 +0100 Subject: [PATCH 07/24] Update changelog --- CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.txt b/CHANGES.txt index e6d0d34..0da409e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,2 +1,3 @@ v0.1, 2014-07-31 -- Initial release. v0.2, 2020-11-16 -- Fixed some bugs. +v0.3, 2021-11-29 -- Fixed some bugs with indices. \ No newline at end of file From 773a0266d19f7c678405823d3ad6cd490fcb7b82 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Tue, 24 May 2022 07:44:31 +0200 Subject: [PATCH 08/24] Add fixtures for tests --- tests/fixtures/index55-1.html | 249 +++++++++++++++++++++++++++++++++ tests/fixtures/index55-2.html | 251 ++++++++++++++++++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100644 tests/fixtures/index55-1.html create mode 100644 tests/fixtures/index55-2.html diff --git a/tests/fixtures/index55-1.html b/tests/fixtures/index55-1.html new file mode 100644 index 0000000..bde1023 --- /dev/null +++ b/tests/fixtures/index55-1.html @@ -0,0 +1,249 @@ + + + + + +PV Webserver + + +
+ + + + + + +
+ + PIKO 5.5 +
+ Namenlos (255) + +
+
Logo
+ + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ AC-Leistung  + Energie
+ aktuell + 112  W + Gesamtenergie + 9290  kWh 
+   +    + Tagesenergie + 19.83  kWh 
+ Status + Einspeisen MPP 
+ + + +

+
+ PV-Generator  + Ausgangsleistung  
+ String 1   + L1   
+ Spannung + 384  V + Spannung + 230  V 
  + Strom + 0.20  A + Leistung + 0  W 
+ String 2   + L2   
+ Spannung + 278  V + Spannung + 232  V 
  + Strom + 0.21  A + Leistung + 112  W 
+ String 3   + L3   
+ Spannung + 0  + V + Spannung + 230  V 
  + Strom + 0.00  +A + Leistung + 0  W 
+ + + +

+
+ + + + + + +
+RS485 Kommunikation
+Wechselrichter  + + +
+
+ +
+ + + + + + +
+ +Historie +      +Infoseite + +Einstellungen
+
+ + diff --git a/tests/fixtures/index55-2.html b/tests/fixtures/index55-2.html new file mode 100644 index 0000000..6c99a03 --- /dev/null +++ b/tests/fixtures/index55-2.html @@ -0,0 +1,251 @@ + + + + + + + +PV Webserver + + +
+ + + + + + +
+ + PIKO 5.5 +
+ PV (255) + +
+
Logo
+ + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ AC-vermogen  + energie
+ actueel + 3126  W + totale energie + 41801  kWh 
+   +    + dagenergie + 13.46  kWh 
+ status + toevoer MPP 
+ + + +

+
+ PV-generator  + uitgangsvermogen  
+ String 1   + L1   
+ spanning + 275  V + spanning + 247  V 
  + stroom + 6.27  A + vermogen + 1032  W 
+ String 2   + L2   
+ spanning + 247  V + spanning + 241  V 
  + stroom + 6.47  A + vermogen + 1027  W 
+ String 3   + L3   
+ spanning + 0  + V + spanning + 246  V 
  + stroom + 0.00  +A + vermogen + 1069  W 
+ + + +

+
+ + + + + + +
+RS485-communicatie
+omvormer  + + +
+
+ +
+ + + + + + +
+ +geschiedenis +      +informatiepagina + +instellingen
+
+ + \ No newline at end of file From b540d158ac69cb6b7904836453d996c2a86081c0 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Tue, 24 May 2022 07:46:11 +0200 Subject: [PATCH 09/24] Safely get values --- kostalpiko/kostalpiko.py | 180 ++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 96 deletions(-) diff --git a/kostalpiko/kostalpiko.py b/kostalpiko/kostalpiko.py index e75f79d..5415947 100644 --- a/kostalpiko/kostalpiko.py +++ b/kostalpiko/kostalpiko.py @@ -150,148 +150,136 @@ def __init__(self, raw_data): elif n_values == 16: self.indices = TRIPLE_STRING_INDICES + def safe_get_value(self, name): + if self._raw_data is not None and name in self.indices and self.indices[name] is not None: + return safe_list_get(self._raw_data, self.indices[name]) + def get_current_power(self): """returns the current power in W""" - if self._raw_data is not None and self.indices['current_power'] is not None: - value = safe_list_get(self._raw_data, self.indices['current_power']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('current_power') + if value is not None: + return int(value) + else: + return None def get_total_energy(self): """returns the total energy in kWh""" - if self._raw_data is not None and self.indices['total_energy'] is not None: - value = safe_list_get(self._raw_data, self.indices['total_energy']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('total_energy') + if value is not None: + return int(value) + else: + return None def get_daily_energy(self): """returns the daily energy in kWh""" - if self._raw_data is not None and self.indices['daily_energy'] is not None: - value = safe_list_get(self._raw_data, self.indices['daily_energy']) - if value is not None: - return float(value) - else: - return None + value = self.safe_get_value('daily_energy') + if value is not None: + return float(value) + else: + return None def get_string1_voltage(self): """returns the voltage from string 1 in V""" - if self._raw_data is not None and self.indices['string1_voltage'] is not None: - value = safe_list_get(self._raw_data, self.indices['string1_voltage']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('string1_voltage') + if value is not None: + return int(value) + else: + return None def get_string1_current(self): """returns the current from string 1 in A""" - if self._raw_data is not None and self.indices['string1_current'] is not None: - value = safe_list_get(self._raw_data, self.indices['string1_current']) - if value is not None: - return float(value) - else: - return None + value = self.safe_get_value('string1_current') + if value is not None: + return float(value) + else: + return None def get_string2_voltage(self): """returns the voltage from string 2 in V""" - if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['string2_voltage'] is not None: - value = safe_list_get(self._raw_data, index) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('string2_voltage') + if value is not None: + return int(value) + else: + return None def get_string2_current(self): """returns the current from string 2 in A""" - if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['string2_current'] is not None: - value = safe_list_get(self._raw_data, index) - if value is not None: - return float(value) - else: - return None + value = self.safe_get_value('string2_current') + if value is not None: + return float(value) + else: + return None def get_string3_voltage(self): """returns the voltage from string 3 in V""" - if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['string3_voltage'] is not None: - value = safe_list_get(self._raw_data, index) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('string3_voltage') + if value is not None: + return int(value) + else: + return None def get_string3_current(self): """returns the current from string 3 in A""" - if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['string3_current'] is not None: - value = safe_list_get(self._raw_data, index) - if value is not None: - return float(value) - else: - return None + value = self.safe_get_value('string3_current') + if value is not None: + return float(value) + else: + return None def get_l1_voltage(self): """returns the voltage from line 1 in V""" - if self._raw_data is not None and self.indices['l1_voltage'] is not None: - value = safe_list_get(self._raw_data, self.indices['l1_voltage']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('l1_voltage') + if value is not None: + return int(value) + else: + return None def get_l1_power(self): """returns the power from line 1 in W""" - if self._raw_data is not None and self.indices['l1_power'] is not None: - value = safe_list_get(self._raw_data, self.indices['l1_power']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('l1_power') + if value is not None: + return int(value) + else: + return None def get_l2_voltage(self): """returns the voltage from line 2 in V""" - if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['l2_voltage'] is not None: - value = safe_list_get(self._raw_data, self.indices['l2_voltage']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('l2_voltage') + if value is not None: + return int(value) + else: + return None def get_l2_power(self): """returns the power from line 1 in W""" - if self.indices == DOUBLE_STRING_INDICES and self._raw_data is not None and self.indices['l2_power'] is not None: - value = safe_list_get(self._raw_data, self.indices['l2_power']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('l2_power') + if value is not None: + return int(value) + else: + return None def get_l3_voltage(self): """returns the voltage from line 3 in V""" - if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['l3_voltage'] is not None: - value = safe_list_get(self._raw_data, self.indices['l3_voltage']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('l3_voltage') + if value is not None: + return int(value) + else: + return None def get_l3_power(self): - if self.indices == TRIPLE_STRING_INDICES and self._raw_data is not None and self.indices['l3_power'] is not None: - value = safe_list_get(self._raw_data, self.indices['l3_power']) - if value is not None: - return int(value) - else: - return None + value = self.safe_get_value('l3_power') + if value is not None: + return int(value) + else: + return None def get_piko_status(self): """returns the power from line 3 in W""" - if self._raw_data is not None and self.indices['status'] is not None: - value = safe_list_get(self._raw_data, self.indices['status']) - if value is not None: - return value - else: - return None + value = self.safe_get_value('status') + if value is not None: + return value + else: + return None class PikoBAData(object): From 535fc43e57d14799153f057b7619f317035f2079 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Tue, 24 May 2022 07:46:24 +0200 Subject: [PATCH 10/24] Add test coverage for PIKO 5.5 --- tests/test_kostalpiko_5_5_1.py | 69 +++++++++++++++++++++++++++++++ tests/test_kostalpiko_5_5_2.py | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 tests/test_kostalpiko_5_5_1.py create mode 100644 tests/test_kostalpiko_5_5_2.py diff --git a/tests/test_kostalpiko_5_5_1.py b/tests/test_kostalpiko_5_5_1.py new file mode 100644 index 0000000..e3ed183 --- /dev/null +++ b/tests/test_kostalpiko_5_5_1.py @@ -0,0 +1,69 @@ +import httpretty +import unittest +from kostalpiko.kostalpiko import Piko +import os + +class Test551(unittest.TestCase): + + def setUp(self): + httpretty.enable(verbose=True) # enable HTTPretty so that it will monkey patch the socket module + httpretty.register_uri(httpretty.GET, "http://example.com/index.fhtml", body=open("./tests/fixtures/index55-1.html").read()) + self.piko = Piko(host='http://example.com') + self.piko.update_data() + + def tearDown(self): + httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module + httpretty.reset() # reset HTTPretty state (clean up registered urls and request history) + + def test_get_raw_content(self): + self.assertEqual(self.piko.data._raw_data, + ['112', '9290', '19.83', '384', '230', '0.20', '0', '278', '232', '0.21', '112', '0', '230', + '0.00', '0', 'Einspeisen MPP']) + + def test_get_current_power(self): + self.assertEqual(self.piko.data.get_current_power(), 112) + + def test_get_total_energy(self): + self.assertEqual(self.piko.data.get_total_energy(), 9290) + + def test_get_daily_energy(self): + self.assertEqual(self.piko.data.get_daily_energy(), 19.83) + + def test_get_string1_voltage(self): + self.assertEqual(self.piko.data.get_string1_voltage(), 384) + + def test_get_string2_voltage(self): + self.assertEqual(self.piko.data.get_string2_voltage(), 278) + + def test_get_string3_voltage(self): + self.assertEqual(self.piko.data.get_string3_voltage(), 0) + + def test_get_string1_current(self): + self.assertEqual(self.piko.data.get_string1_current(), 0.2) + + def test_get_string2_current(self): + self.assertEqual(self.piko.data.get_string2_current(), 0.21) + + def test_get_string3_current(self): + self.assertEqual(self.piko.data.get_string3_current(), 0.0) + + def test_get_l1_voltage(self): + self.assertEqual(self.piko.data.get_l1_voltage(), 230) + + def test_get_l2_voltage(self): + self.assertEqual(self.piko.data.get_l2_voltage(), 232) + + def test_get_l3_voltage(self): + self.assertEqual(self.piko.data.get_l3_voltage(), 230) + + def test_get_l1_power(self): + self.assertEqual(self.piko.data.get_l1_power(), 0) + + def test_get_l2_power(self): + self.assertEqual(self.piko.data.get_l2_power(), 112) + + def test_get_l3_power(self): + self.assertEqual(self.piko.data.get_l3_power(), 0) + + def status(self): + self.assertEqual(self.piko.data.get_status(), 'Einspeisen MPP') diff --git a/tests/test_kostalpiko_5_5_2.py b/tests/test_kostalpiko_5_5_2.py new file mode 100644 index 0000000..0231a12 --- /dev/null +++ b/tests/test_kostalpiko_5_5_2.py @@ -0,0 +1,75 @@ +import httpretty +import unittest +from kostalpiko.kostalpiko import Piko +import os + +class Test551(unittest.TestCase): + + def setUp(self): + httpretty.enable(verbose=True) # enable HTTPretty so that it will monkey patch the socket module + httpretty.register_uri(httpretty.GET, "http://example.com/index.fhtml", body=open("./tests/fixtures/index55-2.html").read()) + self.piko = Piko(host='http://example.com') + self.piko.update_data() + + def tearDown(self): + httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module + httpretty.reset() # reset HTTPretty state (clean up registered urls and request history) + + def test_get_raw_content(self): + self.assertEqual(self.piko.data._raw_data, + ['3126', '41801', '13.46', + '275', '247', + '6.27', '1032', + '247', '241', + '6.47', '1027', + '0', '246', + '0.00', '1069', + 'toevoer MPP']) + + def test_get_current_power(self): + self.assertEqual(self.piko.data.get_current_power(), 3126) + + def test_get_total_energy(self): + self.assertEqual(self.piko.data.get_total_energy(), 41801) + + def test_get_daily_energy(self): + self.assertEqual(self.piko.data.get_daily_energy(), 13.46) + + def test_get_string1_voltage(self): + self.assertEqual(self.piko.data.get_string1_voltage(), 275) + + def test_get_string2_voltage(self): + self.assertEqual(self.piko.data.get_string2_voltage(), 247) + + def test_get_string3_voltage(self): + self.assertEqual(self.piko.data.get_string3_voltage(), 0) + + def test_get_string1_current(self): + self.assertEqual(self.piko.data.get_string1_current(), 06.27) + + def test_get_string2_current(self): + self.assertEqual(self.piko.data.get_string2_current(), 6.47) + + def test_get_string3_current(self): + self.assertEqual(self.piko.data.get_string3_current(), 0.0) + + def test_get_l1_voltage(self): + self.assertEqual(self.piko.data.get_l1_voltage(), 247) + + def test_get_l2_voltage(self): + self.assertEqual(self.piko.data.get_l2_voltage(), 241) + + def test_get_l3_voltage(self): + self.assertEqual(self.piko.data.get_l3_voltage(), 246) + + def test_get_l1_power(self): + self.assertEqual(self.piko.data.get_l1_power(), 1032) + + def test_get_l2_power(self): + self.assertEqual(self.piko.data.get_l2_power(), 1027) + + def test_get_l3_power(self): + self.assertEqual(self.piko.data.get_l3_power(), 1069) + + def status(self): + self.assertEqual(self.piko.data.get_status(), 'toevoer MPP') From 94975b7bd3bbc5542eba8a2f4bf4d03a0afa147e Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Tue, 24 May 2022 07:46:36 +0200 Subject: [PATCH 11/24] Delete old tests --- tests/fixtures/index.html | 249 -------------------------------------- tests/test_kostalpiko.py | 79 ------------ 2 files changed, 328 deletions(-) delete mode 100644 tests/fixtures/index.html delete mode 100644 tests/test_kostalpiko.py diff --git a/tests/fixtures/index.html b/tests/fixtures/index.html deleted file mode 100644 index bde1023..0000000 --- a/tests/fixtures/index.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - -PV Webserver - - -
- - - - - - -
- - PIKO 5.5 -
- Namenlos (255) - -
-
Logo
- - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- AC-Leistung  - Energie
- aktuell - 112  W - Gesamtenergie - 9290  kWh 
-   -    - Tagesenergie - 19.83  kWh 
- Status - Einspeisen MPP 
- - - -

-
- PV-Generator  - Ausgangsleistung  
- String 1   - L1   
- Spannung - 384  V - Spannung - 230  V 
  - Strom - 0.20  A - Leistung - 0  W 
- String 2   - L2   
- Spannung - 278  V - Spannung - 232  V 
  - Strom - 0.21  A - Leistung - 112  W 
- String 3   - L3   
- Spannung - 0  - V - Spannung - 230  V 
  - Strom - 0.00  -A - Leistung - 0  W 
- - - -

-
- - - - - - -
-RS485 Kommunikation
-Wechselrichter  - - -
-
- -
- - - - - - -
- -Historie -      -Infoseite - -Einstellungen
-
- - diff --git a/tests/test_kostalpiko.py b/tests/test_kostalpiko.py deleted file mode 100644 index 36cea91..0000000 --- a/tests/test_kostalpiko.py +++ /dev/null @@ -1,79 +0,0 @@ -import httpretty -import unittest -from kostalpyko.kostalpyko import Piko - - -class Test(unittest.TestCase): - def setUp(self): - httpretty.enable() # enable HTTPretty so that it will monkey patch the socket module - httpretty.register_uri(httpretty.GET, "http://example.com", body=open("fixtures/index.html").read()) - - def tearDown(self): - httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module - httpretty.reset() # reset HTTPretty state (clean up registered urls and request history) - - def test_get_raw_content(self): - p = Piko(host='http://example.com') - self.assertEqual(p._get_raw_content(), - ['112', '9290', '19.83', '384', '230', '0.20', '0', '278', '232', '0.21', '112', '0', '230', - '0.00', '0']) - - def test_get_current_power(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_current_power(), 112) - - def test_get_total_energy(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_total_energy(), 9290) - - def test_get_daily_energy(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_daily_energy(), 19.83) - - def test_get_string1_voltage(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_string1_voltage(), 384) - - def test_get_string2_voltage(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_string2_voltage(), 278) - - def test_get_string3_voltage(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_string3_voltage(), 0) - - def test_get_string1_current(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_string1_current(), 0.2) - - def test_get_string2_current(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_string2_current(), 0.21) - - def test_get_string3_current(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_string3_current(), 0.0) - - def test_get_l1_voltage(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_l1_voltage(), 230) - - def test_get_l2_voltage(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_l2_voltage(), 232) - - def test_get_l3_voltage(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_l3_voltage(), 230) - - def test_get_l1_power(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_l1_power(), 0) - - def test_get_l2_power(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_l2_power(), 112) - - def test_get_l3_power(self): - p = Piko(host='http://example.com') - self.assertEqual(p.get_l3_power(), 0) From c916338a9033c3d877d3137d6380f91822981baf Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Tue, 24 May 2022 08:05:40 +0200 Subject: [PATCH 12/24] Update version --- Makefile | 1 - requirements-dev.txt | 8 ++++---- setup.py | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 8bcaa87..fb8336d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,6 @@ VERSION=`python3 setup.py --version` SDIST=dist/$(NAME)-$(VERSION).tar.gz VENV=/tmp/venv - all: check test source deb dist: source deb diff --git a/requirements-dev.txt b/requirements-dev.txt index aad65b1..f3269c1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -python-stdeb -python-unittest2 -fakeroot -python-all +stdeb +unittest2 +#fakeroot +#python-all diff --git a/setup.py b/setup.py index 8c860c3..50534dd 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="kostalpiko", - version="v0.3", + version="v0.4", packages=["tests", "kostalpiko"], install_requires=["lxml",], url="https://github.com/rcasula/kostalpiko", From abae7dc3bb7708a491cad3ad49f8cdb8494cfb37 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Tue, 24 May 2022 08:20:57 +0200 Subject: [PATCH 13/24] Changelog --- CHANGES.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0da409e..8fef265 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,4 @@ v0.1, 2014-07-31 -- Initial release. v0.2, 2020-11-16 -- Fixed some bugs. -v0.3, 2021-11-29 -- Fixed some bugs with indices. \ No newline at end of file +v0.3, 2021-11-29 -- Fixed some bugs with indices. +v0.4, 2022-05-24 -- Fixed some bugs with indices and added utility to safely get values. \ No newline at end of file From b799a0a1397286202f4ed2ec273e799066d7efdf Mon Sep 17 00:00:00 2001 From: Marc Aubreville Date: Sun, 31 Jul 2022 22:38:37 +0200 Subject: [PATCH 14/24] Added option for 2 strings with 3 phases In the PV inverter here I found an installation with three phases and 2 strings, which was not covered up until now for this library. --- kostalpiko/const.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kostalpiko/const.py b/kostalpiko/const.py index 557c65e..e996fab 100644 --- a/kostalpiko/const.py +++ b/kostalpiko/const.py @@ -22,6 +22,17 @@ "status": 11 } +DOUBLE_STRING_THREE_PHASES_INDICES = { + **BASE_INDICES, + "string2_voltage": 7, + "l2_voltage": 8, + "string2_current": 9, + "l2_power": 10, + "l3_voltage": 8, + "l3_power": 10, + "status": 11 +} + TRIPLE_STRING_INDICES = { **BASE_INDICES, "string2_voltage": 7, @@ -33,4 +44,4 @@ "string3_current": 13, "l3_power": 14, "status": 15 -} \ No newline at end of file +} From 0ffba46e9a69712a508c61fc0bd6dca9480d5754 Mon Sep 17 00:00:00 2001 From: Marc Aubreville Date: Sun, 31 Jul 2022 22:42:01 +0200 Subject: [PATCH 15/24] Update kostalpiko.py Added option for 2 strings / 3 phases, which is apparently also possible :-) Thanks for this great library! --- kostalpiko/kostalpiko.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kostalpiko/kostalpiko.py b/kostalpiko/kostalpiko.py index 5415947..861fa13 100644 --- a/kostalpiko/kostalpiko.py +++ b/kostalpiko/kostalpiko.py @@ -147,6 +147,8 @@ def __init__(self, raw_data): self.indices = SINGLE_STRING_INDICES elif n_values == 12: self.indices = DOUBLE_STRING_INDICES + elif n_values == 14: + self.indices = DOUBLE_STRING_THREE_PHASES_INDICES elif n_values == 16: self.indices = TRIPLE_STRING_INDICES From a25d19cbf087f383ff3e5c5843f47918eb28d4ca Mon Sep 17 00:00:00 2001 From: Marc Aubreville Date: Sun, 31 Jul 2022 22:43:50 +0200 Subject: [PATCH 16/24] Update / fix for last commit Forgot to import the new constant DOUBLE_STRING_THREE_PHASES_INDICES --- kostalpiko/kostalpiko.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kostalpiko/kostalpiko.py b/kostalpiko/kostalpiko.py index 861fa13..cf21e50 100644 --- a/kostalpiko/kostalpiko.py +++ b/kostalpiko/kostalpiko.py @@ -6,7 +6,7 @@ import requests from lxml import html from .utils import safe_list_get -from .const import SINGLE_STRING_INDICES, DOUBLE_STRING_INDICES, TRIPLE_STRING_INDICES +from .const import SINGLE_STRING_INDICES, DOUBLE_STRING_INDICES, TRIPLE_STRING_INDICES, DOUBLE_STRING_THREE_PHASES_INDICES LOG = logging.getLogger(__name__) From 99da75d4c703d59022a3a9186c0ec80882a9ccc4 Mon Sep 17 00:00:00 2001 From: Marc Aubreville Date: Tue, 2 Aug 2022 07:43:42 +0200 Subject: [PATCH 17/24] Update const.py Shouldn't have done a PR close to midnight ;-) Thanks for catching the bug with the indices. --- kostalpiko/const.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kostalpiko/const.py b/kostalpiko/const.py index e996fab..f7ba168 100644 --- a/kostalpiko/const.py +++ b/kostalpiko/const.py @@ -28,9 +28,9 @@ "l2_voltage": 8, "string2_current": 9, "l2_power": 10, - "l3_voltage": 8, - "l3_power": 10, - "status": 11 + "l3_voltage": 11, + "l3_power": 12, + "status": 13 } TRIPLE_STRING_INDICES = { From d4542e71e9689898316dd76ba133b13e8266e25c Mon Sep 17 00:00:00 2001 From: Rasmus Abildgren <26874233+Abildgren@users.noreply.github.com> Date: Wed, 17 Aug 2022 23:59:22 +0200 Subject: [PATCH 18/24] Update to 0.5 Make latest PR (3 phases 2 strings) available in Pypi --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 50534dd..c48d2ee 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="kostalpiko", - version="v0.4", + version="v0.5", packages=["tests", "kostalpiko"], install_requires=["lxml",], url="https://github.com/rcasula/kostalpiko", From 1113325e04ac431831c7bc61bf327cf5a2262114 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Mon, 22 Aug 2022 18:34:16 +0200 Subject: [PATCH 19/24] Update changelog --- CHANGES.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8fef265..8606e54 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ v0.1, 2014-07-31 -- Initial release. v0.2, 2020-11-16 -- Fixed some bugs. v0.3, 2021-11-29 -- Fixed some bugs with indices. -v0.4, 2022-05-24 -- Fixed some bugs with indices and added utility to safely get values. \ No newline at end of file +v0.4, 2022-05-24 -- Fixed some bugs with indices and added utility to safely get values. +v0.5, 2022-08-22 -- Added compatibility to inverters with 3 phases 2 strings. \ No newline at end of file From 6fe35b613ed4ca69e11eaa39674899e48e1602f6 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Mon, 22 Aug 2022 18:44:42 +0200 Subject: [PATCH 20/24] Fix some things --- CHANGES.txt | 3 ++- Makefile | 56 ++++-------------------------------------- kostalpiko-0.5.tar.gz | Bin 0 -> 4897 bytes setup.py | 2 +- 4 files changed, 8 insertions(+), 53 deletions(-) create mode 100644 kostalpiko-0.5.tar.gz diff --git a/CHANGES.txt b/CHANGES.txt index 8606e54..6225b05 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,4 +2,5 @@ v0.1, 2014-07-31 -- Initial release. v0.2, 2020-11-16 -- Fixed some bugs. v0.3, 2021-11-29 -- Fixed some bugs with indices. v0.4, 2022-05-24 -- Fixed some bugs with indices and added utility to safely get values. -v0.5, 2022-08-22 -- Added compatibility to inverters with 3 phases 2 strings. \ No newline at end of file +v0.5, 2022-08-22 -- Added compatibility to inverters with 3 phases 2 strings. +v0.5.1, 2022-08-22 -- Some fixes. \ No newline at end of file diff --git a/Makefile b/Makefile index fb8336d..7fc0ed6 100644 --- a/Makefile +++ b/Makefile @@ -4,59 +4,13 @@ VERSION=`python3 setup.py --version` SDIST=dist/$(NAME)-$(VERSION).tar.gz VENV=/tmp/venv -all: check test source deb +dist: clean build upload -dist: source deb - -source: - $(PYTHON) setup.py sdist - -deb: - $(PYTHON) setup.py --command-packages=stdeb.command bdist_deb - -rpm: - $(PYTHON) setup.py bdist_rpm --post-install=rpm/postinstall --pre-uninstall=rpm/preuninstall - -install: - $(PYTHON) setup.py install - -test: - unit2 discover -s tests -t . - -check: - find . -name \*.py | grep -v "^test_" | xargs pylint --errors-only --reports=n - # pep8 - # pyntch - # pyflakes - # pychecker - # pymetrics +build: + $(PYTHON) -m build upload: - $(PYTHON) setup.py sdist register upload - $(PYTHON) setup.py bdist_wininst upload - -init: - pip3 install -r requirements.txt - -update: - rm ez_setup.py - wget http://peak.telecommunity.com/dist/ez_setup.py - -daily: - $(PYTHON) setup.py bdist egg_info --tag-date - -deploy: - # make sdist - rm -rf dist - python setup.py sdist - - # setup venv - rm -rf $(VENV) - virtualenv $(VENV) - $(VENV)/bin/pip install $(SDIST) + $(PYTHON) -m twine upload --repository pypi dist/* clean: - $(PYTHON) setup.py clean - rm -rf build/ MANIFEST dist build my_program.egg-info deb_dist - find . -name '*.pyc' -delete - + rm -rf dist/* \ No newline at end of file diff --git a/kostalpiko-0.5.tar.gz b/kostalpiko-0.5.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..201068644d07281e8a0e8625b03558965a253edb GIT binary patch literal 4897 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 z Date: Thu, 25 Aug 2022 18:23:30 +0200 Subject: [PATCH 21/24] Add tests for 2 strings single phase --- Makefile | 5 +- tests/fixtures/index2-1.html | 249 +++++++++++++++++++++++++++++++++ tests/test_kostalpiko_2_1.py | 51 +++++++ tests/test_kostalpiko_5_5_1.py | 1 + tests/test_kostalpiko_5_5_2.py | 1 + 5 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/index2-1.html create mode 100644 tests/test_kostalpiko_2_1.py diff --git a/Makefile b/Makefile index 7fc0ed6..eee3eb8 100644 --- a/Makefile +++ b/Makefile @@ -13,4 +13,7 @@ upload: $(PYTHON) -m twine upload --repository pypi dist/* clean: - rm -rf dist/* \ No newline at end of file + rm -rf dist/* + +test: + unit2 discover -s tests -t . \ No newline at end of file diff --git a/tests/fixtures/index2-1.html b/tests/fixtures/index2-1.html new file mode 100644 index 0000000..5deb553 --- /dev/null +++ b/tests/fixtures/index2-1.html @@ -0,0 +1,249 @@ + + + + + +PV Webserver + + +
+ + + + + + +
+ + PIKO 3.6 +
+ VS7 (255) + +
+
Logo
+ + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ AC power  + energy
+ current + 2336  W + total energy + 33523  kWh 
+   +    + daily energy + 11.71  kWh 
+ status + supply MPP 
+ + + +

+
+ PV generator  + output power  
+ String 1   + L1   
+ voltage + 338  V + voltage + 236  V 
  + current + 3.88  A + power + 2336  W 
+ String 2   +    
+ voltage + 336  V + +    
  + current + 3.97  A + +    
+    +    
+ +   + + +    
  + +   + + +    
+ + + +

+
+ + + + + + +
+RS485 communication
+inverter  + + +
+
+ +
+ + + + + + +
+ +history +      +info page + +settings
+
+ + diff --git a/tests/test_kostalpiko_2_1.py b/tests/test_kostalpiko_2_1.py new file mode 100644 index 0000000..fb507c3 --- /dev/null +++ b/tests/test_kostalpiko_2_1.py @@ -0,0 +1,51 @@ +import httpretty +import unittest +from kostalpiko.kostalpiko import Piko +import os + +class Test551(unittest.TestCase): + + def setUp(self): + httpretty.enable(verbose=True) # enable HTTPretty so that it will monkey patch the socket module + httpretty.register_uri(httpretty.GET, "http://example.com/index.fhtml", body=open("./tests/fixtures/index2-1.html").read()) + self.piko = Piko(host='http://example.com') + self.piko.update_data() + # print("Test 2-1") + + def tearDown(self): + httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module + httpretty.reset() # reset HTTPretty state (clean up registered urls and request history) + + def test_get_raw_content(self): + self.assertEqual(self.piko.data._raw_data, + ['2336', '33523', '11.71', '338', '236', '3.88', '2336', '336', '3.97', 'supply MPP']) + + def test_get_current_power(self): + self.assertEqual(self.piko.data.get_current_power(), 2336) + + def test_get_total_energy(self): + self.assertEqual(self.piko.data.get_total_energy(), 33523) + + def test_get_daily_energy(self): + self.assertEqual(self.piko.data.get_daily_energy(), 11.71) + + def test_get_string1_voltage(self): + self.assertEqual(self.piko.data.get_string1_voltage(), 338) + + def test_get_string2_voltage(self): + self.assertEqual(self.piko.data.get_string2_voltage(), 336) + + def test_get_string1_current(self): + self.assertEqual(self.piko.data.get_string1_current(), 3.88) + + def test_get_string2_current(self): + self.assertEqual(self.piko.data.get_string2_current(), 3.97) + + def test_get_l1_voltage(self): + self.assertEqual(self.piko.data.get_l1_voltage(), 236) + + def test_get_l1_power(self): + self.assertEqual(self.piko.data.get_l1_power(), 2336) + + def status(self): + self.assertEqual(self.piko.data.get_status(), 'supply MPP') diff --git a/tests/test_kostalpiko_5_5_1.py b/tests/test_kostalpiko_5_5_1.py index e3ed183..8cf6ef7 100644 --- a/tests/test_kostalpiko_5_5_1.py +++ b/tests/test_kostalpiko_5_5_1.py @@ -10,6 +10,7 @@ def setUp(self): httpretty.register_uri(httpretty.GET, "http://example.com/index.fhtml", body=open("./tests/fixtures/index55-1.html").read()) self.piko = Piko(host='http://example.com') self.piko.update_data() + # print("Test 5-5-1") def tearDown(self): httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module diff --git a/tests/test_kostalpiko_5_5_2.py b/tests/test_kostalpiko_5_5_2.py index 0231a12..55858d0 100644 --- a/tests/test_kostalpiko_5_5_2.py +++ b/tests/test_kostalpiko_5_5_2.py @@ -10,6 +10,7 @@ def setUp(self): httpretty.register_uri(httpretty.GET, "http://example.com/index.fhtml", body=open("./tests/fixtures/index55-2.html").read()) self.piko = Piko(host='http://example.com') self.piko.update_data() + # print("Test 5-5-2") def tearDown(self): httpretty.disable() # disable afterwards, so that you will have no problems in code that uses that socket module From a498f5e2f74d0c449162ab278fc14adee2dcd2e4 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Thu, 25 Aug 2022 18:23:37 +0200 Subject: [PATCH 22/24] remove useless --- kostalpiko-0.1.tar.gz | Bin 3589 -> 0 bytes kostalpiko-0.5.tar.gz | Bin 4897 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 kostalpiko-0.1.tar.gz delete mode 100644 kostalpiko-0.5.tar.gz diff --git a/kostalpiko-0.1.tar.gz b/kostalpiko-0.1.tar.gz deleted file mode 100644 index 875ff5b8b9128e73873040f68075377c0c120c2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3589 zcmV+g4*KyQiwFpd4V+&B|72-%bT4afb97;BaA|9AEif)IE_7jX0PP)XbK*F1KKobb z$W;aIh`~0mq>j4H&SZB}Gm}ZpWOhEJQeFWf_+o6YEQj##uO-=lACOFfNoJx>RS;H7 zYPI?yHI}3|^u{me`0fvaM})p91)37peKwm`YnkTH4WrdI-=MoUJAf!;m_qa0?&J>w zW)Dr7GbMea(`+?6W~XWCy^dj8W?Ors;HQThALQQ+oE!h3sT+;b^uOC~%g>JdY?>LJ zPw9WlFuQM1dzbo8{lO52>+t|>JpTFgUsv{D+5epVZyt8qU1k4YpZ!07|NFt|*>9h= zuK%4@YpwlnnMOwc+a^3h&0XsME4BY03Bx1I@WG#ihK}zYAwzF!XLw4E&{8(EMJ@=5 z_9dE5F`XZwV-(=w4ZbF5>{B%J>5b!EqnX1d2%~c-hfcj)LK&fGO#LZ(FDlgj@TcS; zfMQ2z!dMU$gTFl84@pqW|Kl}9UOgncm1KOv{C$CKZUxfcRJ9+
1c=k-)MELtpA4vpz{B_fW6o$pU=V1 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 L>2gTk0LTCUks&|b diff --git a/kostalpiko-0.5.tar.gz b/kostalpiko-0.5.tar.gz deleted file mode 100644 index 201068644d07281e8a0e8625b03558965a253edb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4897 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 z Date: Thu, 25 Aug 2022 18:24:06 +0200 Subject: [PATCH 23/24] add double strings single phase --- kostalpiko/const.py | 9 ++++++++- kostalpiko/kostalpiko.py | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/kostalpiko/const.py b/kostalpiko/const.py index f7ba168..1d59602 100644 --- a/kostalpiko/const.py +++ b/kostalpiko/const.py @@ -13,7 +13,14 @@ "status": 7 } -DOUBLE_STRING_INDICES = { +DOUBLE_STRING_SINGLE_PHASE_INDICES = { + **BASE_INDICES, + "string2_voltage": 7, + "string2_current": 8, + "status": 9 +} + +DOUBLE_STRING_TWO_PHASES_INDICES = { **BASE_INDICES, "string2_voltage": 7, "l2_voltage": 8, diff --git a/kostalpiko/kostalpiko.py b/kostalpiko/kostalpiko.py index cf21e50..c97d00e 100644 --- a/kostalpiko/kostalpiko.py +++ b/kostalpiko/kostalpiko.py @@ -6,7 +6,7 @@ import requests from lxml import html from .utils import safe_list_get -from .const import SINGLE_STRING_INDICES, DOUBLE_STRING_INDICES, TRIPLE_STRING_INDICES, DOUBLE_STRING_THREE_PHASES_INDICES +from .const import * LOG = logging.getLogger(__name__) @@ -145,8 +145,10 @@ def __init__(self, raw_data): n_values = len(raw_data) if n_values == 8: self.indices = SINGLE_STRING_INDICES + elif n_values == 10: + self.indices = DOUBLE_STRING_SINGLE_PHASE_INDICES elif n_values == 12: - self.indices = DOUBLE_STRING_INDICES + self.indices = DOUBLE_STRING_TWO_PHASES_INDICES elif n_values == 14: self.indices = DOUBLE_STRING_THREE_PHASES_INDICES elif n_values == 16: From e324c6ab465c7f076fb77dea528673a15441ba11 Mon Sep 17 00:00:00 2001 From: Roberto Casula Date: Thu, 25 Aug 2022 18:26:34 +0200 Subject: [PATCH 24/24] Bump version --- CHANGES.txt | 3 ++- setup.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6225b05..09e2565 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,4 +3,5 @@ v0.2, 2020-11-16 -- Fixed some bugs. v0.3, 2021-11-29 -- Fixed some bugs with indices. v0.4, 2022-05-24 -- Fixed some bugs with indices and added utility to safely get values. v0.5, 2022-08-22 -- Added compatibility to inverters with 3 phases 2 strings. -v0.5.1, 2022-08-22 -- Some fixes. \ No newline at end of file +v0.5.1, 2022-08-22 -- Some fixes. +v0.6, 2022-08-25 -- Added compatibility to inverters with 1 phases 2 strings. \ No newline at end of file diff --git a/setup.py b/setup.py index f82f607..edf3ccf 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="kostalpiko", - version="v0.5.1", + version="v0.6", packages=["tests", "kostalpiko"], install_requires=["lxml",], url="https://github.com/rcasula/kostalpiko",