目录
实现
setdata
getdata
首页 微信小程序 微信开发 微信硬件H5开发之控制灯光

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

Mar 16, 2018 pm 02:27 PM
html5 控制 灯光

这次给大家带来微信硬件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中在绑定)之后,再进入循环。

setdata

public 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中文网其它相关文章!

推荐阅读:

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

JavaScript关于IE8兼容问题的处理

以上是微信硬件H5开发之控制灯光的详细内容。更多信息请关注PHP中文网其他相关文章!

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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

HTML 中的表格边框 HTML 中的表格边框 Sep 04, 2024 pm 04:49 PM

HTML 表格边框指南。在这里,我们以 HTML 中的表格边框为例,讨论定义表格边框的多种方法。

HTML 左边距 HTML 左边距 Sep 04, 2024 pm 04:48 PM

HTML 左边距指南。在这里,我们讨论 HTML margin-left 的简要概述及其示例及其代码实现。

HTML 中的嵌套表 HTML 中的嵌套表 Sep 04, 2024 pm 04:49 PM

这是 HTML 中嵌套表的指南。这里我们讨论如何在表中创建表以及相应的示例。

HTML 表格布局 HTML 表格布局 Sep 04, 2024 pm 04:54 PM

HTML 表格布局指南。在这里,我们详细讨论 HTML 表格布局的值以及示例和输出。

HTML 输入占位符 HTML 输入占位符 Sep 04, 2024 pm 04:54 PM

HTML 输入占位符指南。在这里,我们讨论 HTML 输入占位符的示例以及代码和输出。

HTML 有序列表 HTML 有序列表 Sep 04, 2024 pm 04:43 PM

HTML 有序列表指南。在这里我们还分别讨论了 HTML 有序列表和类型的介绍以及它们的示例

在 HTML 中移动文本 在 HTML 中移动文本 Sep 04, 2024 pm 04:45 PM

HTML 中的文本移动指南。在这里我们讨论一下marquee标签如何使用语法和实现示例。

HTML onclick 按钮 HTML onclick 按钮 Sep 04, 2024 pm 04:49 PM

HTML onclick 按钮指南。这里我们分别讨论它们的介绍、工作原理、示例以及各个事件中的onclick事件。

See all articles