調用openApi
微信硬件平台是微信推出連接物與人,物與物的IOT解決方案。也就是說可以通過微信控制各種智能設備。比如一些藍牙設備、空調、電視等等。
我本身不懂硬件(雖然是電子信息專業),硬件是北航的兩個研究生在弄,小團隊里我負責開發H5自定義面板,剛開始看官方文檔各種迷糊,對於jssdk、jsapi、Airkiss、openApi、直連SDK都不知道該用哪個做,官方論壇問問題基本上沒結果,加了幾個微信硬件群問問題,發現好些開發者和我一樣,同一個問題,發到幾個群里問,畫面好心酸。給wxthings發郵件問,能回復就不錯了,往往還是只言片語。吐槽了這么多,還是得去解決問題,畢竟設備能搭上微信是一大賣點,最近摸索出來一些東西,於是有了此文。
一、接入流程
也就是說,首先你得有一個公眾號,然后開通設備功能、添加產品(也就是你的智能設備)。這些過程官方文檔比較清楚,我就不講了。接入方案我們選擇的是微信硬件雲標准接入方案。
設置面板
而我要說的H5面板開發,指的就是在微信中打開的一個H5控制頁面,它如何和微信硬件雲通信,如何讀取和設置設備的狀態。在添加產品的過程中有一欄設置面板
如果選擇標准面板,微信官方給出了三類標准面板:
分別是空調、開關和燈,如果是自定義,則輸入地址即可。如果是標准面板,你是不需要服務器,但如果是自定義的面板,你就需要有自己的服務器,不然你無法處理微信雲發過來的消息。
啟用服務器配置
在設置服務器地址的時候要注意,你必須按照它要求方式處理響應了,你才能啟用成功。
你點擊啟用的時候,微信雲會發過來一個簽名、一個時間戳、一個隨機數和一個隨機字符串,驗證之后,返回那個隨機字符串,微信雲收到你返回的隨機字符串了,就能啟用成功。比如,如果你定義的地址是http://www.xxx.com/device/ReceiveWXMsg,那么先把代碼上傳服務器,然后再點擊啟用,微信雲會向這個地址post數據。

直接返回就行,不要加個json什么的。這個地址是干嘛的呢,往下看。
二、通信方式
Wifi設備和藍牙設備是不同的,藍牙使用Airsync協議和微信通信,而wifi設備的話,我們在自己的服務器上調用微信提供的openApi獲取或設置設備狀態,微信服務器就會把數據以json格式post到我們上面設置的那個地址。硬件方面wifi設備可以使用微信提供的直連SDK來控制設備。
添加完設備,設置好服務器,你還需要硬件的同學打開設備,幫定你的微信。從微信的設置-->設備-->選擇設備-->打開面板。你就可以看到設備並進行控制了。
三、調用openApi
說了這么多前提工作,終於進入調用api環節。可以先看一下openApi的官方文檔:http://iot.weixin.qq.com/wiki/doc/hardwarecloud/openapi.pdf
文檔里面主要講了三個方法,一個是查詢設備狀態,一個是設置設備狀態,一個是接受設備狀態變化的消息,然后是一些錯誤信息等。但觀察api就會發現我們還需要兩個重要的參數,一個是access_token,一個是用戶的openid。還說明一點,網頁是Asp.net mvc。
1.獲取access_token
官方有一個接口調試頁面:http://mp.weixin.qq.com/debug/ ,獲取access_token需要appid和secret。
而這兩個值,是在公眾號后台的基本配置中查看,secret是個很重要的參數,所以請保密好。
查看密鑰還需要掃二維碼得到管理員的確認... 拿到這兩個參數了,我們就可以生成token了。注意返回的json是一個token字符串再加一個超時時間。定義一個TokenResult:
public class TokenResult { public string access_token { get; set; } public int expires_in { get; set; } }
要注意的一點是,token兩小時后會過期。所以在我們的代碼里面需要檢查是否超時,然后自動重新獲取。
public const string AccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
getAccessToken方法定義在一個服務類中,比如WxDeviceService
using SendHelp= Senparc.Weixin.CommonAPIs.CommonJsonSend; public TokenResult GetAccessToken() { var url = string.Format(WxDeviceConfig.AccessTokenUrl, WxDeviceConfig.AppId, WxDeviceConfig.APPSECRET); var res = SendHelp.Send<TokenResult>(null, url, null, CommonJsonSendType.GET); return res; }
使用Senparc.Weixin框架封裝好的api來處理請求。
2.獲取openid
這個參數在查詢和設置設備狀態的時候會用到,對應user參數名。獲取openid需要三個參數,access_token已經有了,然后需要device_type和device_id
public const string GetOpenid ="https://api.weixin.qq.com/device/get_openid?access_token={0}&device_type={1}&device_id={2}";
而type和id是前端頁面傳過來的,當用戶微信中打開控制面板的時候,微信會自動將這兩個參數追加到url后面。而這個url的返回值結構是這樣:
{"open_id":["oxa1otw5sk-Azgd8mx1bmBqoM2_E","oxa1ot8-j9j5bYUJJyAexe9d41_Y","oxa1ot5QTdxn0xNQ0DmYzoN0tUp1"],"resp_msg":{"ret_code":0,"error_info":"ok"}}
openid部分是一個數組,實際使用的是第三個(我目前也不知道前面兩個id是干啥的),定義一個openidResult:
public class OpenIdResult { private List<string> _openId; public List<string> open_id { get { return _openId??(_openId=new List<string>()); } set { _openId = value; } } public resp_msg resp_msg { get; set; } public string GetOpenId() { if (open_id!=null&&open_id.Count==3) { return open_id[2]; } return ""; } }
service中:
public string GetOpenId(string accessToken,string deviceType,string deviceId) { var url = string.Format(WxDeviceConfig.GetOpenid, accessToken, deviceType, deviceId); var res = SendHelp.Send<OpenIdResult>(accessToken, url, null, CommonJsonSendType.GET); return res.GetOpenId(); }
如果你的devicetype和deviceId錯誤,微信會返回一個不太恰當的錯誤json:
對比錯誤代碼列表,我開始以為微信服務器出錯了。其實是我參數填錯了。
遇到的錯誤,將不止官方文檔公布的這幾個。
3.查詢設備狀態
查詢設備和上面的這兩個方法略有不同,因為流程是這的,我們的服務器先像向微信服務器請求,微信接受到請求后馬上返回一個確認json,然后微信服務器會馬上把數據post我們前面設置的那個地址上。
請求url:
public const string GetDeviceStatusUrl="https://api.weixin.qq.com/hardware/mydevice/platform/get_device_status?access_token={0}";
從官方文檔可以看到,查詢設備還需要另外一個重要的參數,它包含device_type和device_id,services,user,data。
需要說明一下的就是services,意思是指設備的能力項,也就是你要查詢設備的哪些屬性,這個在設置設備的時候一樣用到。完成的產品能力定義請看:http://iot.weixin.qq.com/wiki/doc/intro/%E4%BA%A7%E5%93%81%E8%83%BD%E5%8A%9B%E5%AE%9A%E4%B9%89%E6%8C%87%E5%BC%95%20V1.2.pdf
因此定義一個RequestData對象以及設備對應的能力項
public class RequestData { public string device_type { get; set; } public string device_id { get; set; } public string user { get; set; } public Service services { get; set; } public object data { get; set; } }
public class Service { public lightbulb lightbulb { get; set; } public air_conditioner air_conditioner { get; set; } public power_switch power_switch { get; set; } public operation_status operation_status { get; set; } }
service包括兩個部分,一個是能力部分,好比上面這個service,就包含了三種能力,燈、空調以及開關(這只是測試,不是真正產品的能力)。和一個操作狀態。操作狀態就是指這個設備是否開着或者關閉了。而每一個能力,又包括兩部分,拿燈來說:
public class lightbulb { public int alpha { get; set; } public lightbulb_value_range value_range { get; set; } } public class lightbulb_value_range { public string alpha { get; set; } }
燈有一個亮度值,和一個范圍屬性。范圍值中包含了最大和最小值以及單位值。
"lightbulb":{"alpha":10,"value_range":{"alpha":"0|100|1"}},"
這表示燈的亮度是10,返回是0到100,每次可以調節1個單位。
發送查詢請求后,微信返回一個json,定義對象為下:
public class OpenApiResult { public int error_code { get; set; } public string error_msg { get; set; } public string msg_id { get; set; } }
WxDeviceService中:
public OpenApiResult RequestDeviceStatus(string accessToken, RequestData data) { var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken); return SendHelp.Send<OpenApiResult>(accessToken, url, data); }
4.接受消息
那么問題來了,如何接受post過來的數據,以及如何存儲呢。微信post的數據格式如下:
定義了一個WxResponseData對象:

msg_type代表着不同類型的消息, notify 說明是設備變更,set_resp 說明是設置設備 get_resp 說明獲取設備信息。在WxDeviceService中增加GetDeviceStatus方法:
public T GetWxResponse<T>(HttpRequestBase request) { Stream postData = request.InputStream; StreamReader sRead = new StreamReader(postData); string postContent = sRead.ReadToEnd(); if (!string.IsNullOrEmpty(postContent)) { Logger.Debug("收到數據:"+postContent); } try { return JsonConvert.DeserializeObject<T>(postContent); } catch (Exception e) { Logger.Debug(e.Message); throw; } } public WxResponseData GetDeviceStatus(HttpRequestBase request) { return GetWxResponse<WxResponseData>(request); }
需要先讀取請求中的json字符串然后轉換成C#對象。然后在最初啟用的ReceiveWXMsg方法中隨時准備接受消息:
public string ReceiveWXMsg() { var signature = Request.QueryString["signature"]; var timestamp = Request.QueryString["timestamp"]; var echostr = Request.QueryString["echostr"]; var nonce = Request.QueryString["nonce"]; Logger.Debug("signature:" + signature); Logger.Debug("timestamp:" + timestamp); Logger.Debug("nonce:" + nonce); Logger.Debug("echostr:" + echostr); try { var userdata = getUserWxData(); var data = wxDeviceService.GetDeviceStatus(Request); userdata.ResponseData = data; setUserWxData(userdata); } catch (Exception e) { Logger.Debug(e.Message); } return echostr; }
因為讀取到的數據需要及時呈現給頁面,所以這里選用了緩存來存儲設備信息以及用戶相關信息。
UserWxData:


緩存是Nop中的MemoryCacheManager:

而為什么不是PerRequestCacheManager呢,想一想~

5.設置設備狀態
有了前面幾步,這里也好說了。url:
public const string SetDeviceUrl ="https://api.weixin.qq.com/hardware/mydevice/platform/ctrl_device?access_token={0}";
設置設備的參數和請求是一樣的,

調用openApi基本上就這樣了,如有不完善的地方還請指正。
四、常見錯誤
如果硬件通信沒有開啟這個能力,去查詢的會報這個錯誤。
剛開始看到"device not login"實在沒明白什么意思,文檔里也沒說明這個錯誤。設備還需要什么登錄?原來是硬件同學沒有連接設備... ORZ
如果你的requestData結構不對,特別是附加的那個Data參數只能是字符串,不要寫成空對象{},就會出現這個錯誤。
token超時
同一個設備同時被操作。
小結:以上是硬件面板開發過程中遇到的種種“經驗”總結。限於篇幅這一節只講了openapi的調用。下一節將如何用seajs構建h5控制面板。敬請關注!
你的關注和支持是我寫作的最大動力~
書山有路群:452450927
出處:http://www.cnblogs.com/stoneniqiu/
github:https://github.com/stoneniqiu