Compare commits

..

2 Commits

Author SHA1 Message Date
f4f23796d5 DOC-005: Quickstart ENV-Doku hinzufügen
- .env.example mit SILLYHOME_HA_URL und SILLYHOME_HA_TOKEN
- README: Quickstart-Sektion mit Installation, Start, ENV, Prüfung, Tests
- Hinweis zu Secrets und .gitignore
2026-06-11 03:48:33 +02:00
6540d62ff7 ml/rules-recommendations: regelbasierte Heizungsempfehlung und Recommender 2026-06-10 20:46:54 +02:00
7 changed files with 102 additions and 1 deletions

3
.env.example Normal file
View File

@@ -0,0 +1,3 @@
# Home Assistant Zugriff
SILLYHOME_HA_URL=http://localhost:8123
SILLYHOME_HA_TOKEN=dein_long_lived_access_token

View File

@@ -12,3 +12,34 @@ TheSillyHome zeigte die Idee: statt statischer Regeln das Zuhause aus Verhaltens
- Automationen vorschlagen und direkt generieren
- Lokal-first ohne Cloudpflicht
- Erweiterbar, testbar, dokumentiert
## Quickstart (lokaler Betrieb)
1. **Voraussetzungen**
- Python 3.11+
- Home Assistant mit REST-API erreichbar
- `pip install -e .[dev]`
2. **Umgebungsvariablen** (`.env` im Projektroot)
```
SILLYHOME_HA_URL=http://localhost:8123
SILLYHOME_HA_TOKEN=dein_long_lived_access_token
```
Tipp: `.env.example` kopieren und anpassen. Tokens niemals committen!
3. **Server starten**
```
uvicorn app.main:app --reload
```
4. **Prüfen**
- OpenAPI-Docs: http://localhost:8000/docs
- Health: http://localhost:8000/health
- Entities: http://localhost:8000/v1/entities (benötigt gültige HA-Konfiguration)
5. **Tests**
```
pytest -q
ruff check .
mypy app tests
```

1
app/rules/__init__.py Normal file
View File

@@ -0,0 +1 @@
# sillyhome-next.rules

15
app/rules/heating.py Normal file
View File

@@ -0,0 +1,15 @@
from __future__ import annotations
from collections.abc import Sequence
from app.ha.models import HaEntitySummary
from app.rules.recommender import Rule
class HeatingRule(Rule):
def matches(self, entities: Sequence[HaEntitySummary]) -> bool:
domains = {item.domain for item in entities}
return "climate" in domains or "sensor" in domains
def recommendation(self, entities: Sequence[HaEntitySummary]) -> str:
return "Prüfe Heizungsregelung: Aktiviere energiesparenden Modus bei Abwesenheit."

25
app/rules/recommender.py Normal file
View File

@@ -0,0 +1,25 @@
from __future__ import annotations
from collections.abc import Sequence
from app.ha.models import HaEntitySummary
class Rule:
def matches(self, entities: Sequence[HaEntitySummary]) -> bool:
raise NotImplementedError
def recommendation(self, entities: Sequence[HaEntitySummary]) -> str:
raise NotImplementedError
class Recommender:
def __init__(self, rules: Sequence[Rule]) -> None:
self._rules = rules
def run(self, entities: Sequence[HaEntitySummary]) -> list[str]:
results: list[str] = []
for rule in self._rules:
if rule.matches(entities):
results.append(rule.recommendation(entities))
return results

View File

@@ -25,4 +25,4 @@ strict = true
[tool.ruff]
line-length = 100
target-version = "py311"
target-version = "py311"

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from app.ha.models import HaEntitySummary
from app.rules.heating import HeatingRule
from app.rules.recommender import Recommender
def _sensor(entity_id: str) -> HaEntitySummary:
return HaEntitySummary(entity_id=entity_id, domain="sensor")
def _climate(entity_id: str) -> HaEntitySummary:
return HaEntitySummary(entity_id=entity_id, domain="climate")
def test_heating_rule_triggers() -> None:
rule = HeatingRule()
assert rule.matches([_climate("climate.living_room")])
assert rule.matches([_sensor("sensor.temperature_living")])
def test_recommender_uses_rule() -> None:
recommender = Recommender(rules=[HeatingRule()])
assert recommender.run([_climate("climate.living_room")]) == [
"Prüfe Heizungsregelung: Aktiviere energiesparenden Modus bei Abwesenheit."
]