Hệ thống gồm 2 thành phần độc lập giao tiếp qua mạng LAN/Wifi:
Edge Client (smart_home_client.py): Camera bắt cử chỉ tay bằng MediaPipe:
Xòe cả 5 ngón: Lệnh BẬT đèn (LIGHT_ON).
Nắm chặt tay (0 ngón): Lệnh TẮT đèn (LIGHT_OFF).
Giơ ngón trỏ + ngón giữa (Ký hiệu 2 ngón): MỞ ứng dụng giải trí/học tập (LAUNCH_APP).
Hub Server (smart_home_hub.py): Thiết bị trung tâm nhận lệnh qua Socket, hiển thị bảng điều khiển trạng thái đồ họa và điều khiển phần mềm/thiết bị thực tế.
smart_home_hub.pyServer chạy giao diện đồ họa OpenCV mô phỏng chiếc đèn trần (sáng vàng rực rỡ khi BẬT, xám tối khi TẮT) và có thể kích hoạt mở trình duyệt/ứng dụng máy tính.
import socket
import cv2
import numpy as np
import subprocess
import webbrowser
class SmartHomeHub:
def __init__(self, host='0.0.0.0', port=8888):
self.host = host
self.port = port
self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Trạng thái thiết bị trong nhà
self.light_state = False # False: Tắt, True: Bật
self.last_command = "CHỜ TÍN HIỆU..."
def create_dashboard(self):
"""Tạo bảng điều khiển trực quan trạng thái ngôi nhà"""
dash = np.zeros((450, 600, 3), dtype=np.uint8)
# Tiêu đề Hub
cv2.putText(dash, "SMART HOME IOT HUB", (120, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2)
cv2.line(dash, (40, 70), (560, 70), (100, 100, 100), 2)
# Vẽ bóng đèn mô phỏng
light_color = (0, 230, 255) if self.light_state else (50, 50, 50) # Vàng sáng hoặc Xám tối
cv2.circle(dash, (300, 180), 65, light_color, cv2.FILLED)
cv2.circle(dash, (300, 180), 65, (200, 200, 200), 3)
# Trạng thái chữ
status_text = "DEN: DANG BAT (ON)" if self.light_state else "DEN: DANG TAT (OFF)"
text_color = (0, 255, 0) if self.light_state else (0, 0, 255)
cv2.putText(dash, status_text, (180, 290),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, text_color, 2)
# Lệnh nhận gần nhất
cv2.putText(dash, f"Lenh gan nhat: {self.last_command}", (60, 370),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
cv2.putText(dash, "Nhan 'Q' tren cua so de tat Hub", (130, 420),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (150, 150, 150), 1)
return dash
def execute_command(self, cmd):
self.last_command = cmd
if cmd == "LIGHT_ON":
self.light_state = True
print("💡 [HUB] Đã bật đèn trần.")
elif cmd == "LIGHT_OFF":
self.light_state = False
print("🌑 [HUB] Đã tắt đèn trần.")
elif cmd == "LAUNCH_APP":
print("🚀 [HUB] Kích hoạt mở ứng dụng trình duyệt!")
webbrowser.open("https://youtube.com")
def run(self):
self.server_sock.bind((self.host, self.port))
self.server_sock.listen(1)
self.server_sock.settimeout(0.1) # Non-blocking để vòng lặp giao diện chạy mượt
print(f"📡 Smart Home Hub sẵn sàng tại cổng {self.port}...")
conn = None
while True:
# Chờ kết nối Client
if conn is None:
try:
conn, addr = self.server_sock.accept()
conn.settimeout(0.05)
print(f"✅ Đã kết nối với Edge Camera: {addr}")
except socket.timeout:
pass
# Đọc lệnh gửi từ Client
if conn is not None:
try:
data = conn.recv(1024).decode('utf-8')
if data:
commands = data.strip().split("\n")
for c in commands:
if c:
self.execute_command(c)
else:
print("❌ Client ngắt kết nối.")
conn.close()
conn = None
except socket.timeout:
pass
except (ConnectionResetError, BrokenPipeError):
conn.close()
conn = None
# Hiển thị giao diện Dashboard
dashboard = self.create_dashboard()
cv2.imshow("Smart Home Hub Server", dashboard)
if cv2.waitKey(20) & 0xFF == ord('q'):
break
if conn:
conn.close()
self.server_sock.close()
cv2.destroyAllWindows()
if __name__ == "__main__":
hub = SmartHomeHub()
hub.run()
smart_home_client.pyClient phát hiện cử chỉ tay và gửi lệnh tức thì qua Socket.
import cv2
import socket
import time
from mediapipe.python.solutions import hands as mp_hands
from mediapipe.python.solutions import drawing_utils as mp_draw
class SmartHomeClient:
def __init__(self, hub_ip='127.0.0.1', port=8888):
self.hub_ip = hub_ip
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.cap = cv2.VideoCapture(0)
self.hands = mp_hands.Hands(max_num_hands=1, min_detection_confidence=0.7)
self.tip_ids = [8, 12, 16, 20]
self.pip_ids = [6, 10, 14, 18]
# Bộ chống gửi lặp lệnh liên tục (Debounce)
self.last_sent_cmd = None
self.last_sent_time = 0
def connect(self):
try:
self.sock.connect((self.hub_ip, self.port))
print("✅ Đã kết nối thành công tới Hub!")
return True
except Exception as e:
print(f"❌ Không kết nối được Hub: {e}")
return False
def count_fingers(self, lm_list):
if len(lm_list) == 0:
return -1
fingers = []
# Ngón cái (giả định tay phải/trái giơ lòng bàn tay)
fingers.append(1 if lm_list[4][1] < lm_list[3][1] else 0)
# 4 ngón dài
for tip, pip in zip(self.tip_ids, self.pip_ids):
fingers.append(1 if lm_list[tip][2] < lm_list[pip][2] else 0)
return fingers
def send_command(self, cmd):
current_time = time.time()
# Chỉ gửi khi đổi lệnh hoặc cách lệnh cũ tối thiểu 1.5 giây
if cmd != self.last_sent_cmd or (current_time - self.last_sent_time > 1.5):
try:
self.sock.sendall((cmd + "\n").encode('utf-8'))
self.last_sent_cmd = cmd
self.last_sent_time = current_time
print(f"📤 Đã gửi lệnh: {cmd}")
except Exception as e:
print(f"❌ Lỗi gửi Socket: {e}")
def run(self):
if not self.connect() or not self.cap.isOpened():
return
print("--- ĐIỀU KHIỂN NHÀ THÔNG MINH BẰNG CỬ CHỈ ---")
print(" -> Xòe 5 ngón: BẬT ĐÈN")
print(" -> Nắm tay (0 ngón): TẮT ĐÈN")
print(" -> 2 ngón (Trỏ + Giữa): MỞ TRÌNH DUYỆT")
print(" -> Nhấn [Q] để thoát")
while True:
ret, frame = self.cap.read()
if not ret:
break
frame = cv2.flip(frame, 1)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
res = self.hands.process(rgb)
lm_list = []
if res.multi_hand_landmarks:
hand = res.multi_hand_landmarks[0]
mp_draw.draw_landmarks(frame, hand, mp_hands.HAND_CONNECTIONS)
h, w, _ = frame.shape
for lm in hand.landmark:
lm_list.append((int(lm.x * w), int(lm.y * h)))
if lm_list:
fingers = self.count_fingers(lm_list)
total = fingers.count(1)
# Phân tích cử chỉ
if total == 5:
self.send_command("LIGHT_ON")
cv2.putText(frame, "LENH: BAT DEN", (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
elif total == 0:
self.send_command("LIGHT_OFF")
cv2.putText(frame, "LENH: TAT DEN", (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
elif fingers[1] == 1 and fingers[2] == 1 and fingers[3] == 0 and fingers[4] == 0:
self.send_command("LAUNCH_APP")
cv2.putText(frame, "LENH: MO UNG DUNG", (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2)
cv2.imshow("Camera AI Smart Home Client", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
self.cap.release()
self.sock.close()
cv2.destroyAllWindows()
if __name__ == "__main__":
client = SmartHomeClient(hub_ip='127.0.0.1', port=8888)
client.run()