Skip to content

Commit 0fd834c

Browse files
authored
Code Maintenance (#42)
* Code Cleanup * Refactoring: Added EntityFactory and moved parsing * Added first pytest tests * Config Flow refactoring * Removed unused imports * Refactored HTML Parsing * Added tests and testfixture * added requirements * CodeQL and bandit security checks (CI/CD)
1 parent 7c548c2 commit 0fd834c

24 files changed

Lines changed: 1679 additions & 402 deletions

.github/workflows/bandit.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Python Security Check (Bandit)
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
bandit:
9+
name: Run Bandit Security Analysis
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Check out code
14+
uses: actions/checkout@v3
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: '3.12'
20+
21+
- name: Install Bandit
22+
run: pip install bandit
23+
24+
- name: Run Bandit
25+
run: |
26+
bandit -r custom_components/enpal_webparser -ll

.github/workflows/codeql.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: "CodeQL Analysis"
2+
3+
on:
4+
push:
5+
pull_request:
6+
schedule:
7+
- cron: '0 3 * * 0' # optional: wöchentlicher Scan (Sonntag 03:00)
8+
9+
jobs:
10+
analyze:
11+
name: Analyze
12+
runs-on: ubuntu-latest
13+
permissions:
14+
actions: read
15+
contents: read
16+
security-events: write
17+
18+
strategy:
19+
matrix:
20+
language: [python]
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v3
25+
26+
- name: Initialize CodeQL
27+
uses: github/codeql-action/init@v3
28+
with:
29+
languages: ${{ matrix.language }}
30+
31+
- name: Autobuild
32+
uses: github/codeql-action/autobuild@v3
33+
34+
- name: Perform CodeQL Analysis
35+
uses: github/codeql-action/analyze@v3

.github/workflows/tests.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Run Pytest Tests
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Check out repository
13+
uses: actions/checkout@v3
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: '3.12'
19+
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install -r requirements.txt || true
24+
pip install pytest
25+
26+
- name: Set PYTHONPATH
27+
run: echo "PYTHONPATH=$PWD" >> $GITHUB_ENV
28+
29+
- name: Run Pytest
30+
run: pytest

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ __pycache__/
99
.idea/
1010
*.log
1111
*.zip
12+
.coverage

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
[![hacs_badge](https://img.shields.io/badge/HACS-Default-41BDF5.svg)](https://github.com/hacs/integration)
88
[![HACS installs](https://img.shields.io/badge/dynamic/json?color=41BDF5&logo=home-assistant&label=integration%20usage&suffix=%20installs&cacheSeconds=15600&url=https://analytics.home-assistant.io/custom_integrations.json&query=$.enpal_webparser.total)](https://github.com/derolli1976/enpal)
99
[![GitHub release](https://img.shields.io/github/release/derolli1976/enpal.svg)](https://github.com/derolli1976/enpal/releases)
10+
[![Pytest](https://github.com/derolli1976/enpal/actions/workflows/tests.yaml/badge.svg)](https://github.com/derolli1976/enpal/actions/workflows/tests.yaml)
11+
[![CodeQL](https://github.com/derolli1976/enpal/actions/workflows/codeql.yaml/badge.svg)](https://github.com/derolli1976/enpal/actions/workflows/codeql.yaml)
12+
[![Bandit](https://github.com/derolli1976/enpal/actions/workflows/bandit.yaml/badge.svg)](https://github.com/derolli1976/enpal/actions/workflows/bandit.yaml)
13+
14+
1015
[![hacs_install](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=derolli1976&repository=enpal&category=integration)
1116

1217
<a href="https://buymeacoffee.com/derolli1976" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>

custom_components/enpal_webparser/__init__.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
1+
# pyright: reportIncompatibleVariableOverride=false
2+
#
3+
# Home Assistant Custom Component: Enpal Webparser
4+
#
5+
# File: __init__.py
6+
#
7+
# Description:
8+
# Home Assistant integration setup for Enpal Webparser.
9+
# Handles initial integration setup, config entry setup, and platform loading/unloading.
10+
#
11+
# Author: Oliver Stock (github.com/derolli1976)
12+
# License: MIT
13+
# Repository: https://github.com/derolli1976/enpal
14+
#
15+
# Compatible with Home Assistant Core 2024.x and later.
16+
#
17+
# See README.md for setup and usage instructions.
18+
#
19+
120
import logging
221

3-
from homeassistant.core import HomeAssistant
422
from homeassistant.config_entries import ConfigEntry
5-
from homeassistant.helpers.typing import ConfigType
23+
from homeassistant.core import HomeAssistant
624
from homeassistant.exceptions import ConfigEntryNotReady
725
from homeassistant.helpers import config_validation as cv
26+
from homeassistant.helpers.typing import ConfigType
827

928
from .const import DOMAIN
1029

1130
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
1231

13-
1432
_LOGGER = logging.getLogger(__name__)
1533

1634

custom_components/enpal_webparser/button.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
1+
# pyright: reportIncompatibleVariableOverride=false
2+
#
3+
# Home Assistant Custom Component: Enpal Webparser
4+
#
5+
# File: button.py
6+
#
7+
# Description:
8+
# Home Assistant button platform for Enpal wallbox actions.
9+
# Provides start/stop charging and mode selection buttons for Enpal wallbox integration.
10+
#
11+
# Author: Oliver Stock (github.com/derolli1976)
12+
# License: MIT
13+
# Repository: https://github.com/derolli1976/enpal
14+
#
15+
# Compatible with Home Assistant Core 2024.x and later.
16+
#
17+
# See README.md for setup and usage instructions.
18+
#
19+
20+
from functools import cached_property
121
import logging
2-
import aiohttp
3-
from homeassistant.helpers.aiohttp_client import async_get_clientsession
22+
423
from homeassistant.components.button import ButtonEntity
24+
from homeassistant.helpers.aiohttp_client import async_get_clientsession
25+
from homeassistant.helpers.device_registry import DeviceInfo
526
from homeassistant.helpers.entity import EntityCategory
27+
628
from .const import DOMAIN, DEFAULT_WALLBOX_API_ENDPOINT
729

30+
831
_LOGGER = logging.getLogger(__name__)
932

1033
MODES = {
@@ -13,20 +36,21 @@
1336
"solar": "Solar",
1437
}
1538

39+
1640
async def async_setup_entry(hass, config_entry, async_add_entities):
1741
if not config_entry.options.get("use_wallbox_addon", False):
1842
_LOGGER.debug("[Enpal] Wallbox add-on is disabled, skipping button setup.")
1943
return
2044

2145
_LOGGER.info("[Enpal] Setting up Wallbox buttons")
22-
#options = config_entry.options
23-
#base_url = "http://localhost:36725/wallbox"
46+
2447
base_url = DEFAULT_WALLBOX_API_ENDPOINT
2548

2649
buttons = [
2750
EnpalWallboxButton(hass, "Ladevorgang starten", f"{base_url}/start", "start"),
2851
EnpalWallboxButton(hass, "Ladevorgang stoppen", f"{base_url}/stop", "stop"),
2952
]
53+
3054
for key, label in MODES.items():
3155
button_name = f"Modus {label} aktivieren"
3256
buttons.append(EnpalWallboxButton(hass, button_name, f"{base_url}/set_{key}", f"set_{key}"))
@@ -45,14 +69,14 @@ def __init__(self, hass, name, url, unique_id):
4569
self._attr_entity_category = EntityCategory.CONFIG
4670
_LOGGER.debug("[Enpal] Created button entity: %s (URL: %s)", self._attr_name, self._url)
4771

48-
@property
49-
def device_info(self):
50-
return {
51-
"identifiers": {(DOMAIN, "enpal_device")},
52-
"name": "Enpal Webgerät",
53-
"manufacturer": "Enpal",
54-
"model": "Webparser",
55-
}
72+
@cached_property
73+
def device_info(self) -> DeviceInfo:
74+
return DeviceInfo(
75+
identifiers={(DOMAIN, "enpal_device")},
76+
name="Enpal Webgerät",
77+
manufacturer="Enpal",
78+
model="Webparser",
79+
)
5680

5781
async def async_press(self):
5882
_LOGGER.info("[Enpal] Button pressed: %s", self._attr_name)
@@ -68,14 +92,13 @@ async def async_press(self):
6892
_LOGGER.exception("[Enpal] Wallbox request failed: %s", e)
6993

7094
await self._hass.services.async_call(
71-
"homeassistant", "update_entity",
95+
"homeassistant",
96+
"update_entity",
7297
{
7398
"entity_id": [
7499
"sensor.wallbox_lademodus",
75-
"sensor.wallbox_status"
100+
"sensor.wallbox_status",
76101
]
77102
},
78103
blocking=True
79104
)
80-
81-

0 commit comments

Comments
 (0)