从硬件对接到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)
}
}
}
})