Maison  >  Article  >  Applet WeChat  >  Le développement de WeChat H5 appelle openApi

Le développement de WeChat H5 appelle openApi

Y2J
Y2Joriginal
2017-05-09 09:54:414199parcourir

WeChat Hardware Platform est une solution IOT lancée par WeChat pour connecter les objets et les personnes, et les objets et les objets. En d’autres termes, divers appareils intelligents peuvent être contrôlés via WeChat. Par exemple, certains appareils Bluetooth, climatiseurs, téléviseurs, etc.

Je ne connais pas grand chose en matériel (même si je me spécialise en information électronique). Le matériel est travaillé par deux étudiants diplômés de l'Université de Beihang dans une petite entreprise. équipe, je suis responsable du développement des panneaux personnalisés H5. Tout à l'heure, j'ai commencé à lire les documents officiels et j'étais confus concernant jssdk, jsapi, Airkiss, openApi et la connexion directe. SDK, je ne savais pas lequel utiliser. Poser des questions sur le forum officiel n'a pratiquement aucun résultat, j'ai rejoint plusieurs groupes de matériel WeChat pour poser des questions et j'ai découvert que de nombreux développeurs étaient comme moi. groupes, et le tableau était si triste. Si vous envoyez un e-mail à wxthings et posez une question, ce serait bien si vous pouviez obtenir une réponse, qui ne contient souvent que quelques mots. Après tant de plaintes, je dois encore résoudre le problème. Après tout, la capacité de l'appareil à se connecter à WeChat est un argument de vente important, j'ai récemment découvert quelque chose, alors j'ai écrit cet article.

1. Processus d'accès

En d'autres termes, vous devez d'abord avoir un compte public, puis activer les fonctions de l'appareil et ajouter des produits (c'est-à-dire votre appareils intelligents). Les documents officiels sur ces processus sont relativement clairs, je n’entrerai donc pas dans les détails. La solution d'accès que nous avons choisie est la solution d'accès standard WeChat Hardware Cloud.

Panneau de paramètres

Le développement du panneau H5 dont je parle fait référence à une page de contrôle H5 ouverte dans WeChat, à la manière dont elle communique avec le WeChat Hardware Cloud et à la manière de lire et de configurer l'appareil. statut. Dans le processus d'ajout de produits, il existe un panneau de configuration

Si vous choisissez le panneau standard, WeChat propose officiellement trois types de panneaux standard :

sont respectivement les climatiseurs, les interrupteurs et les lumières. S'il est personnalisé, entrez simplement l'adresse. S'il s'agit d'un panel standard, vous n'avez pas besoin de serveur, mais s'il s'agit d'un panel personnalisé, vous devez avoir votre propre serveur, sinon vous ne pourrez pas traiter les messages envoyés depuis WeChat Cloud .

Activer la configuration du serveur

Lors de la définition de l'adresse du serveur, veuillez noter que vous devez traiter la réponse en fonction de ses exigences avant de pouvoir l'activer avec succès.

Lorsque vous cliquez sur Activer, WeChat Cloud enverra une signature, un horodatage , un nombre aléatoire et une chaîne aléatoire , Après vérification, renvoyez la chaîne aléatoire Une fois que WeChat Cloud reçoit la chaîne aléatoire renvoyée par vous, elle peut être activée avec succès. Par exemple, si l'adresse que vous définissez est http://www.xxx.com/device/ReceiveWXMsg, téléchargez d'abord le code sur le serveur , puis cliquez sur Activer, WeChat Cloud y publiera les données. adresse. Chaque fois que WeChat envoie des données au serveur, cette vérification sera envoyée en premier (c'est-à-dire que si elle est renvoyée sans vérification, il y aura des problèmes de sécurité) .


  public string ReceiveWXMsg()
        {            var signature = Request.QueryString["signature"];            var timestamp = Request.QueryString["timestamp"];            var echostr = Request.QueryString["echostr"];            var nonce = Request.QueryString["nonce"];
            Logger.Debug("signature:" + signature);
            Logger.Debug("timestamp:" + timestamp);
            Logger.Debug("nonce:" + nonce);
            Logger.Debug("echostr:" + echostr);            //验证 
            return echostr;
        }

Voir le code

Revenez simplement directement, ne n'ajoutez pas A json ou quelque chose comme ça. A quoi sert cette adresse ?

2. Méthodes de communication

Wifi设备和蓝牙设备是不同的,蓝牙使用Airsync协议和微信通信,而wifi设备的话,我们在自己的服务器上调用微信提供的openApi获取或设置设备状态,微信服务器就会把数据以json格式post到我们上面设置的那个地址。硬件方面wifi设备可以使用微信提供的直连SDK来控制设备。

添加完设备,设置好服务器,你还需要硬件的同学打开设备,帮定你的微信。从微信的设置-->设备-->选择设备-->打开面板。你就可以看到设备并进行控制了。

三、调用openApi

说了这么多前提工作,终于进入调用api环节。可以先看一下openApi的官方文档:iot.weixin.qq.com/wiki/doc/hardwarecloud/openapi.pdf

文档里面主要讲了三个方法,一个是查询设备状态,一个是设置设备状态,一个是接受设备状态变化的消息,然后是一些错误信息等。但观察api就会发现我们还需要两个重要的参数,一个是access_token,一个是用户的openid。还说明一点,网页是Asp.net mvc

1.获取access_token

官方有一个接口调试页面:mp.weixin.qq.com/debug/ ,获取access_token需要appid和secret。

而这两个值,是在公众号后台的基本配置中查看,secret是个很重要的参数,所以请保密好。

查看密钥还需要扫二维码得到管理员的确认...  拿到这两个参数了,我们就可以生成token了。注意返回的json是一个token字符串再加一个超时时间。定义一个TokenResult:

public class TokenResult
    {        public string access_token { get; set; }        public int expires_in { get; set; }
    }

要注意的一点是,token两小时后会过期。所以在我们的代码里面需要检查是否超时,然后自动重新获取。

 public const string AccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";

 getAccessToken方法定义在一个服务类中,比如WxDeviceService

using SendHelp= Senparc.Weixin.CommonAPIs.CommonJsonSend;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;
        }

使用Senparc.Weixin框架封装好的api来处理请求。

2.获取openid

这个参数在查询和设置设备状态的时候会用到,对应user参数名。获取openid需要三个参数,access_token已经有了,然后需要device_type和device_id

 public const string GetOpenid ="https://api.weixin.qq.com/device/get_openid?access_token={0}&device_type={1}&device_id={2}";

而type和id是前端页面传过来的,当用户微信中打开控制面板的时候,微信会自动将这两个参数追加到url后面。而这个url的返回值结构是这样:

{"open_id":["oxa1otw5sk-Azgd8mx1bmBqoM2_E","oxa1ot8-j9j5bYUJJyAexe9d41_Y","oxa1ot5QTdxn0xNQ0DmYzoN0tUp1"],"resp_msg":{"ret_code":0,"error_info":"ok"}}

openid部分是一个数组,实际使用的是第三个(我目前也不知道前面两个id是干啥的),定义一个openidResult:

   List<> List<> {  _openId??(_openId= List<> { _openId = resp_msg resp_msg { ;

service中:

  public string GetOpenId(string accessToken,string deviceType,string deviceId)
        {                var url = string.Format(WxDeviceConfig.GetOpenid, accessToken, deviceType, deviceId);                var res = SendHelp.Send<OpenIdResult>(accessToken, url, null, CommonJsonSendType.GET);                return res.GetOpenId();
        }

如果你的devicetype和deviceId错误,微信会返回一个不太恰当的错误json:

对比错误代码列表,我开始以为微信服务器出错了。其实是我参数填错了。

 

遇到的错误,将不止官方文档公布的这几个。

3.查询设备状态

查询设备和上面的这两个方法略有不同,因为流程是这的,我们的服务器先像向微信服务器请求,微信接受到请求后马上返回一个确认json,然后微信服务器会马上把数据post我们前面设置的那个地址上。

请求url:

      GetDeviceStatusUrl=

从官方文档可以看到,查询设备还需要另外一个重要的参数,它包含device_type和device_id,services,user,data。

 

需要说明一下的就是services,意思是指设备的能力项,也就是你要查询设备的哪些属性,这个在设置设备的时候一样用到。完成的产品能力定义请看:http://iot.weixin.qq.com/wiki/doc/intro/%E4%BA%A7%E5%93%81%E8%83%BD%E5%8A%9B%E5%AE%9A%E4%B9%89%E6%8C%87%E5%BC%95%20V1.2.pdf 

因此定义一个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; }
    }
 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; }
    }

service包括两个部分,一个是能力部分,好比上面这个service,就包含了三种能力,灯、空调以及开关(这只是测试,不是真正产品的能力)。和一个操作状态。操作状态就是指这个设备是否开着或者关闭了。而每一个能力,又包括两部分,拿灯来说:

    public class lightbulb
    {        public int alpha { get; set; }        public lightbulb_value_range value_range { get; set; }
    }    public class lightbulb_value_range
    {        public string alpha { get; set; }
    }

灯有一个亮度值,和一个范围属性。范围值中包含了最大和最小值以及单位值。

"lightbulb":{"alpha":10,"value_range":{"alpha":"0|100|1"}},"

这表示灯的亮度是10,返回是0到100,每次可以调节1个单位。

发送查询请求后,微信返回一个json,定义对象为下:

 public class OpenApiResult
    {        public int error_code { get; set; }        public string error_msg { get; set; }        public string msg_id { get; set; }
    }

 WxDeviceService中:

   public OpenApiResult RequestDeviceStatus(string accessToken, RequestData data)
        {            var url = string.Format(WxDeviceConfig.GetDeviceStatusUrl, accessToken);            return SendHelp.Send<OpenApiResult>(accessToken, url, data);
        }

4.接受消息

那么问题来了,如何接受post过来的数据,以及如何存储呢。微信post的数据格式如下:

定义了一个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; }
    }

View Code

msg_type代表着不同类型的消息, notify 说明是设备变更,set_resp 说明是设置设备 get_resp 说明获取设备信息。在WxDeviceService中增加GetDeviceStatus方法:

   public T GetWxResponse<T>(HttpRequestBase request)
        {
            Stream postData = request.InputStream;
            StreamReader sRead = new StreamReader(postData);            string postContent = sRead.ReadToEnd();            if (!string.IsNullOrEmpty(postContent))
            {
                Logger.Debug("收到数据:"+postContent);
            }            try
            {                return JsonConvert.DeserializeObject<T>(postContent);
            }            catch (Exception e)
            {
                Logger.Debug(e.Message);                throw;
            }
        }        public WxResponseData GetDeviceStatus(HttpRequestBase request)
        {            return GetWxResponse<WxResponseData>(request);
        }

需要先读取请求中的json字符串然后转换成C#对象。然后在最初启用的ReceiveWXMsg方法中随时准备接受消息:

  public string ReceiveWXMsg()
        {            var signature = Request.QueryString["signature"];            var timestamp = Request.QueryString["timestamp"];            var echostr = Request.QueryString["echostr"];            var nonce = Request.QueryString["nonce"];
            Logger.Debug("signature:" + signature);
            Logger.Debug("timestamp:" + timestamp);
            Logger.Debug("nonce:" + nonce);
            Logger.Debug("echostr:" + echostr);
            try
            {                var userdata = getUserWxData();                var data = wxDeviceService.GetDeviceStatus(Request);
                userdata.ResponseData = data;                setUserWxData(userdata);
            }            catch (Exception e)
            {
                Logger.Debug(e.Message);
            }            return echostr;
        }

因为读取到的数据需要及时呈现给页面,所以这里选用了缓存来存储设备信息以及用户相关信息。

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; }
    }

View Code

  private UserWxData getUserWxData()
        {            var target = _cacheManager.Get<UserWxData>(userKey) ?? new UserWxData();            return target;
        }        private string userKey
        {            get
            {                return Session.SessionID;
            }
        }        private void setUserWxData(UserWxData data)
        {
            _cacheManager.Set(userKey, data, 7200);
        }

View Code

缓存是Nop中的MemoryCacheManager:

using System;using System.Collections.Generic;using System.Runtime.Caching;using System.Text.RegularExpressions;namespace Niqiu.Core.Domain.Common
{    /// <summary>
    /// Represents a manager for caching between HTTP requests (long term caching)    /// </summary>
    public partial class MemoryCacheManager : ICacheManager
    {        protected ObjectCache Cache
        {            get
            {                return MemoryCache.Default;
            }
        }        
        /// <summary>
        /// Gets or sets the value associated with the specified key.        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="key">The key of the value to get.</param>
        /// <returns>The value associated with the specified key.</returns>
        public virtual T Get<T>(string key)
        {            return (T)Cache[key];
        }        /// <summary>
        /// Adds the specified key and object to the cache.        /// </summary>
        /// <param name="key">key</param>
        /// <param name="data">Data</param>
        /// <param name="cacheTime">Cache time</param>
        public virtual void Set(string key, object data, int cacheTime)
        {            if (data == null)                return;            var policy = new CacheItemPolicy {AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime)};
            Cache.Add(new CacheItem(key, data), policy);
        }        /// <summary>
        /// Gets a value indicating whether the value associated with the specified key is cached        /// </summary>
        /// <param name="key">key</param>
        /// <returns>Result</returns>
        public virtual bool IsSet(string key)
        {            return (Cache.Contains(key));
        }        /// <summary>
        /// Removes the value with the specified key from the cache        /// </summary>
        /// <param name="key">/key</param>
        public virtual void Remove(string key)
        {
            Cache.Remove(key);
        }        /// <summary>
        /// Removes items by pattern        /// </summary>
        /// <param name="pattern">pattern</param>
        public virtual void RemoveByPattern(string pattern)
        {            var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);            var keysToRemove = new List<String>();            foreach (var item in Cache)                if (regex.IsMatch(item.Key))
                    keysToRemove.Add(item.Key);            foreach (string key in keysToRemove)
            {
                Remove(key);
            }
        }        /// <summary>
        /// Clear all cache data        /// </summary>
        public virtual void Clear()
        {            foreach (var item in Cache)
                Remove(item.Key);
        }
    }
}

View Code

而为什么不是PerRequestCacheManager呢,想一想~

using System;using System.Collections;using System.Collections.Generic;using System.Text.RegularExpressions;using System.Web;namespace Niqiu.Core.Domain.Common
{    /// <summary>
    /// Represents a manager for caching during an HTTP request (short term caching)    /// </summary>
    public partial class PerRequestCacheManager : ICacheManager
    {        /// <summary>
        /// Ctor        /// </summary>
        /// <param name="context">Context</param>
        //public PerRequestCacheManager(HttpContextBase context)        //{        //    this._context = context;        //}
        
        /// <summary>
        /// Creates a new instance of the NopRequestCache class        /// </summary>
        protected virtual IDictionary GetItems()
        {            if (_context != null)                return _context.Items;            return null;
        }        //不用注入
        private HttpContextBase _context
        {            get { return new HttpContextWrapper(HttpContext.Current); }
        }        /// <summary>
        /// Gets or sets the value associated with the specified key.        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="key">The key of the value to get.</param>
        /// <returns>The value associated with the specified key.</returns>
        public virtual T Get<T>(string key)
        {            var items = GetItems();            if (items == null)                return default(T);            return (T)items[key];
        }        /// <summary>
        /// Adds the specified key and object to the cache.        /// </summary>
        /// <param name="key">key</param>
        /// <param name="data">Data</param>
        /// <param name="cacheTime">Cache time</param>
        public virtual void Set(string key, object data, int cacheTime)
        {            var items = GetItems();            if (items == null)                return;            if (data != null)
            {                if (items.Contains(key))
                    items[key] = data;                else
                    items.Add(key, data);
            }
        }        /// <summary>
        /// Gets a value indicating whether the value associated with the specified key is cached        /// </summary>
        /// <param name="key">key</param>
        /// <returns>Result</returns>
        public virtual bool IsSet(string key)
        {            var items = GetItems();            if (items == null)                return false;            
            return (items[key] != null);
        }        /// <summary>
        /// Removes the value with the specified key from the cache        /// </summary>
        /// <param name="key">/key</param>
        public virtual void Remove(string key)
        {            var items = GetItems();            if (items == null)                return;

            items.Remove(key);
        }        /// <summary>
        /// Removes items by pattern        /// </summary>
        /// <param name="pattern">pattern</param>
        public virtual void RemoveByPattern(string pattern)
        {            var items = GetItems();            if (items == null)                return;            var enumerator = items.GetEnumerator();            var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);            var keysToRemove = new List<String>();            while (enumerator.MoveNext())
            {                if (regex.IsMatch(enumerator.Key.ToString()))
                {
                    keysToRemove.Add(enumerator.Key.ToString());
                }
            }            foreach (string key in keysToRemove)
            {
                items.Remove(key);
            }
        }        /// <summary>
        /// Clear all cache data        /// </summary>
        public virtual void Clear()
        {            var items = GetItems();            if (items == null)                return;            var enumerator = items.GetEnumerator();            var keysToRemove = new List<String>();            while (enumerator.MoveNext())
            {
                keysToRemove.Add(enumerator.Key.ToString());
            }            foreach (string key in keysToRemove)
            {
                items.Remove(key);
            }
        }
    }
}

View Code

5.设置设备状态

有了前面几步,这里也好说了。url:

    SetDeviceUrl =

设置设备的参数和请求是一样的,

 public OpenApiResult SetDevice(string accessToken, RequestData data)
        {            var url = string.Format(WxDeviceConfig.SetDeviceUrl, accessToken);            return SendHelp.Send<OpenApiResult>(accessToken, url, data);
        }

View Code

调用openApi基本上就这样了,如有不完善的地方还请指正。 这个方法有权限的问题,可以用查询方法代替,同样可以改变设备状态。不知道这api是个什么鬼。

四、常见错误

 

如果硬件通信没有开启这个能力,去查询的会报这个错误。

刚开始看到"device not login"实在没明白什么意思,文档里也没说明这个错误。设备还需要什么登录?原来是硬件同学没有连接设备... ORZ

如果你的requestData结构不对,特别是附加的那个Data参数只能是字符串,不要写成空对象{},就会出现这个错误。

token超时

同一个设备同时被操作。

【相关推荐】

1. 微信公众号平台源码下载

2. 微信投票源码下载

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn