【天猫精灵系列】天猫精灵 X1 + Home Assistant 智能家居示例:整合 AliGenie 服务

本来想自己写一个天猫精灵的 Skill,来融合自己的智能家居系统。但是发现天猫精灵的开发者平台 AliGenie, 要实现这样的功能比较难,需要自己:

  • 搭建一个外网可以访问的 Home Assistant
  • 编写相应的 Skill

第一步『搭建一个外网可以访问的 Home Assistant』,目前对我来说比较困难。第二步,则在开发者的钉钉群里,有一个叫『胡志远』的先驱已经完成了这个工作。由于这份代码没有外传出来,便发到我的 GitHub 上:

https://github.com/phodal/tmall-x1-home-assistant-example

步骤1:条件准备

在了解详细的配置之前,先让我们了解一下,天猫精灵智能家居开发的基本流程:

  1. 将 Raspberry Pi 映射到外网
  2. 编写相应的 OAuth 2.0 代码
  3. 编写相应的 Skill

步骤2:使用 ngrok 分享服务

以前我们可以通过 ADSL 配个端口映射,借此让本机服务在外网可以访问,但是现在这个方法几乎是不可能的。反正,我试了几个相应的服务都不行。因此,就需要自己搭建 Ngrok 服务。

在先前的《低廉的亚马逊 Amazon Alexa 与 Raspberry Pi、Ngrok 进行 家庭自动化灯开关》中,我们介绍了如何安装 ngrok 进行穿透:

1.下载相应的 zip 文件:

https://ngrok.com/download(PS:获取稳定的 ARM Linux 版)

2.使用 SCP 将其复制到您的 Raspbery Pi上。我喜欢使用 winSCP。你可以在这里找到它:

https://winscp.net/eng/download.php

3.解压:

unzip ngrok-stable-linux-arm.zip

4.运行:

sudo ./ngrok http 5000

步骤3:OAuth 授权(待续)

天猫精灵智能家居采用通用的OAuth2.0开放授权协议,可以让AILabs在不获取合作方用户名和密码的前提下,访问用户授权的资源,协议规范可以访问OAuth2.0官方网站:https://oauth.net/2/

其鉴权流程如下所示:

  1. AILabs在合作方开放平台注册一个应用,获取到相应的App id 和App secret
  2. AILabs 应用向合作方OAuth2.0服务发起一个授权请求
  3. 合作方OAuth2.0服务向用户展示一个授权页面,提醒用户AILabs应用需要获取用户的哪些信息
  4. 用户授权AILabs客户端应用后,可获得一个access_token
  5. 通过access_token,AILabs应用可以通过合作方开放的API访问用户授权的数据

步骤4:配置 AliGenie 服务

你可以从 https://github.com/phodal/tmall-x1-home-assistant-example 中获取一份相应的配置。

其 skill 如下所示:

{
  "name" : "HAtest",
  "invocationName" : "智能家居",
  "createTime" : 1502277802000,
  "image" : "https://g.alicdn.com/forest/ai-platform-web/4.0.83/img/default_domain.png",
  "shortDesc" : "",
  "longDesc" : "",
  "oldSamples" : [ ],
  "newSamples" : [ ],
  "category" : "智能家居",
  "accountLinkUrl" : "",
  "serviceProviders" : [ ],
  "privacyUrl" : "",
  "termsUrl" : "",
  "services" : [ {
    "name" : "switch_off",
    "description" : "关闭开关",
    "method" : "POST",
    "url" : "http://yourhomeassistantsite/api/services/switch/turn_off?api_password=yourpassword",
    "inputParams" : [ {
      "name" : "entity_id",
      "description" : "",
      "required" : true,
      "sample" : "switch.aircondition_sw"
    } ],
    "response" : "[\n  {\n    \"attributes\": {\n      \"No motion since\": 0,\n      \"battery_level\": 53,\n      \"device_class\": \"motion\",\n      \"friendly_name\": \"Hall motion2\"\n    },\n    \"entity_id\": \"binary_sensor.motion_sensor_158d00012353c3\",\n    \"last_changed\": \"2017-08-10T17:53:42.395085+00:00\",\n    \"last_updated\": \"2017-08-10T17:53:42.395085+00:00\",\n    \"state\": \"off\"\n  },\n  {\n    \"attributes\": {\n      \"assumed_state\": false,\n      \"entity_id\": [\n        \"binary_sensor.motion_sensor_158d000171b983\",\n        \"binary_sensor.motion_sensor_158d00012353c3\",\n        \"binary_sensor.motion_sensor_158d0001a2b1df\"\n      ],\n      \"friendly_name\": \"Hall\",\n      \"order\": 1\n    },\n    \"entity_id\": \"group.hall\",\n    \"last_changed\": \"2017-08-10T17:53:42.413496+00:00\",\n    \"last_updated\": \"2017-08-10T17:53:42.413496+00:00\",\n    \"state\": \"off\"\n  },\n  {\n    \"attributes\": {\n      \"assumed_state\": true,\n      \"auto\": true,\n      \"entity_id\": [\n        \"switch.pcserver\",\n        \"switch.aircondition_sw\"\n      ],\n      \"friendly_name\": \"all switches\",\n      \"hidden\": true,\n      \"order\": 8\n    },\n    \"entity_id\": \"group.all_switches\",\n    \"last_changed\": \"2017-08-10T17:53:42.570712+00:00\",\n    \"last_updated\": \"2017-08-10T17:53:42.570712+00:00\",\n    \"state\": \"off\"\n  },\n  {\n    \"attributes\": {\n      \"assumed_state\": true,\n      \"friendly_name\": \"空调\"\n    },\n    \"entity_id\": \"switch.aircondition_sw\",\n    \"last_changed\": \"2017-08-10T17:53:42.564282+00:00\",\n    \"last_updated\": \"2017-08-10T17:53:42.564282+00:00\",\n    \"state\": \"off\"\n  }\n]",
    "extendInfo" : ""
  }, {
    "name" : "switch_on",
    "description" : "打开开关",
    "method" : "POST",
    "url" : "http://yourhomeassistantsite/api/services/switch/turn_on?api_password=yourpassword",
    "inputParams" : [ {
      "name" : "entity_id",
      "description" : "设备ID",
      "required" : true,
      "sample" : "switch.aircondition_sw"
    } ],
    "response" : "[\n  {\n    \"attributes\": {\n      \"assumed_state\": true,\n      \"auto\": true,\n      \"entity_id\": [\n        \"switch.pcserver\",\n        \"switch.aircondition_sw\"\n      ],\n      \"friendly_name\": \"all switches\",\n      \"hidden\": true,\n      \"order\": 8\n    },\n    \"entity_id\": \"group.all_switches\",\n    \"last_changed\": \"2017-08-10T17:49:40.546452+00:00\",\n    \"last_updated\": \"2017-08-10T17:49:40.546452+00:00\",\n    \"state\": \"on\"\n  },\n  {\n    \"attributes\": {\n      \"assumed_state\": true,\n      \"friendly_name\": \"空调\"\n    },\n    \"entity_id\": \"switch.aircondition_sw\",\n    \"last_changed\": \"2017-08-10T17:49:40.527380+00:00\",\n    \"last_updated\": \"2017-08-10T17:49:40.527380+00:00\",\n    \"state\": \"on\"\n  }\n]",
    "extendInfo" : ""
  }],
  "smalltalks" : [ ],
  "nluSolution" : "SFIC",
  "instructions" : null,
  "settingLinkUrl" : null,
  "copyrightUrl" : "",
  "qualificationUrl" : "",
  "hasConfirmCompliance" : null
}

对应的打开设备,配置是:

{
  "name" : "关闭设备",
  "description" : "",
  "type" : "",
  "useWebhook" : false,
  "precedingIntents" : [ ],
  "userSays" : [ {
    "text" : "关闭@{device_name}",
    "labelledText" : "关闭@{device_name}",
    "type" : "SAMPLE",
    "typeUI" : "TEMPLATE"
  } ],
  "followUps" : [ ],
  "parameters" : [ {
    "required" : true,
    "name" : "device_name",
    "entity" : "device_name",
    "prompts" : [ ],
    "replyTemplates" : [ ],
    "valueReference" : "device_name",
    "defaultValue" : ""
  } ],
  "responses" : [ {
    "name" : "reply_ok",
    "speeches" : [ "关闭@{device_name} 完成" ],
    "parameters" : [ ]
  }, {
    "name" : "reply_failed",
    "speeches" : [ "关闭@{device_name} 失败" ],
    "parameters" : [ ]
  } ],
  "executeLogic" : {
    "type" : "XML_CONFIG",
    "content" : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<intent xmlns=\"http://www.da.alibaba.com/coin/ide/task/template\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.da.alibaba.com/coin/ide/task/template ../template/intent_config.xsd\">\n  <params>\n    <param name=\"device_name\" type=\"String\">Request.getSlotValue(\"device_name\")</param>\n  </params>\n  <process-branches>\n    <branch name=\"dowork\">\n      <params />\n      <process-branches>\n        <branch name=\"desk_lamp\">\n          <params>\n            <param name=\"var_desk_lamp\" type=\"Map(String,String)\">Map.of(\"entity_id\",\"light.desk_lamp\")</param>\n            <param name=\"Result\" type=\"String\">Service.request(\"light_off\",var_desk_lamp)</param>\n          </params>\n          <process-switch>\n            <default reply-node=\"reply_ok\" />\n          </process-switch>\n        </branch>\n        <branch name=\"air_condition\">\n          <params>\n            <param name=\"var_air_condition\" type=\"Map(String,String)\">Map.of(\"entity_id\",\"switch.aircondition_sw\")</param>\n            <param name=\"Result\" type=\"String\">Service.request(\"switch_off\",var_air_condition)</param>\n          </params>\n          <process-switch>\n            <default reply-node=\"reply_ok\" />\n          </process-switch>\n        </branch>\n      </process-branches>\n      <process-switch>\n        <case branch-name=\"desk_lamp\">device_name==\"台灯\"</case>\n        <case branch-name=\"air_condition\">device_name==\"空调\"</case>\n        <default reply-node=\"reply_failed\" />\n      </process-switch>\n    </branch>\n  </process-branches>\n  <process-switch>\n    <case branch-name=\"dowork\">device_name!=null</case>\n    <default reply-node=\"reply_failed\" />\n  </process-switch>\n</intent>\n\n",
    "jsonContent" : null,
    "args" : [ "$device_name=空调" ]
  },
  "webhook" : {
    "url" : "",
    "headers" : [ ]
  }
}

更详细的配置可以见 GitHub: https://github.com/phodal/tmall-x1-home-assistant-example

步骤5:完成

待续

2 人评价

观光\评论区

Copyright © 2017 玩点什么. All Rights Reserved.