From 5ccb0a48c21415b5c97f0df407b8f15e94610142 Mon Sep 17 00:00:00 2001 From: pporcheret Date: Mon, 26 Oct 2026 01:38:47 +0100 Subject: [PATCH] V1 --- alfred/app/__pycache__/app.cpython-313.pyc | Bin 32058 -> 32406 bytes alfred/app/app.py | 3 + alfred/app/app.py.bak.admin.20261026_012149 | 422 +++++++++++++++++ alfred/app/cache/weather_outfit_cache.json | 12 +- ...6c09bb6b068eba6f2e78e1e126c42be66aced3.jpg | Bin 25485 -> 25863 bytes ...d71808b567e9fc66dbd8459de0c4a9853d111a.jpg | Bin 26390 -> 25913 bytes alfred/app/config/admin_auth.json | 1 + alfred/app/config/admin_secret.key | Bin 0 -> 32 bytes alfred/app/config/control_center.env | 2 + .../__pycache__/admin_console.cpython-313.pyc | Bin 0 -> 30821 bytes alfred/app/services/admin_console.py | 431 ++++++++++++++++++ alfred/app/static/css/alfred-admin.css | 5 + alfred/app/static/css/alfred-camera-popup.css | 89 ++++ alfred/app/static/js/alfred-admin.js | 183 ++++++++ alfred/app/static/js/alfred-camera-popup.js | 74 +++ alfred/app/templates/config.html | 55 +++ alfred/app/templates/index.html | 47 +- .../index.html.bak.admin.20261026_012149 | 133 ++++++ ...ndex.html.bak.camera_popup.20261026_013410 | 136 ++++++ alfred/app/templates/surveillance.html | 19 + alfred/app/tools/set_admin_password.py | 42 ++ 21 files changed, 1638 insertions(+), 16 deletions(-) create mode 100644 alfred/app/app.py.bak.admin.20261026_012149 create mode 100644 alfred/app/config/admin_auth.json create mode 100644 alfred/app/config/admin_secret.key create mode 100644 alfred/app/config/control_center.env create mode 100644 alfred/app/services/__pycache__/admin_console.cpython-313.pyc create mode 100644 alfred/app/services/admin_console.py create mode 100644 alfred/app/static/css/alfred-admin.css create mode 100644 alfred/app/static/css/alfred-camera-popup.css create mode 100644 alfred/app/static/js/alfred-admin.js create mode 100644 alfred/app/static/js/alfred-camera-popup.js create mode 100644 alfred/app/templates/config.html create mode 100644 alfred/app/templates/index.html.bak.admin.20261026_012149 create mode 100644 alfred/app/templates/index.html.bak.camera_popup.20261026_013410 create mode 100644 alfred/app/templates/surveillance.html create mode 100644 alfred/app/tools/set_admin_password.py diff --git a/alfred/app/__pycache__/app.cpython-313.pyc b/alfred/app/__pycache__/app.cpython-313.pyc index 6b7ea880a5b10d5e1ac756f53f7f0b748b2e96bc..c4f59aeaabea0226b233a2b20f2962d1be7c404d 100644 GIT binary patch delta 6346 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/snapshot') +def ha_camera_snapshot(entity_id): + try: + binary,mime=ha_views.camera_snapshot(entity_id) + return Response(binary,mimetype=mime,headers={'Cache-Control':'no-store','X-Content-Type-Options':'nosniff'}) + except (ha_views.HAError,ValueError) as exc:return _json_error(exc) + +@app.post('/api/ui/home') +def ui_home(): + with STATE_LOCK: + STATE['content']={'type':'home','text':'À ton écoute.'} + socketio.emit('alfred_render',STATE['content']) + return jsonify({'ok':True}) + +@app.post("/api/audio-level") +def api_audio_level(): audio_level((request.get_json(force=True) or {}).get("level",0)); return jsonify({"ok":True}) +@app.get("/api/wake/status") +def wake_status(): return jsonify(wake.status()) +@app.post("/api/wake/start") +def wake_start(): wake.start(); return jsonify({"ok":True,**wake.status()}) +@app.post("/api/wake/stop") +def wake_stop(): wake.stop(); return jsonify({"ok":True,**wake.status()}) +@app.post("/api/tts/say") +def tts_say(): + text=(request.get_json(silent=True) or {}).get("text",""); return jsonify({"ok":speak_async(text)}) + +if __name__ == "__main__": + if os.getenv("WAKE_ENABLED","true").lower() in ("1","true","yes","on") and not audio_settings.mic_muted(): wake.start() + socketio.run(app,host=os.getenv("FLASK_HOST","0.0.0.0"),port=int(os.getenv("FLASK_PORT","54321")),allow_unsafe_werkzeug=True) + diff --git a/alfred/app/cache/weather_outfit_cache.json b/alfred/app/cache/weather_outfit_cache.json index 929e89a..576a120 100644 --- a/alfred/app/cache/weather_outfit_cache.json +++ b/alfred/app/cache/weather_outfit_cache.json @@ -166,8 +166,8 @@ "a4928b9950baed6aad15a8e69c6c09bb6b068eba6f2e78e1e126c42be66aced3": { "file": "a4928b9950baed6aad15a8e69c6c09bb6b068eba6f2e78e1e126c42be66aced3.jpg", "prompt_ids": [ - "e4a383a6-d6b8-4d54-944a-d4f6802d3951", - "dbec0a66-eb40-4988-b512-7a4c3503f02a" + "46f09a0f-205f-4ec5-ad9a-10b186710faf", + "4f043514-8981-4ff3-ab11-2cbaa991853d" ], "reviews": { "child": { @@ -189,14 +189,14 @@ "reference_applied": true } }, - "cached_at": 1792969491.7701209, + "cached_at": 1792973066.884501, "focus_label": "samedi 31 octobre" }, "b4b214f2749f5c44f6aeb3f82ad71808b567e9fc66dbd8459de0c4a9853d111a": { "file": "b4b214f2749f5c44f6aeb3f82ad71808b567e9fc66dbd8459de0c4a9853d111a.jpg", "prompt_ids": [ - "cdd79fd9-0eef-4a3b-b6c6-d4b5bac8db81", - "0efecd05-8734-4335-91ff-10e282a00095" + "8caaff76-5977-4634-ac8b-55ba72ad2160", + "7f7d868f-8168-4c3d-b0a7-f3ad9b14aac4" ], "reviews": { "child": { @@ -218,7 +218,7 @@ "reference_applied": true } }, - "cached_at": 1792970119.676846, + "cached_at": 1792973038.224328, "focus_label": "aujourd’hui" } } \ No newline at end of file diff --git a/alfred/app/cache/weather_outfit_images/a4928b9950baed6aad15a8e69c6c09bb6b068eba6f2e78e1e126c42be66aced3.jpg b/alfred/app/cache/weather_outfit_images/a4928b9950baed6aad15a8e69c6c09bb6b068eba6f2e78e1e126c42be66aced3.jpg index 0d9794f118bdadbf366bb0e5b9e8a65db964ed59..01668a4256591e1a8f9670f0646c65e7cdc1e81b 100644 GIT binary patch literal 25863 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�FZ>yl_`ZO(JRraT+JZ(Ve;gxo>XuQ?Md6Y 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 R95Opa6jidAQAHF1|Jm6lggF2J literal 25485 zcmb5Vbx>Tvw=F!l1P>A%f({x8?hb=9SQsRNySs%DBqaEt!I=RDhu|9A-Q6L$JA_B> z{oQ(1?|%P$d+JP`>h9Ckr>oEIwbx$#H2bs)AW&8SDFBd=005-tAK+;LAPYc2{%8OG zQJxJI9rd4KqNAgsV_{-rV_{-pVdLQAV86h7frW)jh>M3$KtM=8TSy{X9+-wC92TUqeR6 zKte%9!^C>7RwH<>MnOhFLqkJH#=t{TLKWEaQ@0 zLBjAB+%~5D3F}K-PG$QVDWj~ZYw&6A2^lksZv3x~=k6$<=LGHF-27kr*B?3>#&e|r zApjW}2^kdy0|oWD)xRdsm4vADXhb~HFSS0Rzvgw0&8`S)J0WHmTVv#t0lS=jdYT8| zpddZJ4+?e3m;HlQ_ z6Tl|lEwJmkSwR=7)?f#wXzt(Fl9Gpto=qxU>f0)3C+~W_YHWog%{XkHp<*ewT>dfl0jGDH|y24YJx6R0JG21La0nqFE_DXNu(4t z>CBuHdd9S~$R=O7uY)3>+>H_jddd|iud7o{bz8X!E!gSKnK~_5A(&?pSHnTfz*TIW>H+ezZc>HMwgge6&f>_1%9^yo42g zM)qTaYDuB1kxL*aLSIgoqF0J&moq!PYdb6aaZwz#T>$9`;bu+it%#3;y7~caq)&*` zOxshZ(eQU=1H1oqPNLUytaTH%uf#@F=53OP(A94$)7HsCj$r+Ftgg;bi+8j2_a|ym0C9P)S zHCBJCL%`GSzw(l6JO?*3E*{VO*Yy(;adJKtr>--8Ool66(3-cmt)f&G&%V zrf#39jv-m!YU?W;CL;=SR&$L_4C2p~#1#iYgXs4+dieU>c*;C`BeJ7y1~k@ghV~C< zlvqyyh3%Ah8wXR720H_G#be|JY|!xEyj+LZ9FR*^W2F=KYy$Qw~uSqzFbH8 zgNM%E$`?k&u_#l6Sa1Wr84zna=v90@kr{SLQZ%z-=Ck~u({HZAnMPM^cDO2%`o*Vc ztszlGYi!J7SZL&rx4o@WJp3hH53WcO=>P)QWBF-p?rrgC+R4z{{gzgBgvs_<4&fwttWU_ zxpkop>2hamy?kFUy4x6L!S%YPNYyIok=8@=aiLWCUfWudxTw(ojdfY+jd_7kpnym| zsl@^_>nnZMuvD?2jFkJrbI{6kcK6}%+ofSV@s*lN*>7TnQZzy&Vov+t$W-jZ=iNDU zR`@oXn9cMRy#3NN&I-I(KLqQqmDXx7E^J4ay&uiyB>icKK}LdA8tE3ue;?c4ys>AM zFHt|Py4z|D6RO&n?lvD70VGhJPVFBVo7-_@&TtDu5&W0+XZsB5@j=;AtyGGmCxY5(6Ti z&y8?B8Gz8x+7_vo7@0DQ3F>@P#VhS`7As}+b8-=9%O@xts7ln=?_3|hU2W{(D$^(_ zTxcO-TU-|J$9O{LpHVpz+A%t?+(Q4V z0>Ypv7NPSl4YhcLgx8}|*@Vn>xXMwcMn+)Yj$W&Ss=cGYSeUsj0!C+r#l|x>cWe;w}vp>C1NDmC3(WLyziN|BZ zMp-_iXomFABNl$ioV)ba(&Gu>A7*1^*6WY!H(89or_siok!CR&15*N)YGmL(W9MsUv|~N*+G-+kM_fBOq6rv#Aqm47te9r7mhq z*O@MxH(CK8Buvq3`)GGbuKdANr`@mb-8qN(6T8T7dAHdc?eiYDZjVPXpGYTQ*TUIdABxkhDky?x|r!foC3RwuI_@XxT z)9zK8kFV4$Cr3j7BrxL`M6BNq+$C`3uKKvb?2?Sr498}GEwu;01aDdWTGi>Mz_}$6#??Gx4ql^lvaY+nu3z4E@Y(27n!92}A#zo2Y zKXGdAD?$W&(Zzacr|Sl)meL^>u4GoYsMS18A~6C?;(-j{UH4y=Mm3&v() z7Ix+@@$*rQlUU2RL*N<13K*lwPd_6eWjYF#|8jfuazV{H*sK>>RumZNnwR^!YOF7$ zMj#DHC?)jGg*%~aUax`W{HpQNEF>C`n8<{Y@d6Bi-

t;3PmDwa5C>V;}D;Fb8QD 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 C%`m9| diff --git a/alfred/app/cache/weather_outfit_images/b4b214f2749f5c44f6aeb3f82ad71808b567e9fc66dbd8459de0c4a9853d111a.jpg b/alfred/app/cache/weather_outfit_images/b4b214f2749f5c44f6aeb3f82ad71808b567e9fc66dbd8459de0c4a9853d111a.jpg index 44aab425b167f4e13650e411f5eceba66e207050..b8f49a64fe92d6892e9a89b82433c94a2df0ebc6 100644 GIT binary patch literal 25913 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�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 literal 0 HcmV?d00001 diff --git a/alfred/app/config/control_center.env b/alfred/app/config/control_center.env new file mode 100644 index 0000000..1798ac1 --- /dev/null +++ b/alfred/app/config/control_center.env @@ -0,0 +1,2 @@ +# Configuration générale Alfred (page /config) +# ALFRED_ALLOW_WEB_RESTART=true diff --git a/alfred/app/services/__pycache__/admin_console.cpython-313.pyc b/alfred/app/services/__pycache__/admin_console.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a9b009724ef7cdcc2978f02c1fbbcbd6c3f5473 GIT binary patch literal 30821 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 literal 0 HcmV?d00001 diff --git a/alfred/app/services/admin_console.py b/alfred/app/services/admin_console.py new file mode 100644 index 0000000..eae961f --- /dev/null +++ b/alfred/app/services/admin_console.py @@ -0,0 +1,431 @@ +"""Authenticated local configuration and health UI for Alfred. + +Registration is additive and doesn't replace the existing audio, camera, HA, +weather or Hermes endpoints. Passwords are provisioned locally via CLI. +""" +from __future__ import annotations + +import functools +import hmac +import json +import os +import re +import secrets +import shutil +import socket +import subprocess +import threading +import time +from datetime import datetime, timedelta, timezone +from pathlib import Path +from urllib.parse import urlsplit + +import requests +from dotenv import dotenv_values +from flask import Blueprint, current_app, jsonify, render_template, request, session +from werkzeug.security import check_password_hash + +ROOT = Path(__file__).resolve().parents[1] +AUTH_FILE = ROOT / 'config' / 'admin_auth.json' +GENERAL_ENV = ROOT / 'config' / 'control_center.env' +WEATHER_ENV = ROOT / 'config' / 'weather_outfits.env' +CONFIG_LOCK = threading.RLock() +LOGIN_LOCK = threading.Lock() +LOGIN_ATTEMPTS = {} + +# Only these keys are writable; no token, command, system file or shell input. +FIELDS = { + 'ALFRED_NAME': ('general', 'text'), + 'WAKE_WORD': ('general', 'wake'), + 'PIPER_VOICE': ('general', 'voice'), + 'ALFRED_OLLAMA_URL': ('general', 'url'), + 'ALFRED_COMFYUI_URL': ('weather', 'url'), + 'HA_WEATHER_ENTITY': ('general', 'entity'), + 'ALFRED_WEATHER_ILLUSTRATION_ATTEMPTS': ('weather', 'attempts'), + 'ALFRED_WEATHER_ILLUSTRATION_TARGET_SCORE': ('weather', 'score'), + 'ALFRED_WEATHER_DISPLAY_SIZE': ('weather', 'size'), + 'ALFRED_WEATHER_REVIEW': ('weather', 'boolean'), + 'ALFRED_WEATHER_REFERENCE_MODE': ('weather', 'reference_mode'), + 'ALFRED_WEATHER_REFERENCE_DENOISE': ('weather', 'denoise'), + 'ALFRED_WEATHER_PRESERVE_FACE': ('weather', 'boolean'), + 'ALFRED_WEATHER_ACCESSORY_LIMIT': ('weather', 'accessory'), +} +DEFAULTS = { + 'ALFRED_NAME': 'Alfred', 'WAKE_WORD': 'alfred', + 'PIPER_VOICE': 'fr_FR-tom-medium', 'ALFRED_OLLAMA_URL': '', + 'ALFRED_COMFYUI_URL': '', 'HA_WEATHER_ENTITY': '', + 'ALFRED_WEATHER_ILLUSTRATION_ATTEMPTS': '2', + 'ALFRED_WEATHER_ILLUSTRATION_TARGET_SCORE': '8.6', + 'ALFRED_WEATHER_DISPLAY_SIZE': '350', + 'ALFRED_WEATHER_REVIEW': 'true', + 'ALFRED_WEATHER_REFERENCE_MODE': 'auto', + 'ALFRED_WEATHER_REFERENCE_DENOISE': '0.56', + 'ALFRED_WEATHER_PRESERVE_FACE': 'true', + 'ALFRED_WEATHER_ACCESSORY_LIMIT': '1', +} + + +def _auth_config(): + try: + return json.loads(AUTH_FILE.read_text(encoding='utf-8')) + except (OSError, ValueError): + return {} + + +def _authenticated(): + config = _auth_config() + password_hash = config.get('password_hash', '') + if not password_hash: + return False + # Changing the admin password instantly invalidates old signed sessions. + expected_version = __import__('hashlib').sha256(password_hash.encode()).hexdigest()[:24] + return bool(session.get('alfred_admin') and + hmac.compare_digest(str(session.get('auth_version', '')), expected_version)) + + +def _valid_csrf(): + token = request.headers.get('X-Alfred-CSRF', '') + expected = session.get('alfred_csrf', '') + return bool(token and expected and hmac.compare_digest(token, expected)) + + +def _trusted_request(): + origin = request.headers.get('Origin', '') + if origin: + parsed = urlsplit(origin) + if parsed.netloc.lower() != request.host.lower() or parsed.scheme not in ('http', 'https'): + return False + if request.headers.get('Sec-Fetch-Site', '').lower() == 'cross-site': + return False + return True + + +def _require_admin(handler): + @functools.wraps(handler) + def wrapped(*args, **kwargs): + if not _authenticated(): + return jsonify(error='Authentification administrateur nécessaire.'), 401 + if request.method != 'GET' and (not _valid_csrf() or not _trusted_request()): + return jsonify(error='Protection anti-CSRF : requête refusée.'), 403 + return handler(*args, **kwargs) + return wrapped + + +def _validate(key, value): + if key not in FIELDS: + raise ValueError(f'Paramètre non autorisé : {key}.') + kind = FIELDS[key][1] + if kind == 'boolean': + if value not in (True, False, 'true', 'false'): + raise ValueError(f'{key} doit être vrai ou faux.') + return 'true' if value in (True, 'true') else 'false' + if isinstance(value, bool): + raise ValueError(f'Valeur invalide pour {key}.') + value = str(value).strip() + if any(c in value for c in ('\r', '\n', '\x00', '"', "'", '\\', '$', '`')): + raise ValueError(f'Caractères interdits pour {key}.') + if kind == 'text': + if not 2 <= len(value) <= 30: + raise ValueError('Le nom doit contenir entre 2 et 30 caractères.') + elif kind == 'wake': + if not re.fullmatch(r'[A-Za-zÀ-ÖØ-öø-ÿ]{2,22}', value): + raise ValueError('Le mot-clé Vosk doit être un mot simple de 2 à 22 lettres.') + value = value.lower() + elif kind == 'voice': + if not re.fullmatch(r'[A-Za-z0-9_.-]{2,96}', value): + raise ValueError('Identifiant de voix Piper invalide.') + elif kind == 'url': + if not value: + return '' + parsed = urlsplit(value) + if (parsed.scheme not in ('http', 'https') or not parsed.hostname or + parsed.username or parsed.password or parsed.path not in ('', '/') or + parsed.query or parsed.fragment): + raise ValueError(f'{key} attend une URL http(s) de serveur sans chemin ni identifiants.') + if parsed.port and not 1 <= parsed.port <= 65535: + raise ValueError('Port réseau invalide.') + value = value.rstrip('/') + elif kind == 'entity': + if value and not re.fullmatch(r'weather\.[a-z0-9_]{2,80}', value): + raise ValueError('Attendu : weather.identifiant ou une valeur vide.') + elif kind == 'attempts': + if value not in ('1', '2', '3'): + raise ValueError('Nombre d’essais autorisé : 1, 2 ou 3 par personnage.') + elif kind == 'score': + value = str(float(value)) + if not 1 <= float(value) <= 10: + raise ValueError('Score entre 1 et 10.') + elif kind == 'size': + if value not in ('250', '300', '350', '400', '450', '500'): + raise ValueError('Taille autorisée : 250, 300, 350, 400, 450 ou 500 px.') + elif kind == 'denoise': + value = str(float(value)) + if not 0.25 <= float(value) <= 0.85: + raise ValueError('Denoise entre 0.25 et 0.85.') + elif kind == 'reference_mode': + if value not in ('auto', 'required', 'off'): + raise ValueError('Mode référence : auto, required ou off.') + elif kind == 'accessory': + if value not in ('0', '1', '2'): + raise ValueError('Accessoires visibles autorisés : 0, 1 ou 2.') + return value + + +def _read_config(): + local = dict(dotenv_values(GENERAL_ENV)) if GENERAL_ENV.exists() else {} + weather = dict(dotenv_values(WEATHER_ENV)) if WEATHER_ENV.exists() else {} + effective = {} + for key, (group, _) in FIELDS.items(): + env_file = local if group == 'general' else weather + # File values take precedence on the next restart. Show saved values. + effective[key] = str(env_file.get(key) if env_file.get(key) is not None + else os.getenv(key, DEFAULTS[key])) + return effective + + +def _save_env(path: Path, changes: dict): + path.parent.mkdir(parents=True, exist_ok=True) + existing = path.read_text(encoding='utf-8') if path.exists() else '' + lines = existing.splitlines() + updated = set() + result = [] + for line in lines: + match = re.match(r'^\s*([A-Z][A-Z0-9_]*)\s*=', line) + if match and match.group(1) in changes: + name = match.group(1) + if name not in updated: + result.append(f'{name}={changes[name]}') + updated.add(name) + else: + result.append(line) + for key, value in changes.items(): + if key not in updated: + result.append(f'{key}={value}') + tmp = path.with_name(path.name + f'.{secrets.token_hex(4)}.tmp') + try: + with tmp.open('w', encoding='utf-8') as fp: + fp.write('\n'.join(result).rstrip() + '\n') + fp.flush() + os.fsync(fp.fileno()) + os.chmod(tmp, 0o600) + tmp.replace(path) + finally: + tmp.unlink(missing_ok=True) + + +def _probe_http(url, *, token=None, timeout=2): + start = time.monotonic() + try: + response = requests.get(url, headers={'Authorization': f'Bearer {token}'} if token else {}, + timeout=timeout, allow_redirects=False) + if 200 <= response.status_code < 300: + state = 'ok' + detail = f'HTTP {response.status_code}' + elif response.status_code in (401, 403): + state, detail = 'warning', f'Connexion établie, accès refusé ({response.status_code})' + else: + state, detail = 'warning', f'HTTP {response.status_code}' + except requests.RequestException: + state, detail = 'down', 'Aucune réponse HTTP' + return {'state': state, 'detail': detail, 'latency_ms': round((time.monotonic()-start)*1000)} + + +def _probe_tcp(host, port, timeout=1.5): + try: + with socket.create_connection((host, int(port)), timeout=timeout): + return {'state': 'ok', 'detail': f'Port {port} accessible'} + except (OSError, ValueError): + return {'state': 'down', 'detail': f'Port {port} inaccessible'} + + +def _weather_comfy_url(): + # Respect the V5 precedence if possible, without reaching into credentials. + settings = dict(dotenv_values(WEATHER_ENV)) if WEATHER_ENV.exists() else {} + return (_read_config()['ALFRED_COMFYUI_URL'] or settings.get('ALFRED_COMFYUI_URL') or + os.getenv('ALFRED_COMFYUI_URL') or os.getenv('COMFYUI_URL') or + dict(dotenv_values('/opt/hermes/MCP_comfyui/.env')).get('COMFYUI_URL') or '') + + +def _health(wake, audio_settings): + now = datetime.now(timezone.utc).isoformat() + audio = audio_settings.get_settings() + try: + wake_state = wake.status() + except Exception: + wake_state = {'running': False, 'paused': True, 'error': 'Impossible de lire Vosk'} + cam_index = int(os.getenv('CAMERA_INDEX', '0')) + camera_path = Path(f'/dev/video{cam_index}') + camera_ok = camera_path.exists() and os.access(camera_path, os.R_OK) + # Checking /dev/videoN doesn't open the device: no conflict with facial recognition. + camera = {'state':'ok' if camera_ok else 'warning', + 'detail':f'{camera_path} détectée' if camera_ok else f'{camera_path} indisponible ou permissions insuffisantes'} + mute_micro = audio.get('mic_muted', False) + microphone = {'state':'muted' if mute_micro else ('ok' if wake_state.get('running') else 'warning'), + 'detail':'Micro coupé volontairement' if mute_micro else ('Détection Vosk active' if wake_state.get('running') and not wake_state.get('paused') else 'Vosk non disponible ou en pause')} + loudspeaker = {'state':'muted' if audio.get('speaker_muted') else 'ok', + 'detail':'Son coupé' if audio.get('speaker_muted') else f"Volume {audio.get('volume', '?')} %"} + + config = _read_config() + comfy = _weather_comfy_url() + ollama = config.get('ALFRED_OLLAMA_URL') or os.getenv('OLLAMA_HOST') or os.getenv('OLLAMA_URL') + ha_base = os.getenv('HA_URL', '').rstrip('/') + tts_host = os.getenv('PIPER_HOST', '').strip() + tts_port = os.getenv('PIPER_PORT', '10200') + services = {'alfred': {'state':'ok', 'detail':'Serveur Flask actif'}, + 'hermes_cli': {'state':'ok' if shutil.which('hermes') else 'warning', + 'detail':'CLI détectée' if shutil.which('hermes') else 'CLI non détectée dans le PATH du service'}} + services['hermes_dashboard'] = _probe_tcp('127.0.0.1', 9119) + services['hermes_dashboard']['label'] = 'Tableau de bord (optionnel)' + services['comfyui'] = _probe_http(comfy.rstrip('/') + '/system_stats') if comfy else {'state':'unknown','detail':'URL non configurée'} + services['ollama'] = _probe_http(ollama.rstrip('/') + '/api/tags') if ollama else {'state':'unknown','detail':'URL non configurée'} + services['home_assistant'] = _probe_http(ha_base + '/api/', token=os.getenv('HA_TOKEN')) if ha_base else {'state':'unknown','detail':'URL non configurée'} + services['wyoming_piper'] = _probe_tcp(tts_host, tts_port) if tts_host else {'state':'unknown','detail':'Wyoming Piper non configuré (Piper local possible)'} + + mcp_path = Path('/opt/hermes') + mcps = {} + for slug, label in [('MCP_home_assistant','Home Assistant'), ('MCP_comfyui','ComfyUI'), ('MCP_face_recognition','Reconnaissance faciale'),('MCP_wazuh','Wazuh')]: + found = (mcp_path / slug).exists() + mcps[slug] = {'state':'ok' if found else 'unknown', 'detail':'Dossier présent (connexion non vérifiée)' if found else 'Dossier absent', 'label':label} + + disk = shutil.disk_usage(ROOT) + mem = {} + try: + info = {} + for line in Path('/proc/meminfo').read_text().splitlines(): + k, _, v = line.partition(':') + if k in ('MemTotal','MemAvailable'): + info[k] = int(v.strip().split()[0]) * 1024 + if info.get('MemTotal'): + mem = {'total': info['MemTotal'], 'used': info['MemTotal'] - info.get('MemAvailable', 0)} + except (OSError, ValueError): + pass + return {'time': now, 'audio':audio, 'wake':wake_state, 'camera':camera, + 'microphone':microphone, 'speaker':loudspeaker, 'services':services, 'mcps':mcps, + 'system':{'disk_total':disk.total,'disk_used':disk.used, 'memory':mem, + 'load_1m':round(os.getloadavg()[0], 2) if hasattr(os,'getloadavg') else None}} + + +def register_admin_console(app, wake, audio_settings): + """Register pages/API on the existing Flask app. Call exactly once.""" + secret_file = ROOT / 'config' / 'admin_secret.key' + if not secret_file.exists(): + raise RuntimeError('Secret administrateur absent ; exécuter install_patch.py.') + app.secret_key = secret_file.read_bytes() + app.config.update(SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SAMESITE='Strict', + SESSION_COOKIE_SECURE=os.getenv('ALFRED_ADMIN_HTTPS', 'false').lower() == 'true', + PERMANENT_SESSION_LIFETIME=timedelta(hours=8)) + + bp = Blueprint('alfred_admin', __name__) + + @bp.get('/config') + @bp.get('/config.html') + def config_page(): + return render_template('config.html') + + @bp.get('/surveillance') + @bp.get('/surveillance.html') + def surveillance_page(): + return render_template('surveillance.html') + + @bp.get('/api/control/auth') + def auth_status(): + authed = _authenticated() + return jsonify(authenticated=authed, configured=bool(_auth_config().get('password_hash')), + csrf=session.get('alfred_csrf') if authed else None) + + @bp.post('/api/control/login') + def login(): + if not _trusted_request() or not request.is_json: + return jsonify(error='Requête refusée.'), 403 + data = request.get_json(silent=True) or {} + password = data.get('password', '') + if not isinstance(password, str) or len(password) > 500: + return jsonify(error='Mot de passe incorrect.'), 400 + now = time.monotonic() + client = request.remote_addr or 'unknown' + with LOGIN_LOCK: + events = [x for x in LOGIN_ATTEMPTS.get(client, []) if x > now - 900] + LOGIN_ATTEMPTS[client] = events + if len(events) >= 5: + return jsonify(error='Trop de tentatives : réessaie plus tard.'), 429 + config = _auth_config() + password_hash = config.get('password_hash') + if not password_hash: + return jsonify(error='Définis d’abord le mot de passe avec set_admin_password.py.'), 503 + if not check_password_hash(password_hash, password): + with LOGIN_LOCK: + LOGIN_ATTEMPTS[client].append(now) + return jsonify(error='Mot de passe incorrect.'), 401 + with LOGIN_LOCK: + LOGIN_ATTEMPTS.pop(client, None) + session.clear() + session.permanent = True + session['alfred_admin'] = True + session['auth_version'] = __import__('hashlib').sha256(password_hash.encode()).hexdigest()[:24] + session['alfred_csrf'] = secrets.token_urlsafe(32) + return jsonify(authenticated=True, csrf=session['alfred_csrf']) + + @bp.post('/api/control/logout') + @_require_admin + def logout(): + session.clear() + return jsonify(ok=True) + + @bp.get('/api/control/config') + @_require_admin + def config_get(): + return jsonify(values=_read_config(), note='La plupart des réglages nécessitent un redémarrage.') + + @bp.post('/api/control/config') + @_require_admin + def config_update(): + data = request.get_json(silent=True) + if not isinstance(data, dict) or len(data) > len(FIELDS): + return jsonify(error='JSON invalide.'), 400 + try: + normalized = {key: _validate(key, value) for key, value in data.items()} + except (ValueError, TypeError, OverflowError) as exc: + return jsonify(error=str(exc)), 400 + general = {k:v for k,v in normalized.items() if FIELDS[k][0]=='general'} + weather = {k:v for k,v in normalized.items() if FIELDS[k][0]=='weather'} + try: + with CONFIG_LOCK: + if general: _save_env(GENERAL_ENV, general) + if weather: _save_env(WEATHER_ENV, weather) + except OSError as exc: + return jsonify(error=f'Impossible d’enregistrer : {exc}'), 500 + return jsonify(ok=True, restart_required=True, values=_read_config()) + + @bp.get('/api/control/health') + @_require_admin + def health(): + return jsonify(_health(wake, audio_settings)) + + @bp.post('/api/control/restart') + @_require_admin + def restart(): + if os.getenv('ALFRED_ALLOW_WEB_RESTART', 'false').lower() not in ('true', '1', 'yes'): + return jsonify(error='Redémarrage Web désactivé. Activer ALFRED_ALLOW_WEB_RESTART=true (voir README).'), 403 + systemctl = shutil.which('systemctl') + runner = shutil.which('systemd-run') + if not systemctl or not runner: + return jsonify(error='systemd-run ou systemctl absent. Redémarrer depuis le terminal.'), 503 + # Schedule restart in a DIFFERENT systemd unit/cgroup so stopping Alfred + # cannot kill the restart command mid-flight. + command = [runner, '--unit=alfred-web-restart', '--on-active=2s', '--collect', + systemctl, 'restart', 'alfred.service'] + if os.geteuid() != 0: + command = ['sudo', '-n', *command] + def do_restart(): + time.sleep(0.5) + try: + result = subprocess.run(command, capture_output=True, text=True, timeout=12, check=False) + if result.returncode: + app.logger.error('Planification du redémarrage refusée (%s): %s', + result.returncode, result.stderr[:350]) + except (OSError, subprocess.TimeoutExpired) as exc: + app.logger.error('Planification du redémarrage échouée: %s', exc) + threading.Thread(target=do_restart, daemon=True).start() + return jsonify(accepted=True, message='Demande de redémarrage transmise ; contrôler le retour du service.') , 202 + + app.register_blueprint(bp) diff --git a/alfred/app/static/css/alfred-admin.css b/alfred/app/static/css/alfred-admin.css new file mode 100644 index 0000000..1125ac4 --- /dev/null +++ b/alfred/app/static/css/alfred-admin.css @@ -0,0 +1,5 @@ +:root{color-scheme:dark;font-family:Inter,system-ui,-apple-system,"Segoe UI",sans-serif;color:#edf3f9;background:#101924;--panel:#1a2836;--border:#304050;--muted:#9eafbe;--accent:#60b0e5}*{box-sizing:border-box}[hidden]{display:none!important} +body{margin:0;min-height:100vh;background:radial-gradient(ellipse at 60% 10%,#1b2d40 0,#101924 48%,#0d141d 100%)}button,input,select{font:inherit}a{color:inherit;text-decoration:none}button{cursor:pointer} +.admin-layout{max-width:1510px;margin:0 auto;min-height:100vh;padding:0 clamp(16px,3vw,48px) 48px}.admin-header{height:91px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #ffffff16;gap:20px}.brand{display:flex;align-items:center;gap:13px}.brand-orb{width:41px;height:41px;border-radius:13px;display:grid;place-items:center;background:linear-gradient(130deg,#65b5e6,#276eac);color:#fff;font-weight:700;box-shadow:0 10px 30px #1688d32c}.brand-orb.big{width:55px;height:55px;margin:auto;font-size:1.5rem}.brand strong{display:block;font-size:1.24rem;letter-spacing:-.025em}.brand small{display:block;color:#8fa4b6;font-size:.75rem;margin-top:2px}.admin-header nav{display:flex;align-items:center;gap:9px;flex-wrap:wrap}.admin-header nav a,.text-button{padding:11px 15px;color:#a8bbce;border-radius:10px;font-size:.9rem;background:transparent;border:0}.admin-header nav .selected{background:#4b92bc27;color:#d9f1fe;border:1px solid #73bbeb42}.admin-header nav a:hover,.text-button:hover{background:#ffffff0e;color:#fff} +.admin-main{padding-top:35px}.page-top{display:flex;align-items:flex-end;justify-content:space-between;gap:28px;margin-bottom:27px}.eyebrow{color:#7ac5f4;font-size:.72rem;letter-spacing:.16em;font-weight:700;margin:0 0 8px}.page-top h1{font-weight:620;font-size:clamp(1.85rem,3vw,2.8rem);letter-spacing:-.045em;margin:0 0 10px}.muted{color:var(--muted)}.page-top p.muted{margin:0;max-width:650px;line-height:1.6}.page-actions{display:flex;align-items:center;gap:13px;flex-shrink:0}.tag{background:#1c2f3e;border:1px solid #3c576d;color:#b4d0e0;border-radius:40px;padding:9px 12px;font-size:.81rem;white-space:nowrap}.button{border:1px solid #486071;border-radius:11px;color:#e1edf6;background:#243544;padding:11px 17px;font-weight:600;font-size:.88rem}.button:hover{filter:brightness(1.18)}.button.primary{background:#278dcb;border-color:#4baae2;color:#fff}.button.ghost{background:#213444;border-color:#466174}.button.danger{background:#703838;color:#ffded9;border-color:#aa6060;white-space:nowrap}.button.full{width:100%}.two-col{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:17px}.panel{border:1px solid var(--border);border-radius:17px;background:linear-gradient(145deg,#1a2938f2,#162230ef);padding:24px;box-shadow:0 16px 40px #0002}.panel-title{display:flex;gap:13px;align-items:center;padding-bottom:17px;margin-bottom:22px;border-bottom:1px solid #ffffff16}.panel-title h2{font-size:1.1rem;margin:0 0 4px;letter-spacing:-.015em}.panel-title p{font-size:.81rem;color:#91a6b6;margin:0;line-height:1.5}.panel-symbol{background:#2a5a7b52;color:#9bd9ff;border-radius:11px;display:grid;place-items:center;font-weight:700;width:42px;height:42px;flex-shrink:0;font-size:1.3rem}.field{display:flex;flex-direction:column;gap:8px;margin-bottom:20px}.field:last-child{margin-bottom:0}.field label{font-size:.89rem;font-weight:620;color:#e9eff4}.field small{font-size:.76rem;color:#8299ab;line-height:1.5}.field input:not([type=checkbox]):not([type=range]),.field select,.auth-box input{width:100%;background:#0f1c29;border:1px solid #3a5266;border-radius:10px;color:#f1f6fa;padding:12px 14px;outline:none;min-height:44px}.field input:focus-visible,.field select:focus-visible,.button:focus-visible{outline:2px solid #87d5ff;outline-offset:2px}.field select{cursor:pointer}.field input[type=range]{width:100%;accent-color:#65b9ee;cursor:pointer}.range-field{display:flex;align-items:center;gap:14px}.range-field output{white-space:nowrap;font-size:.86rem;color:#b1d8ee;min-width:60px;text-align:right}.switch-field{flex-direction:row;align-items:center;justify-content:space-between;gap:12px}.switch-field label small{display:block;margin-top:5px;font-weight:400}.switch-field input{width:20px;height:20px;accent-color:#63b7e9}.inline{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.inline-link{display:inline-flex;margin:4px 0}.microtext{font-size:.79rem}.message{margin:18px 0 0;min-height:20px;color:#a6c4da;font-size:.9rem}.notice{padding:13px 16px;background:#3e67343a;border:1px solid #77976566;border-radius:10px;margin-bottom:21px;color:#d8efca}.restart-panel{display:flex;justify-content:space-between;align-items:center;gap:25px;margin-top:17px}.restart-panel h2{font-size:1.1rem;margin:0 0 7px}.restart-panel p{margin:0 0 5px}.auth-screen{display:grid;place-items:center;position:fixed;inset:0;background:radial-gradient(ellipse at 50% 35%,#1c3042 0%,#0b141d 80%);z-index:99;padding:20px}.auth-box{text-align:center;width:min(410px,100%);padding:34px;background:#192839;border:1px solid #395169;border-radius:21px;box-shadow:0 32px 80px #0005}.auth-box h1{font-size:1.5rem;margin:21px 0 8px}.auth-box p{color:#a7bacb;line-height:1.55;margin:0 0 22px}.auth-box form{display:flex;flex-direction:column;gap:11px;text-align:left}.auth-box label{font-weight:600;font-size:.87rem}.auth-box small{color:#8399ad}.auth-box .message{margin:13px 0}.monitor-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:15px;margin-bottom:17px}.metric-card{display:flex;gap:13px;min-height:162px}.metric-icon{font-size:1.65rem;background:#294d673d;border:1px solid #5e90ac4a;color:#9ecce6;display:grid;place-items:center;width:47px;height:47px;flex-shrink:0;border-radius:12px}.metric-info{display:flex;flex-direction:column;gap:9px;align-items:flex-start;min-width:0}.metric-info strong{font-size:1rem}.metric-info p{margin:0;font-size:.8rem;line-height:1.5}.status-pill{font-size:.74rem;border-radius:30px;padding:4px 9px;background:#6c778025;color:#b8c2cb;border:1px solid #8796a133;white-space:nowrap}.status-pill[data-state="ok"]{background:#388c6433;color:#80dbab;border-color:#54ae8252}.status-pill[data-state="down"]{background:#ba4a4a29;color:#f4abab;border-color:#d7686852}.status-pill[data-state="muted"],.status-pill[data-state="warning"]{background:#ad81382c;color:#f2d18c;border-color:#b19b6466}.status-pill[data-state="unknown"]{background:#ffffff0d;color:#a6b3bf;border-color:#ffffff25}.monitor-lower{margin-bottom:17px}.service-list{display:flex;flex-direction:column;gap:4px}.service-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 5px;border-bottom:1px solid #ffffff0d}.service-row:last-child{border:0}.service-label{font-weight:600;font-size:.91rem;margin-bottom:3px}.service-sub{font-size:.77rem;color:#92a8b9;overflow-wrap:anywhere}.service-tail{display:flex;align-items:center;gap:10px;flex-shrink:0}.resource{margin:20px 0}.resource-title{display:flex;justify-content:space-between;font-size:.87rem}.resource-title strong{color:#acdfff}.meter{height:9px;background:#0d1924;border-radius:100px;overflow:hidden;margin-top:12px}.meter span{display:block;height:100%;width:0;background:linear-gradient(90deg,#368cbe,#85c8f4);border-radius:100px;transition:width .3s}.resource p{margin:9px 0 0}.info-note{font-size:.8rem;color:#abc4d4;padding:14px 15px;border:1px solid #4a73823f;background:#36536824;border-radius:11px;line-height:1.5}.mcp-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.mcp-item{border:1px solid #ffffff17;border-radius:11px;background:#ffffff08;padding:15px}.mcp-item strong{display:block;margin-bottom:10px}.mcp-item p{font-size:.74rem;color:#91a6b6;line-height:1.5;margin:10px 0 0}.pulse-dot{width:8px;height:8px;border-radius:50%;background:#5dda9f;display:inline-block;margin-right:7px} +@media(max-width:1120px){.monitor-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.mcp-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:780px){.admin-header{height:auto;padding:18px 0;flex-direction:column;align-items:flex-start}.admin-header nav{gap:3px}.admin-header nav a,.text-button{font-size:.8rem;padding:9px}.page-top{align-items:flex-start;flex-direction:column;gap:15px}.two-col{grid-template-columns:1fr}.restart-panel{flex-direction:column;align-items:flex-start}.monitor-grid{grid-template-columns:1fr}.mcp-grid{grid-template-columns:1fr 1fr}.metric-card{min-height:120px}}@media(prefers-reduced-motion:reduce){*{transition:none!important}} diff --git a/alfred/app/static/css/alfred-camera-popup.css b/alfred/app/static/css/alfred-camera-popup.css new file mode 100644 index 0000000..f122f17 --- /dev/null +++ b/alfred/app/static/css/alfred-camera-popup.css @@ -0,0 +1,89 @@ +/* ALFRED_CAMERA_POPUP_V1 : modal caméra, sans vignette sur l'accueil */ +.camera-popup-overlay[hidden] { display:none !important; } +.camera-popup-overlay { + position:fixed; + inset:0; + z-index:1000; + background:rgba(8,14,20,.78); + backdrop-filter:blur(8px); + -webkit-backdrop-filter:blur(8px); + display:grid; + place-items:center; + padding:20px; +} +.alfred-camera-dialog-open { overflow:hidden; } +.camera-popup-frame { + width:min(760px, 100%); + max-height:calc(100dvh - 40px); + overflow:auto; + background:linear-gradient(145deg,#2b343c,#1d242b); + border:1px solid #ffffff30; + border-radius:19px; + box-shadow:0 24px 75px #0009; + padding:18px; +} +.camera-popup-titlebar { + display:flex; + justify-content:space-between; + align-items:center; + gap:16px; + margin:0 0 12px; +} +.camera-popup-title { + font-size:1.15rem; + font-weight:600; + color:#edf2f6; +} +.camera-popup-close { + width:38px; + height:38px; + flex:0 0 38px; + border-radius:11px; + border:1px solid #ffffff28; + background:#ffffff10; + color:#eef3f7; + font-size:26px; + line-height:1; + cursor:pointer; +} +.camera-popup-close:hover { background:#ffffff21; } +.camera-popup-overlay .camera-panel.camera-popup-body { + position:relative; + display:block; + left:auto; + right:auto; + bottom:auto; + width:100%; + max-width:none; + margin:0; + overflow:hidden; + border-radius:12px; + background:#171f25; + box-shadow:none; + backdrop-filter:none; +} +.camera-popup-overlay .camera-viewport { + aspect-ratio:16 / 9; + width:100%; + min-height:180px; +} +.camera-popup-overlay .camera-viewport img { + width:100%; + height:100%; + object-fit:contain; +} +.camera-popup-overlay .panel-heading { min-height:46px; } +.camera-popup-footnote { + margin:11px 4px 0; + color:#b1bfc9; + font-size:.83rem; + line-height:1.45; +} +.camera-popup-close:focus-visible, +#cameraPopupButton:focus-visible { outline:2px solid #9ccbe7; outline-offset:3px; } +@media(max-width:760px) { + .camera-popup-overlay {padding:10px;} + .camera-popup-frame {width:100%;max-height:calc(100dvh - 20px);padding:12px;} + .camera-popup-title {font-size:1rem;} + .camera-popup-overlay .camera-panel.camera-popup-body {grid-row:auto;} +} diff --git a/alfred/app/static/js/alfred-admin.js b/alfred/app/static/js/alfred-admin.js new file mode 100644 index 0000000..eed0c22 --- /dev/null +++ b/alfred/app/static/js/alfred-admin.js @@ -0,0 +1,183 @@ +(() => { + 'use strict'; + const $ = id => document.getElementById(id); + const page = document.body.dataset.page; + const state = {csrf: null, logged: false, baseline: {}}; + const fields = [...document.querySelectorAll('[data-setting]')]; + let polling = null; + const fetchJSON = async (url, options = {}) => { + const headers = { ...(options.headers || {}) }; + if (options.body) headers['Content-Type'] = 'application/json'; + if (options.method && options.method !== 'GET' && state.csrf) headers['X-Alfred-CSRF'] = state.csrf; + const response = await fetch(url, {...options, credentials: 'same-origin', cache:'no-store', headers}); + let data; + try { data = await response.json(); } catch { data = {}; } + if (response.status === 401 && url !== '/api/control/login') { showLogin(); throw new Error('Session expirée. Reconnecte-toi.'); } + if (!response.ok) throw new Error(data.error || 'Erreur HTTP '+response.status); + return data; + }; + const setMessage = (message, bad=false) => { + const el = $('message'); if (!el) return; + el.textContent = message; el.style.color = bad ? '#f1aaaa' : '#a1dcbe'; + }; + const showLogin = () => { + state.logged = false; $('mainArea').hidden = true; $('authScreen').hidden = false; + if (polling) clearInterval(polling); polling = null; + }; + const showPage = () => { state.logged=true; $('mainArea').hidden=false; $('authScreen').hidden=true; }; + const getFieldValue = el => el.type === 'checkbox' ? el.checked : el.value; + function configDirty() { + const changed = fields.some(el => String(getFieldValue(el)) !== String(state.baseline[el.id] ?? '')); + if ($('saveState')) $('saveState').textContent = changed ? 'Modifications non enregistrées' : 'Aucun changement'; + } + function markInput() { + if ($('scoreOut')) $('scoreOut').textContent = Number($('ALFRED_WEATHER_ILLUSTRATION_TARGET_SCORE').value || 8.6).toFixed(1) + ' / 10'; + if ($('denoiseOut')) $('denoiseOut').textContent = Number($('ALFRED_WEATHER_REFERENCE_DENOISE').value || .56).toFixed(2); + configDirty(); + } + async function loadConfig() { + const values = (await fetchJSON('/api/control/config')).values || {}; + fields.forEach(el => { + if (el.type==='checkbox') el.checked = String(values[el.id]).toLowerCase() === 'true'; + else el.value = values[el.id] ?? ''; + state.baseline[el.id] = getFieldValue(el); + }); + markInput(); + } + async function saveConfig() { + const values = {}; + fields.forEach(el => { values[el.id] = getFieldValue(el); }); + try { + const result = await fetchJSON('/api/control/config', {method:'POST', body:JSON.stringify(values)}); + state.baseline = {}; + fields.forEach(el => state.baseline[el.id] = getFieldValue(el)); + configDirty(); + $('pendingNotice').hidden = false; + setMessage(result.ok ? 'Configuration sauvegardée.' : 'Enregistrement inconnu.'); + } catch (error) { setMessage(error.message, true); } + } + function serviceStatus(el, data) { + if (!el) return; + const labels = {ok:'Disponible',down:'Indisponible',warning:'Attention',muted:'Coupé',unknown:'Non vérifié'}; + el.dataset.state = data.state || 'unknown'; + el.textContent = labels[data.state] || 'Inconnu'; + } + const safeText = (id, value) => { if ($(id)) $(id).textContent = String(value ?? '—'); }; + const readable = value => { + const x = Number(value) || 0; + if (x >= 1073741824) return (x / 1073741824).toFixed(1) + ' Go'; + return (x / 1048576).toFixed(1) + ' Mo'; + }; + function renderHealth(data) { + const timestamp = new Date(data.time).toLocaleTimeString('fr-FR', {hour:'2-digit',minute:'2-digit',second:'2-digit'}); + safeText('updatedAt', timestamp); + serviceStatus($('cameraStatus'), data.camera); safeText('cameraDetail',data.camera?.detail); + serviceStatus($('microStatus'), data.microphone); safeText('microDetail',data.microphone?.detail); + serviceStatus($('speakerStatus'), data.speaker); safeText('speakerDetail',data.speaker?.detail); + const wake = data.wake || {}; + serviceStatus($('wakeStatus'), {state:!wake.running?'down':wake.paused?'warning':'ok'}); + safeText('wakeLabel', `Vosk · ${wake.word || 'alfred'}`); + safeText('wakeDetail', wake.error || (wake.paused ? 'Détection en pause' : 'Détection active')); + const labels = {alfred:'Alfred / Flask', hermes_cli:'Hermes Agent (CLI)', hermes_dashboard:'Hermes WebUI', comfyui:'ComfyUI', ollama:'Ollama', home_assistant:'Home Assistant', wyoming_piper:'Wyoming Piper'}; + const list = $('serviceList'); list.replaceChildren(); + for (const [key,service] of Object.entries(data.services || {})) { + const row = document.createElement('div'); row.className='service-row'; + const desc = document.createElement('div'); + const title=document.createElement('div');title.className='service-label';title.textContent=labels[key] || key; + const sub=document.createElement('div');sub.className='service-sub';sub.textContent=service.detail || ''; + desc.append(title,sub); + const aside=document.createElement('div');aside.className='service-tail'; + if (service.latency_ms != null) {const delay=document.createElement('span');delay.className='muted microtext';delay.textContent=service.latency_ms+' ms';aside.append(delay);} + const status=document.createElement('span');status.className='status-pill';serviceStatus(status,service);aside.append(status); + row.append(desc,aside);list.append(row); + } + const sys = data.system || {}; + const diskPercent = sys.disk_total ? Math.round(100*sys.disk_used/sys.disk_total) : 0; + safeText('diskPercent', diskPercent+' %');$('diskBar').style.width=diskPercent+'%'; + safeText('diskText', readable(sys.disk_used)+' utilisés sur '+readable(sys.disk_total)); + const mem=sys.memory || {}; + const memPercent=mem.total ? Math.round(100*mem.used/mem.total) : 0; + safeText('memPercent',mem.total ? memPercent+' %' : 'N/D');$('memBar').style.width=memPercent+'%'; + safeText('memText',mem.total ? readable(mem.used)+' utilisés sur '+readable(mem.total) : 'Non disponible'); + safeText('loadAvg',sys.load_1m); + const mcps=$('mcpList');mcps.replaceChildren(); + for (const mcp of Object.values(data.mcps || {})) { + const card=document.createElement('div');card.className='mcp-item'; + const title=document.createElement('strong');title.textContent=mcp.label || 'MCP'; + const status=document.createElement('span');status.className='status-pill';serviceStatus(status,mcp); + const p=document.createElement('p');p.textContent=mcp.detail; + card.append(title,status,p);mcps.append(card); + } + } + let inFlight = false; + async function refreshHealth() { + if (inFlight || document.hidden) return; + inFlight = true; + try { renderHealth(await fetchJSON('/api/control/health'));setMessage(''); } + catch(err){setMessage('Surveillance : '+err.message,true);} + finally{inFlight=false;} + } + async function loadAudio() { + try { + const d=await fetchJSON('/api/audio/settings'); + $('liveVolume').value=d.volume;$('liveVolumeOut').textContent=d.volume+' %'; + $('liveMic').disabled = d.mic_gain == null; + if(d.mic_gain != null)$('liveMic').value=d.mic_gain; + $('liveMicOut').textContent=d.mic_gain == null ? 'N/D' : d.mic_gain+' %'; + $('muteMicro').checked=Boolean(d.mic_muted);$('muteSpeaker').checked=Boolean(d.speaker_muted); + } catch(err){safeText('audioResult',err.message);} + } + async function updateAudio(values) { + try { + const d=await fetchJSON('/api/audio/settings', {method:'POST',body:JSON.stringify(values)}); + safeText('audioResult','Réglage appliqué.'); + $('liveVolume').value=d.volume;$('liveVolumeOut').textContent=d.volume+' %'; + $('muteMicro').checked=Boolean(d.mic_muted);$('muteSpeaker').checked=Boolean(d.speaker_muted); + } catch(err){safeText('audioResult','Erreur : '+err.message);await loadAudio();} + } + async function onLogin(event) { + event.preventDefault(); + const password=$('adminPassword').value; + try { + const response=await fetchJSON('/api/control/login',{method:'POST',body:JSON.stringify({password})}); + state.csrf=response.csrf;$('adminPassword').value='';await enterApp(); + } catch(err){safeText('loginMessage',err.message);} + } + async function enterApp() { + showPage(); + if(page==='config'){ + await Promise.all([loadConfig(),loadAudio()]); + }else{ + await refreshHealth(); + polling=setInterval(refreshHealth,15000); + } + } + async function init() { + $('loginForm')?.addEventListener('submit', onLogin); + $('logoutButton')?.addEventListener('click', async ()=>{ + try{await fetchJSON('/api/control/logout',{method:'POST'});}catch(e){} + state.csrf=null;showLogin(); + }); + fields.forEach(el=>{el.addEventListener('change',markInput);el.addEventListener('input',markInput);}); + $('saveConfig')?.addEventListener('click',saveConfig); + $('restartButton')?.addEventListener('click',async ()=>{ + if(!confirm('Redémarrer Alfred maintenant ? Les opérations en cours seront interrompues.'))return; + try{const data=await fetchJSON('/api/control/restart',{method:'POST'});setMessage(data.message||'Demande transmise.');$('restartButton').disabled=true;setTimeout(()=>$('restartButton').disabled=false,5000);} + catch(err){setMessage(err.message,true);} + }); + $('testVoice')?.addEventListener('click',async()=>{ + try{await fetchJSON('/api/tts/say',{method:'POST',body:JSON.stringify({text:'Bonjour, je suis Alfred. Voici un test de ma voix actuelle.'})});setMessage('Test vocal demandé.');} + catch(err){setMessage(err.message,true);} + }); + let volDelay, gainDelay; + $('liveVolume')?.addEventListener('input',e=>{safeText('liveVolumeOut',e.target.value+' %');clearTimeout(volDelay);volDelay=setTimeout(()=>updateAudio({volume:Number(e.target.value)}),250);}); + $('liveMic')?.addEventListener('input',e=>{safeText('liveMicOut',e.target.value+' %');clearTimeout(gainDelay);gainDelay=setTimeout(()=>updateAudio({mic_gain:Number(e.target.value)}),250);}); + $('muteMicro')?.addEventListener('change',e=>updateAudio({mic_muted:e.target.checked})); + $('muteSpeaker')?.addEventListener('change',e=>updateAudio({speaker_muted:e.target.checked})); + $('refreshHealth')?.addEventListener('click',refreshHealth); + const login=await fetchJSON('/api/control/auth').catch(()=>({authenticated:false})); + if (login.authenticated){state.csrf=login.csrf;try{await enterApp();}catch(err){setMessage(err.message,true);}} + else{showLogin();if(!login.configured)safeText('loginMessage','Mot de passe non défini : exécute set_admin_password.py sur Alfred.');} + } + document.addEventListener('DOMContentLoaded',init); +})(); diff --git a/alfred/app/static/js/alfred-camera-popup.js b/alfred/app/static/js/alfred-camera-popup.js new file mode 100644 index 0000000..df3953f --- /dev/null +++ b/alfred/app/static/js/alfred-camera-popup.js @@ -0,0 +1,74 @@ +/* ALFRED_CAMERA_POPUP_V1 + * Utilise le pilote caméra MJPEG existant (alfred-camera.js), sans ouvrir + * un deuxième flux concurrent : ouverture = clic sur cameraToggle ; + * fermeture = arrêt immédiat du flux, libérant la webcam pour le MCP visage. + */ +(() => { + 'use strict'; + const trigger = document.getElementById('cameraPopupButton'); + const overlay = document.getElementById('cameraPopup'); + const closeButton = document.getElementById('cameraPopupClose'); + const toggle = document.getElementById('cameraToggle'); + if (!trigger || !overlay || !closeButton || !toggle) return; + let previousFocus = null; + + function active() { return toggle.getAttribute('aria-pressed') === 'true'; } + function opened() { return !overlay.hidden; } + function setExpanded(value) { trigger.setAttribute('aria-expanded', String(value)); } + + function openPopup() { + if (opened()) return; + previousFocus = document.activeElement; + overlay.hidden = false; + document.body.classList.add('alfred-camera-dialog-open'); + setExpanded(true); + closeButton.focus(); + // Réutilise la logique précédente pour accéder à /api/camera/stream. + if (!active()) toggle.click(); + } + + function closePopup() { + if (!opened()) return; + // L'ancienne implémentation du bouton ferme le flux MJPEG et retire src. + if (active()) toggle.click(); + // Garde-fou contre un éventuel gestionnaire caméra incomplet. + const feed = document.getElementById('cameraFeed'); + if (feed && feed.hasAttribute('src')) feed.removeAttribute('src'); + overlay.hidden = true; + document.body.classList.remove('alfred-camera-dialog-open'); + setExpanded(false); + if (previousFocus && document.contains(previousFocus) && typeof previousFocus.focus === 'function') { + previousFocus.focus(); + } else { + trigger.focus(); + } + previousFocus = null; + } + + trigger.addEventListener('click', () => opened() ? closePopup() : openPopup()); + closeButton.addEventListener('click', closePopup); + overlay.addEventListener('pointerdown', evt => { + if (evt.target === overlay) closePopup(); + }); + document.addEventListener('keydown', evt => { + if (!opened()) return; + if (evt.key === 'Escape') { + evt.preventDefault(); + closePopup(); + } else if (evt.key === 'Tab') { + // La page principale ne doit pas recevoir le focus derrière la modale. + const nodes = [...overlay.querySelectorAll('button:not([disabled]),a[href],input:not([disabled])')] + .filter(node => node.getClientRects().length); + if (!nodes.length) return; + const first = nodes[0], last = nodes[nodes.length - 1]; + if (evt.shiftKey && document.activeElement === first) { + evt.preventDefault(); last.focus(); + } else if (!evt.shiftKey && document.activeElement === last) { + evt.preventDefault(); first.focus(); + } + } + }); + // Le flux est également fermé par alfred-camera.js en cas de page masquée. + window.addEventListener('pagehide', () => { if (opened()) closePopup(); }); + setExpanded(false); +})(); diff --git a/alfred/app/templates/config.html b/alfred/app/templates/config.html new file mode 100644 index 0000000..9b49a47 --- /dev/null +++ b/alfred/app/templates/config.html @@ -0,0 +1,55 @@ + + + + +Alfred · Configuration + + + + +
+
+
A
AlfredCentre de contrôle
+ +
+
+

PERSONNALISATION

Configuration

Paramètres de l’assistant, voix et services. Les modifications prennent effet après redémarrage.

Aucun changement
+ +
+
✦

Identité et voix

Nom affiché, mot d'activation et voix de synthèse

+
Visible dans l’interface après redémarrage.
+
Un seul mot. Le modèle Vosk doit pouvoir le reconnaître ; le mode OpenWakeWord nécessite un modèle entraîné.
+
Exemple : fr_FR-tom-medium. La voix doit exister sur ton serveur Wyoming ; pour Piper local, c’est le fichier modèle qui détermine la voix.
+
Utilise les réglages déjà actifs, sans redémarrage
+
+
☁

Illustrations météo

Qualité, visages de référence et accessoires ComfyUI

+
+
8.6 / 10
+
+
+
+
0.56
Une valeur élevée modifie davantage la tenue, mais risque de changer le visage.
+
+
+
+
♫

Audio en direct

Volume, sensibilité et coupures immédiates

+
—
+
—
+
+
+

Les réglages audio sont appliqués sans redémarrage.

+
+
⌘

Connexions et météo

Adresses des serveurs et source météo

+
Laisse vide pour conserver la configuration Hermes existante.
+
Cette adresse sert au contrôle de disponibilité ; Hermes garde sa propre configuration LLM.
+
Exemple : weather.forecast_home. Le jeton Home Assistant n’est pas exposé ici.
+ Voir la surveillance des services +
+
+

Maintenance

Redémarrer Alfred pour appliquer les paramètres enregistrés. Action protégée par mot de passe administrateur.

Le bouton requiert une autorisation systemd explicitement activée.
+

+
+
A

Accès administrateur

Connecte-toi pour modifier la configuration d'Alfred.

Accès local / réseau privé uniquement.
+
+ + diff --git a/alfred/app/templates/index.html b/alfred/app/templates/index.html index 54bd6bc..517b3d8 100644 --- a/alfred/app/templates/index.html +++ b/alfred/app/templates/index.html @@ -9,6 +9,7 @@ +
@@ -20,11 +21,25 @@
+ + ⚙ + ▤ + + +
-
-

Bonjour.

À votre écoute.

-
- - - - - +

Le flux est interrompu à la fermeture pour laisser la webcam disponible aux autres fonctions d’Alfred.

+ + + +
+

Bonjour.

À votre écoute.

+
+ + + +