QT学习开发笔记(项目实战之智能家居物联 UI 界面开发 )

智能家居物联 UI 界面开发

项目路径为 4/01_smarthome/01_smarthome/01_smarthome.pro,先看项目界面。项目界面如
下,采用暗黑主题设计,结合黄色作为亮色,让用户一目了然。界面笔者从一些智能家居界面
中找到灵感的,编写设计完成的效果不错!请自行查阅源码,掌握了本教程前面第七章的内容,
就可以理解这个界面是如何设计的。

QT学习开发笔记(项目实战之智能家居物联 UI 界面开发 )_第1张图片

原子云 API 接口

我们想要与原子云通信,那么必须先了解原子云平台的 API 接口。请参阅原子云平台 API

文档 V1.2.pdf 文档。原子云平台 API 写的非常详细了,请自行翻阅。需要我们从原子云平台了
解原子云 API 的通信流程。

下图是原子云平台 API 的使用流程图。 QT学习开发笔记(项目实战之智能家居物联 UI 界面开发 )_第2张图片
我们写 Qt 应用就应该重点放在 HTTPS 与 WebSocket 方向上。查阅原子云平台 API 可以知
道,下面是重点!一些帐号信息,与设备信息是通过 HTTPS 协议接口获取的,通信用 WebSocket

协议接口。那么我们就按原子云平台的协议流程编写应用程序。
源码路径为 4/01_smarthome/webapi/webapi.cpp。内容如下。

 /****************************************************************** 
 Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. 
 * @projectName webapi 
 * @brief webapi.cpp 
 * @author Deng Zhimao 
 * @email [email protected] 
 * @net www.openedv.com 
 * @date 2021-05-27 
 *******************************************************************/ 

1 #include "webapi.h" 

2 #include <QUuid> 

3 #include <QRegularExpression> 

4 

5 Webapi::Webapi(QObject *parent) 

6 { 

7 this->setParent(parent); 

8 /* 数组清空 */ 

9 groupID.clear(); 

10 deviceID.clear(); 

11 deviceNumber.clear(); 

12 

13 timer = new QTimer(); 
14 connect(timer, SIGNAL(timeout()), this, SLOT(onTimerTimeOut())); 

15 

16 networkAccessManager = new QNetworkAccessManager(this); 

17 

18 orgURL = "https://cloud.alientek.com/api/orgs"; 

19 /* 请填写自己的 token 信息!!! */ 

20 api_token = "bf591984c8fa417584d18f6328e0ef73"; 

21 

22 /* 获取账号机构列表 */ 

23 getOrgURL(); 

24 

25 QUuid uuid = QUuid::createUuid(); 

26 random_token = uuid.toString(); 

27 

28 webSocket = new QWebSocket(); 

29 /* 需要加一些安全配置才能访问 https */ 

30 QSslConfiguration config; 

31 config.setPeerVerifyMode(QSslSocket::VerifyNone); 

32 config.setProtocol(QSsl::TlsV1SslV3); 

33 webSocket->setSslConfiguration(config); 

34 

35 connect(webSocket, SIGNAL(connected()), 

36 this, SLOT(webSocketConnected())); 

37 connect(webSocket, SIGNAL(binaryMessageReceived(QByteArray)), 

38 this, SLOT(onBinaryMessageReceived(QByteArray))); 

39 } 

40 

41 Webapi::~Webapi() 

42 { 

43 delete timer; 

44 delete webSocket; 

45 webSocket = nullptr; 

46 } 

47 

48 void Webapi::getOrgURL() 

49 { 

50 getDataFromWeb(QUrl(orgURL)); 

51 } 

52 

53 /* 获取设备分组列表 */ 

54 void Webapi::getGroupListUrl() 

55 { 

56 getDataFromWeb(QUrl(groupListUrl)); 

57 } 

58 

59 /* 获取设备的信息 */ 

60 void Webapi::getDevOfGroupUrl() 

61 { 

62 getDataFromWeb(QUrl(devOfGroupUrl)); 

63 } 

64 

65 /* 获取设备连接状态 */ 

66 void Webapi::getConStateUrl() 

67 { 

68 getDataFromWeb(QUrl(conStateUrl)); 

69 } 

70 

71 /* 从云服务器获取数据 */ 

72 void Webapi::getDataFromWeb(QUrl url) 

73 { 

74 /* 网络请求 */ 

75 QNetworkRequest networkRequest; 

76 

77 /* 需要加一些安全配置才能访问 https */ 

78 QSslConfiguration config; 

79 config.setPeerVerifyMode(QSslSocket::VerifyNone); 

80 config.setProtocol(QSsl::TlsV1SslV3); 

81 networkRequest.setSslConfiguration(config); 

82 

83 /* 设置访问的地址 */ 

84 networkRequest.setUrl(url); 

85 

86 /* 网络响应 */ 

87 networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, 

88 "application/json;charset=UTF-8"); 

89 

90 /* 参数二为原子云帐号的 token 信息,填写自己的 */ 

91 networkRequest.setRawHeader("token", api_token.toLatin1()); 

92 

93 QNetworkReply *newReply = 

94 networkAccessManager->get(networkRequest); 

95 

96 connect(newReply, SIGNAL(finished()), 

97 this, SLOT(replyFinished())); 

98 connect(newReply, SIGNAL(readyRead()), 

99 this, SLOT(readyReadData())); 

100 

101 } 

102 void Webapi::replyFinished() 

103 { 

104 QNetworkReply *reply = (QNetworkReply *)sender(); 

105 

106 if (reply->url() == QUrl(orgURL)) { 

107 /* 设备分组列表 ID */ 

108 getID(dataString, reply); 

109 } 

110 

111 if (reply->url() == QUrl(groupListUrl)) { 

112 /* 列表 ID */ 

113 getID(dataString, reply); 

114 

115 /* 获取到组 ID 再开启定时器 */ 

116 if (!timer->isActive()) 

117 timer->start(2000); 

118 } 

119 

120 /* 设备的信息 */ 

121 if (reply->url() == QUrl(devOfGroupUrl)) { 

122 getID(dataString, reply); 

123 getNumber(dataString); 

124 getName(dataString); 

125 } 

126 

127 /* 设备的连接状态 */ 

128 if (reply->url() == QUrl(conStateUrl)) { 

129 getConnectState(dataString); 

130 } 

131 

132 reply->deleteLater(); 

133 reply = nullptr; 

134 } 

135 void Webapi::readyReadData() 

136 { 

137 QNetworkReply *reply = (QNetworkReply *)sender(); 

138 QByteArray data = reply->readAll(); 

139 dataString = QString(data); 

140 qDebug()<<dataString<<endl; 

141 } 

142 

143 /* 获取 ID,包括分组 id,设备 id */ 

144 void Webapi::getID(QString data, QNetworkReply *reply) 

145 { 

146 /* 多个匹配,因为可能有多个合适的字段 */ 

147 QRegularExpression pattern("\"id\":(\\d+)"); 

148 

149 QRegularExpressionMatchIterator i = pattern.globalMatch(data); 

150 while (i.hasNext()) { 

151 QRegularExpressionMatch match = i.next(); 

152 if (match.hasMatch()) { 

153 if (reply->url() == QUrl(orgURL)) { 

154 org_id = match.captured(1); 

155 groupListUrl = "https://cloud.alientek.com/api/orgs/" 

156 + org_id + "/grouplist"; 

157 getGroupListUrl(); 

158 /* Socket 连接 */ 

159 
webSocket->open(QUrl(QString("wss://cloud.alientek.com/connection/%1/or
g/%2?token=%3") 

160 .arg(api_token).arg(org_id).arg(rando
m_token))); 

161 } 

162 

163 if (reply->url() == QUrl(groupListUrl)) { 

164 group_id = match.captured(1); 

165 /* 存储组 ID,再由定时器根据组的 ID 获取设备信息 */ 

166 groupID.append(group_id); 

167 qDebug()<<"组 ID:"<<group_id<<endl; 

168 

169 } 

170 

171 if (reply->url() == QUrl(devOfGroupUrl)) { 

172 device_id = match.captured(1); 

173 /* 存储设备 ID,再由定时器根据设备的 ID 获取连接状态 */ 

174 deviceID.append(device_id); 

175 qDebug()<<"设备 ID:"<<device_id<<endl; 

176 } 

177 } 

178 } 

179 } 

180 

181 void Webapi::getNumber(QString data) 

182 { 

183 QRegularExpression pattern("\"number\":\"(\\d+)\""); 

184 

185 QRegularExpressionMatchIterator i = pattern.globalMatch(data); 

186 while (i.hasNext()) { 

187 QRegularExpressionMatch match = i.next(); 

188 if (match.hasMatch()) { 

189 device_number = match.captured(1); 

190 deviceNumber.append(device_number); 

191 qDebug()<<"设备编号:"<<device_number<<endl; 

192 } 

193 } 

194 } 

195 

196 void Webapi::getName(QString data) 

197 { 

198 /* 匹配中文字符,设备起名需要为中文 */ 

199 QRegularExpression pattern("\"name\":\"([\u4e00-\u9fa5]*)"); 

200 

201 QRegularExpressionMatchIterator i = pattern.globalMatch(data); 

202 while (i.hasNext()) { 

203 QRegularExpressionMatch match = i.next(); 

204 if (match.hasMatch()) { 

205 device_name = match.captured(1); 

206 deviceName.append(device_name); 

207 qDebug()<<"设备名称:"<<device_name<<endl; 

208 } 

209 } 

210 } 

211 

212 /* 获取设备的连接状态 */ 

213 void Webapi::getConnectState(QString data) 

214 { 

215 QString pattern = "\"data\":\"(\\S*)\""; 

216 QRegularExpression regularExpression(pattern); 

217 QRegularExpressionMatch match = regularExpression.match(data, 0); 

218 if(match.hasMatch()) { 

219 qDebug()<<"设备连接状态"<<match.captured(1); 

220 deviceConnectState.append(match.captured(1)); 

221 } 

222 } 

223 

224 void Webapi::webSocketConnected() 

225 { 

226 qDebug()<<"webSocket 连接原子云成功"<<endl; 

227 } 

228 

229 void Webapi::onBinaryMessageReceived(QByteArray str) 

230 { 

231 

232 QString temp(str); 

233 if (temp.contains("online")) { 

234 for (int i = 0; i < deviceNumber.count() ; i++) { 

235 if (temp.contains(deviceNumber[i])) { 

236 /* 发送如客厅灯在线信号*/ 

237 emit deviceStateChanged(deviceName[i] + "|在线"); 

238 qDebug()<<deviceName[i] + "|在线"<<endl; 

239 break; 

240 } 

241 } 

242 } 

243 } 

244 

245 /* 延时函数 */ 

246 void Webapi::sleep(double second) 

247 { 

248 usleep(second * 1000000); 

249 } 

250 

251 void Webapi::onTimerTimeOut() 

252 { 

253 static int i = 0; 

254 if (i < groupID.count()) { 

255 /* 获取分组下的设备列表 */ 

256 devOfGroupUrl = "https://cloud.alientek.com/api/orgs/" 

257 + org_id + "/groups/" 

258 + groupID[i] + "/devices"; 

259 dataString.clear(); 

260 getDevOfGroupUrl(); 

261 } else if (i >= groupID.count() 

262 && i < groupID.count() + deviceID.count() ) { 

263 timer->start(1000); 

264 conStateUrl = "https://cloud.alientek.com/api/orgs/" 

265 + org_id + "/devicestate/" 

266 + deviceID[i - groupID.count()]; 

267 getConStateUrl(); 

268 

269 } else { 

270 /* 订阅设备的消息 */ 

271 for (int j = 0; j < deviceNumber.count(); j++) { 

272 QByteArray cmd; 

273 cmd[0] = 0x01; 

274 sendCmd(deviceNumber[j], cmd); 

275 } 

276 

277 timer->stop(); 

278 } 

279 

280 i++; 

281 } 

282 

283 /* 订阅指定设备的消息,cmd = 0x01 */ 

284 void Webapi::sendCmd(QString number, QByteArray cmd) 

285 { 

286 QStringList list = number.split(""); 

287 for (int i = 0; i < list.count(); i++) { 

288 if (!list[i].isEmpty()) { 

289 cmd.append(list[i]); 

290 } 

291 } 

292 

293 webSocket->sendBinaryMessage(cmd); 

294 } 

295 

296 /* 发送消息到指定设备,cmd = 0x03 */ 

297 void Webapi::sendCmdMessage(QString number, 

298 QByteArray cmd, QString message) 

299 { 

300 QStringList list = number.split(""); 

301 for (int i = 0; i < list.count(); i++) { 

302 if (!list[i].isEmpty()) { 

303 cmd.append(list[i]); 

304 } 

305 } 

306 

307 cmd.append(message); 

308 

309 webSocket->sendBinaryMessage(cmd); 

310 } 

311 

312 void Webapi::whichDeviceNameSendCmd(QString name, 

313 QString message) { 

314 

315 for (int i = 0; i < deviceName.count(); i++) { 

316 if (name == deviceName[i]) { 

317 QByteArray cmd; 

318 cmd[0] = 0x03; 

319 sendCmdMessage(deviceNumber[i], cmd, message); 

320 break; 

321 } 

322 } 

323 } 

第 20 行,需要填写自己的原子云平台帐号 api_token 信息,请在原子云》帐号信息中查看!
剩余的代码都按照原子云平台 API 文档编写,首先是通过网络请求 networkRequest,访问
需要访问的地址,然后通过网络回应对象 newReply 来接收网络回复的结果。结果是 JSION 格
式的文本,笔者使用正则表达式提取回复的内容,作为下一个地址的参数,如此反复,就可以
将原子云服务器的帐号下的设备信息提取出来。
第 159 行,提取出来的信息转交 webSocket 对象,让 webSocket 获取原子云平台的鉴权,
就可以实现通信了。
流程都是按照原子云平台 API 文档的走,剩下的就是 webSocket 通信了,与 TCP,UDP 的

socket 通信相似,这里就不多解释了,和第十一章的 TCP/UDP Socket 通信内容相似。重点是流
程,再参考代码看。

你可能感兴趣的:(QT开发学习笔记,qt,ui,学习)