Ở bài trước, chúng ta đã có một hệ thống giám sát cửa và chuyển động khá hoàn chỉnh ở mức local:
- Raspberry Pi đọc cảm biến cửa từ và PIR
- Python tạo event
- rule engine quyết định khi nào cần cảnh báo
- hệ thống phân biệt armed / disarmed
- có cooldown để giảm spam alert
Tuy nhiên, nếu mọi thứ chỉ dừng ở terminal hoặc file log local, hệ thống vẫn còn thiếu một phần rất quan trọng:
👉 khả năng giao tiếp với bên ngoài
Một hệ thống giám sát thực tế thường cần làm được ít nhất một trong các việc sau:
- gửi cảnh báo ngay đến người dùng
- đẩy event sang hệ thống khác
- publish trạng thái lên MQTT broker
- hiển thị tình trạng cửa và chuyển động trên dashboard
- lưu lịch sử để xem lại sau
Đó là mục tiêu của bài này.
Trong bài này, chúng ta sẽ nâng cấp project từ một hệ thống theo dõi local thành một hệ thống có thể:
- gửi notify
- publish event lên MQTT
- đồng bộ trạng thái lên dashboard
Đây cũng là bước cuối để hoàn thiện mini series 24 theo đúng tư duy IoT:
sensor -> event -> rule -> notify -> integration
Bạn sẽ làm được gì sau bài này
Sau khi hoàn thành bài này, bạn sẽ có thể:
- gửi cảnh báo từ Raspberry Pi ra bên ngoài
- publish event cửa và PIR lên MQTT
- publish trạng thái hiện tại của hệ thống
- đẩy dữ liệu sang dashboard hoặc backend
- hoàn thiện một hệ thống giám sát nhà thông minh mini có khả năng mở rộng thật sự
Vì sao cần notify và MQTT
Một event local như:
door_open
chỉ thực sự có giá trị khi nó đến được đúng nơi cần đến.
Ví dụ:
- chủ nhà cần nhận được thông báo trên điện thoại
- dashboard cần biết cửa đang mở hay đóng
- backend cần ghi lại lịch sử hoạt động
- một ESP32 khác cần biết hệ thống đang armed hay disarmed
Nếu không có lớp giao tiếp ra ngoài, Raspberry Pi chỉ giống như một thiết bị tự biết chuyện của mình, nhưng không chia sẻ được với các thành phần khác.
MQTT và notify giúp giải quyết đúng bài toán này.
Kiến trúc hoàn chỉnh của hệ thống
Sau khi thêm notify và MQTT, kiến trúc của Hệ thống giám sát nhà thông minh mini sẽ như sau:
Door Sensor + PIR -> Raspberry Pi -> Event Monitor -> Rule Engine -> Notify / MQTT / Dashboard
Trong đó:
- Event Monitor: đọc GPIO và tạo event
- Rule Engine: quyết định alert hay chỉ log
- Notify: gửi cảnh báo cho người dùng
- MQTT: publish event và trạng thái
- Dashboard: hiển thị dữ liệu theo thời gian thực
Đây là một kiến trúc rất thực tế và cũng rất hợp với các dự án smart home mini.
Những gì nên được gửi ra ngoài
Không phải mọi thứ đều cần gửi notify.
Ta nên phân biệt rõ 3 loại dữ liệu:
1. Event thô
Là các sự kiện do cảm biến sinh ra:
door_opendoor_closedmotion_detectedmotion_idle
Loại này phù hợp để:
- publish MQTT
- lưu lịch sử
- hiển thị timeline trên dashboard
2. Alert
Là các cảnh báo đã qua rule engine:
door_open_alertmotion_alert
Loại này phù hợp để:
- gửi Telegram / email / webhook
- hiển thị dạng notification nổi bật
- đẩy tới backend xử lý tiếp
3. Current state
Là trạng thái mới nhất của hệ thống:
- cửa đang mở hay đóng
- có chuyển động hay không
- hệ thống đang armed hay disarmed
Loại này phù hợp để:
- dashboard hiển thị trạng thái realtime
- các service khác query nhanh mà không cần đọc lại toàn bộ event log
Đây là cách tư duy rất quan trọng:
event, alert và state là ba lớp dữ liệu khác nhau.
Chọn cách gửi cảnh báo nào
Trong bài này, ta có thể chia notify thành ba hướng phổ biến:
Telegram
Rất tiện cho demo và cá nhân:
- nhanh
- dễ nhận
- gần realtime
Phù hợp nếu cần lưu lại hoặc gửi kiểu trang trọng hơn, nhưng chậm hơn notify tức thời.
Webhook
Phù hợp khi muốn đẩy alert sang:
- backend
- automation service
- app khác
- workflow nội bộ
Để dễ hiểu và dễ mở rộng, trong bài này chúng ta sẽ xây code theo kiểu có một Notification Service, để về sau bạn thay Telegram, email hay webhook đều dễ.
Thiết kế topic MQTT cho hệ thống giám sát
Đây là phần rất quan trọng.
Nếu topic MQTT được đặt tốt ngay từ đầu, hệ thống sẽ dễ mở rộng hơn nhiều.
Nhóm 1: Event
smart-home/events/door
smart-home/events/motion
Nhóm 2: Alert
smart-home/alerts/security
Nhóm 3: State
smart-home/state/door
smart-home/state/motion
smart-home/state/system
Cách đặt rõ hơn theo site hoặc room
Nếu sau này hệ thống lớn hơn, bạn có thể mở rộng thành:
iotlabs/home1/frontdoor/events
iotlabs/home1/livingroom/motion
iotlabs/home1/security/alerts
iotlabs/home1/system/state
Trong bài này, để giữ code gọn và rõ, ta sẽ dùng bộ topic đơn giản:
smart-home/events/doorsmart-home/events/motionsmart-home/alerts/securitysmart-home/state/doorsmart-home/state/motionsmart-home/state/system
Thiết kế payload gửi MQTT
Payload nên có cấu trúc rõ ràng, tránh chỉ gửi text thô.
Ví dụ event cửa
{
"ts": "2026-04-11 21:30:00",
"sensor": "door",
"event": "door_open"
}
Ví dụ event PIR
{
"ts": "2026-04-11 21:30:05",
"sensor": "pir",
"event": "motion_detected"
}
Ví dụ alert
{
"ts": "2026-04-11 21:30:05",
"type": "motion_alert",
"level": "high",
"message": "Motion detected while system is armed"
}
Ví dụ state cửa
{
"ts": "2026-04-11 21:30:10",
"sensor": "door",
"state": "open"
}
Ví dụ state hệ thống
{
"ts": "2026-04-11 21:30:10",
"armed": true
}
Payload kiểu này vừa dễ đọc, vừa dễ dùng cho dashboard hoặc backend.
Cài thư viện MQTT cho Python
Chúng ta sẽ dùng Paho MQTT client.
Cài bằng lệnh:
pip install paho-mqtt
Tạo MQTT service đơn giản
Tạo file:
mqtt_service.py
Code mẫu
import json
import paho.mqtt.client as mqtt
BROKER_HOST = "127.0.0.1"
BROKER_PORT = 1883
class MQTTService:
def __init__(self):
self.client = mqtt.Client()
self.client.connect(BROKER_HOST, BROKER_PORT, 60)
def publish(self, topic, payload):
self.client.publish(topic, json.dumps(payload))
print(f"MQTT -> {topic}: {payload}")
Code này đủ để:
- connect broker
- publish bất kỳ event, alert hay state nào
Gửi event lên MQTT
Giả sử bạn có event cửa:
event = {
"ts": "2026-04-11 21:30:00",
"sensor": "door",
"event": "door_open"
}
Ta publish như sau:
mqtt_service.publish("smart-home/events/door", event)
Với PIR:
event = {
"ts": "2026-04-11 21:30:05",
"sensor": "pir",
"event": "motion_detected"
}
mqtt_service.publish("smart-home/events/motion", event)
Gửi current state lên MQTT
Ngoài event, bạn nên publish trạng thái mới nhất.
State cửa
door_state_payload = {
"ts": "2026-04-11 21:30:00",
"sensor": "door",
"state": "open"
}
mqtt_service.publish("smart-home/state/door", door_state_payload)
State PIR
motion_state_payload = {
"ts": "2026-04-11 21:30:05",
"sensor": "motion",
"state": "detected"
}
mqtt_service.publish("smart-home/state/motion", motion_state_payload)
State hệ thống
system_state_payload = {
"ts": "2026-04-11 21:30:05",
"armed": True
}
mqtt_service.publish("smart-home/state/system", system_state_payload)
Vì sao cần state riêng
Vì dashboard thường chỉ cần biết:
- trạng thái hiện tại là gì
Thay vì phải đọc lại toàn bộ event log.
Tạo Notification Service
Bây giờ ta xây một lớp gửi cảnh báo.
Tạo file:
notification_service.py
Code mẫu đơn giản
class NotificationService:
def send(self, alert):
print("NOTIFY:", alert)
Ở giai đoạn đầu, ta chỉ in ra terminal.
Sau đó bạn có thể thay bằng:
- Telegram API
- webhook
Điểm quan trọng là các phần khác của hệ thống không cần biết cách gửi cụ thể ra sao. Chúng chỉ cần gọi:
notification_service.send(alert)
Tích hợp rule engine + notify + MQTT
Giả sử ta có event monitor tạo ra event như sau:
event = {
"ts": now_str(),
"sensor": "door",
"event": "door_open"
}
Luồng xử lý sẽ là:
- publish event
- publish current state
- truyền event cho rule engine
- nếu có alert thì gửi notify
- đồng thời publish alert lên MQTT
Ví dụ xử lý event cửa
def handle_event(event):
if event["sensor"] == "door":
mqtt_service.publish("smart-home/events/door", event)
state_payload = {
"ts": event["ts"],
"sensor": "door",
"state": "open" if event["event"] == "door_open" else "closed"
}
mqtt_service.publish("smart-home/state/door", state_payload)
elif event["sensor"] == "pir":
mqtt_service.publish("smart-home/events/motion", event)
state_payload = {
"ts": event["ts"],
"sensor": "motion",
"state": "detected" if event["event"] == "motion_detected" else "idle"
}
mqtt_service.publish("smart-home/state/motion", state_payload)
alert = rule_engine.process_event(event)
if alert:
notification_service.send(alert)
mqtt_service.publish("smart-home/alerts/security", alert)
Đây là một flow rất sạch và đúng kiến trúc.
Code mẫu tích hợp hoàn chỉnh
from datetime import datetime
from mqtt_service import MQTTService
from notification_service import NotificationService
from rule_engine import RuleEngine
mqtt_service = MQTTService()
notification_service = NotificationService()
rule_engine = RuleEngine()
def now_str():
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def handle_event(event):
if event["sensor"] == "door":
mqtt_service.publish("smart-home/events/door", event)
state_payload = {
"ts": event["ts"],
"sensor": "door",
"state": "open" if event["event"] == "door_open" else "closed"
}
mqtt_service.publish("smart-home/state/door", state_payload)
elif event["sensor"] == "pir":
mqtt_service.publish("smart-home/events/motion", event)
state_payload = {
"ts": event["ts"],
"sensor": "motion",
"state": "detected" if event["event"] == "motion_detected" else "idle"
}
mqtt_service.publish("smart-home/state/motion", state_payload)
system_state_payload = {
"ts": event["ts"],
"armed": rule_engine.armed
}
mqtt_service.publish("smart-home/state/system", system_state_payload)
alert = rule_engine.process_event(event)
if alert:
notification_service.send(alert)
mqtt_service.publish("smart-home/alerts/security", alert)
# Demo thử
handle_event({
"ts": now_str(),
"sensor": "door",
"event": "door_open"
})
handle_event({
"ts": now_str(),
"sensor": "pir",
"event": "motion_detected"
})
Cách kiểm tra bằng MQTT subscriber
Bạn có thể dùng mosquitto_sub để xem message.
Subscribe event cửa
mosquitto_sub -h 127.0.0.1 -t smart-home/events/door
Subscribe event chuyển động
mosquitto_sub -h 127.0.0.1 -t smart-home/events/motion
Subscribe alert
mosquitto_sub -h 127.0.0.1 -t smart-home/alerts/security
Subscribe state hệ thống
mosquitto_sub -h 127.0.0.1 -t smart-home/state/system
Khi test, bạn sẽ thấy từng nhóm dữ liệu đi đúng topic.
Dashboard sẽ dùng dữ liệu nào
Một dashboard đơn giản cho hệ thống này thường cần 3 phần chính.
1. Current status
Hiển thị:
- cửa đang mở hay đóng
- có chuyển động hay không
- hệ thống đang armed hay disarmed
Dữ liệu lấy từ:
smart-home/state/doorsmart-home/state/motionsmart-home/state/system
2. Timeline event
Hiển thị lịch sử gần nhất:
- cửa mở lúc nào
- chuyển động xuất hiện lúc nào
- có bao nhiêu event trong ngày
Dữ liệu lấy từ:
smart-home/events/doorsmart-home/events/motion
3. Alert panel
Hiển thị:
- cảnh báo mới nhất
- mức độ nghiêm trọng
- nội dung cảnh báo
Dữ liệu lấy từ:
smart-home/alerts/security
Đây là lý do ngay từ đầu ta tách event, state và alert ra riêng.
Cách kết nối dashboard đơn giản
Bạn có thể chọn nhiều hướng:
Hướng 1: Node-RED dashboard
Rất nhanh cho demo:
- subscribe MQTT
- hiển thị trạng thái
- dễ dựng prototype
Hướng 2: Web app riêng
Ví dụ:
- backend subscribe MQTT
- lưu DB
- frontend hiển thị timeline và trạng thái
Hướng 3: Dashboard trong chính Raspberry Pi
Ví dụ:
- Flask / FastAPI + WebSocket
- hoặc web app nhẹ đọc state từ backend
Trong mini series này, điều quan trọng nhất là:
- dữ liệu đã được đẩy ra MQTT đúng cấu trúc
Khi đó dashboard chỉ là lớp tiêu thụ dữ liệu.
Gửi Telegram / webhook thật sau này như thế nào
Dù ví dụ hiện tại mới chỉ in NOTIFY: ra terminal, cấu trúc của bạn đã đúng rồi.
Về sau chỉ cần thay nội dung trong NotificationService.
Ví dụ tư duy Telegram
class NotificationService:
def send(self, alert):
message = f"[{alert['level']}] {alert['message']}"
# gọi Telegram API ở đây
Ví dụ webhook
class NotificationService:
def send(self, alert):
# requests.post(webhook_url, json=alert)
pass
Nhờ vậy:
- rule engine không cần biết Telegram là gì
- event monitor không cần biết webhook là gì
- toàn bộ hệ thống dễ thay đổi và bảo trì hơn
Gợi ý retained message cho state
Nếu broker hỗ trợ retained message, bạn nên cân nhắc dùng retained cho các topic state:
smart-home/state/doorsmart-home/state/motionsmart-home/state/system
Lợi ích:
- dashboard mới kết nối vào sẽ nhận ngay trạng thái hiện tại
- không cần chờ event mới
Trong khi đó, event và alert thường không nên retained theo cùng cách, vì chúng là dữ liệu theo dòng thời gian.
Tổ chức project sau khi hoàn thành
Tới thời điểm này, project có thể được sắp xếp như sau:
smart_home_monitor/
├── main.py
├── sensors/
│ ├── door_sensor.py
│ └── pir_sensor.py
├── monitor/
│ └── event_monitor.py
├── rules/
│ └── rule_engine.py
├── services/
│ ├── mqtt_service.py
│ └── notification_service.py
└── logs/
└── event_logger.py
Đây là cấu trúc rất ổn để tiếp tục mở rộng về sau.
Các lỗi thường gặp
Event có nhưng dashboard không thấy gì
Nguyên nhân:
- publish sai topic
- subscriber đang nghe sai topic
- payload không đúng format JSON
Cách xử lý:
- kiểm tra topic chính xác từng ký tự
- log payload raw
- test bằng
mosquitto_sub
Alert gửi quá nhiều
Nguyên nhân:
- cooldown trong rule engine quá ngắn
- PIR quá nhạy
- mỗi event đều bị coi là alert
Cách xử lý:
- tăng cooldown
- tinh chỉnh PIR
- chỉ alert cho các event đúng điều kiện armed
Dashboard hiện sai trạng thái
Nguyên nhân:
- state không được publish khi event đổi
- dùng event để suy ra state nhưng logic chưa chắc chắn
Cách xử lý:
- luôn publish state riêng sau mỗi event
- tách state topic riêng biệt
MQTT broker đang chạy nhưng publish lỗi
Nguyên nhân:
- sai host hoặc port
- service connect thất bại
- broker yêu cầu auth
Cách xử lý:
- test kết nối broker độc lập trước
- thêm username/password nếu cần
- in log kết nối rõ ràng
Notify và MQTT bị dính chặt vào nhau
Nguyên nhân:
- code viết trực tiếp tất cả trong một function lớn
Cách xử lý:
- tách
MQTTService - tách
NotificationService - để
main.pychỉ làm nhiệm vụ orchestration
Kết luận
Bài này là bước hoàn thiện cực kỳ quan trọng của “Hệ thống giám sát nhà thông minh” mini này.
Từ một hệ thống:
- đọc cảm biến
- tạo event
- xét rule local
chúng ta đã nâng nó thành một hệ thống có thể:
- gửi cảnh báo ra ngoài
- publish event lên MQTT
- đồng bộ trạng thái cho dashboard
- sẵn sàng tích hợp vào smart home hoặc backend lớn hơn
Nói cách khác, Raspberry Pi lúc này không còn chỉ là một board đọc sensor nữa, mà đã trở thành:
👉 một trung tâm giám sát và phân phối dữ liệu cho hệ thống nhà thông minh mini
Đó cũng là đích đến đúng của chúng ta, không chỉ học cách nối dây và đọc GPIO, mà còn xây được một hệ thống nhỏ nhưng có kiến trúc đúng, có thể mở rộng thật sự.


