構建本身的 Smart Life 私有云(一)-> 破解塗鴉智能插座

博客搬遷至https://blog.wangjiegulu.comhtml

RSS訂閱:https://blog.wangjiegulu.com/feed.xmllinux

原文連接https://blog.wangjiegulu.com/2018/09/26/private-smart-life-cloud-a--hack-tuya-smart-plug/android

構建本身的 Smart Life 私有云(一)-> 破解塗鴉智能插座

本系列文章的目標是經過本身搭建的私有云IFTTTSlackGoogle Assistant塗鴉智能圖靈機器人等等第三方服務,構建本身的「智能生活」基本的框架。ios

前段時間我在京東上購買了一個塗鴉智能的插座。塗鴉智能是一個把產品智能化的一個平臺,從軟件和硬件和雲端上面提供給廠商一個一站式人工智能物聯網的解決方案(確實不是塗鴉智能的廣告- -.)。因此嚴格來講應該是「鵲起」基於塗鴉解決方案所開發出來的一個產品,基於塗鴉提供的一切規範,全部軟硬雲端的開發規範均可以在官網無權限限制地查看到(https://docs.tuya.com/cn/web

初試插座

我首先下載了塗鴉的官方app:Smart Life,註冊登陸、智能配對插座,經過手機控制插座開關,一切順利,還支持設置定時開關和 Schedule,並且手機控制到插座的反應十分靈敏(看樣子應該使用了長連接)。算法

破解插座

固然使用官方 app 顯然是沒法知足咱們需求,因此查看官方文檔,發現文檔中的 tuya.m.device.dp.publish 接口正好能夠知足咱們的需求。json

經過接入指南,咱們能夠知道接入方式提供了兩種:api

  1. MQTT,果真有使用長連接(app端默認用的應該就是長連接了)
  2. https,這個目前比較適合咱們,暫時無論延遲怎麼樣,先選擇用 https 來驗證控制的可行性

下面有列出通用的參數:session

參數名稱 參數類型 是否必須 是否簽名 參數描述
a String API名稱
v String API接口版本
sid String 用戶登陸受權成功後,ATOP頒發給應用的用戶session
time String 時間戳,格式爲數字,大小到秒非毫秒,時區爲標準時區,例如:1458010495。API服務端容許客戶端請求最大時間偏差爲540分鐘。
sign String API輸入參數簽名結果,簽名算法參照下面的介紹
clientId String 用戶的APPID(注各平臺不同如:ios、android、云云對接的id都不同)
lang String APP的語言,如"en",「zh_cn」,錯誤信息根據語言自動翻譯
ttid String APP渠道或雲端渠道,如公司名,用於數據分析跟蹤
os String 手機操做系統,如"Android",「ios」,雲端能夠寫linux或寫公司名

上述咱們沒法拿到的是哪些呢?app

  • sid:登陸後天然就能拿到了
  • sign:全部參數都有了,那sign天然能算出來(文檔下面有sign算法)
  • clientId:這玩意就比較麻煩了,至關於一個apiKey
  • ttid:這個非必須,暫時能夠無論

因此只要拿到 clientId,咱們就能

那怎麼去拿到 clientId 呢?反編譯試試(你們應該都知道怎麼作),class.dex 有點多,最後拿到了

  • client_id:8qp5cfk*******3mpmc3(這個打碼了)
  • app_secret:g75ktcvsae8**********e95j738tawg(一樣打碼)

拿到登陸 token

打開 Postman按照文檔 POST countryCodemobile 能夠獲得 token:

{
    "api":"tuya.m.user.mobile.token.get",
    "result":{
        "exponent":"3",
        "pExponent":"...",
        "publicKey":"...",
        "token":"..."
    },
    "status":"ok",
    "success":true
}

經過 token 進行登陸

{
    "countryCode":"86",
    "mobile":"11745678923",
    "passwd":"根據獲取token接口返回的公鑰對 md5(明文密碼) 進行rsa加密",
    "token":"126bb7570dcae343980b0607e6b35084",
    "ifencrypt":1
}

根據登陸接口的文檔,密碼須要使用 gettoken 接口返回的公鑰對 md5以後的密碼進行 rsa 加密,apk 反編譯以後代碼能夠徹底看到,直接拷貝便可

登陸完以後,就能夠拿到具體的 sid (sessionId)了,有了 sessionId,就能夠去對插座進行下發指令。

插座下發指令

一樣根據下發指令接口文檔

{
    "devId": "002yt001sf000000sfV3",
    "dps": {
       "1":1,
       "2":5
    }
}

能夠看到,POST 的數據除了 sid,還須要 devId 和 dps

dps 能夠參考這裏的文檔:https://fchelp.cloud.alipay.com/queryArticleContent.htm?tntInstId=WRNQWLCN&articleId=89429815&helpCode=SCE_00000019

咱們要控制插座開關的話,那就使用 {"1": true} / {"1": false} 便可

那 devId 呢?它表明我添加設備 id,那我怎麼知道我剛給在 Smart Life 上添加的插座 id 呢?抓包試試(你們應該都知道),經過抓包,咱們能夠很容易拿到全部你綁定的 devId。

至此,咱們能夠經過 http 來控制插座的開關了。

搭建本身的私有云項目

我爲本身的項目取名爲 Angelia,安革利亞,古希臘神話人物之一,爲「消息女神」。

搭建 web 項目,新建 AngeliaController

@RestController
@RequestMapping("/angelia")
@Configuration
class AngeliaController {
    // ...
}

增長 Tuya 的配置文件:tuyaclient.properties

tuya.client.client_id=xxxxxx
tuya.client.app_secret=xxxxxx
tuya.client.ttid=xxxxxx
tuya.client.v=1.0
tuya.client.base_url=https://a1.tuyacn.com/api.json

# smart life app 登陸手機號
tuya.client.user_mobile=18511111111
tuya.client.user_country_code=86
tuya.client.user_rsa_encrypted_passwd=xxx

# 設備信息
tuya.client.lang=en
tuya.client.os=Android
tuya.client.os_system=9
tuya.client.time_zone_id=Asia/Shanghai
tuya.client.platform=Pixel
tuya.client.sdk_version=2.6.4
tuya.client.app_version=3.4.3
tuya.client.app_rn_version=5.6
# 設備 id,模擬手機的 deviceId
tuya.client.device_id=e507510a0288accd7******
tuya.client.imei=xxxxxx
tuya.client.imsi=xxxxxx

# 智能設備,能夠多個:別名|id|dpid,別名|id|dpid,別名|id|dpid
# dpid: 1. 開關;4.rgb;5. 檔位;6. 溫度;15. 紅外數據
tuya.client.dev_ids=[\
  {\
    "aliasList": ["plug a", "plug 1", "插座a", "洗手間總電源"],\
    "devId": "111222333",\
    "dpId": "1"\
  },\
  {\
    "aliasList": ["plug b", "plug 2", "插座b", "臥室總電源"],\
    "devId": "12341234",\
    "dpId": "1"\
  },\
  // ...
]

以上,除了剛剛的那些必要的參數之類,在這個配置文件裏面還增長了對全部設備的配置,每一個設備對應它的 dpId,devId,還有別名(控制的時候不可能直接說 「devId 爲 a1d2f32a1d2f32 的插座關掉」,而是說 「關掉插座1」 / 「關掉洗手間總電源」等等),每一個設備別名能夠有多個。

好了,回到 AngeliaController,新增一個接口:

/**
* 插座控制接口
*/
@Autowired
lateinit var tuyaClientService: TuyaClientService

@Autowired
lateinit var tuyaClientProperties: TuyaClientProperties

@PostMapping("/control/plug")
fun controlPlug(@RequestBody request: PlugRequestVo): JSONObject {
   val dev = tuyaClientProperties.findDev(request.alias)
   return try {
       if (null == dev) {
           JsonResult.error("Device named ${request.alias} is not found")
       } else {
           tuyaClientService.controlPlug(dev.devId, request.turnOn)
           JsonResult.success()
       }
   } catch (e: Exception) {
       JsonResult.error(e.message)
   }
}

data class PlugRequestVo(
     val alias: String,
     val turnOn: Boolean
)

以上,該接口接收別名(alias)和開閉狀態(turnOn)兩個入參。首先,經過請求的別名去 properties 查找設備,若是找到,則經過 tuyaClientServicecontrolPlug 方法來下發指令,controlPlug 的實現就是剛剛上面說的 獲取登陸接口-登陸-下發指令幾步。

構建、部署,經過 Postman 訪問 http://localhost:xxxxx/angelia/control/plug, body 設置爲 {"alias": "臥室總電源", "turnOn": true},插座便可打開。

相關文章
相關標籤/搜索