This commit is contained in:
2026-02-21 16:55:10 +01:00
commit 1b8bf79d46
49 changed files with 4347 additions and 0 deletions

View File

View File

@@ -0,0 +1,134 @@
import json
from datetime import datetime
from sqlmodel import Session
from app.database import get_engine
from app.ha_client import ha_client
from app.models import EntityCache, EntityFlag, AuditLog
def _get_current_state(session: Session, entity_id: str) -> str | None:
"""Récupère l'état actuel d'une entité depuis le cache."""
entity = session.get(EntityCache, entity_id)
return entity.state if entity else None
def _save_original_state(session: Session, entity_id: str):
"""Sauvegarde l'état original avant désactivation."""
flag = session.get(EntityFlag, entity_id)
if not flag:
flag = EntityFlag(entity_id=entity_id)
# Ne sauvegarder que si pas déjà désactivé (garder le vrai état original)
if not flag.original_state:
flag.original_state = _get_current_state(session, entity_id)
flag.disabled_at = datetime.utcnow()
session.add(flag)
return flag
def _clear_original_state(session: Session, entity_id: str):
"""Efface l'état original lors de la réactivation."""
flag = session.get(EntityFlag, entity_id)
if flag:
flag.original_state = None
flag.disabled_at = None
session.add(flag)
async def disable_entity(entity_id: str) -> dict:
mode = "local_flag"
error = ""
# Sauvegarder l'état original
with Session(get_engine()) as session:
_save_original_state(session, entity_id)
session.commit()
# Tenter désactivation via HA registry
try:
await ha_client.update_entity_registry(entity_id, disabled_by="user")
mode = "ha_registry"
except Exception as e:
error = str(e)
# Fallback : flag local
with Session(get_engine()) as session:
flag = session.get(EntityFlag, entity_id)
if not flag:
flag = EntityFlag(entity_id=entity_id)
flag.ignored_local = True
session.add(flag)
session.commit()
_log_action("disable", [entity_id], mode, error)
return {"entity_id": entity_id, "mode": mode, "error": error}
async def enable_entity(entity_id: str) -> dict:
mode = "local_flag"
error = ""
try:
await ha_client.update_entity_registry(entity_id, disabled_by=None)
mode = "ha_registry"
except Exception as e:
error = str(e)
with Session(get_engine()) as session:
flag = session.get(EntityFlag, entity_id)
if flag:
flag.ignored_local = False
session.add(flag)
session.commit()
# Effacer l'état original
with Session(get_engine()) as session:
_clear_original_state(session, entity_id)
session.commit()
_log_action("enable", [entity_id], mode, error)
return {"entity_id": entity_id, "mode": mode, "error": error}
def set_flag(entity_ids: list[str], action: str) -> list[dict]:
results = []
with Session(get_engine()) as session:
for eid in entity_ids:
flag = session.get(EntityFlag, eid)
if not flag:
flag = EntityFlag(entity_id=eid)
if action == "favorite":
flag.favorite = True
elif action == "unfavorite":
flag.favorite = False
elif action == "ignore":
# Sauvegarder l'état original avant ignore
if not flag.original_state:
flag.original_state = _get_current_state(session, eid)
flag.disabled_at = datetime.utcnow()
flag.ignored_local = True
elif action == "unignore":
flag.ignored_local = False
flag.original_state = None
flag.disabled_at = None
session.add(flag)
results.append({"entity_id": eid, "action": action, "ok": True})
session.commit()
_log_action(action, entity_ids, "ok", "")
return results
def _log_action(action: str, entity_ids: list[str], result: str, error: str):
with Session(get_engine()) as session:
log = AuditLog(
ts=datetime.utcnow(),
action=action,
entity_ids_json=json.dumps(entity_ids),
result=result,
error=error,
)
session.add(log)
session.commit()

View File

@@ -0,0 +1,53 @@
import json
from datetime import datetime
from sqlmodel import Session, select
from app.database import get_engine
from app.ha_client import ha_client, normalize_entity
from app.models import EntityCache
from app.scan_state import scan_state
async def run_scan():
scan_state.start()
try:
states = await ha_client.fetch_all_states()
scan_state.total = len(states)
# Tenter de récupérer le registry (peut échouer si WS non dispo)
registry_map: dict[str, dict] = {}
try:
registry = await ha_client.fetch_entity_registry()
registry_map = {e["entity_id"]: e for e in registry}
except Exception:
pass # On continue sans registry
with Session(get_engine()) as session:
for i, state in enumerate(states):
entity_id = state.get("entity_id", "")
reg_entry = registry_map.get(entity_id)
normalized = normalize_entity(state, reg_entry)
existing = session.get(EntityCache, entity_id)
if existing:
for key, value in normalized.items():
if key != "entity_id":
setattr(existing, key, value)
existing.fetched_at = datetime.utcnow()
else:
entity = EntityCache(
**normalized,
fetched_at=datetime.utcnow(),
)
session.add(entity)
scan_state.progress = i + 1
session.commit()
scan_state.finish(len(states))
except Exception as e:
scan_state.fail(str(e))
raise