温馨提示: 这篇文章于715天前编写,现在可能不再适用或落后.

前言

实现效果展示

https://www.bilibili.com/video/BV1Ca411e77B/

开发语言

  • Golang【这里开发用的golang,也可以自行选择】
  • TypeScript【sdk使用ts,也可以自行使用其他sdk语言】

需要准备的设备

  • 一台树莓派或长期开启的小型服务器或支持ssh的路由器【以下简称服务器】

唤醒方案

  • 通过局域网内WOL网络唤醒实现开机

休眠方案

  • 通过内置Windows系统服务提供API实现休眠

实现流程

  • 1.在【服务器】上运行编写好的wol唤醒程序,通过目标电脑的mac地址和网卡名称远程唤醒。
  • 2.在【笔记本】上安装编写好的Windows服务,通过【服务器】调用接口实现。
  • 3.在「休眠」和「唤醒」都处理好之后,就可以开始接入【米家】了,这里使用【点灯科技】提供的米家物联网平台实现。
  • 4.在点灯科技app上创建独立设备,名称设置为【电脑】,获取秘钥,然后在开发文档中下载nodejs-sdk,找到例子中的miot文件夹,找到开关的例子,填写设备秘钥。
  • 5.然后ts-node运行对应例子文件代码,然后对着小爱同学说【打开电脑】这样当前启动的代码就能收到消息,然后根据收到的消息状态,去调用之前的休眠、唤醒接口就可以实现开关机操作了。
  • 6.当nodejs例子连接对应的点灯独立设备成功后,会在点灯APP上显示在线状态,这时候就可以在米家->我的->其他平台设备->添加点灯科技账号->同步设备这时候就会有你创建的独立设备了,如果米家同步后没有你的设备,请检查你的设备是否在线、使用的SDK是否是miot下的(重要)其他文档请参考点灯开放文档
  • 7.如果你想要通过外网接口来访问接口的话,请自行使用frp映射端口到外网实现

自行编译 Windows休眠服务 - Golang

https://github.com/balrogsxt/wol-api

#安装依赖
go mod tidy
#构建当前平台
go build
#或使用make构建linux平台(一般都放linux吧。。)
make build-linux

如果接口出现没有权限请设置开机运行命令

sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"

自行编译 Wol唤醒以及调用休眠的HTTP接口服务

https://github.com/balrogsxt/win-sleep-service

服务使用方法

#安装依赖
go mod tidy
#构建项目为当前平台,如果是mac请自行交叉编译成windows
go build
#停止服务
xt-service.exe stop
#卸载服务
xt-service.exe uninstall
#安装服务
xt-service.exe install
#启动服务
xt-service.exe start
############
#日志文件会出现在: C:\Windows\System32\Logs\

接入点灯SDK - TypeScript

安装sdk 【官方提供的node-sdk部分代码是有问题的,请参考底部修改,只针对开关设备,其他设备没试过】

npm i -g ts-node
git clone https://github.com/blinker-iot/blinker-js.git
cd blinker-js
npm i

编写独立设备代码

/**
 * 电脑开关
 */
//请自行调整
import { BlinkerDevice } from './../lib/blinker'; 
import { Miot, VA_TYPE } from './../lib/voice-assistant';
import axios from 'axios';

let device = new BlinkerDevice('填写点灯SDK的设备资料');

let miot = device.addVoiceAssistant(new Miot(VA_TYPE.OUTLET));
device.ready().then(() => {
    // 米家电源状态改变
    miot.powerChange.subscribe(async message => {
        switch (message.data.set.pState) {
            case 'true':
                if(await WindowsAPI.open()){
                    message.power('true').update();
                }
                break;
            case 'false':
                if(await WindowsAPI.close()){
                    message.power('false').update();
                }
                break;
            default:
                break;
        }
    })
    //由于只处理米家设备,不处理点灯那边app操作,所以这下面都没管它
    device.dataRead.subscribe(message => {
          
    })

    device.builtinSwitch.change.subscribe(message => {
    
    })

});

//基于本地实现的Http接口实现唤醒和休眠功能
class WindowsAPI{
    private static url:string = "http://127.0.0.1:8089";//长期启动的那台设备上的接口
    private static token:string = "";//令牌

    //唤醒电脑
    public static async open(){
        let params = "isForce=true";
        return axios.post(`${this.url}/windows/wol_call`,params,{
            headers:{
                token:this.token
            }
        }).then(r=>{
            console.error('唤醒电脑成功: ',r.data);
            return true;
        }).catch(r=>{
            console.error('唤醒电脑失败: ',r);
            return false;
        });
    }

    //休眠电脑
    public static async close(){
        return axios.post(`${this.url}/windows/sleep_call`,"",{
            headers:{
                token:this.token
            }
        }).then(r=>{
            console.error('休眠电脑成功: ',r.data);
            return true;
        }).catch(r=>{
            console.error('休眠电脑失败: ',r);
            return false;
        });
    }

}

遇到的坑 - SDK修改部分

点灯科技Nodejs-sdk的BUG

  • 1.接收MIOT消息时,无法走到订阅代码部分。

    //找到文件lib/voice-assistant.ts,VoiceAssistant类中90行左右的listen事件,替换成以下代码
    listen() {
          this.device.mqttClient.on('message', (topic, message) => {
              if (topic.indexOf(this.subTopic.substr(0, this.subTopic.length - 1)) > -1) {
                  let data;
                  let fromDevice;
                  let messageId;
                  try {
                      let messageString = u8aToString(message)
                      let messageObject = JSON.parse(messageString);
                      //判断是否data里面有form
                      data = messageObject.data;
                      if(typeof(data.from) != 'undefined'){
                          fromDevice = data.from;
                      }else{
                          fromDevice = messageObject.fromDevice;
                      }
                      this.targetDevice = fromDevice
                      messageId = topic.split('/')[6]
                      vaLog(data, `${this.vaName}>device`)
                  } catch (error) {
                      console.log(error);
                  }
                  // console.log('收到MQTT订阅消息: ',fromDevice,this.vaName,topic)
                  if (fromDevice == this.vaName){
                      this.processData(messageId, data)
                  }
              }
          })
      }
    
  • 2.上报米家状态时,格式错误,导致小爱同学提示【操作的设备出错了】

//找到文件lib/voice-assistant.ts,200行左右VaMessage类中的update方法,替换成以下代码
update() {
        let messageId = {messageId:this.data.messageId}
        this.response = Object.assign(this.response,messageId)
        let responseStr = JSON.stringify(this.response)
        let data = `{ "fromDevice": "${this.device.config.deviceName}", "toDevice": "ServerReceiver", "data": ${responseStr}, "deviceType": "vAssistant"}`
        this.device.mqttClient.publish(this.voiceAssistant.pubTopic, data)
        vaLog(data, `device>${this.voiceAssistant.vaName}`)
    }

阁下需要登录后才可以查看评论哦~

随便看看

    载入中...

    正在准备穿越次元,阁下请稍等片刻~