
基于Python的智能家居控制系统开发:从设备联动到场景管理实战
大家好,作为一名折腾智能家居多年的开发者,我常常觉得市面上的中心化控制系统要么太“重”,要么不够灵活。于是,我决定用Python自己动手,打造一个轻量、可高度定制化的智能家居控制核心。今天,我就和大家分享一下如何用Python实现设备联动与场景管理,过程中踩过的坑和收获的惊喜,都会一一奉上。
我的核心思路是:用一个Python服务作为“大脑”,通过各厂商的开放API或本地协议(如MQTT)与设备通信,然后基于事件驱动模型,实现“如果...就...”的联动规则和复杂的场景一键执行。这个方案的优势在于,你可以完全掌控逻辑,无缝集成不同品牌的设备,甚至接入一些“野生”的DIY硬件。
一、搭建基础:环境与通信框架
首先,我们需要一个可靠的通信基础。我选择了 asyncio 来处理并发,用 aiohttp 调用HTTP API,用 paho-mqtt 或 asyncio-mqtt 来连接那些支持本地MQTT的设备(比如很多ESPHome或Tasmota固件的设备)。
第一步,创建项目并安装核心依赖:
mkdir smart_home_brain && cd smart_home_brain
python -m venv venv
source venv/bin/activate # Windows: venvScriptsactivate
pip install aiohttp asyncio-mqtt pydantic
接下来,我们定义一个基础的设备模型。使用 pydantic 来做数据验证和序列化非常方便:
# models/device.py
from pydantic import BaseModel
from typing import Any, Optional
from enum import Enum
class DeviceType(Enum):
LIGHT = "light"
SWITCH = "switch"
SENSOR = "sensor"
CLIMATE = "climate"
class Device(BaseModel):
id: str
name: str
type: DeviceType
state: dict[str, Any] = {} # 如 {"on": True, "brightness": 255}
attributes: dict[str, Any] = {} # 如 {"ip": "192.168.1.100", "model": "yeelight"}
provider: str # 设备来源,如 "yeelight_local", "mqtt_esphome"
def update_state(self, new_state: dict):
"""更新设备状态"""
self.state.update(new_state)
print(f"[Device] {self.name} 状态更新: {self.state}")
踩坑提示:一开始我把状态更新做得太复杂,试图对比每一次变化。后来发现,对于联动逻辑,通常只需要知道最新状态。所以一个简单的字典更新就够了,但记得要深拷贝如果需要历史记录。
二、核心引擎:事件总线与联动规则
智能家居的“智能”体现在自动化联动上。我设计了一个简单的事件总线(Event Bus)来解耦设备状态变化和联动动作。当传感器状态改变、时间到达或手动触发时,就向总线发布一个事件。联动规则引擎监听这些事件,并执行预定义的动作。
# core/event_bus.py
import asyncio
from typing import Callable, Any
import logging
logger = logging.getLogger(__name__)
class EventBus:
def __init__(self):
self._listeners = {}
def on(self, event_type: str, callback: Callable):
"""注册事件监听器"""
if event_type not in self._listeners:
self._listeners[event_type] = []
self._listeners[event_type].append(callback)
async def emit(self, event_type: str, **data):
"""异步触发事件"""
logger.info(f"事件触发: {event_type}, 数据: {data}")
if event_type in self._listeners:
for callback in self._listeners[event_type]:
try:
# 异步执行回调,避免阻塞
asyncio.create_task(callback(**data))
except Exception as e:
logger.error(f"执行事件 {event_type} 的回调失败: {e}")
# 定义一些标准事件类型
EVENT_DEVICE_STATE_CHANGED = "device.state_changed"
EVENT_TIME_SCHEDULE = "time.schedule"
EVENT_MANUAL_TRIGGER = "manual.trigger"
有了事件总线,我们就可以定义联动规则了。我将规则定义为“触发器(Trigger)+ 条件(Condition)+ 动作(Action)”的模式。
# core/rule_engine.py
from core.event_bus import EventBus, EVENT_DEVICE_STATE_CHANGED
from models.device import Device
class Rule:
def __init__(self, name, trigger, condition=None, actions=None):
self.name = name
self.trigger = trigger # 例如: {"type": EVENT_DEVICE_STATE_CHANGED, "device_id": "motion_sensor", "state": {"motion": True}}
self.condition = condition or (lambda **kwargs: True) # 可选的判断函数
self.actions = actions or [] # 要执行的动作函数列表
async def evaluate(self, event_data):
"""评估规则是否执行"""
# 检查触发器类型和设备ID是否匹配
if event_data.get('type') != self.trigger['type']:
return False
if event_data.get('device_id') != self.trigger.get('device_id'):
return False
# 检查状态条件(简单实现,可扩展)
trigger_state = self.trigger.get('state', {})
for key, value in trigger_state.items():
if event_data.get('state', {}).get(key) != value:
return False
# 执行自定义条件判断
if not self.condition(**event_data):
return False
# 执行所有动作
for action in self.actions:
await action(**event_data)
return True
class RuleEngine:
def __init__(self, event_bus: EventBus):
self.event_bus = event_bus
self.rules = []
# 监听所有设备状态变化事件
self.event_bus.on(EVENT_DEVICE_STATE_CHANGED, self._handle_event)
async def _handle_event(self, **event_data):
for rule in self.rules:
await rule.evaluate(event_data)
def add_rule(self, rule: Rule):
self.rules.append(rule)
print(f"[RuleEngine] 规则已添加: {rule.name}")
实战经验:这里的条件判断(condition)我设计成了一个可调用的函数,这带来了极大的灵活性。例如,你可以写一个条件函数,只在工作日晚上7点后且室外天暗了(通过光照传感器判断)才执行开灯动作。
三、设备集成与场景管理实战
现在,我们把设备、事件和规则串联起来。假设我们集成了一个Yeelight智能灯和一个DIY的人体传感器(通过MQTT上报)。
首先,写一个设备管理器来统一管理所有设备实例:
# managers/device_manager.py
class DeviceManager:
def __init__(self, event_bus: EventBus):
self.devices = {} # device_id -> Device object
self.event_bus = event_bus
def register_device(self, device: Device):
self.devices[device.id] = device
print(f"[DeviceManager] 设备注册: {device.name}({device.id})")
async def update_device_state(self, device_id: str, new_state: dict):
if device_id in self.devices:
device = self.devices[device_id]
old_state = device.state.copy()
device.update_state(new_state)
# 状态变化时,触发事件
await self.event_bus.emit(
EVENT_DEVICE_STATE_CHANGED,
type=EVENT_DEVICE_STATE_CHANGED,
device_id=device_id,
device_name=device.name,
old_state=old_state,
state=new_state
)
然后,我们定义一个具体的“回家场景”。这个场景由一条规则触发:当人体传感器在晚上6点到12点之间检测到运动时,且客厅灯是关闭状态,就自动开启客厅灯并调到舒适亮度。
# scenes/back_home_scene.py
import asyncio
from datetime import datetime, time
async def turn_on_light(device_id, brightness=300, **kwargs):
# 这里应该包含调用真实设备API的代码,例如通过Yeelight的LAN控制协议
print(f"[动作] 正在打开灯 {device_id}, 亮度 {brightness}")
# 模拟API调用
# async with aiohttp.ClientSession() as session:
# async with session.post(f'http://{device_ip}/api/light', json={"on": True, "bri": brightness}) as resp:
# pass
await asyncio.sleep(0.1) # 模拟网络延迟
def is_evening_and_weekday(**kwargs):
"""条件函数:判断是否是工作日晚上"""
now = datetime.now()
is_weekday = now.weekday() < 5 # 0-4是周一到周五
evening_start = time(18, 0)
evening_end = time(23, 59)
return is_weekday and evening_start <= now.time() <= evening_end
# 构建规则
back_home_rule = Rule(
name="晚上回家自动开灯",
trigger={
"type": EVENT_DEVICE_STATE_CHANGED,
"device_id": "livingroom_motion_sensor",
"state": {"motion": True}
},
condition=is_evening_and_weekday, # 附加时间条件
actions=[
lambda **data: turn_on_light("yeelight_desklamp", brightness=300)
]
)
踩坑提示:在动作函数中直接进行网络IO(如HTTP请求)时,一定要使用异步函数(async def)和await,否则会阻塞整个事件循环,导致系统响应变慢。我最初用了同步请求,当同时触发多个规则时,系统几乎卡死。
四、系统组装与未来展望
最后,我们在主程序中把所有模块组装起来:
# main.py
import asyncio
from core.event_bus import EventBus
from core.rule_engine import RuleEngine, Rule
from managers.device_manager import DeviceManager
from models.device import Device, DeviceType
from scenes.back_home_scene import back_home_rule
async def main():
# 1. 初始化核心组件
event_bus = EventBus()
device_manager = DeviceManager(event_bus)
rule_engine = RuleEngine(event_bus)
# 2. 模拟注册一些设备
desk_lamp = Device(id="yeelight_desklamp", name="书桌灯", type=DeviceType.LIGHT, provider="yeelight")
motion_sensor = Device(id="livingroom_motion_sensor", name="客厅人体传感器", type=DeviceType.SENSOR, provider="mqtt")
device_manager.register_device(desk_lamp)
device_manager.register_device(motion_sensor)
# 3. 添加场景规则
rule_engine.add_rule(back_home_rule)
print("智能家居控制系统启动成功!")
# 4. 模拟传感器触发事件
print("模拟:人体传感器检测到运动...")
await device_manager.update_device_state("livingroom_motion_sensor", {"motion": True})
# 保持主循环运行,在实际应用中这里会连接MQTT、启动Web服务器等
await asyncio.sleep(2)
if __name__ == "__main__":
asyncio.run(main())
运行这个程序,你会看到规则被成功触发,执行了开灯动作。当然,这是一个高度简化的演示。在实际项目中,你还需要:
- 持久化:将设备配置和联动规则保存到数据库或文件中。
- 更丰富的触发器:集成定时触发器、天气触发器(调用天气API)等。
- 用户界面:可以搭配一个简单的Web界面(用FastAPI或Flask)来添加设备和编辑规则。
- 稳定性:增加重试机制、异常处理和健康检查。
通过这个项目,我不仅实现了符合自己习惯的自动化,更重要的是,我完全掌控了家里的“数字神经中枢”。它可能没有商业系统那么华丽,但每一行代码都知其所以然,这种成就感是无与伦比的。希望这篇教程能给你带来启发,欢迎一起交流,在开源社区里完善这个“智慧大脑”!

评论(0)