0

0

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

php中世界最好的语言

php中世界最好的语言

发布时间:2018-03-16 14:27:00

|

3801人浏览过

|

来源于php中文网

原创

这次给大家带来微信硬件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;
}
    
    
    我的灯泡
    
    
    

ON OFF

灯已开

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中在绑定)之后,再进入循环。

setdata

public ActionResult RequestDeviceStatus(string reqstr)
        {            if (string.IsNullOrEmpty(reqstr))
            {                return Json("-1", JsonRequestBehavior.AllowGet);
            }            var args = JsonConvert.DeserializeObject(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(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(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(accessToken, url, data);
        }        public OpenApiResult SetDevice(string accessToken, RequestData data)
        {            var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken);            return SendHelp.Send(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(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(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; }        /// 
        /// notify 说明是设备变更        /// set_resp 说明是设置设备        /// get_resp 说明获取设备信息        /// 
        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中文网其它相关文章!

推荐阅读:

独响
独响

一个轻笔记+角色扮演的app

下载

JS里特别好用的轻量级日期插件

JavaScript关于IE8兼容问题的处理

相关文章

微信app下载
微信app下载

微信是一款手机通信软件,支持通过手机网络发送语音短信、视频、图片和文字。微信可以单聊及群聊,还能根据地理位置找到附近的人,带给大家全新的移动沟通体验,有需要的小伙伴快来保存下载体验吧!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

28

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

8

2026.01.26

苹果官方查询网站 苹果手机正品激活查询入口
苹果官方查询网站 苹果手机正品激活查询入口

苹果官方查询网站主要通过 checkcoverage.apple.com/cn/zh/ 进行,可用于查询序列号(SN)对应的保修状态、激活日期及技术支持服务。此外,查找丢失设备请使用 iCloud.com/find,购买信息与物流可访问 Apple (中国大陆) 订单状态页面。

31

2026.01.26

npd人格什么意思 npd人格有什么特征
npd人格什么意思 npd人格有什么特征

NPD(Narcissistic Personality Disorder)即自恋型人格障碍,是一种心理健康问题,特点是极度夸大自我重要性、需要过度赞美与关注,同时极度缺乏共情能力,背后常掩藏着低自尊和不安全感,影响人际关系、工作和生活,通常在青少年时期开始显现,需由专业人士诊断。

3

2026.01.26

windows安全中心怎么关闭 windows安全中心怎么执行操作
windows安全中心怎么关闭 windows安全中心怎么执行操作

关闭Windows安全中心(Windows Defender)可通过系统设置暂时关闭,或使用组策略/注册表永久关闭。最简单的方法是:进入设置 > 隐私和安全性 > Windows安全中心 > 病毒和威胁防护 > 管理设置,将实时保护等选项关闭。

5

2026.01.26

2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】
2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】

铁路12306提供起售时间查询、起售提醒、购票预填、候补购票及误购限时免费退票五项服务,并强调官方渠道唯一性与信息安全。

35

2026.01.26

个人所得税税率表2026 个人所得税率最新税率表
个人所得税税率表2026 个人所得税率最新税率表

以工资薪金所得为例,应纳税额 = 应纳税所得额 × 税率 - 速算扣除数。应纳税所得额 = 月度收入 - 5000 元 - 专项扣除 - 专项附加扣除 - 依法确定的其他扣除。假设某员工月工资 10000 元,专项扣除 1000 元,专项附加扣除 2000 元,当月应纳税所得额为 10000 - 5000 - 1000 - 2000 = 2000 元,对应税率为 3%,速算扣除数为 0,则当月应纳税额为 2000×3% = 60 元。

12

2026.01.26

oppo云服务官网登录入口 oppo云服务登录手机版
oppo云服务官网登录入口 oppo云服务登录手机版

oppo云服务https://cloud.oppo.com/可以在云端安全存储您的照片、视频、联系人、便签等重要数据。当您的手机数据意外丢失或者需要更换手机时,可以随时将这些存储在云端的数据快速恢复到手机中。

40

2026.01.26

抖币充值官方网站 抖币性价比充值链接地址
抖币充值官方网站 抖币性价比充值链接地址

网页端充值步骤:打开浏览器,输入https://www.douyin.com,登录账号;点击右上角头像,选择“钱包”;进入“充值中心”,操作和APP端一致。注意:切勿通过第三方链接、二维码充值,谨防受骗

7

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 6.8万人学习

HTML+CSS基础与实战
HTML+CSS基础与实战

共132课时 | 9.7万人学习

前端开发(基础+实战项目合集)
前端开发(基础+实战项目合集)

共60课时 | 3.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号