大家好,上一期发布的教程叫大家如何利用华为云物联网平台提供的官方Java Demo去编写Java上位机程序,由于主要是用的是GET请求去查询设备影子和查询设备,之后接到了很多小伙伴私信咨询POST请求的实现,现在带着大家,写一个不基于官方Java Demo,而是完全参考于华为云帮助文档去写的一个Android应用,具体用到了POST请求获取鉴权的Token、设备命令下发,Get请求获取设备属性影子数据(依旧是用于解析属性)和查询设备(依旧是用于解析设备在线状态),先给大家看一下最终效果:


根据华为云官方提供的帮助文档,我们可以知道,在调用接口前,我们需要完成认证鉴权,我们本次教程采用的是Token认证(帮助文档参考链接:认证鉴权_设备接入 IoTDA_API参考_应用侧API参考_如何调用API_华为云)

上图的最后一行,我们可以看到,官方给出了一个调用IAM用户Token(使用密码)的帮助信息

通过上述界面我们可以知道,该接口的URI以及需要准备的参数,大家可以仔细阅读,教程中我们直接查看页面中的示例:

我们需要准备的参数有:
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 static String gettoken() throws Exception
{
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";
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(postbody);
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);
}
return null;
}
然后我们在主函数中调用一下
public httpGetHuaweiIOT()throws Exception
{
gettoken();
}
我们选择运行

我们检查控制台的输出

接口返回的响应消息头中“X-Subject-Token”就是需要获取的用户Token,此时我们可以修改一下函数,在函数最后加上这两行:
String token = urlCon.getHeaderField("X-Subject-Token");
System.out.println("X-Subject-Token"+" : "+token);
返回值null改为return token;我们再run一下
此时我们的认证鉴权就完事了。(文章后面我们会修改类,把gettoken函数写在类的构造函数内调用)
【延伸】现在的postbody参数时我们写死的,大家也可以把参数做成变量传递进去;由于我们获取的TOKEN是有24小时有效期的,大家也可以学着华为云提供的java Demo在获取token以后去生成token.text文件,当过期时再重新获取,否则直接读取token保存的数据,本教程不在次延伸扩展。
接下来我们完成通过查询设备属性影子来获取设备属性信息。
首先我们查看华为云官方提供的帮助文档(https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html)

我们需要的URI

我们可以这么拼接URI
String strurl="https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s/shadow";
String project_id="*********";
String device_id="*********";
strurl = String.format(strurl, project_id,device_id);
需要注意的是,除了上述两个必备参数以外,我们的token在这就派上用场了

写程序之前,我们先导入安装一个jackson-databind、jackson-core、jackson-annotations的jar,我用的是jackson-databind-2.8.1.jar、 jackson-core-2.8.1.jar 和jackson-annotations-2.8.1.jar (大家可在网站上搜索下载,或者在文章末尾下载)

我们切换为Java的project目录,在app/libs中粘贴并载入jar包


剩下的两个jar包也是这样操作,如下图所示

然后我们在类头中引入我们用到的路径

接下来我们编写获取设备影子数据函数:
public static String getdev() throws Exception
{
String strurl="https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s/shadow";
String project_id="*********";
String device_id="********* ";
strurl = String.format(strurl, project_id,device_id);
String token=gettoken();
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);
return null;
}
主函数中run一下
public static void main(String[] args)throws Exception {
//gettoken();
getdevshadow();
}
我们在控制台可以看到第一行是获取的token,第二行是我们得到的设备影子json数据

大家可以在华为云设备接入IOTDA的控制台修改产品:

我们检查一下我们需要的数据:

在华为云控制台那可以看一下:

为了清楚的查看,我们格式化一下:

其中的“report”->”properties”内的数据为我们要解析的设备属性,我们在函数中加上下面几句进行提取
比如我们获取temp属性
String pro="temp";
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(result, JsonNode.class);
JsonNode tempNode = jsonNode.get("shadow").get(0).get("reported").get("properties").get(pro);
String attvaluestr = tempNode.asText();
System.out.println(pro+"=" + attvaluestr);
运行一下

为了方便调用,我们改造一下函数定义,给他添上参数,改为:
public static String getdev(String pro) throws Exception{}
主函数这么调用:
getdev("temp");
getdev("humi");
getdev("light");
我们跑一下:

为了能够调用,我们最后要把return null;改为return attvaluestr;
同样我们查看华为云的帮助文档

我们可以得主要区别是
URI不同: "https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s;
解析的数据不同,我们还是在getdev函数中进行修改,再给他加一个参数,如
public static String getdev(String mode,String pro) throws Exception
{
String strurl="";
if(mode.equals("shadow")) strurl="https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s/shadow";
else if(mode.equals("status")) strurl="https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s";
//-----下面的程序不做变化
//-----省略
}
主函数调用就改为:
getdev("shadow","temp");
getdev("shadow","humi");
getdev("shadow","light");
运行结果是一样的
然后我们把最后的影子解析先注释掉:
//String pro="temp";
/*ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(result, JsonNode.class);
JsonNode tempNode = jsonNode.get("shadow").get(0).get("reported").get("properties").get(pro);
String attvaluestr = tempNode.asText();
System.out.println(pro+"=" + attvaluestr);*/
//return attvaluestr;
加上return null;然后主函数添加:
getdev("status","");
运行一下:

我们把最后的数据格式化一下

其中status是我们需要提取的,我们取消原来的注释,改造一下解析数据的代码
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(result, JsonNode.class);
if(mode=="shadow")
{
JsonNode tempNode = jsonNode.get("shadow").get(0).get("reported").get("properties").get(pro);
String attvaluestr = tempNode.asText();
System.out.println(pro+"=" + attvaluestr);
return attvaluestr;
}
if(mode=="status")
{
JsonNode statusNode = jsonNode.get("status");
String statusstr = statusNode.asText();
System.out.println("status = " + statusstr);
return statusstr;
}
return "something is error ";
我们运行一下:

老样子,我们依旧查看华为云帮助文档



我们得到的信息:
URI为:
strurl="https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s/commands";
消息体是:
String body="{\"paras\":{\""+"*****"+"\""+":"+"*****"+"},\"service_id\":\"***** \",\"command_name\":\"*****\"}";
我们写一下实现方法:
public String setCom(String com,String value) throws Exception{
String strurl="";
strurl="https://iotda.cn-north-4.myhuaweicloud.com"+"/v5/iot/%s/devices/%s/commands";
String project_id="*****";
String device_id="*****";
strurl = String.format(strurl, project_id,device_id);
URL url = new URL(strurl);
HttpURLConnection urlCon = (HttpURLConnection)url.openConnection();
urlCon.addRequestProperty("Content-Type", "application/json");
String token=gettoken();
urlCon.addRequestProperty("X-Auth-Token",token);
urlCon.setDoOutput(true);
urlCon.setRequestMethod("POST");
urlCon.setUseCaches(false);
urlCon.setInstanceFollowRedirects(true);
urlCon.connect();
String body = "{\"paras\":{\""+com+"\""+":"+value+"},\"service_id\":\"*****\",\"command_name\":\"*****\"}";
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;
}
在主函数调用一下
setCom("led","1");
我们在华为云控制台的设备那查看

我们可以发现命令是成功的,但还有一个错误代码,那是因为设备不在线或没有对命令进行处理响应,这个我们交给设备处理,不在此介绍
另外设备需要订阅topic: $oc/devices/{device_id}/sys/commands/request_id={request_id}

同样在控制台也可以看到数据:

最后我们可以看到这个token每次都要调用,其实是没必要的,我们在这里把放到构造函数里:
String token="";
public huaweiIOT()throws Exception
{
token= gettoken();
}
最后我们删除的getdev的static声明,getdev函数的String token=gettoken();,删除main函数,现在我们的函数结构是:
public class httpGetHuaweiIOT {
String token="";
public httpGetHuaweiIOT()throws Exception
{
token=gettoken();
}
public String getAtt(String att,String mode) throws Exception{}
public String setCom(String com,String value) throws Exception{}
这样我们的核心功能就完成了,接下来是安卓界面与逻辑部分
我们在protected void onCreate(Bundle savedInstanceState) {}添加一个线程用于获取数据和命令下次测试
Thread t = new Thread() {
@Override
public void run() {
try {
huaweiIOT hwiot=new huaweiIOT();
System.out.println("获取状态");
String str="";
str=hwiot.getdev("status","");
if(str.equals("ONLINE"))
{
str="设备在线";
}
else if(str.equals("OFFLINE"))
{
str="设备离线";
}
System.out.println("获取成功,状态:"+str);
System.out.println("获取温度");
str=hwiot.getdev("shadow","temp");
System.out.println("获取成功,温度:"+str);
str=hwiot.getdev("shadow","humi");
System.out.println("获取成功,湿度:"+str);
str=hwiot.getdev("shadow","light");
System.out.println("获取成功,光照强度:"+str);
}
catch (Exception e) {
e.printStackTrace();
System.out.println("获取失败:"+e.toString());
}
}
};
t.start();
注意,在run app之前我们要加入联网的权限,我们打开AndroidManifest文件,在<application>前添加下述:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"/>


到这里大家已经学习到了华为云物联网平台Android应用开发的基础操作啦,本期教程就到这里啦,大家可以完成自己的华为云物联网平台的Android应用开发;
如果有需要上述参考工程,请微信搜索公众号“IOT趣制作”,回复关键字“华为云安卓”,领取教程的实例Demo,然后在HuaweiIOT类文件中填写参数信息运行即可。


对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain