Skip to content

Commit 5979997

Browse files
authored
Add files via upload
Update 2.4.1
1 parent 2202fe7 commit 5979997

File tree

9 files changed

+362
-124
lines changed

9 files changed

+362
-124
lines changed

custom_components/apsystems_ecu_reader/__init__.py

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from .const import DOMAIN
2323
from .ecu_api import APsystemsSocket, APsystemsInvalidData
24+
from .gui_helpers import set_inverter_state, set_zero_export, reboot_ecu # Import the functions
2425

2526
_LOGGER = logging.getLogger(__name__)
2627

@@ -39,58 +40,17 @@ def __init__(self, ipaddr, wifi_ssid, wifi_password, show_graphs):
3940
self.cached_data = {}
4041

4142
# called from switch.py
42-
def set_inverter_state(self, inverter_id, state):
43+
async def set_inverter_state(self, inverter_id, state):
4344
"""Set the on/off state of an inverter. 1=on, 2=off"""
44-
action = {"ids[]": f'{inverter_id}1' if state else f'{inverter_id}2'}
45-
headers = {'X-Requested-With': 'XMLHttpRequest', "Connection": "keep-alive"}
46-
url = f'http://{self.ipaddr}/index.php/configuration/set_switch_state'
47-
48-
try:
49-
response = requests.post(url, headers=headers, data=action, timeout=15)
50-
_LOGGER.debug(
51-
"Response from ECU on switching the inverter %s to state %s: %s",
52-
inverter_id, 'on' if state else 'off',
53-
re.search(r'"message":"([^"]+)"', response.text).group(1)
54-
)
45+
await set_inverter_state(self.ipaddr, inverter_id, state)
5546

56-
except (requests.ConnectionError, requests.Timeout, requests.HTTPError) as err:
57-
_LOGGER.debug(
58-
"Attempt to switch inverter %s failed with error: %s\n\t"
59-
"This switch is only compatible with ECU-ID 2162... series and ECU-C models",
60-
state, err
61-
)
47+
async def set_zero_export(self, state):
48+
"""Set the bridge state for zero export. 0=closed, 1=open"""
49+
await set_zero_export(self.ipaddr, state)
6250

6351
async def reboot_ecu(self):
6452
""" Reboot the ECU (compatible with ECU-ID 2162... series and ECU-C models) """
65-
_LOGGER.debug("ecu_id: %s", self.cached_data.get("ecu_id", None))
66-
if (
67-
(self.cached_data.get("ecu_id", None)[0:3] == "215")
68-
or (self.cached_data.get("ecu_id", None)[0:4] == "2162")
69-
):
70-
action = {
71-
'SSID': self.wifi_ssid,
72-
'channel': 0,
73-
'method': 2,
74-
'psk_wep': '',
75-
'psk_wpa': self.wifi_password
76-
}
77-
headers = {'X-Requested-With': 'XMLHttpRequest'}
78-
url = 'http://' + str(self.ipaddr) + '/index.php/management/set_wlan_ap'
79-
80-
try:
81-
async with (
82-
aiohttp.ClientSession() as session,
83-
async_timeout.timeout(15),
84-
session.post(url, headers=headers, data=action) as response
85-
):
86-
return await response.text()
87-
except (
88-
aiohttp.ClientError,
89-
aiohttp.ClientConnectionError,
90-
asyncio.TimeoutError,
91-
) as err:
92-
return err
93-
53+
return await reboot_ecu(self.ipaddr, self.wifi_ssid, self.wifi_password, self.cached_data)
9454

9555
async def update(self, port_retries, cache_reboot, show_graphs):
9656
""" Fetch ECU data or use cached data if querying failed. """

custom_components/apsystems_ecu_reader/binary_sensor.py

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,16 @@
22

33
import logging
44

5-
from homeassistant.components.binary_sensor import (
6-
BinarySensorEntity,
7-
)
5+
from homeassistant.components.binary_sensor import BinarySensorEntity
86
from homeassistant.helpers.entity import EntityCategory
9-
from homeassistant.helpers.update_coordinator import (
10-
CoordinatorEntity,
11-
)
7+
from homeassistant.helpers.update_coordinator import CoordinatorEntity
128

13-
from .const import (
14-
DOMAIN,
15-
CACHE_ICON
16-
)
9+
from .const import DOMAIN, CACHE_ICON
1710

1811
_LOGGER = logging.getLogger(__name__)
1912

2013
async def async_setup_entry(hass, _, add_entities):
21-
"""Set up the binary sensor for the APsystems ECU data cache"""
22-
14+
"""Set up the binary sensor for the APsystems ECU data cache."""
2315
ecu = hass.data[DOMAIN].get("ecu")
2416
coordinator = hass.data[DOMAIN].get("coordinator")
2517

@@ -31,18 +23,14 @@ async def async_setup_entry(hass, _, add_entities):
3123
])
3224

3325
class APsystemsECUBinarySensor(CoordinatorEntity, BinarySensorEntity):
34-
"""Representation of a binary sensor for APsystems ECU"""
26+
"""Representation of a binary sensor for APsystems ECU."""
3527

3628
def __init__(self, coordinator, ecu, field, label=None, icon=None):
37-
3829
super().__init__(coordinator)
39-
4030
self.coordinator = coordinator
4131
self._ecu = ecu
4232
self._field = field
43-
self._label = label
44-
if not label:
45-
self._label = field
33+
self._label = label or field
4634
self._icon = icon
4735
self._name = f"ECU {self._label}"
4836
self._state = None
@@ -65,14 +53,13 @@ def icon(self):
6553

6654
@property
6755
def extra_state_attributes(self):
68-
69-
attrs = {
70-
"ecu_id" : self._ecu.ecu.ecu_id,
71-
"firmware" : self._ecu.ecu.firmware,
72-
"timezone" : self._ecu.ecu.timezone,
73-
"last_update" : self._ecu.ecu.last_update
56+
"""Return the state attributes of the sensor."""
57+
return {
58+
"ecu_id": self._ecu.ecu.ecu_id,
59+
"firmware": self._ecu.ecu.firmware,
60+
"timezone": self._ecu.ecu.timezone,
61+
"last_update": self._ecu.ecu.last_update
7462
}
75-
return attrs
7663

7764
@property
7865
def entity_category(self):

custom_components/apsystems_ecu_reader/config_flow.py

Lines changed: 76 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,73 @@
66
from .const import DOMAIN, KEYS
77
from .ecu_api import APsystemsSocket, APsystemsInvalidData
88

9-
109
_LOGGER = logging.getLogger(__name__)
1110

11+
SCAN_INTERVAL = 300
12+
PORT_RETRIES = 5
13+
CACHE_REBOOT = 3
14+
SHOW_GRAPHS = True
15+
WIFI_SSID = "ECU-WIFI_local"
16+
WIFI_PASSWORD = "default"
1217

1318
@config_entries.HANDLERS.register(DOMAIN)
14-
class FlowHandler(config_entries.ConfigFlow):
19+
class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
1520
"""Handle a config flow."""
1621
VERSION = 1
1722

23+
def __init__(self):
24+
"""Initialize the flow handler."""
25+
self.ecu_id = None
26+
self.user_input = {}
27+
1828
async def async_step_user(self, user_input=None):
1929
errors = {}
2030
init_schema = vol.Schema({
21-
vol.Required(KEYS[0], default=''): str, # ECU Host
22-
vol.Required(KEYS[1], default=300): int, # scan interval
23-
vol.Required(KEYS[2], default=5): vol.All(int, vol.Range(min=1, max=10)), # Port retries
24-
vol.Required(KEYS[3], default=3): vol.All(int, vol.Range(min=3, max=5)), # Cache reboot
25-
vol.Optional(KEYS[4], default=True): bool, # Show graphs
26-
vol.Optional(KEYS[5], default="ECU-WIFI_local"): str, # SSID
27-
vol.Optional(KEYS[6], default="default"): str, # Password
31+
vol.Required(KEYS[0], default=''): str,
32+
vol.Required(KEYS[1], default=SCAN_INTERVAL): int,
33+
vol.Required(KEYS[2], default=PORT_RETRIES): vol.All(int, vol.Range(1, 10)),
34+
vol.Optional(KEYS[4], default=SHOW_GRAPHS): bool,
2835
})
2936

3037
if user_input:
3138
ecu_id = await test_ecu_connection(user_input)
3239
if ecu_id:
33-
await self.async_set_unique_id(ecu_id)
40+
_LOGGER.debug("ECU connection successful, ECU ID: %s", ecu_id)
41+
self.ecu_id = ecu_id
42+
self.user_input = user_input
43+
if ecu_id.startswith(("215", "2162")):
44+
return await self.async_step_additional_options()
3445
return self.async_create_entry(title="APsystems", data=user_input)
3546
errors["ecu_host"] = "no_ecu_found"
3647

37-
# Show form because user input is empty.
3848
return self.async_show_form(
3949
step_id="user",
4050
data_schema=init_schema,
4151
errors=errors
4252
)
4353

54+
async def async_step_additional_options(self, user_input=None):
55+
"""Handle additional options for ECU-R-Pro and ECU-C devices."""
56+
errors = {}
57+
additional_schema = vol.Schema({
58+
vol.Required(KEYS[3], default=CACHE_REBOOT): vol.All(int, vol.Range(3, 5)),
59+
vol.Optional(KEYS[5], default=WIFI_SSID): str,
60+
vol.Optional(KEYS[6], default=WIFI_PASSWORD): str,
61+
})
62+
63+
if user_input:
64+
_LOGGER.debug("Received additional options: %s", user_input)
65+
user_input.update(self.user_input)
66+
await self.async_set_unique_id(self.ecu_id)
67+
return self.async_create_entry(title="APsystems", data=user_input)
68+
69+
return self.async_show_form(
70+
step_id="additional_options",
71+
data_schema=additional_schema,
72+
errors=errors,
73+
description_placeholders={"title": "Additional Configuration"}
74+
)
75+
4476
@staticmethod
4577
@callback
4678
def async_get_options_flow(config_entry):
@@ -52,34 +84,17 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
5284

5385
def __init__(self, entry: config_entries.ConfigEntry) -> None:
5486
"""Initialize options flow."""
55-
super().__init__()
5687
self.entry = entry
5788

5889
async def async_step_init(self, user_input=None):
5990
"""Manage the options."""
6091
errors = {}
61-
# Get current options
62-
current_config = {**self.entry.data}
92+
_cfg = {**self.entry.data}
6393
alter_schema = vol.Schema({
64-
vol.Required(KEYS[0], default=current_config.get(KEYS[0])): str, # ECU Host
65-
vol.Required(KEYS[1], default=current_config.get(KEYS[1], 300)): int, # Scan interval
66-
vol.Required(
67-
KEYS[2],
68-
default=current_config.get(KEYS[2], 5)
69-
): vol.All(
70-
int,
71-
vol.Range(min=1, max=10)
72-
), # Port retries
73-
vol.Required(
74-
KEYS[3],
75-
default=current_config.get(KEYS[3], 3)
76-
): vol.All(
77-
int,
78-
vol.Range(min=3, max=5)
79-
), # Cache reboot
80-
vol.Optional(KEYS[4], default=current_config.get(KEYS[4], True)): bool, # Show graphs
81-
vol.Optional(KEYS[5], default=current_config.get(KEYS[5], "ECU-local")): str, # SSID
82-
vol.Optional(KEYS[6], default=current_config.get(KEYS[6], "default")): str, # Password
94+
vol.Required(KEYS[0], default=_cfg.get(KEYS[0])): str,
95+
vol.Required(KEYS[1], default=_cfg.get(KEYS[1], SCAN_INTERVAL)): int,
96+
vol.Required(KEYS[2], default=_cfg.get(KEYS[2], PORT_RETRIES)): vol.All(int, vol.Range(1, 10)),
97+
vol.Optional(KEYS[4], default=_cfg.get(KEYS[4], SHOW_GRAPHS)): bool,
8398
})
8499

85100
if user_input:
@@ -89,14 +104,42 @@ async def async_step_init(self, user_input=None):
89104
self.entry,
90105
data={**self.entry.data, **user_input}
91106
)
107+
if ecu_id.startswith(("215", "2162")):
108+
return await self.async_step_additional_options()
92109
return self.async_create_entry(title="APsystems", data={})
93110
errors["ecu_host"] = "no_ecu_found"
111+
94112
return self.async_show_form(
95113
step_id="init",
96114
data_schema=alter_schema,
97115
errors=errors
98116
)
99117

118+
async def async_step_additional_options(self, user_input=None):
119+
"""Handle additional options."""
120+
errors = {}
121+
_cfg = {**self.entry.data}
122+
additional_schema = vol.Schema({
123+
vol.Required(KEYS[3], default=_cfg.get(KEYS[3], CACHE_REBOOT)): vol.All(int, vol.Range(3, 5)),
124+
vol.Optional(KEYS[5], default=_cfg.get(KEYS[5], WIFI_SSID)): str,
125+
vol.Optional(KEYS[6], default=_cfg.get(KEYS[6], WIFI_PASSWORD)): str,
126+
})
127+
128+
if user_input:
129+
_LOGGER.debug("Received additional options: %s", user_input)
130+
self.hass.config_entries.async_update_entry(
131+
self.entry,
132+
data={**self.entry.data, **user_input}
133+
)
134+
return self.async_create_entry(title="APsystems", data={})
135+
136+
return self.async_show_form(
137+
step_id="additional_options",
138+
data_schema=additional_schema,
139+
errors=errors,
140+
description_placeholders={"title": "Additional Configuration"}
141+
)
142+
100143
async def test_ecu_connection(input_data):
101144
"""Test the connection to the ECU and return the ECU ID if successful."""
102145
try:

custom_components/apsystems_ecu_reader/ecu_api.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import socket
77
import logging
88

9-
from .helper import (
9+
from .ecu_helpers import (
1010
aps_datetimestamp,
1111
aps_str,
1212
aps_int_from_bytes,
@@ -126,7 +126,6 @@ async def query_ecu(self, port_retries, show_graphs):
126126
await self.open_socket(port_retries)
127127
self.ecu_raw_data, status = await self.send_read_from_socket(self.ecu_cmd)
128128
await self.close_socket()
129-
_LOGGER.debug("ECU raw data: %s", self.ecu_raw_data.hex())
130129
if status or not self.ecu_raw_data:
131130
raise APsystemsInvalidData(
132131
f"querying ECU where status is: {status}"

0 commit comments

Comments
 (0)