專題總審簡報:https://www.canva.com/design/DAGYJFZzXxM/vv14oABO_XdY974AIL4eYA/edit?utm_content=DAGYJFZzXxM&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton
智慧家庭已經成為現代生活的一部分。智慧家電的普及不僅提升了我們的生活品質,還在節能減碳方面展現了很大的潛力。
然而現在的智慧家庭系統通常是各自為政,缺乏一個統一的管理平台來整合不同品牌的智慧家電,而且這些系統在使用者體驗和互動性上仍有很多可以改進之處,因此我們決定開發一個基於 Home Assistant 平台的家庭自動化系統,開發一個可以整合多品牌智慧家電的系統,並可透過智慧語音助理來控制 Home Assistant 及各種家電。
- Vue.js:前端介面使用 Vue.js 開發,提供使用者操作的視覺化介面。
- Linux Server:借用校內具有對外 IP 的 Ubuntu 22.04 主機,提供以下兩項功能:
- 反向代理(Reverse Proxy):利用 Nginx 將前端請求導向 Raspberry Pi 上的 Node.js server 與 Home Assistant Web UI。
- 資料庫(Database):使用 MySQL 儲存帳號密碼等資訊。
- Access Point:將 Raspberry Pi 和 IoT 設備部屬於同一區網內,方便彼此通訊。
- IoT 設備:整合多品牌的智慧家居設備,如燈泡、插座、感應器等。
- Node.js:
- 與資料庫與 Home Assistant API 串接,提供登入、註冊、新增裝置等 RESTful API 功能給前端使用。
- 使用 iputils 工具(例如 ping)來掃描區網中的 IoT 設備。
- Home Assistant:利用其官方/非官方整合模組(Integrations)管理不同品牌的設備。提供 RESTful API 供後端取得設備資訊與執行控制命令。
- 聊天機器人功能:
- 設備查詢與控制:使用者可透過自然語言查詢設備狀態,或下達控制指令(如「開啟插座」)。
- 情境模式切換:提供六種情境模式(回家、出門、起床、睡眠、工作、節能),可快速一鍵切換。
- 自動化與人性化互動:
- 支援設備聯動與定時功能。
- 理解如「能否讓臥室更亮?」等語意性問題並給予建議或直接操作。
- 掃描裝置
點擊左下角的「加號」按鈕,選擇品牌後,系統將顯示偵測到的裝置。選取您想新 增的裝置,即可完成新增操作。
本系統目前設有「臥室」、「客廳」和「廚房」三個分區,旨在幫助用戶更高效地 管理和區分設備。
本系統目前支援多種類型的設備,包括 Matter 燈泡、Tapo 插座、Tuya 插座、小米門窗感應器、Tuya 溫濕度感應器以及 Tapo 監視器。
設備狀態會以顏色區分:關閉時顯示為灰色(如圖十八),開啟時顯示為黃色(如圖十九)。
點擊設備左下角的「…」圖示,可檢視或設定更多設備資訊,例如區域、自動化設定、歷史記錄等。此外,用戶還可以調整設備的分區位置或新增自動化設定,讓設備管理更加高效與便捷。
若要新增更多自動化選項,可點擊右箭頭進入設定頁面,點擊加號以新增觸發條件和動作,完成後點擊「儲存」,即可成功新增。
如果是燈條或燈泡,用戶可以控制其開關、亮度以及色彩,讓設備更符合用戶的使用需求與環境氛圍。
如果是有計電功能的插座,用戶可以查看電量統計資訊。內容包括:本月至今累積電費、總用電量、功耗等。此外,用戶也可以根據折線圖的記錄,直觀的了解插座的使用時段及電量變化情況。
點擊主畫面右上角的「機器人」按鈕,即可啟用聊天機器人。您可以透過輸入文字提問,或使用快捷訊息快速向聊天機器人詢問問題。
情境模式是智慧家居系統中的核心功能,旨在根據預設場景或觸發條件,自動協調多個設備的運行,以滿足用戶的日常需求,並提升生活的便利性、效率與舒適度。
透過這些模式,系統能有效整合燈光、空調、智能插座等多種設備,根據使用習慣智能運行,創造個性化的生活體驗。
本系統針對不同的生活情境,設計了六種主要情境模式:工作模式、出門模式、回家模式、節能模式、睡眠模式及起床模式。
點擊主畫面右上角的「三個點」按鈕,選擇「設備歷史紀錄查詢」,即可查閱設備的相關歷史紀錄。若需查看更多詳細資訊,點擊「顯示更多資訊」即可進一步了解設備查看到更完整的歷史紀錄。
- packet sniffing
- 攔截 tapo app 和 device 溝通的封包並觀察內容
- reverse engineering
- 將 tapo app 的
.apk反編譯成 java source code
- 將 tapo app 的
- tapo 中,有部分裝置會使用 http 和 app 溝通,因此我們可以觀察封包的內容,並嘗試利用我們建立的 http request 而不是 tapo app 和裝置溝通
- handshake request : POST
http://<device-ip>/app- body
{ "method": "handshake", "params": { "key": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCiHkY5laTugGN1Hf/sBHiiw6mnnkohmvVHHHGJqwRx59RjQaL/SPBoLpeNRgN3B/uykzYTLUVMpTcWSZHsS6FfhdoOkJ1B6nit6nheIfltbP99uJduP1JQ44S9dqUr73w++Lpl6TKrzK3KOc5z/vc9xmqiKK6PYbFZu2evCsL19wIDAQAB-----END PUBLIC KEY-----\n" }, "requestTimeMils": 0 } - 實作 key 的 java source code : 利用 RSA 產生非對稱式加密的 public/private key,再利用 base64 編碼後傳輸
public void mo35029c() { KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA"); instance.initialize(1024, new SecureRandom()); KeyPair generateKeyPair = instance.generateKeyPair(); String str = new String(Base64.encode(((RSAPublicKey) generateKeyPair.getPublic()).getEncoded(), 0)); String str2 = new String(Base64.encode(((RSAPrivateKey) generateKeyPair.getPrivate()).getEncoded(), 0)); this.f20965b.put(0, str); this.f20965b.put(1, str2); }
- body
- authentication request
- 將 tapo app 的帳號密碼給該 device,device 會再去確認是否可以登入
- request body
{ "method": "login_device", "params": { "password": "ITcyNjU....", "username": "MzhhNTk2NT..." }, "requestTimeMils": 0 }
- data request
- 可以開始傳輸資料做 device 設定
- request body
{ "method": "set_device_info", "params": { "device_on": false }, "requestTimeMils": 1602840338865, "terminalUUID": "88-54-DE-AD-52-E1" }
plugp100: 目前此 custom component 實作了 tapo 部分裝置的搜尋和控制(新增裝置到 HA, 更改裝置狀態)的功能- 搜尋問題
- 有時候會找不到全部的 device:後來發現因此 component 使用 udp broadcast 來尋找 device,但是在指定的 timeout 時間內,udp socket 不一定會接受到所有 device 的 response
- 找到無法控制的 device :因為新出的 tapo device 的新版 firmware 會使用不同 protocol(從 http 變 https)和 tapo app 溝通,但是此情況下 plugp100 仍會顯示新出的 tapo device,但該 tapo device 無法用 plugp100 目前提供的控制方式(http)讓該 device 連接到 home assistant
- 解決方法
- 利用
ping和 plugp100 提供的控制方式寫一個偵測和新增 tapo device 的功能解決上述兩個問題 - 利用 multithreading 加速尋找的效能
- 利用
- https://k4czp3r.xyz/blog/post/reverse-engineering-tp-link-tapo
- https://github.com/petretiandrea/plugp100/blob/main/plugp100/discovery/tapo_discovery.py
- note
- 只要有關於會爬取 home assistant web 的資料的 API,大約會等 10s(現在沒有爬蟲了)
- testAPI.py
- GET
api/history_data- 回傳所有開關的設備的歷史資訊
import requests url = 'http://localhost:8122/api/history_data' headers = { 'Content-Type': 'application/json' } response = requests.get(url) print(response.json())
- 回傳所有開關的設備的歷史資訊
- POST
api/history_data- 回傳特定開關的設備的歷史資訊
# entity_id:ENTITY_ID(替換成實體ID) import requests url = 'http://localhost:8122/api/history_data' headers = { 'Content-Type': 'application/json' } data={ 'entity_id': ENTITY_ID } response = requests.post(url, headers=headers, json=data) print(response.json())
- 回傳特定開關的設備的歷史資訊
- GET
api/turn_gate- 回傳所有開關的設備的資訊
import requests url = 'http://localhost:8122/api/turn_gate' headers = { 'Content-Type': 'application/json' } response = requests.get(url) print(response.json())
- 回傳所有開關的設備的資訊
- GET
api/turn_gate/sensor(light、switch)- 回傳特定設備的資訊
import requests url = 'http://localhost:8122/api/turn_gate/sensor' headers = { 'Content-Type': 'application/json' } response = requests.get(url) print(response.json())
- 回傳特定設備的資訊
- POST
api/turn_gate- 開啟/關閉 所有開關的設備
# state:'on' or 'ofF' # entity_id:'all' or ENTITY_ID(替換成實體ID) url = 'http://localhost:8122/api/turn_gate' headers = { 'Content-Type': 'application/json' } data={ 'state': 'on', 'entity_id': ENTITY_ID } response = requests.post(url, headers=headers, json=data) print(response.json())
- 開啟/關閉 所有開關的設備
- POST
api/automation/switch(light)- 在某個時間點開啟/關閉
# entity_id:ENTITY_ID # state:'on' or 'ofF' # triggerTime:'HH:MM:SS' import requests url = 'http://localhost:8122/api/automation/switch' headers = { 'Content-Type': 'application/json' } data={ "entity_id": ENTITY_ID, "state": "off", "triggerTime": "17:00:00" } response = requests.post(url, headers=headers, json=data) print(response.json())
- 在某個時間點開啟/關閉
- POST
api/conversation- 使用HA助理
# chatText:對話內容 import requests url = 'http://localhost:8122/api/conversation' headers = { 'Content-Type': 'application/json' } data={ 'chatText': TEXT } response = requests.post(url, headers=headers, json=data) print(response.json())
- 使用HA助理
- POST
api/automation- 使用HA助理
import requests url = 'http://localhost:8122/api/automation' headers = { 'Content-Type': 'application/json' } data={ "entity_id": "switch.your_switch_id", "state": "on", "triggerTime": "14:30:00" } response = requests.post(url, headers=headers, json=data) print(response.json()) - POST
api/light_control/brightness- 控制電燈亮度
import requests url = 'http://localhost:8122/api/light_control/brightness' # 替換為你的 API 端點 headers = { 'Content-Type': 'application/json' } data = { "entity_id": "light.your_light_id", # 替換為你的燈泡實體 ID "brightness": 200 # 設定亮度值,範圍是 0 到 255 } response = requests.post(url, headers=headers, json=data) print(f"Status Code: {response.status_code}") print(f"Response: {response.json()}") - POST
api/light_control/color_temp- 控制電燈色溫
import requests url = 'http://localhost:8122/api/light_control/color_temp' # 替換為你的 API 端點 headers = { 'Content-Type': 'application/json' } data = { "entity_id": "light.your_light_id", # 替換為你的燈泡實體 ID "color_temp_kelvin": 3000 # 設定色溫值,以開爾文(Kelvin)為單位 } response = requests.post(url, headers=headers, json=data) print(f"Status Code: {response.status_code}") print(f"Response: {response.json()}") - POST
api/light_control/color- 控制電燈顏色
import requests url = 'http://localhost:8122/api/light_control/color' # 替換為你的 API 端點 headers = { 'Content-Type': 'application/json' } data = { "entity_id": "light.your_light_id", # 替換為你的燈泡實體 ID "rgb_color": [255, 100, 100] # 設定 RGB 顏色值,範圍是 0 到 255 的三個整數組成的數組 } response = requests.post(url, headers=headers, json=data) print(f"Status Code: {response.status_code}") print(f"Response: {response.json()}") - POST
/api/login>>> import requests >>> a = requests.post("http://163.22.17.184:8122/api/login", json={'account' : 'test', 'password' : '123'}) >>> a.text '{"success":true}'
- python3
- node : v16 up
-
clone repo
git clone https://github.com/Anna0131/HomeAssisstantIntegration.git
-
Create ssh tunnel (On Home Assistant Web Terminal)
cd HaWebTunnellingpython3 main.py &
-
Create API Server
cd APInode node_modules\puppeteer\install.mjsnode index.js- API Server is open on 127.0.0.1:8122
- 楊于葳:前端開發、場景佈置
- 蔣馥安:後端開發、Linux Server 維運&管理、硬體實作
- 陳維德:後端開發
- 謝欣秀:撰寫總審文件、製作簡報
- 邱靖恩:設計系統 UI、製作宣傳影片和海報
- 廖書嫻:設計系統 UI、製作宣傳影片和海報


