您好,欢迎访问一九零五行业门户网

微信硬件H5开发之控制灯光

这次给大家带来微信硬件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的话,就不需要这么多的类型转换了。
最后可以看下效果:
相信看了本文案例你已经掌握了方法,更多精彩请关注其它相关文章!
推荐阅读:
js里特别好用的轻量级日期插件
javascript关于ie8兼容问题的处理
以上就是微信硬件h5开发之控制灯光的详细内容。
其它类似信息

推荐信息