这次给大家带来微信硬件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; }nbsp;html meta meta title 我的灯泡 /title link link p /p p /p p /p p /p p /p p /p p i /i span ON /span span OFF /span /p p /p h2 灯已开 /h2 p /p p /p p i /i /p p /p p /p p /p i /i p span /span /p i /i script /script script seajs.config({ base: #39;/js/ #39;, //map: [[/^(.*\.(?:css|js))(.*)$/i, $1 ]], charset: #39;utf-8 #39; seajs.use( baby /script
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 amp; amp; 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对象,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获得的。
getdatapublic 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; } } } } /openidresult /openapiresult /openapiresult /wxresponsedata /tokenresult
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(); } } /userwxdata
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中文网其它相关文章!
推荐阅读:
JS里特别好用的轻量级日期插件
JavaScript关于IE8兼容问题的处理
以上就是微信硬件H5开发之控制灯光的详细内容,更多请关注php中文网其它相关文章!
微信app下载
微信是一款手机通信软件,支持通过手机网络发送语音短信、视频、图片和文字。微信可以单聊及群聊,还能根据地理位置找到附近的人,带给大家全新的移动沟通体验,有需要的小伙伴快来保存下载体验吧!