構建自己的 Smart Life 私有云(二)-> 連通 IFTTT & Slack
構建自己的 Smart Life 私有云(二)-> 連通 IFTTT & Slack
上一篇我們破解了塗鴉的插座,搭建了自己的 web 服務,暴露了一個介面來控制插座的開關。這篇我們配合 IFTTT、Slack 來控制插座:
- 說 "OK Google" 喚醒 Google Assistant,然後說 “幫我開啟臥室的電源”,最後插座被開啟。
- 建立 Slack 機器人
Angelia
,對它發訊息“幫我開啟臥室的電源”,然後插座開啟,Angelia
回覆說 “好的,已經開啟”。 - 通過 Slack 機器人
Angelia
,傳送 Slash Commands,開啟關閉插座。
建立自己私人的 Slack Workspace
建立 Slack App
建立完畢登入之後,預設應該有 #general
、#random
等 channel,但暫時不用這兩個 channel。
接下來,我們來建立一個 App。
輸入 App 名稱和你要新增到的 workspace。
設定 Bot 資訊
建立完畢之後,我們需要設定這個 app 的機器人相關資訊,開啟 app 設定頁面,選擇 Bot Users
:
設定機器人的名稱(Display name 和 Default name)。勾選 Always Show My Bot as Online
,點選 Save Changes
設定 Events API
Event API 可以在各種時間發生的時候觸發呼叫,比如 訊息傳送的時候、channels 改變的時候等等。
我們先回到我們的 web 服務,開啟上一章建立的 AngeliaController
,新增一個處理 Event 的 api:
@PostMapping("/say/at") fun say(@RequestBody request: BotEventRequestVo): JSONObject { logger.info("[slack event request]request -> \n$request") return JsonResult.success( "token" to request.token, "challenge" to request.challenge, "message" to message ) } data class BotEventRequestVo( val challenge: String?, val token: String?, val team_id: String?, val api_app_id: String?, val event: BotEventVo?, val type: String?, val event_id: String?, val event_time: String?, val authed_users: List<String>? ) data class BotEventVo( val type: String?, val user: String?, val text: String?, val client_msg_id: String?, val ts: String?, val channel: String?, val event_ts: String?, val channel_type: String? )
構建,部署到伺服器。
開啟 Slack App 設定頁面的 Event Subscriptions
:
在 Request URL
中填寫剛剛在我們 web 服務上建立的介面 http://[server ip]:xxx/angelia/say/at
,並且點選驗證。
注意:這裡認證的依據是,你的介面 Response 需要返回請求中的
challenge
資料就算認證成功。
然後在 Subscribe to Bot Events
中新增訂閱的事件,需要增加的是 message.im
message.im
表示當你跟 bot 的私聊中產生訊息的時候(有可能是你傳送訊息給 Bot,也有可能是 Bot 發訊息給你),事件就會觸發。
點選儲存。
這時,當你在 Slack 中傳送訊息給機器人的時候,你的 web 服務端就能收到請求了。
處理事件
你的 web 伺服器收到請求之後,需要對此進行處理,所以你需要去解析發的訊息中的資訊,然後開啟/關閉對應裝置(插座)的開關。完善之前的 say
介面:
@Autowired
lateinit var tuyaClientService: TuyaClientService
@Autowired
lateinit var angeliaSlackProperties: AngeliaSlackProperties
/**
* Angelia 機器人 對話入口
*/
@PostMapping("/say/at")
fun say(@RequestBody request: BotEventRequestVo): JSONObject {
try {
val text = request.event?.text
val eventType = request.event?.type
if (eventType == "message" // 直接對話
||
request.event.user != angeliaSlackProperties.angelia_id // angelia自己發的忽略掉
) {
val message = angeliaBotService.parseTuyaClient(text)
?: "Sorry, I can not understand."
angeliaSlackService.postMessage(JSONObject().apply {
this["text"] = "$message"
this["channel"] = request.event.channel
this["as_user"] = true
})
}
return JsonResult.success(
"token" to request.token,
"challenge" to request.challenge,
"message" to "Request eventId(${request.event_id}) done."
)
} catch (e: Exception) {
angeliaSlackService.postMessage(JSONObject().apply {
this["text"] = "Sorry! Something is wrong: ${e.message}"
this["channel"] = request.event?.channel
this["as_user"] = true
})
return JsonResult.error(e.message)
}
}
上面程式碼的邏輯很簡單:
- 首先,eventType是直接對話的(與機器人 bot 私聊),並且是我發給機器人的(機器人發給我的訊息不用處理)才會去處理
- 然後通過
angeliaBotService.parseTuyaClient()
方法進行文字解析和處理 - 如果解析不出來,則返回 null,message 就是 "Sorry, I can not understand."
- 最後返回 Response(帶上 message),這裡的 message 就是 Bot 傳送給我的資料。
這裡需要做一些 Slack 的配置 slack.properties:
# token for bot
angelia.slack.bot_token=Bearer xoxb-2923xxxxxxxxxxxxxxxxxxxx
# slack api
angelia.slack.api_base_url=https://slack.com/api
angelia.slack.api_chat_post_message=/chat.postMessage
angelia.slack.angelia_id=UCWxxxxxx
angelia.slack.bot_token
:是 Bot 傳送訊息到 Slack 的token,這個 token 可以在 app 設定頁面的 OAuth & Permissions
中拿到
注意:是下面的那個
Bot User OAuth Access Token
,並且新增到配置檔案的時候需要加上Bearer
(注意後面有個空格)
angelia.slack.api_base_url
和 angelia.slack.api_chat_post_message
是傳送訊息的 url,不用改動。
angelia.slack.angelia_id
表示機器人的id,可以通過在 slack 左側選中機器人右鍵複製連結,path 最後部分就是 id
最後,你就能在 slack 中開啟與機器人聊天框,傳送“關閉插座a”來控制插座:
整合 Google Assistant 和 IFTTT
首先確保你的手機裝有 Google Assistant(Google Home 先不討論。。。是的,我沒買- -)。
首先我們需要在 web 伺服器端再建立如下一個介面:
/**
* 插座控制介面
*/
@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
)
構建部署到伺服器。
然後開啟IFTTT、註冊(如果還沒有賬戶)登入,建立 Applet
This:選擇 Google Assistant:
That:選擇 Webhook:
注意:POST 請求,在 body 中填寫如上 json 資料。 關閉的 Applet 也是類似,把 body 中的 turnOn 改成 false 就可以了
最後,你就可以通過 “OK, Google” 喚醒 Google Assistant,然後說"Turn on plug a"來開啟插座了。
吐槽下塗鴉的 Google Assistant
本來想直接使用 Google Assistant 的 Smart Life 的,但是一直沒成功,很多人也在反映這個事情,不過貌似沒什麼效果(看下面這個評分,估計反映一直是被無視的- -):
使用 Slack 的 Slash Command 控制
在 web 服務中再新增兩個介面用於 Slash Command:
/**
* 插座控制介面,For Slack command line(slash commands)
*/
@PostMapping("/plug/turnon")
fun plugTurnOnForCommand(@RequestBody body: String): JSONObject {
return JsonResult.success("text" to plugControlForCommand(body, true))
}
/**
* For Slack command line(slash commands)
*/
@PostMapping("/plug/turnoff")
fun plugTurnOffForCommand(@RequestBody body: String): JSONObject {
return JsonResult.success("text" to plugControlForCommand(body, false))
}
/**
* For Slack command line(slash commands) control
*/
private fun plugControlForCommand(body: String, turnOn: Boolean): String {
try {
return body.split("&").map {
val pair = it.split("=")
Pair(pair[0], URLDecoder.decode(pair[1], "UTF-8"))
}.firstOrNull {
it.first == "text"
}?.let {
val dev = tuyaClientProperties.findContainsDev(it.second)
if (null == dev) {
"Sorry for failed command, Device named ${it.second} is not found."
} else {
tuyaClientService.controlPlug(dev.devId, turnOn)
"${if (turnOn) "Turn On" else "Turn Off"} Done(${it.second})."
}
} ?: "Sorry for failed command, Device name required."
} catch (e: Exception) {
return "Sorry for failed command, ${e.message}."
}
}
開啟 App 設定頁面的 Slash Commands
,
點選 Create New Command
,
然後在聊天的輸入框中就可以通過"/"顯示 command 提示,選擇命令,後面跟上你要執行該命令的裝置別名就行了。
其它場景
還有其它很多場景可以實現。比如:
- 結合 IFTTT,當離開家門100m遠的時候,自動觸發 webhook,讓你的私有云幫你把電源、智慧們關閉。
- 實時檢測你的位置和家門,如果你不在家,自動呼叫 Google Calendar 確定你的日程安排,如果又沒有外出的安排,則通過 Slack 傳送 Interactive messages 到你手機上提醒,提供按鈕一鍵鎖門。
- 小細節,晚上手機充電的時候,可以設定手機一旦充滿,關閉電源,又如果手機電源掉電過快,低於90%的電量,重新自動開啟電源,確保你早上起床的時候手機電量肯定在某個值之上。
- 等等等等,太多的智慧場景可以去實現。
章尾
現在越來越多的廠商製作著各種各樣的智慧裝置,但是又在自己的一畝三分地固步自封。做個插座,提供一個 app 控制下開關、定個時、做個 schedule 就是所謂的智慧了,你買了我的裝置就必須要用我的軟硬體產品。那些需要使用者花心思去考慮什麼時候我該怎麼的裝置不是冰冷的,沒有生命的麼?智慧是人類賦予了裝置生命,掌握了“思考”的能力,現在的生活如此多元化,再牛的公司也不可能覆蓋你的所有生活領域,如果買了這樣的智慧裝置但自此被囚困在這裡,我想,這才是我非智慧生活的開始吧。