Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .bruno/environments/default.bru
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
vars {
USER_AGENT: okhttp/4.12.0
}
vars:secret [
EMAIL,
DEVICE_ID,
Expand Down
38 changes: 0 additions & 38 deletions .github/release-drafter.yml

This file was deleted.

67 changes: 0 additions & 67 deletions .github/workflows/codeql.yaml

This file was deleted.

50 changes: 50 additions & 0 deletions .github/workflows/quality.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Code Quality
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- 'releases/*' # The release branches

jobs:
quality-gate:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }} # use the PR head commit when available, otherwise the push SHA
fetch-depth: 0 # a full history is required for pull request analysis
- uses: actions/setup-python@v5
name: Setup Python
with:
python-version: 3.13
- name: Update pip
run: python -m pip install --upgrade pip
- name: Install dependencies
run: pip install --upgrade -r requirements.txt
- name: Run Unit Tests
run: |
mkdir -p .qodana/code-coverage
coverage run -m pytest
coverage xml -o .qodana/code-coverage/coverage.xml
- name: Run Type Checks
run: pyright
- name: Verify coverage report exists
run: |
test -f .qodana/code-coverage/coverage.xml
wc -c .qodana/code-coverage/coverage.xml
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
name: qodana-coverage-data
path: .qodana/code-coverage
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2025.3
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_810067939 }}
QODANA_ENDPOINT: 'https://qodana.cloud'
11 changes: 0 additions & 11 deletions .github/workflows/release-drafter.yaml

This file was deleted.

25 changes: 0 additions & 25 deletions .github/workflows/tests.yaml

This file was deleted.

5 changes: 5 additions & 0 deletions .sonarlint/connectedMode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sonarCloudOrganization": "dalinicus",
"projectKey": "homeassistant-acinfinity",
"region": "EU"
}
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"sonarlint.connectedMode.project": {
"connectionId": "dalinicus",
"projectKey": "homeassistant-acinfinity"
}
}
24 changes: 10 additions & 14 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ The integration polls the AC Infinity cloud API and exposes device state, enviro
### IDE Configuration

- Use VS Code with Python extension
- Configure mypy for strict type checking
- Enable ruff and black formatters
- Configure pyright for type checking
- Set up pytest for test running

### Bruno API Collection Setup
Expand Down Expand Up @@ -223,11 +222,9 @@ Note: Callback signatures vary by entity scope:

### Style Guidelines

- **Quotes**: Single quotes preferred (`ruff Q000`)
- **Linting**: `ruff` as primary linter with custom rules in `pyproject.toml`
- **Formatting**: `black` for consistent formatting
- **Type Hints**: Strict mypy checking with Python 3.13 type annotations
- **Imports**: Organized and linted automatically
- **Formatting**: Follow the existing code style in the repository
- **Type Hints**: `pyright` checking with Python 3.13 type annotations
- **Imports**: Keep imports organized and readable

### Naming Conventions

Expand Down Expand Up @@ -279,8 +276,8 @@ async def test_entity_value(setup):
### Running Tests

```bash
# Run all tests with coverage
pytest --cov
# Run all tests with coverage XML report (used by SonarQube)
pytest --cov=custom_components/ac_infinity --cov-report=xml --cov-report=term

# Run specific test file
pytest tests/test_sensor.py -v
Expand All @@ -306,9 +303,8 @@ pytest -v
### CI/CD

GitHub Actions runs:
- `pytest --cov` for testing
- `ruff check` for linting
- `mypy` for type checking
- Coverage reporting
- `pytest --cov=custom_components/ac_infinity --cov-report=xml --cov-report=term` for testing and coverage collection
- `pyright` for type checking
- Qodana scan and quality gate to block PRs when conditions are not met

Access Home Assistant at `http://localhost:8123`
Access Home Assistant at `http://localhost:8123`
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# homeassistant-acinfinity

[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs)

[![codecov](https://codecov.io/gh/dalinicus/homeassistant-acinfinity/graph/badge.svg?token=C4TMDAU344)](https://codecov.io/gh/dalinicus/homeassistant-acinfinity)
[![Tests](https://github.com/dalinicus/homeassistant-acinfinity/actions/workflows/tests.yaml/badge.svg)](https://github.com/dalinicus/homeassistant-acinfinity/actions/workflows/tests.yaml)

[![HACS/HASS](https://github.com/dalinicus/homeassistant-acinfinity/actions/workflows/validate.yaml/badge.svg)](https://github.com/dalinicus/homeassistant-acinfinity/actions/workflows/validate.yaml)
Expand Down
12 changes: 6 additions & 6 deletions custom_components/ac_infinity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ async def __initialize_new_devices_if_any(
port_count = ac_infinity.get_controller_property(device_id, ControllerPropertyKey.PORT_COUNT)

device_config = {
"controller": EntityConfigValue.SensorsOnly,
"sensors": EntityConfigValue.SensorsOnly,
"controller": EntityConfigValue.SENSORS_ONLY,
"sensors": EntityConfigValue.SENSORS_ONLY,
}

for i in range(1, port_count + 1):
device_config[f"port_{i}"] = EntityConfigValue.SensorsOnly
device_config[f"port_{i}"] = EntityConfigValue.SENSORS_ONLY

entities_config[str(device_id)] = device_config

Expand Down Expand Up @@ -132,12 +132,12 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
device_name = ac_infinity.get_controller_property(device_id, ControllerPropertyKey.DEVICE_NAME, f"Device {device_id}")

device_config = {
"controller": EntityConfigValue.SensorsAndSettings,
"sensors": EntityConfigValue.SensorsOnly,
"controller": EntityConfigValue.SENSORS_AND_SETTINGS,
"sensors": EntityConfigValue.SENSORS_ONLY,
}

for i in range(1, port_count + 1):
device_config[f"port_{i}"] = EntityConfigValue.All
device_config[f"port_{i}"] = EntityConfigValue.ALL

new_data[ConfigurationKey.ENTITIES][str(device_id)] = device_config

Expand Down
18 changes: 10 additions & 8 deletions custom_components/ac_infinity/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from dataclasses import dataclass
from typing import Any

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
Expand All @@ -14,6 +15,7 @@
DOMAIN,
ControllerPropertyKey,
DevicePropertyKey,
MdiIcon,
SensorPropertyKey,
SensorReferenceKey,
SensorType,
Expand Down Expand Up @@ -42,9 +44,9 @@ class ACInfinityBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes ACInfinity Binary Sensor Entities."""

key: str
device_class: BinarySensorDeviceClass | None
icon: str | None
translation_key: str | None
device_class: BinarySensorDeviceClass | None = None
icon: str | None = None
translation_key: str | None = None


@dataclass(frozen=True)
Expand Down Expand Up @@ -92,7 +94,7 @@ def __get_value_fn_controller_property_default(

def __get_value_fn_device_property_default(
entity: ACInfinityEntity, device: ACInfinityDevice
):
) -> Any:
return entity.ac_infinity.get_device_property(
device.controller.controller_id, device.device_port, entity.data_key, False
)
Expand Down Expand Up @@ -130,7 +132,7 @@ def __get_value_fn_sensor_value_default(
ACInfinityControllerBinarySensorEntityDescription(
key=ControllerPropertyKey.ONLINE,
device_class=BinarySensorDeviceClass.CONNECTIVITY,
icon="mdi:power-plug",
icon=MdiIcon.POWER_PLUG,
translation_key="controller_online",
enabled_fn=enabled_fn_sensor,
suitable_fn=__suitable_fn_controller_property_default,
Expand All @@ -142,7 +144,7 @@ def __get_value_fn_sensor_value_default(
SensorType.WATER: ACInfinitySensorBinarySensorEntityDescription(
key=SensorReferenceKey.WATER,
device_class=BinarySensorDeviceClass.MOISTURE,
icon="mdi:waves",
icon=MdiIcon.WAVES,
translation_key="water_sensor",
enabled_fn=enabled_fn_sensor,
suitable_fn=__suitable_fn_sensor_default,
Expand All @@ -154,7 +156,7 @@ def __get_value_fn_sensor_value_default(
ACInfinityDeviceBinarySensorEntityDescription(
key=DevicePropertyKey.ONLINE,
device_class=BinarySensorDeviceClass.CONNECTIVITY,
icon="mdi:power-plug",
icon=MdiIcon.POWER_PLUG,
translation_key="port_online",
enabled_fn=enabled_fn_sensor,
suitable_fn=__suitable_fn_device_property_default,
Expand All @@ -163,7 +165,7 @@ def __get_value_fn_sensor_value_default(
ACInfinityDeviceBinarySensorEntityDescription(
key=DevicePropertyKey.STATE,
device_class=BinarySensorDeviceClass.POWER,
icon="mdi:power",
icon=MdiIcon.POWER,
translation_key="port_state",
enabled_fn=enabled_fn_sensor,
suitable_fn=__suitable_fn_device_property_default,
Expand Down
Loading
Loading