发布日期:2024年6月20日
一、系统架构设计
本教程将构建一个完整的智能家居控制系统,主要功能模块包括:
- 设备连接层:MQTT协议对接物联网设备
- 实时通信层:WebSocket双向数据通道
- 3D可视化层:Three.js家居场景渲染
- 控制逻辑层:自动化规则引擎
- 用户界面层:响应式控制面板
技术栈:Node.js + Express + WebSocket + MQTT.js + Three.js
二、项目初始化与配置
1. 创建项目结构
smart-home/
├── server/ # 后端服务
│ ├── mqtt/ # MQTT连接
│ ├── websocket/ # WebSocket服务
│ └── api/ # REST API
├── client/ # 前端应用
│ ├── 3d/ # Three.js场景
│ ├── controls/ # 控制面板组件
│ └── store/ # 状态管理
├── shared/ # 共享代码
│ ├── models/ # 数据模型
│ └── utils/ # 工具函数
└── docker-compose.yml # 容器配置
2. 基础服务安装
# 初始化项目
mkdir smart-home && cd smart-home
npm init -y
# 安装后端依赖
cd server
npm install express ws mqtt.js cors body-parser
# 安装前端依赖
cd ../client
npm install three @tweenjs/tween.js chart.js
三、物联网设备连接
1. MQTT服务集成
// server/mqtt/client.js
import mqtt from 'mqtt'
class MQTTClient {
constructor() {
this.client = mqtt.connect('mqtt://iot.eclipse.org:1883')
this.topics = new Map()
this.client.on('connect', () => {
console.log('MQTT Connected')
this.subscribe('home/+/status')
})
this.client.on('message', (topic, message) => {
const handler = this.topics.get(topic)
if (handler) {
handler(JSON.parse(message.toString()))
}
})
}
subscribe(topic, callback) {
this.client.subscribe(topic)
if (callback) {
this.topics.set(topic, callback)
}
}
publish(topic, message) {
this.client.publish(topic, JSON.stringify(message))
}
}
export default new MQTTClient()
2. 设备状态管理
// server/mqtt/devices.js
import mqtt from './client'
const devices = new Map()
mqtt.subscribe('home/+/status', (data) => {
const { deviceId, status, values } = data
const device = devices.get(deviceId) || { id: deviceId }
device.status = status
device.values = values
device.lastUpdated = new Date()
devices.set(deviceId, device)
broadcastDeviceUpdate(device)
})
function broadcastDeviceUpdate(device) {
// 推送到WebSocket
}
export function getDevice(deviceId) {
return devices.get(deviceId)
}
export function controlDevice(deviceId, command) {
mqtt.publish(`home/${deviceId}/control`, command)
}
四、实时通信服务
1. WebSocket服务端
// server/websocket/server.js
import WebSocket from 'ws'
import { handleMessage } from './handler'
const clients = new Set()
export function createWebSocketServer(server) {
const wss = new WebSocket.Server({ server })
wss.on('connection', (ws) => {
clients.add(ws)
ws.on('message', (message) => {
handleMessage(ws, JSON.parse(message))
})
ws.on('close', () => {
clients.delete(ws)
})
})
return wss
}
export function broadcast(data) {
const message = JSON.stringify(data)
clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(message)
}
})
}
2. 前端WebSocket连接
// client/store/websocket.js
class WebSocketClient {
constructor() {
this.socket = null
this.listeners = new Map()
this.reconnectAttempts = 0
}
connect() {
this.socket = new WebSocket(`ws://${window.location.host}`)
this.socket.onopen = () => {
this.reconnectAttempts = 0
console.log('WebSocket Connected')
}
this.socket.onmessage = (event) => {
const { type, data } = JSON.parse(event.data)
const handlers = this.listeners.get(type) || []
handlers.forEach(handler => handler(data))
}
this.socket.onclose = () => {
const delay = Math.min(1000 * this.reconnectAttempts, 10000)
setTimeout(() => this.connect(), delay)
this.reconnectAttempts++
}
}
on(type, handler) {
if (!this.listeners.has(type)) {
this.listeners.set(type, [])
}
this.listeners.get(type).push(handler)
}
send(type, data) {
if (this.socket?.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({ type, data }))
}
}
}
export const ws = new WebSocketClient()
五、3D可视化实现
1. Three.js场景初始化
// client/3d/scene.js
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export class HomeScene {
constructor(container) {
this.container = container
this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
)
this.renderer = new THREE.WebGLRenderer({ antialias: true })
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
this.devices = new Map()
this.init()
this.animate()
}
init() {
this.renderer.setSize(
this.container.clientWidth,
this.container.clientHeight
)
this.container.appendChild(this.renderer.domElement)
this.camera.position.set(5, 5, 5)
this.controls.update()
// 添加灯光
const ambientLight = new THREE.AmbientLight(0x404040)
this.scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(1, 1, 1)
this.scene.add(directionalLight)
// 加载房屋模型
this.loadHouseModel()
}
animate() {
requestAnimationFrame(() => this.animate())
this.controls.update()
this.renderer.render(this.scene, this.camera)
}
}
2. 设备3D模型交互
// client/3d/devices.js
export class DeviceManager {
constructor(scene) {
this.scene = scene
this.raycaster = new THREE.Raycaster()
this.mouse = new THREE.Vector2()
window.addEventListener('click', (event) => this.onClick(event))
}
addDevice(device) {
const geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5)
const material = new THREE.MeshBasicMaterial({
color: device.status === 'online' ? 0x00ff00 : 0xff0000
})
const cube = new THREE.Mesh(geometry, material)
cube.position.set(
device.position.x,
device.position.y,
device.position.z
)
cube.userData.deviceId = device.id
this.scene.add(cube)
this.devices.set(device.id, cube)
}
onClick(event) {
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
this.raycaster.setFromCamera(this.mouse, this.scene.camera)
const intersects = this.raycaster.intersectObjects(this.scene.children)
if (intersects.length > 0) {
const deviceId = intersects[0].object.userData.deviceId
if (deviceId) {
this.onDeviceSelected(deviceId)
}
}
}
}
六、自动化规则引擎
1. 规则DSL设计
// shared/models/rule.js
export class Rule {
constructor(config) {
this.id = config.id
this.name = config.name
this.conditions = config.conditions
this.actions = config.actions
this.enabled = config.enabled || false
}
evaluate(deviceStates) {
return this.conditions.every(condition => {
const device = deviceStates[condition.deviceId]
if (!device) return false
switch (condition.operator) {
case 'eq': return device.value === condition.value
case 'gt': return device.value > condition.value
case 'lt': return device.value {
publishDeviceCommand(action.deviceId, action.command)
})
}
}
// 示例规则配置
const exampleRule = {
id: 'rule1',
name: '夜间自动关灯',
conditions: [
{
deviceId: 'motion1',
operator: 'eq',
value: 'no_motion'
},
{
deviceId: 'clock1',
operator: 'gt',
value: '22:00'
}
],
actions: [
{
deviceId: 'light1',
command: 'turn_off'
}
]
}
2. 规则执行引擎
// server/automation/engine.js
import { Rule } from '../../shared/models/rule'
export class RuleEngine {
constructor() {
this.rules = new Map()
this.deviceStates = {}
}
addRule(ruleConfig) {
const rule = new Rule(ruleConfig)
this.rules.set(rule.id, rule)
if (rule.enabled) {
this.activateRule(rule.id)
}
}
activateRule(ruleId) {
const rule = this.rules.get(ruleId)
if (rule) {
rule.enabled = true
}
}
updateDeviceState(deviceId, state) {
this.deviceStates[deviceId] = state
this.evaluateRules()
}
evaluateRules() {
this.rules.forEach(rule => {
if (rule.enabled && rule.evaluate(this.deviceStates)) {
rule.execute()
}
})
}
}
七、控制面板实现
1. 设备控制组件
// client/controls/DeviceCard.vue
<template>
<div class="device-card" :class="status">
<h3>{{ device.name }}</h3>
<div class="status">{{ status }}</div>
<template v-if="device.type === 'light'">
<el-slider
v-model="brightness"
:min="0"
:max="100"
@change="adjustBrightness"
/>
</template>
<template v-if="device.type === 'thermostat'">
<el-input-number
v-model="temperature"
:min="10"
:max="30"
@change="setTemperature"
/>
</template>
</div>
</template>
<script>
export default {
props: {
device: Object
},
data() {
return {
brightness: this.device.values?.brightness || 0,
temperature: this.device.values?.temperature || 20
}
},
computed: {
status() {
return this.device.status || 'offline'
}
},
methods: {
adjustBrightness(value) {
this.$ws.send('device:control', {
deviceId: this.device.id,
command: 'set_brightness',
value
})
},
setTemperature(value) {
this.$ws.send('device:control', {
deviceId: this.device.id,
command: 'set_temperature',
value
})
}
}
}
</script>
2. 实时数据图表
// client/controls/DeviceChart.vue
<template>
<div class="chart-container">
<canvas ref="chart"></canvas>
</div>
</template>
<script>
import Chart from 'chart.js'
export default {
props: {
deviceId: String,
metric: String
},
data() {
return {
chart: null,
dataPoints: []
}
},
mounted() {
this.initChart()
this.$ws.on(`device:update:${this.deviceId}`, this.updateChart)
},
methods: {
initChart() {
this.chart = new Chart(this.$refs.chart, {
type: 'line',
data: {
labels: [],
datasets: [{
label: this.metric,
data: [],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}
})
},
updateChart(data) {
const value = data.values[this.metric]
if (value === undefined) return
const now = new Date().toLocaleTimeString()
this.dataPoints.push({ time: now, value })
if (this.dataPoints.length > 20) {
this.dataPoints.shift()
}
this.chart.data.labels = this.dataPoints.map(p => p.time)
this.chart.data.datasets[0].data = this.dataPoints.map(p => p.value)
this.chart.update()
}
}
}
</script>
八、总结与扩展
通过本教程,您已经掌握了:
- 物联网设备连接与通信
- 实时双向数据传输
- 3D场景可视化技术
- 自动化规则引擎设计
- 响应式控制面板开发
扩展学习方向:
- WebRTC视频流集成
- 语音控制接口开发
- 机器学习行为预测
- 边缘计算方案