Skip to content

Commit 2119016

Browse files
authored
Merge pull request #18 from derolli1976/feature/16-feature-wallbox-als-onoff-switch
Added select box for lademodus and switch to turn on/off (Closes #16)
2 parents bc4f03d + 1233f24 commit 2119016

4 files changed

Lines changed: 171 additions & 2 deletions

File tree

custom_components/enpal_webparser/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
2424
hass.data.setdefault(DOMAIN, {})
2525

2626
use_wallbox_addon = entry.options.get("use_wallbox_addon", False)
27-
platforms = ["sensor"] + (["button"] if use_wallbox_addon else [])
27+
28+
platforms = ["sensor"]
29+
if use_wallbox_addon:
30+
platforms.extend(["button", "switch", "select"])
2831

2932
hass.data[DOMAIN][entry.entry_id] = {
3033
"config": entry.data,

custom_components/enpal_webparser/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
"iot_class": "local_polling",
99
"issue_tracker": "https://github.com/derolli1976/enpal/issues",
1010
"requirements": [],
11-
"version": "2.0.2"
11+
"version": "2.1.0"
1212
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import logging
2+
3+
from homeassistant.components.select import SelectEntity
4+
from homeassistant.helpers.aiohttp_client import async_get_clientsession
5+
6+
from .const import DOMAIN
7+
8+
_LOGGER = logging.getLogger(__name__)
9+
10+
# Mapping: API → UI
11+
MODE_MAP = {
12+
"eco": "Eco",
13+
"fast": "Full",
14+
"solar": "Solar",
15+
}
16+
17+
# UI → API
18+
REVERSE_MODE_MAP = {v.lower(): k for k, v in MODE_MAP.items()}
19+
20+
async def async_setup_entry(hass, config_entry, async_add_entities):
21+
if not config_entry.options.get("use_wallbox_addon", False):
22+
return
23+
24+
async_add_entities([EnpalWallboxModeSelect(hass)], True)
25+
26+
class EnpalWallboxModeSelect(SelectEntity):
27+
def __init__(self, hass):
28+
self._hass = hass
29+
self._attr_name = "Wallbox Mode"
30+
self._attr_unique_id = "enpal_wallbox_mode_select"
31+
self._attr_options = list(MODE_MAP.values())
32+
self._current_option = None
33+
self._pending_change = None
34+
self._base_url = "http://localhost:36725/wallbox"
35+
36+
@property
37+
def current_option(self):
38+
return self._pending_change or self._current_option
39+
40+
@property
41+
def device_info(self):
42+
return {
43+
"identifiers": {(DOMAIN, "enpal_device")},
44+
"name": "Enpal Webgerät",
45+
"manufacturer": "Enpal",
46+
"model": "Webparser",
47+
}
48+
49+
async def async_update(self):
50+
mode_entity = self._hass.states.get("sensor.wallbox_lademodus")
51+
if not mode_entity:
52+
return
53+
54+
mode = mode_entity.state.lower()
55+
new_option = MODE_MAP.get(mode)
56+
57+
if not new_option:
58+
_LOGGER.warning("Unknown wallbox mode from sensor: %s", mode)
59+
return
60+
61+
62+
if self._pending_change:
63+
if self._pending_change == new_option:
64+
65+
_LOGGER.debug("Pending wallbox mode %s confirmed by sensor.", new_option)
66+
self._current_option = new_option
67+
self._pending_change = None
68+
else:
69+
_LOGGER.debug("Wallbox mode change pending: %s (sensor reports %s)", self._pending_change, new_option)
70+
else:
71+
self._current_option = new_option
72+
73+
async def async_select_option(self, option: str):
74+
key = REVERSE_MODE_MAP.get(option.lower())
75+
if key:
76+
self._pending_change = option
77+
self.async_write_ha_state()
78+
79+
await self._call_wallbox_api(f"/set_{key}")
80+
81+
else:
82+
_LOGGER.warning("Unknown selected option: %s", option)
83+
84+
async def _call_wallbox_api(self, endpoint):
85+
url = f"{self._base_url}{endpoint}"
86+
try:
87+
session = async_get_clientsession(self._hass)
88+
async with session.post(url, timeout=5) as resp:
89+
if resp.status != 200:
90+
_LOGGER.warning("Wallbox API call failed: %s", url)
91+
except Exception as e:
92+
_LOGGER.error("Error calling wallbox API endpoint %s: %s", endpoint, e)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import logging
2+
3+
from homeassistant.components.switch import SwitchEntity
4+
from homeassistant.helpers.aiohttp_client import async_get_clientsession
5+
6+
from .const import DOMAIN
7+
8+
_LOGGER = logging.getLogger(__name__)
9+
10+
async def async_setup_entry(hass, config_entry, async_add_entities):
11+
if not config_entry.options.get("use_wallbox_addon", False):
12+
return
13+
14+
async_add_entities([EnpalWallboxSwitch(hass)], True)
15+
16+
class EnpalWallboxSwitch(SwitchEntity):
17+
def __init__(self, hass):
18+
self._hass = hass
19+
self._attr_name = "Wallbox Charging"
20+
self._attr_unique_id = "enpal_wallbox_charging_switch"
21+
self._is_on = False
22+
self._pending_state = None
23+
self._base_url = "http://localhost:36725/wallbox"
24+
25+
@property
26+
def is_on(self):
27+
return self._pending_state if self._pending_state is not None else self._is_on
28+
29+
@property
30+
def device_info(self):
31+
return {
32+
"identifiers": {(DOMAIN, "enpal_device")},
33+
"name": "Enpal Webgerät",
34+
"manufacturer": "Enpal",
35+
"model": "Webparser",
36+
}
37+
38+
async def async_update(self):
39+
status_entity = self._hass.states.get("sensor.wallbox_status")
40+
if not status_entity:
41+
return
42+
43+
status = status_entity.state.lower()
44+
new_state = status == "loading"
45+
46+
if self._pending_state is not None:
47+
if self._pending_state == new_state:
48+
_LOGGER.debug("Wallbox switch state confirmed by sensor: %s", status)
49+
self._is_on = new_state
50+
self._pending_state = None
51+
else:
52+
_LOGGER.debug("Wallbox switch state pending: requested=%s, sensor=%s", self._pending_state, new_state)
53+
else:
54+
self._is_on = new_state
55+
56+
async def async_turn_on(self, **kwargs):
57+
await self._call_wallbox_api("/start")
58+
self._pending_state = True
59+
self.async_write_ha_state()
60+
61+
async def async_turn_off(self, **kwargs):
62+
await self._call_wallbox_api("/stop")
63+
self._pending_state = False
64+
self.async_write_ha_state()
65+
66+
async def _call_wallbox_api(self, endpoint):
67+
url = f"{self._base_url}{endpoint}"
68+
try:
69+
session = async_get_clientsession(self._hass)
70+
async with session.post(url, timeout=5) as resp:
71+
if resp.status != 200:
72+
_LOGGER.warning("Wallbox API call failed: %s", url)
73+
except Exception as e:
74+
_LOGGER.error("Error calling wallbox API endpoint %s: %s", endpoint, e)

0 commit comments

Comments
 (0)