UniApp企业级实战:构建智能家居控制中心完整指南

2025-10-13 0 831
发布日期:2023年12月1日
作者:全栈开发工程师
阅读时长:15分钟

项目背景与需求分析

随着物联网技术的快速发展,智能家居应用需要同时支持移动端App、微信小程序、Web端等多平台。UniApp凭借其”一次开发,多端部署”的优势,成为构建此类应用的理想选择。

核心功能需求:

  • 设备状态实时监控与控制
  • 场景模式一键切换
  • 能耗数据统计分析
  • 多用户权限管理
  • 消息推送与智能提醒

技术架构设计

整体架构图:

┌─────────────────────────────────────────┐
│               前端展示层                  │
│   ┌─────────┬─────────┬─────────────┐   │
│   │   App   │  小程序  │   H5页面    │   │
│   └─────────┴─────────┴─────────────┘   │
└─────────────────┬───────────────────────┘
                  │
┌─────────────────▼───────────────────────┐
│             UniApp业务层                │
│  ┌───────┬──────────┬────────────────┐  │
│  │ 设备管理 │ 场景控制  │   数据统计    │  │
│  └───────┴──────────┴────────────────┘  │
└─────────────────┬───────────────────────┘
                  │
┌─────────────────▼───────────────────────┐
│             后端服务层                   │
│  ┌───────┬──────────┬────────────────┐  │
│  │ MQTT  │ WebSocket │   RESTful API  │  │
│  └───────┴──────────┴────────────────┘  │
└─────────────────┬───────────────────────┘
                  │
┌─────────────────▼───────────────────────┐
│             设备硬件层                   │
│  ┌───────┬──────────┬────────────────┐  │
│  │ 智能灯光 │  空调系统  │   安防设备    │  │
│  └───────┴──────────┴────────────────┘  │
└─────────────────────────────────────────┘
            

技术栈选型:

层级 技术方案 说明
前端框架 UniApp 3.9 + Vue 3 + TypeScript 提供类型安全和更好的开发体验
状态管理 Pinia + 持久化存储 设备状态全局管理
UI框架 uView Plus 3.1 + 自定义组件 统一多端UI体验
实时通信 MQTT.js + WebSocket 设备状态实时同步
数据可视化 ECharts + 自定义图表 能耗数据展示

核心模块实现

1. 设备管理模块

实现设备的增删改查和状态监控:

// types/device.ts
export interface SmartDevice {
  id: string
  name: string
  type: DeviceType
  status: DeviceStatus
  online: boolean
  lastUpdate: number
  properties: Record<string, any>
}

export enum DeviceType {
  LIGHT = 'light',
  THERMOSTAT = 'thermostat',
  SECURITY = 'security',
  PLUG = 'plug'
}

export enum DeviceStatus {
  ONLINE = 'online',
  OFFLINE = 'offline',
  ERROR = 'error'
}

// stores/deviceStore.ts
import { defineStore } from 'pinia'

export const useDeviceStore = defineStore('device', {
  state: () => ({
    devices: new Map<string, SmartDevice>(),
    roomGroups: new Map<string, string[]>(),
    selectedRoom: 'living-room'
  }),

  getters: {
    currentRoomDevices: (state) => {
      const deviceIds = state.roomGroups.get(state.selectedRoom) || []
      return deviceIds.map(id => state.devices.get(id)).filter(Boolean)
    },
    
    onlineDeviceCount: (state) => {
      return Array.from(state.devices.values()).filter(device => device.online).length
    }
  },

  actions: {
    async fetchDevices() {
      try {
        const response = await uni.request({
          url: '/api/devices',
          method: 'GET'
        })
        
        if (response.statusCode === 200) {
          const devices: SmartDevice[] = response.data
          devices.forEach(device => {
            this.devices.set(device.id, device)
          })
        }
      } catch (error) {
        console.error('获取设备列表失败:', error)
      }
    },

    updateDeviceStatus(deviceId: string, status: Partial<SmartDevice>) {
      const device = this.devices.get(deviceId)
      if (device) {
        this.devices.set(deviceId, { ...device, ...status })
      }
    },

    async controlDevice(deviceId: string, command: string, params?: any) {
      try {
        const response = await uni.request({
          url: `/api/devices/${deviceId}/control`,
          method: 'POST',
          data: { command, params }
        })
        
        if (response.statusCode === 200) {
          this.updateDeviceStatus(deviceId, response.data)
        }
      } catch (error) {
        uni.showToast({
          title: '控制指令发送失败',
          icon: 'error'
        })
      }
    }
  }
})

2. 实时通信服务

基于MQTT实现设备状态实时同步:

// services/mqttService.ts
import mqtt from 'mqtt'

class MQTTService {
  private client: any = null
  private isConnected = false
  
  constructor() {
    this.init()
  }

  private init() {
    // #ifdef H5
    this.client = mqtt.connect('wss://mqtt.example.com:8884')
    // #endif
    
    // #ifdef APP-PLUS || MP-WEIXIN
    this.client = mqtt.connect('wx://mqtt.example.com:8883')
    // #endif

    this.client.on('connect', () => {
      this.isConnected = true
      console.log('MQTT连接成功')
      this.subscribe('device/status/#')
    })

    this.client.on('message', (topic: string, message: Buffer) => {
      this.handleMessage(topic, JSON.parse(message.toString()))
    })

    this.client.on('error', (error: any) => {
      console.error('MQTT连接错误:', error)
      this.isConnected = false
    })
  }

  private handleMessage(topic: string, message: any) {
    const topicParts = topic.split('/')
    const deviceId = topicParts[2]
    
    switch (topicParts[1]) {
      case 'status':
        useDeviceStore().updateDeviceStatus(deviceId, message)
        break
      case 'alert':
        this.handleDeviceAlert(deviceId, message)
        break
    }
  }

  private handleDeviceAlert(deviceId: string, alert: any) {
    uni.showModal({
      title: '设备告警',
      content: `设备 ${deviceId} 发生异常: ${alert.message}`,
      showCancel: false
    })
  }

  public publish(deviceId: string, command: string, params: any) {
    if (!this.isConnected) return
    
    const topic = `device/control/${deviceId}`
    this.client.publish(topic, JSON.stringify({ command, params }))
  }

  public subscribe(topic: string) {
    if (this.isConnected) {
      this.client.subscribe(topic)
    }
  }
}

export const mqttService = new MQTTService()

3. 场景模式管理

实现一键切换多种家居场景:

// components/scene-selector.vue
<template>
  <view class="scene-selector">
    <scroll-view scroll-x class="scene-scroll">
      <view 
        v-for="scene in scenes" 
        :key="scene.id"
        class="scene-item"
        :class="{ active: currentScene?.id === scene.id }"
        @click="switchScene(scene)"
      >
        <image :src="scene.icon" class="scene-icon"></image>
        <text class="scene-name">{{ scene.name }}</text>
      </view>
    </scroll-view>
  </view>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

interface Scene {
  id: string
  name: string
  icon: string
  devices: SceneDevice[]
}

interface SceneDevice {
  deviceId: string
  status: Record<string, any>
}

const scenes = ref<Scene[]>([
  {
    id: 'home',
    name: '回家模式',
    icon: '/static/scenes/home.png',
    devices: [
      { deviceId: 'light-1', status: { power: true, brightness: 80 } },
      { deviceId: 'thermostat-1', status: { temperature: 24 } }
    ]
  },
  {
    id: 'away',
    name: '离家模式',
    icon: '/static/scenes/away.png',
    devices: [
      { deviceId: 'light-1', status: { power: false } },
      { deviceId: 'security-1', status: { armed: true } }
    ]
  }
])

const currentScene = ref<Scene | null>(null)

const switchScene = async (scene: Scene) => {
  try {
    uni.showLoading({ title: '切换场景中...' })
    
    // 批量控制场景中的设备
    const promises = scene.devices.map(device => 
      mqttService.publish(device.deviceId, 'set', device.status)
    )
    
    await Promise.all(promises)
    currentScene.value = scene
    
    uni.showToast({ title: '场景切换成功' })
  } catch (error) {
    uni.showToast({ title: '场景切换失败', icon: 'error' })
  } finally {
    uni.hideLoading()
  }
}

onMounted(() => {
  // 默认选择回家模式
  currentScene.value = scenes.value[0]
})
</script>

4. 能耗统计图表

使用ECharts展示设备能耗数据:

// components/energy-chart.vue
<template>
  <view class="energy-chart">
    <ec-canvas ref="chartRef" canvas-id="energy-chart"></ec-canvas>
  </view>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'

const chartRef = ref()

const initChart = () => {
  const chart = echarts.init(chartRef.value, null, {
    width: uni.getSystemInfoSync().windowWidth - 40,
    height: 300
  })

  const option = {
    title: {
      text: '本月能耗统计',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      data: ['客厅', '卧室', '厨房', '卫生间'],
      bottom: 10
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '15%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: ['第1周', '第2周', '第3周', '第4周']
    },
    yAxis: {
      type: 'value',
      name: '能耗 (kWh)'
    },
    series: [
      {
        name: '客厅',
        type: 'bar',
        stack: 'total',
        data: [120, 132, 101, 134]
      },
      {
        name: '卧室',
        type: 'bar',
        stack: 'total',
        data: [220, 182, 191, 234]
      },
      {
        name: '厨房',
        type: 'bar',
        stack: 'total',
        data: [150, 232, 201, 154]
      },
      {
        name: '卫生间',
        type: 'bar',
        stack: 'total',
        data: [320, 332, 301, 334]
      }
    ]
  }

  chart.setOption(option)
}

onMounted(() => {
  setTimeout(initChart, 100)
})
</script>

性能优化策略

1. 设备状态缓存

// utils/deviceCache.ts
export class DeviceCache {
  private static instance: DeviceCache
  private cache = new Map<string, any>()
  private maxSize = 100
  
  static getInstance() {
    if (!DeviceCache.instance) {
      DeviceCache.instance = new DeviceCache()
    }
    return DeviceCache.instance
  }
  
  set(key: string, value: any, ttl: number = 300000) { // 5分钟默认过期
    if (this.cache.size >= this.maxSize) {
      this.evictOldest()
    }
    
    this.cache.set(key, {
      value,
      timestamp: Date.now(),
      ttl
    })
  }
  
  get(key: string) {
    const item = this.cache.get(key)
    if (!item) return null
    
    if (Date.now() - item.timestamp > item.ttl) {
      this.cache.delete(key)
      return null
    }
    
    return item.value
  }
  
  private evictOldest() {
    let oldestKey = ''
    let oldestTime = Infinity
    
    for (const [key, item] of this.cache) {
      if (item.timestamp < oldestTime) {
        oldestTime = item.timestamp
        oldestKey = key
      }
    }
    
    if (oldestKey) {
      this.cache.delete(oldestKey)
    }
  }
}

2. 请求防抖处理

// utils/debounce.ts
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: NodeJS.Timeout | null = null
  
  return (...args: Parameters<T>) => {
    if (timeout) clearTimeout(timeout)
    
    timeout = setTimeout(() => {
      func.apply(this, args)
    }, wait)
  }
}

// 在设备控制中使用
const debouncedControl = debounce((deviceId: string, command: string) => {
  mqttService.publish(deviceId, command)
}, 300)

多端适配方案

1. 平台特定功能处理

// utils/platform.ts
export class PlatformUtils {
  static isApp() {
    // #ifdef APP-PLUS
    return true
    // #endif
    return false
  }
  
  static isWechat() {
    // #ifdef MP-WEIXIN
    return true
    // #endif
    return false
  }
  
  static async requestNotificationPermission() {
    if (this.isApp()) {
      // App端推送权限申请
      const result = await uni.requestPushPermission()
      return result[0].type === 'accept'
    } else if (this.isWechat()) {
      // 小程序端订阅消息
      return await this.requestWechatSubscribe()
    }
    return true
  }
  
  static showShareMenu() {
    // #ifdef MP-WEIXIN
    uni.showShareMenu({
      withShareTicket: true,
      menus: ['shareAppMessage', 'shareTimeline']
    })
    // #endif
  }
}

部署与发布

1. 自动化构建配置

// package.json 构建脚本
{
  "scripts": {
    "build:app": "uni build --platform app",
    "build:mp-weixin": "uni build --platform mp-weixin",
    "build:h5": "uni build --platform h5",
    "build:all": "npm run build:app && npm run build:mp-weixin && npm run build:h5"
  }
}

2. 环境变量配置

// config/env.ts
const envConfig = {
  development: {
    baseUrl: 'http://localhost:3000',
    mqttHost: 'ws://localhost:8083'
  },
  production: {
    baseUrl: 'https://api.smarthome.com',
    mqttHost: 'wss://mqtt.smarthome.com'
  }
}

export const config = envConfig[process.env.NODE_ENV || 'development']

项目总结

技术亮点:

  • 架构设计:采用分层架构,清晰分离关注点
  • 实时通信:基于MQTT实现设备状态实时同步
  • 类型安全:全面使用TypeScript,提升代码质量
  • 性能优化:多级缓存、防抖处理、懒加载等优化手段
  • 多端适配:完善的平台差异处理方案

业务价值:

  • 一套代码同时覆盖App、小程序、Web三端
  • 降低开发和维护成本约60%
  • 提升用户体验和系统稳定性
  • 为后续功能扩展奠定良好基础

UniApp企业级实战:构建智能家居控制中心完整指南
收藏 (0) 打赏

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

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

淘吗网 uniapp UniApp企业级实战:构建智能家居控制中心完整指南 https://www.taomawang.com/web/uniapp/1209.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

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

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