Improve SillyHome discovery and feedback learning
This commit is contained in:
@@ -38,12 +38,22 @@ class ManualAssignmentRequest(BaseModel):
|
||||
note: str | None = Field(default=None, max_length=500)
|
||||
|
||||
|
||||
class FeedbackRequest(BaseModel):
|
||||
correct: bool
|
||||
expected_state: str | None = Field(default=None, max_length=100)
|
||||
|
||||
|
||||
@router.get("/discovery", response_model=list[HaEntitySummary])
|
||||
def discover_actuators(ha_reader: HaReader = Depends(get_ha_reader)) -> list[HaEntitySummary]:
|
||||
entities = {entity.entity_id: entity for entity in ha_reader.read_entities()}
|
||||
discovered = ha_reader.discover()
|
||||
actuator_ids = sorted(
|
||||
entity.entity_id for entity in discovered if entity.role is EntityRole.ACTUATOR
|
||||
actuator_ids = _deduplicate_actuator_ids(
|
||||
[
|
||||
(entity.entity_id, entity.category)
|
||||
for entity in discovered
|
||||
if entity.role is EntityRole.ACTUATOR
|
||||
],
|
||||
entities,
|
||||
)
|
||||
return [entities[entity_id] for entity_id in actuator_ids if entity_id in entities]
|
||||
|
||||
@@ -116,6 +126,22 @@ def evaluate_actuator(
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/{actuator_entity_id}/feedback", response_model=ActuatorRecord)
|
||||
def record_feedback(
|
||||
actuator_entity_id: str,
|
||||
payload: FeedbackRequest,
|
||||
request: Request,
|
||||
) -> ActuatorRecord:
|
||||
try:
|
||||
return _behavior(request).record_feedback(
|
||||
actuator_entity_id,
|
||||
correct=payload.correct,
|
||||
expected_state=payload.expected_state,
|
||||
)
|
||||
except KeyError as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/{actuator_entity_id}/activation", response_model=ActuatorRecord)
|
||||
def set_activation(
|
||||
actuator_entity_id: str,
|
||||
@@ -233,3 +259,38 @@ def _behavior(request: Request) -> BehaviorEngine:
|
||||
detail="Verhaltenslernen ist nicht initialisiert.",
|
||||
)
|
||||
return engine
|
||||
|
||||
|
||||
def _deduplicate_actuator_ids(
|
||||
discovered: list[tuple[str, str]],
|
||||
entities: dict[str, HaEntitySummary],
|
||||
) -> list[str]:
|
||||
priority = {
|
||||
"light": 0,
|
||||
"cover_shutter": 1,
|
||||
"heating": 2,
|
||||
"lock": 3,
|
||||
"fan": 4,
|
||||
"switch_socket": 5,
|
||||
"button": 6,
|
||||
"helper": 7,
|
||||
}
|
||||
selected: dict[str, tuple[int, str]] = {}
|
||||
for entity_id, category in discovered:
|
||||
entity = entities.get(entity_id)
|
||||
if entity is None:
|
||||
continue
|
||||
key = _actuator_duplicate_key(entity, category)
|
||||
rank = priority.get(category, 50)
|
||||
current = selected.get(key)
|
||||
if current is None or (rank, entity_id) < current:
|
||||
selected[key] = (rank, entity_id)
|
||||
return sorted(entity_id for _, entity_id in selected.values())
|
||||
|
||||
|
||||
def _actuator_duplicate_key(entity: HaEntitySummary, category: str) -> str:
|
||||
if entity.device_id and category in {"light", "switch_socket", "button"}:
|
||||
return f"device:{entity.device_id}:control"
|
||||
if entity.device_name and category in {"light", "switch_socket", "button"}:
|
||||
return f"device-name:{entity.device_name.lower()}:control"
|
||||
return f"entity:{entity.entity_id}"
|
||||
|
||||
Reference in New Issue
Block a user