JavaScript全栈智能家居系统开发:从物联网连接到3D可视化控制面板实战 | Web开发进阶

发布日期: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>

八、总结与扩展

通过本教程,您已经掌握了:

  1. 物联网设备连接与通信
  2. 实时双向数据传输
  3. 3D场景可视化技术
  4. 自动化规则引擎设计
  5. 响应式控制面板开发

扩展学习方向:

  • WebRTC视频流集成
  • 语音控制接口开发
  • 机器学习行为预测
  • 边缘计算方案
JavaScript全栈智能家居系统开发:从物联网连接到3D可视化控制面板实战 | Web开发进阶
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 javascript JavaScript全栈智能家居系统开发:从物联网连接到3D可视化控制面板实战 | Web开发进阶 https://www.taomawang.com/web/javascript/867.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务