Skip to content

Commit 48b7b77

Browse files
authored
Kostal Piko Old for software2 (#2773)
* Kostal Piko Old for software2 * wr2
1 parent 851a275 commit 48b7b77

File tree

11 files changed

+183
-87
lines changed

11 files changed

+183
-87
lines changed

modules/wr2_kostalpikovar2/main.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ openwbDebugLog ${DMOD} 2 "WR User: ${wr2_piko2_user}"
2121
openwbDebugLog ${DMOD} 2 "WR Passwort: ${wr2_piko2_pass}"
2222
openwbDebugLog ${DMOD} 2 "WR URL: ${wr2_piko2_url}"
2323

24-
bash "$OPENWBBASEDIR/packages/legacy_run.sh" "wr_kostalpikovar2.kostal_piko_var2" 2 "${wr2_piko2_url}" "${wr2_piko2_user}" "${wr2_piko2_pass}" >>"$MYLOGFILE" 2>&1
24+
bash "$OPENWBBASEDIR/packages/legacy_run.sh" "modules.devices.kostal_piko_old.device" "inverter" "${wr2_piko2_url}" "${wr2_piko2_user}" "${wr2_piko2_pass}" 2 >>"$MYLOGFILE" 2>&1
2525
ret=$?
2626

2727
openwbDebugLog ${DMOD} 2 "RET: ${ret}"

modules/wr_kostalpikovar2/kostal_piko_var2.py

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

modules/wr_kostalpikovar2/kostal_piko_var2_test.py

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

modules/wr_kostalpikovar2/main.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ openwbDebugLog ${DMOD} 2 "WR User: ${wr_piko2_user}"
2121
openwbDebugLog ${DMOD} 2 "WR Passwort: ${wr_piko2_pass}"
2222
openwbDebugLog ${DMOD} 2 "WR URL: ${wr_piko2_url}"
2323

24-
bash "$OPENWBBASEDIR/packages/legacy_run.sh" "wr_kostalpikovar2.kostal_piko_var2" 1 "${wr_piko2_url}" "${wr_piko2_user}" "${wr_piko2_pass}" >>"$MYLOGFILE" 2>&1
24+
bash "$OPENWBBASEDIR/packages/legacy_run.sh" "modules.devices.kostal_piko_old.device" "inverter" "${wr_piko2_url}" "${wr_piko2_user}" "${wr_piko2_pass}" 1 >>"$MYLOGFILE" 2>&1
2525
ret=$?
2626

2727
openwbDebugLog ${DMOD} 2 "RET: ${ret}"

packages/modules/devices/kostal_piko_old/__init__.py

Whitespace-only changes.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from typing import Optional
2+
from helpermodules.auto_str import auto_str
3+
from modules.common.component_setup import ComponentSetup
4+
5+
6+
@auto_str
7+
class KostalPikoOldConfiguration:
8+
def __init__(self, ip_address: Optional[str] = None, user: Optional[str] = None, password: Optional[str] = None):
9+
self.ip_address = ip_address
10+
self.user = user
11+
self.password = password
12+
13+
14+
@auto_str
15+
class KostalPikoOld:
16+
def __init__(self,
17+
name: str = "Kostal Piko (alte Generation)",
18+
type: str = "kostal_piko_old",
19+
id: int = 0,
20+
configuration: KostalPikoOldConfiguration = None) -> None:
21+
self.name = name
22+
self.type = type
23+
self.id = id
24+
self.configuration = configuration or KostalPikoOldConfiguration()
25+
26+
27+
@auto_str
28+
class KostalPikoOldInverterConfiguration:
29+
def __init__(self):
30+
pass
31+
32+
33+
@auto_str
34+
class KostalPikoOldInverterSetup(ComponentSetup[KostalPikoOldInverterConfiguration]):
35+
def __init__(self,
36+
name: str = "Kostal Piko (alte Generation) Wechselrichter",
37+
type: str = "inverter",
38+
id: int = 0,
39+
configuration: KostalPikoOldInverterConfiguration = None) -> None:
40+
super().__init__(name, type, id, configuration or KostalPikoOldInverterConfiguration())
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
import logging
3+
from typing import Iterable, Optional, List
4+
5+
from helpermodules.cli import run_using_positional_cli_args
6+
from modules.common import req
7+
from modules.common.abstract_device import DeviceDescriptor
8+
from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater
9+
from modules.devices.kostal_piko_old import inverter
10+
from modules.devices.kostal_piko_old.config import KostalPikoOld, KostalPikoOldConfiguration, KostalPikoOldInverterSetup
11+
from modules.devices.kostal_piko_old.inverter import KostalPikoOldInverter
12+
13+
log = logging.getLogger(__name__)
14+
15+
16+
def create_device(device_config: KostalPikoOld):
17+
def create_inverter_component(component_config: KostalPikoOldInverterSetup):
18+
return KostalPikoOldInverter(component_config)
19+
20+
def update_components(components: Iterable[KostalPikoOldInverter]):
21+
response = req.get_http_session().get(device_config.configuration.ip_address, verify=False, auth=(
22+
device_config.configuration.user, device_config.configuration.password), timeout=5).text
23+
for component in components:
24+
component.update(response)
25+
26+
return ConfigurableDevice(
27+
device_config=device_config,
28+
component_factory=ComponentFactoryByType(
29+
inverter=create_inverter_component,
30+
),
31+
component_updater=MultiComponentUpdater(update_components)
32+
)
33+
34+
35+
COMPONENT_TYPE_TO_MODULE = {
36+
"inverter": inverter
37+
}
38+
39+
40+
def read_legacy(component_type: str, ip_address: str, user: str, password: str, num: Optional[int]) -> None:
41+
device_config = KostalPikoOld(
42+
configuration=KostalPikoOldConfiguration(ip_address=ip_address, user=user, password=password))
43+
dev = create_device(device_config)
44+
if component_type in COMPONENT_TYPE_TO_MODULE:
45+
component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory()
46+
else:
47+
raise Exception(
48+
"illegal component type " + component_type + ". Allowed values: " +
49+
','.join(COMPONENT_TYPE_TO_MODULE.keys())
50+
)
51+
component_config.id = num
52+
dev.add_component(component_config)
53+
54+
log.debug('KostalPikoOld IP-Adresse: ' + ip_address)
55+
log.debug('KostalPikoOld user: ' + user)
56+
log.debug('KostalPikoOld Passwort: ' + password)
57+
58+
dev.update()
59+
60+
61+
def main(argv: List[str]):
62+
run_using_positional_cli_args(read_legacy, argv)
63+
64+
65+
device_descriptor = DeviceDescriptor(configuration_factory=KostalPikoOld)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python3
2+
import logging
3+
import re
4+
5+
from modules.common.component_state import InverterState
6+
from modules.common.component_type import ComponentDescriptor
7+
from modules.common.fault_state import ComponentInfo
8+
from modules.common.store import get_inverter_value_store
9+
from modules.devices.kostal_piko_old.config import KostalPikoOldInverterSetup
10+
11+
log = logging.getLogger(__name__)
12+
13+
14+
class KostalPikoOldInverter:
15+
def __init__(self, component_config: KostalPikoOldInverterSetup) -> None:
16+
self.component_config = component_config
17+
self.store = get_inverter_value_store(self.component_config.id)
18+
self.component_info = ComponentInfo.from_component_config(self.component_config)
19+
20+
def update(self, response) -> None:
21+
# power may be a string "xxx" when the inverter is offline, so we cannot match as a number
22+
# state is just for debugging currently known states:
23+
# - Aus
24+
# - Leerlauf
25+
result = re.search(
26+
r"aktuell</td>\s*<td[^>]*>\s*([^<]+).*"
27+
r"Gesamtenergie</td>\s*<td[^>]*>\s*(\d+).*"
28+
r"Status</td>\s*<td[^>]*>\s*([^<]+)",
29+
response,
30+
re.DOTALL
31+
)
32+
if result is None:
33+
raise Exception("Given HTML does not match the expected regular expression. Ignoring.")
34+
log.debug("Inverter data: state=%s, power=%s, exported=%s" %
35+
(result.group(3), result.group(1), result.group(2)))
36+
try:
37+
power = -int(result.group(1))
38+
except ValueError:
39+
log.info("Inverter power is not a number! Inverter may be offline. Setting power to 0 W.")
40+
power = 0
41+
inverter_state = InverterState(
42+
exported=int(result.group(2)) * 1000,
43+
power=power
44+
)
45+
self.store.set(inverter_state)
46+
47+
48+
component_descriptor = ComponentDescriptor(configuration_factory=KostalPikoOldInverterSetup)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from pathlib import Path
2+
from unittest.mock import Mock
3+
4+
import pytest
5+
from modules.common.component_state import InverterState
6+
7+
from modules.devices.kostal_piko_old import inverter
8+
from modules.devices.kostal_piko_old.config import KostalPikoOldInverterSetup
9+
10+
11+
@pytest.mark.parametrize("sample_file_name, expected_inverter_state",
12+
[pytest.param("sample.html", InverterState(power=-50, exported=73288000), id="Inverter on"),
13+
pytest.param("sample_off.html", InverterState(
14+
power=0, exported=42906000), id="Inverter off")]
15+
)
16+
def test_parse_html(sample_file_name, expected_inverter_state, monkeypatch):
17+
# setup
18+
sample = (Path(__file__).parent / sample_file_name).read_text()
19+
mock_inverter_value_store = Mock()
20+
monkeypatch.setattr(inverter, 'get_inverter_value_store', Mock(return_value=mock_inverter_value_store))
21+
inv = inverter.KostalPikoOldInverter(KostalPikoOldInverterSetup())
22+
23+
# execution
24+
inv.update(sample)
25+
26+
# evaluation
27+
assert mock_inverter_value_store.set.call_count == 1
28+
assert vars(mock_inverter_value_store.set.call_args[0][0]) == vars(expected_inverter_state)

modules/wr_kostalpikovar2/kostal_piko_var2_test_sample.html renamed to packages/modules/devices/kostal_piko_old/sample.html

File renamed without changes.

0 commit comments

Comments
 (0)