#!/usr/bin/env python3 """Elmer — главный вход. Порядок работы: 1. Подключается к ELM327 по Bluetooth 2. Читает VIN 3. Читает коды ошибок (stored + pending) 4. Читает базовые параметры ЭБУ 5. Отправляет в DeepSeek 6. Печатает диагноз 7. Сохраняет всё в SQLite Использование: DEEPSEEK_API_KEY=sk-... python run.py # полный цикл python run.py --no-llm # только чтение данных (без LLM) python run.py --no-llm --port /dev/rfcomm0 # свой порт """ import argparse import sys from elmer.config import load from elmer.db import Database from elmer.diagnose import Diagnoser from elmer.elm import ELM327 from elmer.prompts import SYSTEM_PROMPT, build_user_prompt def main(): parser = argparse.ArgumentParser(description="Elmer — диагностика авто через ELM327 + LLM") parser.add_argument("--no-llm", action="store_true", help="Только чтение данных, без LLM") parser.add_argument("--port", help="Порт ELM327 (по умолчанию из config.yaml)") args = parser.parse_args() config = load() # ── 1. ELM327 ──────────────────────────────────── print("🔌 Подключение к ELM327...") elm_cfg = config["elm327"] port = args.port or elm_cfg["port"] try: elm = ELM327(port=port, baudrate=elm_cfg["baudrate"]) except Exception as e: print(f"❌ Не удалось открыть порт {port}: {e}") print(" Проверь Bluetooth-сопряжение:") print(" bash scripts/setup-bt.sh") sys.exit(1) if not elm.init(): print("❌ ELM327 не ответил на ATZ. Проверь соединение.") elm.close() sys.exit(1) print("✅ ELM327 готов") # ── 2. VIN ─────────────────────────────────────── print("🚗 Чтение VIN...") vin = elm.read_vin() if not vin: print("❌ Не удалось прочитать VIN. Машина поддерживает режим 09?") elm.close() sys.exit(1) print(f"✅ VIN: {vin}") # ── 3. Коды ошибок ────────────────────────────── print("🔍 Чтение кодов ошибок...") dtc_stored = elm.read_dtc_codes("03") dtc_pending = elm.read_dtc_codes("07") dtc_codes = dtc_stored + dtc_pending if dtc_codes: for dtc in dtc_codes: print(f" ⚠️ {dtc['code']} [{dtc['status']}]") else: print(" ✅ Ошибок нет") # ── 4. Параметры ЭБУ ──────────────────────────── print("📊 Чтение параметров...") parameters = elm.read_all_pids(config.get("pids")) for p in parameters: print(f" {p['name']}: {p['value']} {p['unit']}") elm.close() # ── 5. Сохранение в SQLite (всегда) ────────────── db = Database() car_id = db.get_or_create_car(vin) token_id = db.create_token(car_id) for dtc in dtc_codes: db.add_dtc(token_id, dtc["code"], dtc.get("description", ""), dtc["status"]) for p in parameters: db.add_parameter(token_id, p["pid_code"], p["name"], p["value"], p["unit"]) print(f"\n💾 Сохранено в elmer.db (token_id={token_id})") # ── 6. DeepSeek (опционально) ──────────────────── if args.no_llm: print("\n⏭️ Пропуск LLM (--no-llm). Данные сохранены в elmer.db") return api_key = config["deepseek"]["api_key"] if not api_key: print("\n❌ DEEPSEEK_API_KEY не задан.") print(" Установи: export DEEPSEEK_API_KEY=sk-...") print(" Или запусти без LLM: python run.py --no-llm") return print("\n🧠 Отправка в DeepSeek...") diagnoser = Diagnoser( api_key=api_key, model=config["deepseek"].get("model", "deepseek-chat"), ) user_prompt = build_user_prompt(vin, dtc_codes, parameters) answer = diagnoser.diagnose(SYSTEM_PROMPT, user_prompt) print("\n" + "=" * 60) print(answer) print("=" * 60) db.add_llm_message(token_id, "user", user_prompt) db.add_llm_message(token_id, "assistant", answer) print(f"\n💾 LLM-ответ сохранён (token_id={token_id})") if __name__ == "__main__": main()