一、提前准备
1.Android Studio IDE 软件
如果使用模拟器运行程序,应确保模拟器网络正常
2.设备已接入华为云IOTDA
至少设备在控制台显示在线并能够完成设备属性显示,可以参考这篇文章:STM32ExampleCode-12-ESP8266-IOT-HUAWEI.
3.MQTT.fx软件(可省略步骤)
MQTT.fx软件能够模拟设备登录
4.相关topic数据与格式
(1). 产品ID、设备ID、设备属性
(2). 服务ID、控制名称、控制参数
默认认为大家都完成了在上传设备属性和命令下发时,需要准备使用到服务ID、控制名称和控制参数,具体设置如下图:
(3). IAM账户
a) 简介
IAM账户对初次接触华为云物联网平台的应用侧开发的伙伴来说可能比较陌生,首先介绍一下在我们使用API时的流程,API的使用需要通过IAM服务鉴权来获取token,然后将这个Token作为API的参数之一,官方开发文档:华为云物联网平台——API使用指导,如下图:
b) 创建IAM账户
首先进入华为云控制台,在右上角进入统一身份认证平台:
然后点击右上角的创建用户:
然后按下图创建用户:
c) IAM账户登录测试
首先复制登录链接:
然后打开新的浏览器(推荐)或者退出当前账户登录,然后进入刚才复制的链接:
输入新建的IAM用户和密码:
能够登录即可,登陆后如下图所示:
(4).相关API
a) 获取Token
查看开发文档:Token认证,如下图,可知需要构建一个HTTP请求,并且按下述格式完成调用。
其中地区可以在控制台接入信息中的地址中查看,大家选择自己对应的地区,不要随意修改:
我的HTTP请求JSON数据格式:
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "funiot_xyz",
"password": "www.funiot.xyz",
"domain": {
"name": "hw_0086xxxxxxxxxxx"
}
}
}
},
"scope": {
"project": {
"name": "cn-north-4"
}
}
}
}
b) 获取设备属性(通过设备影子)
在完成token获取后我们即可进行其他API的调用,例如获取设备影子来解析设备属性,只需要利用HTTP完成对应URL的GET请求,然后解析响应数据即可。
GET https://{endpoint}/v5/iot/{project_id}/devices/{device_id}/shadow
c) 下发设备命令
同获取设备影子的API类似,也是利用HTTP完成对应URL的相应请求,然后解析响应数据即可,在下发设备命令时,我们暂时对返回结果不关心。
POST https://{endpoint}/v5/iot/{project_id}/devices/{device_id}/commands
{
"service_id" : "Dev_data",
"command_name" : "Control",
"paras" : {
"led" : "ON"
}
}
d) 终端节点Endpoint
大家可能看到了上面请求的URL链接中包含一个Endpoint信息,Endpoint为指定承载REST服务端点的服务器域名或IP,不同服务不同区域的Endpoint不同,我们可以在 控制台/总览/接入信息地区和终端节点中查看:
e) 准备信息汇总
截止到目前,我们准备的信息有:产品ID、设备ID、设备属性、用户名、IAM用户名、IAM用户密码、服务ID、控制名称、控制参数、相应API的数据格式等,接下来我们就可以开始安卓程序的开发了。
String HUAWEINAME=""; //华为账号名
String IAMINAME=""; //IAM账户名
String IAMPASSWORD=""; //IAM账户密码
String project_id=""; //产品ID
String device_id=""; //设备ID
String service_id=""; //服务ID
String commands=""; //命令名称
String token=""; //获取的token`
二、Android程序开发
1. 新建工程
2. 导入需要的Jar包
(1)下载Jar包
大家可以自行下载jackson-databind-2.8.1.jar、 jackson-core-2.8.1.jar 和jackson-annotations-2.8.1.jar,或在文末直接下载完整工程后提取
(2)切换目录
(3)导入Jar包
(4)应用Jar包
3. 补充网络权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
4. HuaweiIOT类(文末下载)
(1) 新建类,用于实现我们需要的所有方法
(2) 补充参数并新建相关方法
(3) 认证鉴权
根据华为云官方提供的帮助文档,我们可以知道调用API时需要一个Toekn的参数,获取toekn需要准备的参数有:IAM用户所属帐号名、IAM用户名、IAM用户密码、项目名称所属等,然后我们将准备的参数填入上述的JSON数据体,例如:
String postbody="{"+"""+"auth"+"""+": {"+"""+"identity"+"""+": {"+"""+"methods"+"""+": ["+"""+"password"+"""+"],"+"""+"password"+"""+": {"+"""+"user"+"""+":{"+"""+"domain": {"name": ""},"name": "","password": "********"}}},"scope": {"project": {"name": "cn-north-4"}}}}";
String strurl="https://iam.cn-north-4.myhuaweicloud.com"+"/v3/auth/tokens?nocatalog=false";
现在我们写一个获取token的方法:
public String gettoken( )throws Exception
{
String strurl="";
strurl="https://iam.cn-north-4.myhuaweicloud.com"+"/v3/auth/tokens?nocatalog=false";
String tokenstr="{"+"\""+"auth"+"\""+": {"+"\""+"identity"+"\""+": {"+"\""+"methods"+"\""+": ["+"\""+"password"+"\""+"],"+"\""+"password"+"\""+": {"+"\""+"user"+"\""+":{"+"\""+"domain\": {\"name\": \""+HUAWEINAME+"\"},\"name\": \""+IAMINAME+"\",\"password\": \""+IAMPASSWORD+"\"}}},\"scope\": {\"project\": {\"name\": \"cn-north-4\"}}}}";
URL url = new URL(strurl);
HttpURLConnection urlCon = (HttpURLConnection)url.openConnection();
urlCon.addRequestProperty("Content-Type", "application/json;charset=utf8");
urlCon.setDoOutput(true);
urlCon.setRequestMethod("POST");
urlCon.setUseCaches(false);
urlCon.setInstanceFollowRedirects(true);
urlCon.connect();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(urlCon.getOutputStream(),"UTF-8"));
writer.write(tokenstr);
writer.flush();
writer.close();
Map headers = urlCon.getHeaderFields();
Set<String> keys = headers.keySet();
/*for( String key : keys ){
String val = urlCon.getHeaderField(key);
System.out.println(key+" "+val);
}*/
String token = urlCon.getHeaderField("X-Subject-Token");
System.out.println("X-Subject-Token"+":"+token);
return token;
}
然后我们在主函数中调用一下,并检查控制台的输出
//用子线程进行网络通信
final Thread t = new Thread() {
@Override
public void run() {
try {
HuaweiIOT huaweiiot=new HuaweiIOT();
}
catch (Exception e) {
e.printStackTrace();
System.out.println("获取失败:"+e.toString());
}
}
};
t.start();
【延伸】由于我们获取的TOKEN是有24小时有效期的,大家也可以学着华为云提供的java Demo在获取token以后去生成token.text文件,当过期时再重新获取,否则直接读取token保存的数据,本教程不在次延伸扩展。
接下来我们完成通过查询设备属性影子来获取设备属性信息。
(3) 获取设备影子(解析属性和状态)
public String getAtt(String att,String mode) throws Exception{
String strurl="";
if(mode=="shadow") strurl="https://%s/v5/iot/%s/devices/%s/shadow";
if(mode=="status") strurl="https://%s/v5/iot/%s/devices/%s";
strurl = String.format(strurl, endpoint,project_id,device_id);
URL url = new URL(strurl);
HttpURLConnection urlCon = (HttpURLConnection)url.openConnection();
urlCon.addRequestProperty("Content-Type", "application/json");
urlCon.addRequestProperty("X-Auth-Token",token);
urlCon.connect();
InputStreamReader is = new InputStreamReader(urlCon.getInputStream());
BufferedReader bufferedReader = new BufferedReader(is);
StringBuffer strBuffer = new StringBuffer();
String line = null;
while ((line = bufferedReader.readLine()) != null) {
strBuffer.append(line);
}
is.close();
urlCon.disconnect();
String result = strBuffer.toString();
System.out.println(result);
if(mode=="shadow")
{
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(result, JsonNode.class);
JsonNode tempNode = jsonNode.get("shadow").get(0).get("reported").get("properties").get(att);
String attvaluestr = tempNode.asText();
System.out.println(att+"=" + attvaluestr);
return attvaluestr;
}
if(mode=="status")
{
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(result, JsonNode.class);
JsonNode statusNode = jsonNode.get("status");
String statusstr = statusNode.asText();
System.out.println("status = " + statusstr);
return statusstr;
}
return "error";
}
(4) 命令下发
public String setCom(String com,String value) throws Exception{
String strurl="";
strurl="https://%s"+"/v5/iot/%s/devices/%s/commands";
strurl = String.format(strurl, endpoint,project_id,device_id);
URL url = new URL(strurl);
HttpURLConnection urlCon = (HttpURLConnection)url.openConnection();
urlCon.addRequestProperty("Content-Type", "application/json");
urlCon.addRequestProperty("X-Auth-Token",token);
urlCon.setRequestMethod("POST");
urlCon.setDoOutput(true);
urlCon.setUseCaches(false);
urlCon.setInstanceFollowRedirects(true);
urlCon.connect();
String body = "{\"paras\":{\""+com+"\""+":"+value+"},\"service_id\":\""+service_id+"\",\"command_name\":\""+commands+"\"}";
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(urlCon.getOutputStream(),"UTF-8"));
writer.write(body);
writer.flush();
writer.close();
InputStreamReader is = new InputStreamReader(urlCon.getInputStream());
BufferedReader bufferedReader = new BufferedReader(is);
StringBuffer strBuffer = new StringBuffer();
String line = null;
while ((line = bufferedReader.readLine()) != null) {
strBuffer.append(line);
}
is.close();
urlCon.disconnect();
String result = strBuffer.toString();
System.out.println(result);
return result;
}
我们补全代码后在主函数调用一下,例如:
//用子线程进行网络通信
final Thread t = new Thread() {
@Override
public void run() {
try {
HuaweiIOT huaweiiot=new HuaweiIOT();
System.out.println("获取温度");
String resut=huaweiiot.getAtt("temp","shadow");
System.out.println("获取温度:"+resut);
resut=huaweiiot.getAtt("humi","shadow");
System.out.println("获取湿度:"+resut);
resut=huaweiiot.getAtt("","status");
System.out.println("获取状态:"+resut);
if(resut.equals("ONLINE"))
huaweiiot.setCom("led","0");
}
catch (Exception e) {
e.printStackTrace();
System.out.println("获取失败:"+e.toString());
}
}
};
t.start();