背景介紹
hi 大家好,我是三合,作為一個非著名懶人,智慧家居簡直剛需,在上一篇文章 他來了他來了,.net開源智慧家居之蘋果HomeKit的c#原生sdk【Homekit.Net】1.0.0釋出,快來打造你的私人智慧家居吧 中有靚仔提到,沒有蘋果裝置,有一說一,蘋果手機很貴,並且原生支援蘋果HomeKit的智慧家居裝置也很貴,同時沒幾款可選的,還是米家的智慧家居裝置價效比更高同時品類更豐富,作為一個資深米粉,手機從紅米note到小米6,小米8,再到小米10,接著如今全屋智慧家居基本都是米家的,我對小米可謂愛的深沉,歡迎大家選購智慧家居裝置的時候優先選擇小米,因為他真的很棒(手動狗頭,雷軍義父,請打錢謝謝)。但米家也有缺點,那就是米家並沒有提供c#的sdk讓我們能原生呼叫api,只能透過米家app操控裝置,沒有api這可玩性就太低了,神說:要有光,於是我今天便帶著[MiHome.Net]來了,這個庫主要參考了python的【python-miio】和【hass-xiaomi-miot】這2個專案。有了原生api就有了無限可能,大家可以盡情的發揮想象力去搞事了,接下來,我將介紹他的用法。
米家中的一些基本概念
米家中每一個智慧家居裝置稱為一個裝置(Device),每個裝置擁有多個服務(Services),每個服務又有多個屬性(Property)以及(0-N)個方法(Action,大家把方法理解為封裝好的函式即可,我們可以直接呼叫方法來完成一些操作),所有裝置都有裝置資訊服務,這個服務裡包含了多個基本屬性,包括裝置型號,裝置製造商,裝置序列號等,並且裝置根據功能還有另外一些獨有的服務,我們以一個開關為例,開關本身就是一個裝置,他擁有一個服務叫Switch,這個服務下面,有一個屬性叫Switch Status,也就是開關,我們給這個屬性賦值true,就代表開,賦值false,就代表關,同時米裝潢置支援2種操控方式,基於雲端和基於本地,這兩者的主要區別就在於雲端需要裝置以及手機有網際網路連線,使用者的操作指令是先傳送到小米伺服器,然後小米伺服器下達指令給到智慧裝置,而基於本地則不同,他是直接透過ip和token,將指令傳送給智慧裝置,這個過程,只需要區域網即可。所有米家智慧家居裝置都支援雲端操作,但不是所有裝置都支援本地操作,主要看裝置情況。
MiHome.Net存在的意義
使用本依賴包,使用者可以透過雲端或者本地的方式用c#原生api來操作米家智慧家居裝置。
Getting Started
Nuget
接下來我將演示如何使用【MiHome.Net】,你可以執行以下命令在你的專案中安裝 MiHome.Net 。
PM> Install-Package MiHome.Net
支援框架
net 6,net 8
api使用講解
本庫基於依賴注入,首先新建一個控制檯應用,引入MiHome.Net的nuget包,接著新增小米米家的驅動服務,需要配置米家賬號和密碼,程式碼如下:
var hostBuilder = Host.CreateDefaultBuilder();
//新增小米米家的驅動服務,需要小米賬號和密碼
hostBuilder.ConfigureServices(it => it.AddMiHomeDriver(x =>
{
x.UserName = "<這裡填寫米家賬號>";
x.Password = "<這裡填寫米家米家>";
}));
var host = hostBuilder.Build();
var miHomeDriver = host.Services.GetService<IMiHomeDriver>();
獲取到米家驅動服務以後,我們首先透過雲端的方式呼叫介面,列出家庭裡所有的智慧家居裝置,接著透過米家app裡自己設定的裝置名稱找出自己想要操作的智慧家居裝置,我這裡演示的是一個【米家智慧插座2】,
我將3D列印出來的月球燈連到了這個插座上,並在米家app裡將這個插座命名為月球燈,這樣就將月球燈接入了智慧家居。繼續講解api,接下來透過裝置型號獲取裝置規格,這一步的目的,主要是瞭解我們要操作的智慧家居裝置都有哪些服務,哪些方法,哪些屬性,並獲得它們的id,因為我們操作智慧家居需要用到裝置id(即did),服務id(即siid),屬性id(即piid),方法id(即aiid),程式碼如下:
//列出家庭裡所有的智慧家居裝置
var deviceList = await miHomeDriver.Cloud.GetDeviceListAsync();
//透過米家app裡自己設定的智慧家居名稱找出自己想要操作的智慧家居裝置
var moonLight = deviceList.FirstOrDefault(it => it.Name == "月球燈");
//透過裝置型號獲取裝置規格
var result = await miHomeDriver.Cloud.GetDeviceSpec(moonLight.Model);
我這裡將獲取到的規格結果截圖出來給大家講解一下
從上圖中可以看到,這個裝置總共有8個服務,逐一點開後我發現我只需要了解switch服務(即開關服務)即可,它的iid為2(即siid為2),同時這個服務下有3個屬性,逐一點開檢視後我發現,我只需要了解Switch Status屬性即可,他的iid為1(即piid為1),同時這個屬性值的格式(Format)是bool型別,即這個屬性值只能為true或者false,接下來我將演示如何獲取開關狀態,程式碼如下:
//透過本地方式獲取屬性值
var r1 = await miHomeDriver.Local.GetPropertyAsync(moonLight.LocalIp, moonLight.Token, new GetPropertyPayload()
{
Siid = 2,
Piid = 1
});
//透過雲端方式獲取屬性值
var r7 = await miHomeDriver.Cloud.GetPropertyAsync(new GetPropertyDto()
{
Did = moonLight.Did,
Siid = 2,
Piid = 1
});
如上程式碼所示,支援本地以及雲端的方式獲取屬性值,本地獲取的方式需要傳入智慧家居裝置的ip,智慧家居裝置的token,這兩者都是之前透過雲端裝置介面GetDeviceListAsync返回的,以及引數siid和piid,這兩者是我們之前透過檢視裝置規格獲得的,透過雲端的方式獲取屬性值,則額外需要did(即裝置id),它同樣是透過雲端裝置介面GetDeviceListAsync返回的,本地或雲端呼叫後我們就獲取到了開關當前的狀態,呼叫結果如下圖,value值為false,即代表開關當前處於關閉狀態
接下來我將演示如何設定開關狀態,程式碼如下:
//透過本地方式設定屬性值
var r2 = await miHomeDriver.Local.SetPropertyAsync(moonLight.LocalIp, moonLight.Token, new SetPropertyPayload()
{
Siid = 2,
Piid = 1,
Value = true
});
//透過雲端方式設定屬性值
var r8 = await miHomeDriver.Cloud.SetPropertyAsync(new SetPropertyDto()
{
Did = moonLight.Did,
Siid = 2,
Piid = 1,
Value = true
});
如上程式碼所示,支援本地以及雲端的方式設定屬性值,引數和獲取屬性值差不多,只是多了一個value引數,代表我們要設定的值,這裡根據裝置規格中format為bool,我們將它設定為true,即代表開。同時這些操作也支援批次獲取和批次設定,程式碼如下
//透過本地方式批次獲取屬性值
var r3 = await miHomeDriver.Local.GetPropertiesAsync(moonLight.LocalIp, moonLight.Token, new List<GetPropertyPayload>(){new GetPropertyPayload()
{
Siid = 2,
Piid = 1
}});
//透過雲端方式批次獲取屬性值
var r5 = await miHomeDriver.Cloud.GetPropertiesAsync(new List<GetPropertyDto>()
{
new GetPropertyDto()
{
Did = moonLight.Did,
Siid = 2,
Piid = 1
}
});
//透過本地方式批次設定屬性值
var r4 = await miHomeDriver.Local.SetPropertiesAsync(moonLight.LocalIp, moonLight.Token, new List<SetPropertyPayload>(){new SetPropertyPayload()
{
Siid = 2,
Piid = 1,
Value =true
}});
//透過雲端方式批次設定屬性值
var r6 = await miHomeDriver.Cloud.SetPropertiesAsync(new List<SetPropertyDto>()
{
new SetPropertyDto()
{
Did = moonLight.Did,
Siid = 2,
Piid = 1,
Value = true
}
});
接下來我給大家演示如何透過雲端或者本地的方式呼叫裝置服務裡的方法,因為米家智慧插座2沒啥方法可以呼叫,所以我將使用【Gosund智慧排插CP5 Pro】和【小愛音響Play增強版】來給大家演示呼叫,先來【Gosund智慧排插CP5 Pro】,這個排插長這樣
他上面的4個插座都支援獨立控制,我個人非常喜歡,強烈推薦(手動狗頭,廠家打錢!),接下來我將演示呼叫4個插座其中插座3的方法來控制插座3的開關,
和上面一樣,我們首先查規格,程式碼如下
var cp5pro = deviceList.FirstOrDefault(it => it.Name == "Gosund智慧排插CP5 Pro");
var result3 = await miHomeDriver.Cloud.GetDeviceSpec(cp5pro.Model);
獲得的規格如下圖
如上圖可以看出,插座3有一個方法(即action)叫toggle,這個方法主要就是改變插座3當前的狀態,如果原來是關,呼叫即為開,如果原來是開,呼叫即為關,同時iid為1(即aiid為1),服務id為5(即siid為5),入參in為空陣列,即不需要傳入引數,out也為空陣列,表示呼叫沒有返回,不多說了,上程式碼:
//使用雲端方式呼叫Gosund智慧排插CP5 Pro中4個開關中第3個開關的toggle方法
var r11 = await miHomeDriver.Cloud.CallActionAsync(new CallActionInputDto()
{
Did = cp5pro.Did,
Aiid = 1,
Siid = 5,
In = new List<string>() { }
});
//使用本地方式呼叫Gosund智慧排插CP5 Pro中4個開關中第3個開關的toggle方法
var r10 = await miHomeDriver.Local.CallActionAsync(cp5pro.LocalIp, cp5pro.Token, new CallActionPayload()
{
Siid = 5,
Aiid = 1,
In = new List<string>() { }
});
如上程式碼所示,和設定/獲取屬性不同,呼叫方法是透過服務id(即siid)和方法id(即aiid)以及入參in來實現的。
接下來演示呼叫【小愛音響Play增強版】的服務方法來播放自定義文字,先上圖
和上面一樣,我們首先查規格,程式碼如下
var xiaoAi = deviceList.FirstOrDefault(it => it.Name == "小愛音響Play增強版");
var result2 = await miHomeDriver.Cloud.GetDeviceSpec(xiaoAi.Model);
獲得的規格如下圖
如上圖可以看出,小愛音響有一個方法(即action)叫Play Text,這個方法用來播放自定義語音,同時iid為3(即aiid為3),服務id為5(即siid為5),入參in為陣列,需要傳入一個引數,即我們的自定義文字,out為空陣列,表示呼叫沒有返回,同時這個方法只支援雲端呼叫,不支援本地呼叫,為啥我知道?因為我試過了,本地方法沒反應,這個應該是需要伺服器把文字轉為語音,再回傳到小愛音響來播放,不多說了,上程式碼:
//使用小愛音響Play增強版播放我們的自定義文字
var r9 = await miHomeDriver.Cloud.CallActionAsync(new CallActionInputDto()
{
Did = xiaoAi.Did,
Aiid = 3,
Siid = 5,
In = new List<string>() { "門前大橋下,遊過一群鴨" }
});
開源地址,歡迎star
本專案基於MIT協議開源,地址為
https://github.com/TripleView/MiHome.Net
同時感謝以下專案
-
python-miio
-
hass-xiaomi-miot
寫在最後
如果各位靚仔覺得這個專案不錯,歡迎一鍵三連(推薦,star,關注),有了【MiHome.Net】和【Homekit.Net】,想必各位靚仔應該能自己寫程式將米家智慧家居裝置橋接到HomeKit生態裡去了,反正我自己已經用了很久了,然後希望大家不要過於頻繁的進行雲端呼叫,以免對米家伺服器造成不良影響。如果你也對智慧家居很感興趣,歡迎新增我的vx,hezp666。