1.前言
之前買了小愛同學音響,一直想讓其讓我的生活變得更智能,編寫一些程序來完成一些自動化任務,但是經過搜索發現,官方開發者平臺不能用了,尋找api階段浪費了我很長時間。最后在github 開源項目發現了倆個比較關鍵的項目:
https://github.com/Yonsm/MiService
https://github.com/yihong0618/xiaogpt
其實關鍵點在于,我需要倆個接口:
- 我說了什么
- 讓音響答復我說了什么
只需要這倆個接口
2. 實現
對于第一個接口我找了很久,最后在xiaogpt這個項目中找到了這個api 的實現,xiaogpt也是miservice 這個項目實現的。
這里我簡單做了個demo,相信能幫助大家節約很長時間:
#!/usr/bin/python3
from miservice.miaccount import MiAccount
from miservice.minaservice import MiNAService
from aiohttp import ClientSession
from requests.utils import cookiejar_from_dict
import time
import os
import json
import asyncioCOMMAND = {
"打開主機":["wakeonlan xx:xx:xx:xx:xx:xx","已開啟主機"],
"關閉主機":["ssh -o ConnectTimeout=1 root@192.168.123.60 shutdown -h now","已關閉主機"],
"打開桌面":["/root/tools/switch/switch.py on","已打開桌面電腦"],
"關閉桌面":["ssh -o ConnectTimeout=1 root@192.168.123.100 shutdown -h now; sleep 30; /root/tools/switch/switch.py off","已關閉桌面電腦"]
}async def main():user_id = "小米賬號" password = "密碼"hardware = "LX06" # 音響型號polling_event = asyncio.Event()misession = ClientSession()account = MiAccount(misession, user_id,password,"mi.token")await account.login("micoapi")service = MiNAService(account)device_list = await service.device_list()deviceID = device_list[0]['deviceID']LATEST_ASK_API= "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}×tamp={timestamp}&limit=1"async with ClientSession() as session:cookies = dict(deviceId=deviceID,)misession.cookie_jar.update_cookies(cookiejar_from_dict(cookies))session._cookie_jar = misession.cookie_jar# print(session._cookie_jar._cookies)while True:try:r = await session.get(LATEST_ASK_API.format(hardware=hardware, timestamp=str(int(time.time()* 1000))))data = await r.json()query = json.loads(data["data"])['records'][0]['query']qtime = int(json.loads(data["data"])['records'][0]['time']/1000)# print(qtime,int(time.time()))if abs(qtime - int(time.time())) < 5 and COMMAND.get(query):print(f"{query} 已執行命令")os.popen(COMMAND.get(query)[0])await service.text_to_speech(deviceID, COMMAND.get(query)[1])time.sleep(5)except Exception as e:print(e)time.sleep(1)loop = asyncio.get_event_loop()
loop.run_until_complete(main())
要使用上述代碼,首先要滿足幾個條件:
- python支持異步(忘記是幾點幾版本開始支持了)
- 安裝miservice
pip install miservice
其他把賬號、密碼、音響型號 配置進去 就可以在COMMAND 字典中自定義shell 命令了
這里簡單介紹一些 COMMAND 字典,鍵 為語控命令,值 為一個列表,列表中第一個值 為 要執行的shell 命令,第二個值 為 音響答復。
后面要增加命令可以直接修改 COMMAND字典。這里可以看到我對倆個電腦開關機進行控制:
對于桌面電腦,我是先 執行shutdown -h now, 等待30s后,再對電源進行關閉操作(switch為我寫的控制電源的一個腳本),因為我總覺得直接下電對 電腦不好。
對于主機電腦,我直接使用 網絡喚醒實現的開機(連接網線),對于桌面電腦由于我沒有網線接口了,所有用上電自動開機實現的開機控制。
3.其他一些細節
3.1 讓小愛知道這個指令
由于自定義的一些指令,直接問小愛,小愛會回答不知道這個指令,那么我們可以在 小愛訓練中加入這些自定義指令:
這里小愛,會先回答“好的”, 然后我們api響應的話語會打斷說話,緊接著說“已開啟主機”
3.2 回答內容為自定義查詢的內容
在COMMAND 里,我們可以用這種方式進行定義列表中的第二個字段:
f"查詢的信息是 {data}"
可以注意到,這里我執行shell, 使用 os.popen 來執行的(非阻塞),一方面也是為了可以讀取返回內容,然后給與相應的賦值
data 直接賦值為 os.popen 返回的read() 即可
當然os.popen不是一種良好的執行 命令的方式,有更官方的寫法,懶得優化了。
4.效果展示
https://7.z.wiki/autoupload/20240707/Yfx4/1.mp4
終于可以在床上控制電腦開關機了