UniApp跨平台智能家居控制App开发实战:物联网与多端适配 | 移动端IoT解决方案

2025-08-10 0 934

从硬件对接到UI设计的全链路物联网应用开发指南

一、智能家居App技术架构

主流智能家居方案技术对比:

技术方案 开发成本 设备兼容性 实时性
原生App开发 高(双端独立) 最佳
UniApp跨平台 低(一套代码) 良好 中高
小程序方案 最低 受限
Web应用

二、系统架构设计

1. 分层架构设计

设备层 → 通信层 → 业务逻辑层 → 状态管理层 → 界面层
    ↑          ↑             ↑              ↑
蓝牙/WiFi  协议适配器     设备控制逻辑     Vuex状态管理
            

2. 多端适配方案

统一代码库 → 条件编译 → 平台特性适配 → 多端打包
    ↑              ↑              ↑            ↑
公共组件      #ifdef MP-WEIXIN    API差异处理    H5/小程序/App

三、核心模块实现

1. 蓝牙设备通信模块

// bluetooth.js
class BluetoothManager {
  constructor() {
    this.devices = new Map()
    this.currentDevice = null
  }
  
  // 初始化蓝牙模块
  init() {
    return new Promise((resolve, reject) => {
      uni.openBluetoothAdapter({
        success: (res) => {
          this.startDiscovery()
          resolve(res)
        },
        fail: (err) => {
          console.error('蓝牙初始化失败:', err)
          reject(err)
        }
      })
    })
  }
  
  // 开始搜索设备
  startDiscovery() {
    uni.onBluetoothDeviceFound((devices) => {
      devices.forEach(device => {
        if (device.name && !this.devices.has(device.deviceId)) {
          this.devices.set(device.deviceId, device)
          uni.$emit('device-found', device)
        }
      })
    })
    
    uni.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: false,
      success: () => console.log('开始搜索设备'),
      fail: (err) => console.error('搜索失败:', err)
    })
  }
  
  // 连接设备
  connect(deviceId) {
    return new Promise((resolve, reject) => {
      uni.createBLEConnection({
        deviceId,
        success: (res) => {
          this.currentDevice = deviceId
          this.discoverServices(deviceId)
          resolve(res)
        },
        fail: reject
      })
    })
  }
  
  // 发现服务
  discoverServices(deviceId) {
    uni.getBLEDeviceServices({
      deviceId,
      success: (res) => {
        res.services.forEach(serviceId => {
          this.discoverCharacteristics(deviceId, serviceId)
        })
      }
    })
  }
}

2. 设备控制指令封装

// device-controller.js
export class DeviceController {
  static COMMANDS = {
    POWER_ON: [0x01, 0x01],
    POWER_OFF: [0x01, 0x00],
    SET_TEMP: [0x02],
    SET_HUMIDITY: [0x03]
  }

  static sendCommand(deviceId, serviceId, characteristicId, command, value) {
    let buffer
    switch (command) {
      case 'POWER_ON':
        buffer = new Uint8Array(this.COMMANDS.POWER_ON).buffer
        break
      case 'POWER_OFF':
        buffer = new Uint8Array(this.COMMANDS.POWER_OFF).buffer
        break
      case 'SET_TEMP':
        const tempCmd = [...this.COMMANDS.SET_TEMP, value]
        buffer = new Uint8Array(tempCmd).buffer
        break
      default:
        throw new Error('未知指令')
    }

    return new Promise((resolve, reject) => {
      uni.writeBLECharacteristicValue({
        deviceId,
        serviceId,
        characteristicId,
        value: buffer,
        success: resolve,
        fail: reject
      })
    })
  }

  static async togglePower(device, isOn) {
    try {
      const command = isOn ? 'POWER_ON' : 'POWER_OFF'
      await this.sendCommand(
        device.id,
        device.serviceId,
        device.characteristicId,
        command
      )
      return true
    } catch (err) {
      console.error('控制失败:', err)
      return false
    }
  }
}

四、高级功能实现

1. 场景模式自动化

// scene-manager.js
export class SceneManager {
  constructor() {
    this.scenes = uni.getStorageSync('saved_scenes') || []
  }
  
  addScene(scene) {
    this.scenes.push(scene)
    this._saveScenes()
  }
  
  executeScene(sceneId) {
    const scene = this.scenes.find(s => s.id === sceneId)
    if (!scene) return Promise.reject('场景不存在')
    
    const promises = scene.actions.map(action => {
      return DeviceController.sendCommand(
        action.deviceId,
        action.serviceId,
        action.characteristicId,
        action.command,
        action.value
      )
    })
    
    return Promise.all(promises)
  }
  
  createHomeScene() {
    return {
      id: 'goodnight',
      name: '晚安模式',
      actions: [
        {
          deviceId: 'light-001',
          command: 'POWER_OFF',
          serviceId: '00001234-0000-1000-8000-00805f9b34fb',
          characteristicId: '00001235-0000-1000-8000-00805f9b34fb'
        },
        {
          deviceId: 'ac-001',
          command: 'SET_TEMP',
          value: 26,
          serviceId: '00002234-0000-1000-8000-00805f9b34fb',
          characteristicId: '00002235-0000-1000-8000-00805f9b34fb'
        }
      ]
    }
  }
  
  _saveScenes() {
    uni.setStorageSync('saved_scenes', this.scenes)
  }
}

2. 设备状态同步策略

// state-sync.js
export class StateSyncManager {
  constructor() {
    this.listeners = new Map()
    this.syncInterval = null
  }
  
  startSync(devices) {
    // 实时监听状态变化
    devices.forEach(device => {
      this._setupNotification(device)
    })
    
    // 定时全量同步
    this.syncInterval = setInterval(() => {
      this._fullSync(devices)
    }, 60000)
  }
  
  stopSync() {
    clearInterval(this.syncInterval)
    this.listeners.forEach((listener, deviceId) => {
      uni.closeBLEConnection({ deviceId })
    })
  }
  
  _setupNotification(device) {
    uni.notifyBLECharacteristicValueChange({
      deviceId: device.id,
      serviceId: device.serviceId,
      characteristicId: device.notifyCharId,
      state: true,
    })
    
    uni.onBLECharacteristicValueChange((res) => {
      if (res.deviceId === device.id) {
        this._handleStateUpdate(device, res.value)
      }
    })
    
    this.listeners.set(device.id, true)
  }
  
  _handleStateUpdate(device, buffer) {
    const dataView = new DataView(buffer)
    const state = {
      power: dataView.getUint8(0),
      temperature: dataView.getUint8(1),
      humidity: dataView.getUint8(2)
    }
    
    uni.$emit(`device-state:${device.id}`, state)
  }
  
  _fullSync(devices) {
    devices.forEach(device => {
      uni.readBLECharacteristicValue({
        deviceId: device.id,
        serviceId: device.serviceId,
        characteristicId: device.readCharId,
        success: (res) => {
          this._handleStateUpdate(device, res.value)
        }
      })
    })
  }
}

五、多端适配方案

1. 平台差异化处理

// 设备能力检测
export function checkBluetoothSupport() {
  // #ifdef APP-PLUS
  return new Promise((resolve) => {
    plus.bluetooth.getBluetoothAdapterState({
      success: () => resolve(true),
      fail: () => resolve(false)
    })
  })
  // #endif
  
  // #ifdef MP-WEIXIN
  return new Promise((resolve) => {
    wx.getBluetoothAdapterState({
      success: () => resolve(true),
      fail: () => resolve(false)
    })
  })
  // #endif
  
  // #ifdef H5
  return Promise.resolve(!!navigator.bluetooth)
  // #endif
}

// UI适配示例
<template>
  <view class="device-card">
    <!-- 微信小程序使用开放能力 -->
    <!-- #ifdef MP-WEIXIN -->
    <button open-type="getPhoneNumber" @getphonenumber="bindPhone">
      绑定手机
    </button>
    <!-- #endif -->
    
    <!-- App端使用原生按钮 -->
    <!-- #ifdef APP-PLUS -->
    <button @click="requestPhonePermission">
      获取手机号
    </button>
    <!-- #endif -->
  </view>
</template>

2. 响应式布局方案

// 响应式混入
export const responsiveMixin = {
  data() {
    return {
      windowWidth: 375,
      deviceType: 'phone'
    }
  },
  created() {
    this.updateWindowSize()
    uni.onWindowResize(this.updateWindowSize)
  },
  methods: {
    updateWindowSize() {
      const { windowWidth, windowHeight } = uni.getSystemInfoSync()
      this.windowWidth = windowWidth
      
      // 根据宽度判断设备类型
      if (windowWidth >= 768) {
        this.deviceType = 'tablet'
      } else if (windowWidth >= 1024) {
        this.deviceType = 'desktop'
      } else {
        this.deviceType = 'phone'
      }
    }
  },
  computed: {
    isLandscape() {
      return this.windowWidth > this.windowHeight
    },
    colSpan() {
      return this.deviceType === 'phone' ? 24 : 12
    }
  }
}

// 使用示例
<template>
  <view :class="['layout', deviceType]">
    <view class="sidebar" v-if="deviceType !== 'phone'">
      <!-- 侧边栏内容 -->
    </view>
    <view class="main-content" :style="{width: deviceType === 'phone' ? '100%' : '75%'}">
      <!-- 主内容区 -->
    </view>
  </view>
</template>

六、实战案例:智能灯光控制

1. 灯光控制页面

<template>
  <view class="light-control">
    <device-header :device="currentDevice" />
    
    <view class="control-panel">
      <slider 
        :value="brightness" 
        @change="setBrightness" 
        min="0" 
        max="100" 
      />
      
      <color-picker 
        :value="color" 
        @change="setColor" 
      />
      
      <view class="presets">
        <button 
          v-for="preset in presets" 
          :key="preset.name"
          @click="applyPreset(preset)"
        >
          {{ preset.name }}
        </button>
      </view>
    </view>
  </view>
</template>

<script>
import { DeviceController } from '@/utils/device-controller'

export default {
  data() {
    return {
      currentDevice: null,
      brightness: 50,
      color: '#ffffff',
      presets: [
        { name: '阅读模式', brightness: 80, color: '#ffe08c' },
        { name: '影院模式', brightness: 30, color: '#ff0000' }
      ]
    }
  },
  onLoad(options) {
    this.currentDevice = JSON.parse(options.device)
    this.loadCurrentState()
  },
  methods: {
    async loadCurrentState() {
      const state = await DeviceController.getState(this.currentDevice.id)
      this.brightness = state.brightness
      this.color = `#${state.color.toString(16).padStart(6, '0')}`
    },
    async setBrightness(e) {
      this.brightness = e.detail.value
      await DeviceController.sendCommand(
        this.currentDevice.id,
        this.currentDevice.serviceId,
        this.currentDevice.characteristicId,
        'SET_BRIGHTNESS',
        this.brightness
      )
    },
    applyPreset(preset) {
      this.brightness = preset.brightness
      this.color = preset.color
      this.setBrightness({ detail: { value: preset.brightness } })
      this.setColor(preset.color)
    }
  }
}
</script>

2. 设备状态实时同步

// 在App.vue中初始化状态同步
export default {
  onLaunch() {
    const devices = uni.getStorageSync('connected_devices') || []
    if (devices.length > 0) {
      const syncManager = new StateSyncManager()
      syncManager.startSync(devices)
      
      // 监听全局状态变化
      devices.forEach(device => {
        uni.$on(`device-state:${device.id}`, (state) => {
          this.$store.dispatch('updateDeviceState', {
            deviceId: device.id,
            state
          })
        })
      })
    }
  }
}

// Vuex状态管理
const store = new Vuex.Store({
  state: {
    devices: {}
  },
  mutations: {
    UPDATE_DEVICE_STATE(state, { deviceId, state }) {
      Vue.set(state.devices, deviceId, {
        ...state.devices[deviceId],
        ...state
      })
    }
  },
  actions: {
    updateDeviceState({ commit }, payload) {
      commit('UPDATE_DEVICE_STATE', payload)
      
      // 如果当前正在查看该设备,触发页面更新
      const pages = getCurrentPages()
      const currentPage = pages[pages.length - 1]
      if (currentPage && currentPage.$vm.updateDeviceState) {
        currentPage.$vm.updateDeviceState(payload.state)
      }
    }
  }
})
UniApp跨平台智能家居控制App开发实战:物联网与多端适配 | 移动端IoT解决方案
收藏 (0) 打赏

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

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

淘吗网 uniapp UniApp跨平台智能家居控制App开发实战:物联网与多端适配 | 移动端IoT解决方案 https://www.taomawang.com/web/uniapp/791.html

常见问题

相关文章

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

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