|
導讀微信(WeChat)是騰訊公司于2011年1月21日推出的一個為智能終端提供即時通訊服務的免費應用程序,由張小龍所帶領的騰訊廣州研發中心產品團隊打造 [2] 。微信支持跨通信運營商、跨操作系統平臺... 微信(WeChat)是騰訊公司于2011年1月21日推出的一個為智能終端提供即時通訊服務的免費應用程序,由張小龍所帶領的騰訊廣州研發中心產品團隊打造 [2] 。微信支持跨通信運營商、跨操作系統平臺通過網絡快速發送免費(需消耗少量網絡流量)語音短信、視頻、圖片和文字,同時,也可以使用通過共享流媒體內容的資料和基于位置的社交插件“搖一搖”、“漂流瓶”、“朋友圈”、”公眾平臺“、”語音記事本“等服務插件。 這次給大家帶來微信硬件H5開發之控制燈光,微信硬件H5開發控制燈光的注意事項有哪些,下面就是實戰案例,一起來看一下。你可以自己扒,帶參數的頁面在瀏覽器中打開會馬上跳轉,不帶參數的會提示參數不全,需要用mobile模式觀看。 呈現的界面如下:
目錄結構解壓開lamp.js ,目錄如下,這個demo是基于sea.js+zepto實現,sea.js用來加載模塊,zepto提供ajax請求和tab事件等。
common中包含了一個keyConfig.js(地址參數),一個reqData.js(請求封裝)還有一個zepto,ui里是一個上面圖片的中的slider一樣的組件。util中是一組方法集合。最重要的就是lamp.js 。 define(function (require) { var $ = require("common/zepto"); var keyConfig = require("common/keyConfig"); var reqData = require("common/reqData"); var util = require("util/util"); var ProcessBar = require("ui/process-bar"); var pageParam = {
device_id: util.getQuery("device_id"),
device_type: util.getQuery("device_type"),
appid: util.getQuery("appid")
}; var lastModTime = 0; var powerBtn = $("#powerBtn"), // 開關按鈕 lightBar; var device_status= {
services: {
lightbulb: {alpha:0},
operation_status:{status:0}
}
}; // 數據對象
(function () { if(!pageParam.device_id || !pageParam.device_type){
alert("頁面缺少參數"); return;
}
log("appid:" + pageParam.appid);
log("device_id:" + pageParam.device_id);
log("device_type:" + pageParam.device_type);
powerBtn.on("tap", togglePower); // 開關按鈕事件 initBar();
initInterval(); // todo : for test, delete before submit// renderPage({}); })(); /**
* 初始化進度條 */
function initBar() {
log("初始化lightBar");
lightBar = new ProcessBar({
$id: "lightBar",
min: 0,
stepCount: 100,
step: 1,
touchEnd: function (val) {
device_status.services.lightbulb.alpha = val;
log("亮度值為:"+val); setData();
}
});
} /**
* 請求數據 */
function getData() {
reqData.ajaxReq({ //url: keyConfig.GET_LAMP_STATUS,
url:'https://api.weixin.qq.com/device/getlampstatus',
data: pageParam,
onSuccess: renderPage,
onError:function(msg) {
log("獲取數據失敗:" + JSON.stringify(msg));
}
});
} /**
* 設置數據 */
function setData() {
console.log("setUrl", keyConfig.SET_LAMP_STATUS);
lastModTime = new Date().getTime(); // 更新最后一次操作時間 reqData.ajaxReq({ // url: keyConfig.SET_LAMP_STATUS,
url: 'https://api.weixin.qq.com/device/setlampstatus',
type: "POST",
data: JSON.stringify(device_status)
});
log("setData:" + JSON.stringify(device_status));
} /**
* 開關按鈕事件 */
function togglePower() {
$("#switchBtn").toggleClass("on").toggleClass("off");
log("燈的狀態status:"+device_status.services.operation_status.status); if(device_status.services.operation_status.status==0){
device_status.services.operation_status.status = 1;
log("燈的狀態:1");
} else {
device_status.services.operation_status.status = 0;
log("燈的狀態:0");
} setData();
} /**
* 輪詢 */
function initInterval() {
getData();
setInterval(function () { if((new Date().getTime() - lastModTime) > 2000){ // 當有設置操作時,停止1s輪詢,2秒后繼續輪詢 getData();
}
}, 1000);
} /**
* 渲染頁面 */
function renderPage(json) { // todo : for test, delete before submit// json = {// device_status: {// services: {// operation_status: {// status: 0// },// lightbulb: {// alpha: 0// }// }// }// };
log("renderPage:"+json); if(!json.device_status){ return;
}
console.log("json", json);
device_status = json.device_status;
log(device_status); if(device_status.services.operation_status.status==0){
$("#switchBtn").addClass("on").removeClass("off");
} else {
$("#switchBtn").addClass("off").removeClass("on");
}
lightBar.setVal(device_status.services.lightbulb.alpha);
}
});/* |xGv00|4199711a9ade00e2807e7ea576d92f55 */首先我們看到pageParam對象是獲取頁面上參數的,device_id,device_type以及appid三個參數。其實有用的只有前面兩個,因為appid的話,后臺服務器已經配置了,而且在微信中的通過“進入面板”的時候只附帶了id和type兩個參數。然后device_status是一個設備狀態對象對象是燈,根據微信services的定義,燈有一個亮度值。這個在上一篇提到過。然后是一個立即執行的匿名函數,這個函數函數里面會先檢查一下參數,然后初始化開關和亮度條。最好進入循環。initInterval中就是不斷的通過getdata獲取數據。注意到這兒有一個lastModTime的比較,然后延時2秒再觸發,這個地方主要是因為每次設置之后再從服務器撈到數據有一個延時。原本是10,你設置了20,bar也到了20的位置,但是呢,服務器還有一個10在路上發過來,你設置的20并沒有馬上失效,這會有一個卡頓的效果。但這個兩秒也不是那么的有效,卡頓還是會有;另外一方面就是,不能設置太快,設置太快了會報50019的錯誤(設備正在被操作);getdata成功后,就是renderpage,這個不用解釋了。注意到在綁定開關時間的地方,其實是先調用了一次setdata powerBtn.on("tap", togglePower); function togglePower() {
$("#switchBtn").toggleClass("on").toggleClass("off");
log("燈的狀態status:"+device_status.services.operation_status.status); if(device_status.services.operation_status.status==0){
device_status.services.operation_status.status = 1;
log("燈的狀態:1");
} else {
device_status.services.operation_status.status = 0;
log("燈的狀態:0");
} setData();
}這個作用有兩個,一個是獲取設備目前的狀態,因為設備可能沒有開啟,或者沒有聯網,二個是將參數傳遞給后臺,不然getdata無效。最后理清一下思路就是 獲取參數-->初始化-->setdata一次-->循環-->渲染頁面 界面操作-->setdata-->延時讀取。 加上后端的部分,全部的流程圖如下。
所以拿到前端代碼只是一半,后端還需要自己實現。 實現純靜態文件是無法請求微信服務器的,所以我們需要自己實現后臺的部分,這也是第一節中要講的目的。 html: @{
Layout = null;
}<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>我的燈泡</title>
<link href="/css/common.css" rel="stylesheet" />
<link href="/css/light_switch.css" rel="stylesheet" /></head><body>
<p>
<p class="body">
<p class="inner">
<p id="switchBtn" class="status_button off">
<p class="button_wrp">
<p class="button_mask">
<p class="alerter_button" id="powerBtn">
<i class="status_pot"></i>
<span class="on">ON</span>
<span class="off">OFF</span>
</p>
</p>
</p>
<p class="on">
<h2>燈已開</h2>
</p>
</p>
<p id="reData"></p>
</p>
</p>
<p class="foot">
<p class="slider_box J_slider_box">
<i class="slider_box_icon icon dark"></i>
<p id="lightBar" class="slider_box_bar">
<p class="slider_box_slider J_slider" style="left:0%">
<p class="slider_box_slider_label J_value"></p>
<i class="slider_box_slider_touch"></i>
</p>
<p class="slider_box_line">
<span class="slider_box_line_fill J_fill" style="width:0%"></span>
</p>
</p>
<i class="slider_box_icon icon light"></i>
</p>
</p>
</p>
<script src="/js/sea.js"></script>
<script>
seajs.config({
base: '/js/', //map: [[/^(.*\.(?:css|js))(.*)$/i, "$1"]], charset: 'utf-8'
});
seajs.use("baby"); </script></body></html>View Code 自己的實現就拿掉了遮罩和config部分,將sea.js的目錄改到自己對應的目錄即可: seajs.config({
base: '/js/', //map: [[/^(.*\.(?:css|js))(.*)$/i, "$1"]],
charset: 'utf-8'
});
seajs.use("baby");這個baby(命名和產品有關~)就相當于是lamp。 另外就是,修改請求地址。也就是通過后臺調用api來實現getdate和setdata。第一版我修改的js和lamp.js的差別不大 就增加了一個log為了調試,修改調用路徑。 define(function (require) { var $ = require("common/zepto"); var util = require("util/util"); var ProcessBar = require("ui/process-bar");
var requestData = {
services: {
lightbulb: { alpha: 10 },
air_conditioner: {},
power_switch: {},
operation_status: { status: 0 }
},
device_type: util.getQuery("device_type"),
device_id: util.getQuery("device_id"),
user: '',
}; var lastModTime = 0; var powerBtn = $("#powerBtn"), // 開關按鈕 lightBar; function log(msg, arg) {
console.log(msg, arg);
msg = JSON.stringify(msg); if (arg) {
msg = msg + "," + JSON.stringify(arg);
}
$.post('/device/log', { msg: msg });
}
(function () {
bindEvent(); if (!requestData.device_id || !requestData.device_type) {
alert("頁面缺少參數"); return;
}
powerBtn.on("tap", togglePower); // 開關按鈕事件 initBar();
queryDevice();
})(); function bindEvent() {
$(".footer .nav_side li").click(function () {
activePage($(this).data("index"), $(this));
});
} function activePage(index, $self) {
$self.parent('li').addClass("on");
$body.find('.page:eq(' + index + ')').addClass("active").siblings().removeClass("active");
} /**
* 初始化進度條 */
function initBar() {
log("初始化lightBar");
lightBar = new ProcessBar({
$id: "lightBar",
min: 0,
stepCount: 100,
step: 1,
touchEnd: function (val) {
requestData.services.lightbulb.alpha = val;
log("亮度值為:" + val);
setData();
}
});
} /**
* 開關按鈕事件 */
function togglePower() {
$("#switchBtn").toggleClass("on").toggleClass("off"); if (requestData.services.operation_status.status == 0) {
requestData.services.operation_status.status = 1;
log("燈的狀態:1");
} else {
requestData.services.operation_status.status = 0;
log("燈的狀態:0");
}
setData();
} function queryDevice() {
$.getJSON('/device/RequestDeviceStatus', { reqstr: JSON.stringify(requestData) }, function (data) {
console.log(data); if (data.error_code == 0) { //請求成功; initInterval();
console.log("查詢成功");
} else {
alert(data.error_msg);
}
});
} /**
* 輪詢 */
function initInterval() {
getData();
setInterval(function () { if ((new Date().getTime() - lastModTime) > 2000) { // 當有設置操作時,停止1s輪詢,2秒后繼續輪詢 getData();
}
}, 1000);
} function setData() {
$.getJSON('/device/RequestDeviceStatus', { reqstr: JSON.stringify(requestData) }, function (data) {
console.log(data);
lastModTime = new Date().getTime(); if (data.error_code == 0) {
console.log("設置成功");
}
});
} function getData() {
$.post('/device/getData', function (data) {
$("#reData").html(JSON.stringify(data)); if (data && data.services) {
renderPage(data);
}
});
}; function renderPage(json) { if (!json.services) { return;
}
console.log("json", json);
requestData = json; if (requestData.services.operation_status.status == 0) {
$("#switchBtn").addClass("off").removeClass("on");
} else {
$("#switchBtn").addClass("on").removeClass("off");
}
lightBar.setVal(requestData.services.lightbulb.alpha);
}
})View Code 我將pageParam和device_status做成了一個對象。requestData。 var requestData = {
services: {
lightbulb: { alpha: 10 }, // air_conditioner: {}, power_switch: {},
operation_status: { status: 0 }
},
device_type: util.getQuery("device_type"),
device_id: util.getQuery("device_id"),
user: '',
};后臺就是兩個主要方法,一個設置(查詢頁就是設置),一個讀取。這里又回到上一節的內容了。我先查詢一次設備(lamp中在綁定)之后,再進入循環。 setdatapublic ActionResult RequestDeviceStatus(string reqstr)
{ if (string.IsNullOrEmpty(reqstr))
{ return Json("-1", JsonRequestBehavior.AllowGet);
} var args = JsonConvert.DeserializeObject<RequestData>(reqstr);
args.user = getOpenId(args.device_type, args.device_id);
Session["warmwood"] = args.device_id; //args.services.air_conditioner = null;
args.services.power_switch = null;
args.services.lightbulb.value_range = null; try
{ var res = wxDeviceService.RequestDeviceStatus(getToken(), args); if (res.error_code != 0)
{
Logger.Debug("error_code:" + res.error_code);
Logger.Debug("error_msg:" + res.error_msg);
} return Json(res, JsonRequestBehavior.AllowGet);
} catch (ErrorJsonResultException e)
{ if (e.JsonResult.errcode.ToString() == "access_token expired")
{ //重新獲取token }
Logger.Debug("請求失敗:" + e.Message);
} return Json("-1", JsonRequestBehavior.AllowGet);
}這個方法先將字符串轉成我們的RequestData對象,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; }
}services就是根據微信services定義的,可以參考上一節,然后用wxDeviceService請求。 var res = wxDeviceService.RequestDeviceStatus(getToken(), args); if (res.error_code != 0)
{
Logger.Debug("error_code:" + res.error_code);
Logger.Debug("error_msg:" + res.error_msg);
} return Json(res, JsonRequestBehavior.AllowGet);設置之后馬上會受到是否設置成功的響應,error_code 可能為50019(設置頻繁),50013(網絡問題)等等。真正的設備狀態是通過getdata獲得的。 getdata public JsonResult GetData()
{ var userdata = getUserWxData(); return Json(userdata.ResponseData, JsonRequestBehavior.AllowGet);
}getdata比較簡單就是返回數據,但是這個數據是在ReceiveWXMsg方法中設置的。這個上一節也講過,這是在公眾號后臺我們設置的一個地址。 public string ReceiveWXMsg()
{
//somecode
try
{ var userdata = getUserWxData(); var data = wxDeviceService.GetDeviceStatus(Request);
userdata.ResponseData = data;
Logger.Debug("ResponseData.asy_error_code:" + userdata.ResponseData.asy_error_code);
Logger.Debug("ResponseData.asy_error_msg:" + userdata.ResponseData.asy_error_msg);
setUserWxData(userdata);
} catch (Exception e)
{
Logger.Debug(e.Message);
} return echostr;
}wxDeviceService如下:
using System;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.Linq;using System.Net.Http;using System.Web;using Newtonsoft.Json;using Niqiu.Core.Domain.Common;using Senparc.Weixin;using Senparc.Weixin.Exceptions;using SendHelp= Senparc.Weixin.CommonAPIs.CommonJsonSend;namespace Portal.MVC.WXDevice
{ public class WxDeviceService:IWxDeviceService
{ //private readonly ICacheManager _cacheManager; //public WxDeviceService(ICacheManager cacheManager) //{ // _cacheManager = cacheManager; //}
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;
} public WxResponseData GetDeviceStatus(HttpRequestBase request)
{
Stream postData = request.InputStream;
StreamReader sRead = new StreamReader(postData); string postContent = sRead.ReadToEnd(); if (!string.IsNullOrEmpty(postContent))
{
Logger.Debug("收到數據:" + postContent);
} try
{ var data = JsonConvert.DeserializeObject<WxResponseData>(postContent);
data.rawStr = postContent;
Logger.Debug("轉換消息狀態:" + data.asy_error_msg); return data;
} catch (Exception e)
{
Logger.Debug(e.Message); throw;
}
} public OpenApiResult RequestDeviceStatus(string accessToken, RequestData data)
{ var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken); return SendHelp.Send<OpenApiResult>(accessToken, url, data);
} public OpenApiResult SetDevice(string accessToken, RequestData data)
{ var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken); return SendHelp.Send<OpenApiResult>(accessToken, url, data);
} public string GetOpenId(string accessToken,string deviceType,string deviceId)
{ try
{ var url = string.Format(WxDeviceConfig.GetOpenid, accessToken, deviceType, deviceId); var res = SendHelp.Send<OpenIdResult>(accessToken, url, null, CommonJsonSendType.GET); return res.GetOpenId();
} catch (ErrorJsonResultException e)
{
Logger.Debug(e.Message); throw;
}
}
}
}View Code 這方法讀到數據后就交給了userdata 緩存起來。在getdata方法中返回。 private UserWxData getUserWxData()
{ var target = _cacheManager.Get<UserWxData>(userKey) ?? new UserWxData(); return target;
} private string userKey
{ get
{ var key = Session["warmwood"] ?? Session.SessionID;
Session.Timeout = 240; return key.ToString();
}
}View Code UserWxData是我自定義的對象,包含了下面的幾個熟悉。 public class UserWxData
{ private WxResponseData _responseData; public UserWxData()
{
CreateTime = DateTime.Now;
} public DateTime CreateTime { get; set; } public TokenResult AccessToken { get; set; } public WxResponseData ResponseData
{ get { return _responseData??(_responseData=new WxResponseData()); } set { _responseData = value; }
} public string OpenId { get; set; }
}比較重要的是token和responseData。WxResponseData 也就是最終要發給頁面上的對象。包含你需要的功能的參數。 public class WxResponseData
{ public int asy_error_code { get; set; } public string asy_error_msg { get; set; } public string create_time { get; set; } public string msg_id { get; set; } /// <summary>
/// notify 說明是設備變更 /// set_resp 說明是設置設備 /// get_resp 說明獲取設備信息 /// </summary>
public string msg_type { get; set; } public string device_type { get; set; } public string device_id { get; set; } public object data { get; set; } public Service services { get; set; } public string user { get; set; } public string rawStr { get; set; }
}severices看自己的設備定義,比如我現在包含了空調,開關,溫度濕度。 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; } public tempe_humidity tempe_humidity { get; set; }
}到這兒,整個過程就講完了,獲取token和openid上一節講過,就不贅述了。如果后端是node的話,就不需要這么多的類型轉換了。 最后可以看下效果:
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章! 推薦閱讀: 以上就是微信硬件H5開發之控制燈光的詳細內容,更多請關注php中文網其它相關文章! 微信提供公眾平臺、朋友圈、消息推送等功能,用戶可以通過“搖一搖”、“搜索號碼”、“附近的人”、掃二維碼方式添加好友和關注公眾平臺,同時微信將內容分享給好友以及將用戶看到的精彩內容分享到微信朋友圈。 |
溫馨提示:喜歡本站的話,請收藏一下本站!