chore: Projektgeruest, Manifest, Konstanten und CI
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
cache-dependency-glob: "requirements_dev.txt"
|
||||
- run: uv venv --python 3.14 .venv && uv pip install --python .venv/bin/python -r requirements_dev.txt
|
||||
- run: .venv/bin/ruff check . && .venv/bin/ruff format --check .
|
||||
- run: .venv/bin/pytest -q --cov --cov-report=term-missing
|
||||
hassfest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: home-assistant/actions/hassfest@master
|
||||
hacs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: hacs/action@main
|
||||
with:
|
||||
category: integration
|
||||
@@ -0,0 +1 @@
|
||||
"""The ninlight integration."""
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Constants for the ninlight integration. Deliberately free of Home Assistant imports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Final
|
||||
|
||||
DOMAIN: Final = "ninlight"
|
||||
|
||||
# Public default token of the ninlight firmware (CMakeLists.txt of the firmware repo).
|
||||
DEFAULT_TOKEN: Final = "aabbccddeeff0011223344"
|
||||
|
||||
CONF_SCAN_INTERVAL: Final = "scan_interval"
|
||||
CONF_LAMP_BASE_URL: Final = "lamp_base_url"
|
||||
|
||||
DEFAULT_SCAN_INTERVAL: Final = 30
|
||||
MIN_SCAN_INTERVAL: Final = 10
|
||||
MAX_SCAN_INTERVAL: Final = 300
|
||||
|
||||
MAX_TRANSITION_MS: Final = 60_000
|
||||
|
||||
FIRMWARE_DIR_NAME: Final = "ninlight_firmware"
|
||||
CATALOG_SCAN_INTERVAL: Final = timedelta(minutes=5)
|
||||
SIGNAL_CATALOG_UPDATED: Final = f"{DOMAIN}_catalog_updated"
|
||||
|
||||
FIRMWARE_URL_PREFIX: Final = "/api/ninlight/firmware/"
|
||||
# len(secrets.token_urlsafe(32))
|
||||
FIRMWARE_KEY_LENGTH: Final = 43
|
||||
# The lamp rejects fw_update URLs longer than this (nl_api.c: get_string(cmd, "url", 8, 255)).
|
||||
MAX_LAMP_URL_LENGTH: Final = 255
|
||||
|
||||
OTA_POLL_INTERVAL_S: Final = 5.0
|
||||
# The firmware aborts a stalled download after 10 min; a dead peer can hang ~7 min in TCP.
|
||||
OTA_TIMEOUT_S: Final = 12 * 60.0
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"domain": "ninlight",
|
||||
"name": "ninlight",
|
||||
"codeowners": ["@ninharp"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["http"],
|
||||
"documentation": "https://github.com/ninharp/ha-ninlight",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"issue_tracker": "https://github.com/ninharp/ha-ninlight/issues",
|
||||
"requirements": [],
|
||||
"version": "0.1.0",
|
||||
"zeroconf": [{ "type": "_ninlight._tcp.local." }]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "ninlight",
|
||||
"render_readme": true,
|
||||
"homeassistant": "2026.9.0"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests_unit", "tests"]
|
||||
asyncio_mode = "auto"
|
||||
addopts = "-p no:cacheprovider"
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["custom_components/ninlight"]
|
||||
branch = true
|
||||
|
||||
[tool.coverage.report]
|
||||
fail_under = 95
|
||||
show_missing = true
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py314"
|
||||
line-length = 100
|
||||
exclude = ["docs/"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "ASYNC"]
|
||||
# E501: line length is the formatter's job; long literals in test tables stay on one line.
|
||||
# RUF012: HA entities declare _attr_* lists as plain class attributes by convention.
|
||||
ignore = ["E501", "RUF012"]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
known-first-party = ["custom_components.ninlight", "tests", "tests_unit"]
|
||||
force-sort-within-sections = true
|
||||
@@ -0,0 +1,4 @@
|
||||
homeassistant==2026.9.0
|
||||
pytest-homeassistant-custom-component==0.13.363
|
||||
pytest-cov
|
||||
ruff>=0.9
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Fixtures for tests that run against a Home Assistant instance."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def auto_enable_custom_integrations(enable_custom_integrations: None) -> Generator[None]:
|
||||
"""Load custom_components/ in every Home Assistant test."""
|
||||
yield
|
||||
@@ -0,0 +1,20 @@
|
||||
"""The manifest must agree with the constants the code relies on."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from custom_components.ninlight.const import DOMAIN
|
||||
|
||||
MANIFEST = Path(__file__).parents[1] / "custom_components" / "ninlight" / "manifest.json"
|
||||
|
||||
|
||||
def test_manifest_matches_constants() -> None:
|
||||
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
assert manifest["domain"] == DOMAIN
|
||||
assert manifest["zeroconf"] == [{"type": "_ninlight._tcp.local."}]
|
||||
assert manifest["iot_class"] == "local_polling"
|
||||
assert manifest["config_flow"] is True
|
||||
assert manifest["requirements"] == []
|
||||
assert manifest["dependencies"] == ["http"]
|
||||
Reference in New Issue
Block a user