taro创建微信小程序通过机智云sdk实现控制设备

370 阅读5分钟

最近公司业务需要一个小程序去控制硬件设备,利用机智云sdk做了一个小demo,效果还不错,微信小程序控制效果比react native的要好

1. taro创建小程序

这个地方之前一直踩坑,用最新版的脚手架有问题,会跑不起来,后来咨询了群里一个大佬,换了taro3.6版本就ok了,执行命令 npx @tarojs/cli@3.6 init myApp 然后就得到了这样的项目目录, image.png

2. 接下来我们下载机智云小程序sdk

  1. 执行命令yarn add mini_program_gizwits_sdk
  2. 为了后续开发方便,这里就简单的封装一下机智云sdk

3. 封装机智云sdk

1. 这里不需要状态,所以直接在utils里封装,避免在状态管理时可能出现的循环引用问题

image.png

2.新建constant.ts来存放一些常量,我这里把产品需要的id和screct放这,当然也可能放环境变量中

export const ANDROID_INFO = {
  androidId: "91732**********3",
  androidSecret: "a4e*******d8d78d74",
};
export const PRODUCTION_INFO = {
  productKey: "e3****bb6",
  productSecret: "a5***46fa24",
};

3.然后我们创建globalData.ts文件

在里面初始化机智云sdk,上代码

import GizwitsSdk from 'mini_program_gizwits_sdk'
import { ANDROID_INFO, PRODUCTION_INFO } from './constants';

const gizConfig = {
  appID: ANDROID_INFO.androidId,
  appSecret: ANDROID_INFO.androidSecret,
  productInfo: [
    {
      productKey: PRODUCTION_INFO.productKey,
      productSecret: PRODUCTION_INFO.productSecret,
    },
  ],
  uid: "giz_uid", // 必填
  token: "giz_token", // 必填
};

let gizSdk: GizwitsSdk | null = null;
const initSdk =  async () => {
     gizSdk = await new GizwitsSdk(gizConfig);
    console.log("giz", gizSdk);
    return gizSdk;
}

export {
    gizSdk,
    initSdk
}

4. 我这里只做蓝牙设备连接

所以不需要登录,所以uid和token也不用填,然后我们可以在用户进入小程序时初始化机智云sdk,更改app.ts代码

import { PropsWithChildren } from 'react'
import { useLaunch } from '@tarojs/taro'
import 'taro-ui/dist/style/index.scss' // 全局引入一次即可
import './app.less'
import {initSdk} from './utils/globalData'

function App({ children }: PropsWithChildren<any>) {

  useLaunch(() => {
    initSdk() // 初始化SDK
    console.log('App launched.')
  })

  // children 是将要会渲染的页面
  return children
}

export default App

5. 接着要初始化蓝牙设备

这里我使用了zustand进行状态管理,由于后续需要对设备列表进行管理,所以这段代码也写进入了,新建文件 store/useDeviceStore.ts

import { create } from "zustand";
import GizwitsSdk from 'mini_program_gizwits_sdk'
import {gizSdk} from '../utils/globalData'

6. 创建store,初始化蓝牙

以及订阅,取消订阅等相关功能,initBle后来想了一下,还是放到utils里比较好

import { create } from "zustand";
import GizwitsSdk from 'mini_program_gizwits_sdk'
import {gizSdk} from '../utils/globalData'


interface IDeviceStore {
  devices: Array<any>;
  setDevices: (devices: Array<any>) => void;
  gizSdk: GizwitsSdk | null;
  scanning: boolean;
  curDeivce: any | null;
  setCurDevice: (curDeivce: any) => void;
  setScanning: (scanning: boolean) => void;
  initBle: () => Promise<void>;
  scanBle: () => Promise<void>;
  stopScan: () => Promise<void>;
  removeOnScanListChange: () => void;
  subscribe: (device?) => any;
  unSubscribe: (device?) => any;
}

const useDeviceStore = create<IDeviceStore>((set,get) => ({
  scanning: false,
  setScanning: (scanning: boolean) => set({ scanning }),
  curDeivce: null,
  setCurDevice: (curDeivce: any) => set({ curDeivce }),
  devices: [],
  setDevices: (devices) => set({ devices }),
  gizSdk: gizSdk,
  initBle: async () => {
    const res = await gizSdk.initBle();
    console.log('res',res);
     gizSdk.addEventListener("onScanListChange", (devices) => {
      console.log("扫描列表发生变化", devices);
      set({ devices });
    });
    gizSdk.addEventListener("GizDeviceAttrsNotifications", (res) => {
      // console.log("====设备状态发生变化====", device, data);
    });
    gizSdk.addEventListener("GizDeviceStatusNotifications", (e) => {
      // console.log("设备在线状态变化", e);
    });
    gizSdk.addEventListener("GizDeviceListNotifications", (deviceList) => {
      // console.log("列表发生变化", deviceList);
    });
    
  },
  /**
   * 扫描蓝牙
   */
  scanBle : async () => {
    const data = await gizSdk.scanBleDevice();
    set({scanning: true})
    console.log("开始扫描", data);
  },
  /**
   * 停止扫描
   */
  stopScan: async () => {
    const data = await gizSdk.stopScanBleDevice();
    set({scanning: false})
    console.log("停止扫描", data);
  },
  /**
   * 移除监听
   */
  removeOnScanListChange:() => {
      gizSdk.removeEventListener("onScanListChange")
  },
  /**
   * 订阅设备
   * @returns 
   */
  subscribe:(device = get().curDeivce) => {
    return gizSdk.subscribe(device)
  },
  /**
   * 取消订阅设备
   * @returns 
   */
  unSubscribe:(device = get().curDeivce) => {
    return gizSdk.unSubscribe(device)
  }
}))

export default useDeviceStore;

7. 然后再创建一个页面或者组件都可以,就可以引入了

import { useEffect, useState } from "react";
import { View, Text } from "@tarojs/components";
import { AtButton, AtList, AtListItem, AtSlider } from "taro-ui";
import "./index.less";
import useDeviceStore from "@/store/useDeviceStore";
import { setNightLightBrightness, toggleNightLight, togglePower, toggleStartStatus } from "@/utils/deviceAction";
import { toastTip } from "@/utils/toastUtil";

function DeviceConnection() {
  const {
    curDeivce,
    devices,
    setCurDevice,
    scanning,
    scanBle,
    initBle,
    removeOnScanListChange,
    subscribe,
    unSubscribe,
    stopScan,
  } = useDeviceStore();
  const [stepVal,setStepVal] = useState(0)

  useEffect(() => {
    // 初始化蓝牙
    initBle();
    // 清理函数
    return () => {
      removeOnScanListChange();
    };
  }, []);

  // 6. 渲染UI
  return (
    <View style={{ padding: "10px 20px" }}>
      {/* 开关扫描蓝牙 */}
      <AtButton
        circle
        type="primary"
        onClick={scanBle}
        disabled={scanning}
        className="scan-button"
      >
        {scanning ? "Scanning..." : "Start Scan"}
      </AtButton>
      <AtButton
        type="secondary"
        circle
        className="scan-button"
        onClick={stopScan}
        disabled={!scanning}
      >
        Stop Scan
      </AtButton>
      <AtButton
      className="scan-button"
        type="primary"
        circle
        onClick={() => {
          const res = unSubscribe(curDeivce);
          toastTip({
            success: res.success,
            successMsg: "取消订阅成功",
            errorMsg: "取消订阅失败",
          });
        }}
      >
        取消订阅
      </AtButton>
      <AtButton
       className="scan-button"
        type="primary"
        circle
        onClick={async() => {
          const res = await toggleStartStatus(curDeivce,true);
          toastTip({
            success: res.success,
            successMsg: "启动设备成功",
            errorMsg: "启动设备失败",
          });
        }}
      >
        启动设备
      </AtButton>
      <AtButton
       className="scan-button"
        type="secondary"
        circle
        onClick={async() => {
          const res = await toggleStartStatus(curDeivce,false);
          toastTip({
            success: res.success,
            successMsg: "关闭设备成功",
            errorMsg: "关闭设备失败",
          });
        }}
      >
        关闭设备
      </AtButton>
      <View style={{ marginTop: "20px", marginBottom: "20px" }}>
        设备总数:{devices.length}
      </View>
      {/* 设备列表 */}
      <View>
        <Text>设备列表</Text>
        <AtList>
          {devices.map((device: any) => (
            <AtListItem
              title={device.mac}
              onClick={async () => {
                setCurDevice(device);
                const res = await subscribe();
                toastTip({
                  success: res.success,
                  successMsg: "订阅成功",
                  errorMsg: "订阅失败",
                });
              }}
            />
          ))}
        </AtList>
      </View>
      {/* 夜灯控制 */}
      <View style={{ display: "flex", marginTop: "20px" }}>
        <AtButton
          type="primary"
          onClick={() => {
            console.log("夜灯开");
            toggleNightLight(curDeivce, true);
          }}
        >
          夜灯开
        </AtButton>
        <AtButton
          type="secondary"
          onClick={() => {
            console.log("夜灯关");
            toggleNightLight(curDeivce, false);
          }}
        >
          夜灯关
        </AtButton>
      </View>
      {/* 夜灯亮度 */}
      <AtSlider max={100} min={0} step={1} blockColor='#4285F4' value={stepVal} showValue onChange={async (value) => {
        const res = await setNightLightBrightness(curDeivce, value); //
        setStepVal(value)
        console.log("亮度", value,res);
      }}/>
      {/* 电源控制 */}
      <View
        style={{ display: "flex", marginTop: "20px", marginBottom: "20px" }}
      >
        <AtButton
          type="primary"
          onClick={() => {
            console.log("电源开");
            togglePower(curDeivce, true);
          }}
        >
          电源开
        </AtButton>
        <AtButton
          type="secondary"
          onClick={() => {
            console.log("电源关");
            togglePower(curDeivce, false);
          }}
        >
          电源关
        </AtButton>
      </View>
    </View>
  );
}

export default DeviceConnection;

4. 总结

差不多就是这些,我这里使用了taroui,使用自带的ui也可以,页面没有做太多的样式调整,大概就是,一个按钮点击开始扫描,扫描附近蓝牙,前提要打开手机的蓝牙和定位权限,然后扫到相应设备会出现在设备列表中,点击就能进行连接订阅,提示连接成功,后面就可以愉快地控制设备了,进行灯光开关,调节,电源开关等

注:这里的设备必须是机智云后台创建的对应的产品硬件,也就是前文所说的配置文件里的pk和ps要和机智云后台pk和ps对应,这样的设备才能扫描到连接成功,别的设备不行