这次给大家带来微信硬件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开发之控制灯光的详细内容。