Smart Home - Khi Jarvis bước ra đời thực (Kết nối Home Assistant)

AI Hunter

Member
Chúng ta đã dạy Jarvis đủ thứ trên đời, nhưng nó vẫn chỉ là một phần mềm nằm trong ổ cứng. Hôm nay, chúng ta sẽ cho nó "đôi tay" để tác động vật lý lên ngôi nhà của bạn.

Giải pháp tối ưu nhất hiện nay là kết nối Jarvis với Home Assistant (HASS).
  • Home Assistant: Quản lý thiết bị (Đèn, quạt, công tắc, cảm biến...).
  • Jarvis: Quản lý tư duy và ra lệnh.

Smart Home - Khi Jarvis bước ra đời thực (Kết nối Home Assistant).jpg

1. Nguyên lý hoạt động​


Chúng ta sẽ không code lại driver cho từng bóng đèn (quá khổ). Chúng ta sẽ dùng cơ chế Function Calling của OpenAI để gọi API của HASS.

Quy trình:
  1. User nói: "Bật đèn phòng khách lên."
  2. Jarvis (LLM) phân tích và gọi tool: control_device(entity_id="light.living_room", action="turn_on").
  3. Backend (FastAPI) gửi HTTP Request sang Home Assistant.
  4. Home Assistant bật đèn thật. 💡
  5. Jarvis báo lại: "Đã bật đèn rồi sếp nhé."

2. Chuẩn bị (Lấy chìa khóa vào nhà)​


Giả sử bạn đã có Home Assistant chạy ở nhà (hoặc chạy giả lập).
1. Truy cập HASS -> Click vào Avatar (Profile).
2. Cuộn xuống dưới cùng -> **Long-Lived Access Tokens**.
3. Tạo một Token mới -> Copy chuỗi ký tự dài ngoằng đó.

3. Code Backend (FastAPI)​


Mở file server.py. Chúng ta sẽ thêm một tool mới cho Jarvis.

Python:
import requests
import json
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from openai import OpenAI

app = FastAPI()
client = OpenAI(api_key="sk-proj-xxxx")

# --- CẤU HÌNH HOME ASSISTANT ---
HASS_URL = "http://192.168.1.100:8123" # Thay IP HASS của bạn
HASS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6Ik..." # Token vừa copy

# 1. Hàm gọi API Home Assistant (Cánh tay robot)
def call_hass_service(domain, service, entity_id):
    url = f"{HASS_URL}/api/services/{domain}/{service}"
    headers = {
        "Authorization": f"Bearer {HASS_TOKEN}",
        "content-type": "application/json",
    }
    payload = {"entity_id": entity_id}
   
    try:
        response = requests.post(url, headers=headers, json=payload)
        return response.status_code == 200
    except Exception as e:
        print(f"Lỗi kết nối HASS: {e}")
        return False

# 2. Định nghĩa Tool cho OpenAI hiểu
tools = [
    {
        "type": "function",
        "function": {
            "name": "control_home",
            "description": "Điều khiển các thiết bị trong nhà thông minh (đèn, quạt, rèm, khóa).",
            "parameters": {
                "type": "object",
                "properties": {
                    "device_name": {
                        "type": "string",
                        "description": "Tên thiết bị (ví dụ: đèn phòng khách, quạt trần, cổng chính).",
                    },
                    "action": {
                        "type": "string",
                        "enum": ["turn_on", "turn_off"],
                        "description": "Hành động cần thực hiện (bật hoặc tắt).",
                    },
                },
                "required": ["device_name", "action"],
            },
        },
    }
]

# Mapping từ tên tiếng Việt sang ID thiết bị trong HASS
# (Thực tế bạn nên lưu cái này trong Database)
DEVICE_MAPPING = {
    "đèn phòng khách": "light.living_room",
    "đèn ngủ": "light.bedroom",
    "quạt": "fan.ceiling_fan",
    "cổng": "switch.gate"
}

class ChatRequest(BaseModel):
    message: str

@app.post("/chat")
async def chat_endpoint(req: ChatRequest):
    messages = [
        {"role": "system", "content": "Bạn là Jarvis quản gia thông minh."},
        {"role": "user", "content": req.message}
    ]

    # Gọi OpenAI lần 1: Để xem nó có muốn dùng Tool không?
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        tools=tools,
        tool_choice="auto"
    )
   
    msg = response.choices[0].message
   
    # Nếu Jarvis muốn gọi Tool (Function Calling)
    if msg.tool_calls:
        tool_call = msg.tool_calls[0]
        func_name = tool_call.function.name
        args = json.loads(tool_call.function.arguments)
       
        if func_name == "control_home":
            device_name = args["device_name"].lower()
            action = args["action"]
           
            # Tra cứu ID thiết bị
            entity_id = DEVICE_MAPPING.get(device_name)
           
            if entity_id:
                # Tách domain (light/switch) từ entity_id
                domain = entity_id.split(".")[0]
               
                # Thực hiện gọi HASS thật
                success = call_hass_service(domain, action, entity_id)
               
                result_text = "Thành công" if success else "Thất bại"
            else:
                result_text = "Không tìm thấy thiết bị này trong hệ thống mapping."
           
            # Gửi kết quả thực hiện lại cho Jarvis để nó báo cáo user
            messages.append(msg)
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": f"Đã thực hiện lệnh {action} với {device_name}. Kết quả: {result_text}"
            })
           
            # Gọi OpenAI lần 2: Để ra câu trả lời cuối cùng
            final_resp = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=messages
            )
            return {"answer": final_resp.choices[0].message.content}

    # Nếu không gọi tool, trả lời bình thường
    return {"answer": msg.content}

4. Test thực tế​


Kịch bản:
User: "Jarvis, tối quá, bật đèn phòng khách lên giúp tôi."

Điều gì xảy ra ngầm bên dưới?
1. GPT-4o nhận lệnh -> Trích xuất: {"device_name": "đèn phòng khách", "action": "turn_on"}.
2. Backend tra mapping: "đèn phòng khách" -> "light.living_room".
3. Backend gọi API HASS: POST /api/services/light/turn_on với entity light.living_room.
4. Đèn sáng! 💡
5. Jarvis trả lời: "Đã bật đèn phòng khách rồi ạ. Sếp cần gì nữa không?".

5. Mở rộng nâng cao​


Code trên mới chỉ là cơ bản (Hardcode mapping). Để xịn hơn, bạn có thể:
  • Sync Device List: Khi khởi động, Jarvis tự gọi API HASS để lấy danh sách toàn bộ thiết bị hiện có, không cần mapping tay.
  • Query Status: Hỏi "Đèn phòng ngủ có đang bật không?" -> Jarvis gọi GET /api/states/{entity_id} để kiểm tra.
  • Automation: "Nếu nhiệt độ trên 30 độ thì bật máy lạnh" -> Jarvis tự tạo Automation trong HASS.

Tổng kết​


Vậy là Jarvis của chúng ta đã chính thức bước ra thế giới thực.
Bây giờ, bạn có thể kết hợp với bài **Voice Interaction** để tạo ra combo hủy diệt:
Vừa bước vào nhà, nói vọng vào không trung: "Jarvis, I'm home!" -> Đèn bật, nhạc lên, rèm mở. 😎
 
Back
Top