智能家居物聯(lián) UI 界面開發(fā)
項(xiàng)目路徑為 4/01_smarthome/01_smarthome/01_smarthome.pro,先看項(xiàng)目界面。項(xiàng)目界面如
下,采用暗黑主題設(shè)計(jì),結(jié)合黃色作為亮色,讓用戶一目了然。界面筆者從一些智能家居界面
中找到靈感的,編寫設(shè)計(jì)完成的效果不錯(cuò)!請(qǐng)自行查閱源碼,掌握了本教程前面第七章的內(nèi)容,
就可以理解這個(gè)界面是如何設(shè)計(jì)的。
原子云 API 接口
我們想要與原子云通信,那么必須先了解原子云平臺(tái)的 API 接口。請(qǐng)參閱原子云平臺(tái) API
文檔 V1.2.pdf 文檔。原子云平臺(tái) API 寫的非常詳細(xì)了,請(qǐng)自行翻閱。需要我們從原子云平臺(tái)了
解原子云 API 的通信流程。
下圖是原子云平臺(tái) API 的使用流程圖。
我們寫 Qt 應(yīng)用就應(yīng)該重點(diǎn)放在 HTTPS 與 WebSocket 方向上。查閱原子云平臺(tái) API 可以知
道,下面是重點(diǎn)!一些帳號(hào)信息,與設(shè)備信息是通過 HTTPS 協(xié)議接口獲取的,通信用 WebSocket
協(xié)議接口。那么我們就按原子云平臺(tái)的協(xié)議流程編寫應(yīng)用程序。
源碼路徑為 4/01_smarthome/webapi/webapi.cpp。內(nèi)容如下。
/******************************************************************
Copyright ? Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName webapi
* @brief webapi.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @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 /* 數(shù)組清空 */
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 /* 請(qǐng)?zhí)顚懽约旱?token 信息?。?! */
20 api_token = "bf591984c8fa417584d18f6328e0ef73";
21
22 /* 獲取賬號(hào)機(jī)構(gòu)列表 */
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 /* 獲取設(shè)備分組列表 */
54 void Webapi::getGroupListUrl()
55 {
56 getDataFromWeb(QUrl(groupListUrl));
57 }
58
59 /* 獲取設(shè)備的信息 */
60 void Webapi::getDevOfGroupUrl()
61 {
62 getDataFromWeb(QUrl(devOfGroupUrl));
63 }
64
65 /* 獲取設(shè)備連接狀態(tài) */
66 void Webapi::getConStateUrl()
67 {
68 getDataFromWeb(QUrl(conStateUrl));
69 }
70
71 /* 從云服務(wù)器獲取數(shù)據(jù) */
72 void Webapi::getDataFromWeb(QUrl url)
73 {
74 /* 網(wǎng)絡(luò)請(qǐng)求 */
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 /* 設(shè)置訪問的地址 */
84 networkRequest.setUrl(url);
85
86 /* 網(wǎng)絡(luò)響應(yīng) */
87 networkRequest.setHeader(QNetworkRequest::ContentTypeHeader,
88 "application/json;charset=UTF-8");
89
90 /* 參數(shù)二為原子云帳號(hào)的 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 /* 設(shè)備分組列表 ID */
108 getID(dataString, reply);
109 }
110
111 if (reply->url() == QUrl(groupListUrl)) {
112 /* 列表 ID */
113 getID(dataString, reply);
114
115 /* 獲取到組 ID 再開啟定時(shí)器 */
116 if (!timer->isActive())
117 timer->start(2000);
118 }
119
120 /* 設(shè)備的信息 */
121 if (reply->url() == QUrl(devOfGroupUrl)) {
122 getID(dataString, reply);
123 getNumber(dataString);
124 getName(dataString);
125 }
126
127 /* 設(shè)備的連接狀態(tài) */
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,設(shè)備 id */
144 void Webapi::getID(QString data, QNetworkReply *reply)
145 {
146 /* 多個(gè)匹配,因?yàn)榭赡苡卸鄠€(gè)合適的字段 */
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 /* 存儲(chǔ)組 ID,再由定時(shí)器根據(jù)組的 ID 獲取設(shè)備信息 */
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 /* 存儲(chǔ)設(shè)備 ID,再由定時(shí)器根據(jù)設(shè)備的 ID 獲取連接狀態(tài) */
174 deviceID.append(device_id);
175 qDebug()<<"設(shè)備 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()<<"設(shè)備編號(hào):"<<device_number<<endl;
192 }
193 }
194 }
195
196 void Webapi::getName(QString data)
197 {
198 /* 匹配中文字符,設(shè)備起名需要為中文 */
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()<<"設(shè)備名稱:"<<device_name<<endl;
208 }
209 }
210 }
211
212 /* 獲取設(shè)備的連接狀態(tài) */
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()<<"設(shè)備連接狀態(tài)"<<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 /* 發(fā)送如客廳燈在線信號(hào)*/
237 emit deviceStateChanged(deviceName[i] + "|在線");
238 qDebug()<<deviceName[i] + "|在線"<<endl;
239 break;
240 }
241 }
242 }
243 }
244
245 /* 延時(shí)函數(shù) */
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 /* 獲取分組下的設(shè)備列表 */
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 /* 訂閱設(shè)備的消息 */
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 /* 訂閱指定設(shè)備的消息,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 /* 發(fā)送消息到指定設(shè)備,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 行,需要填寫自己的原子云平臺(tái)帳號(hào) api_token 信息,請(qǐng)?jiān)谠釉啤穾ぬ?hào)信息中查看!
剩余的代碼都按照原子云平臺(tái) API 文檔編寫,首先是通過網(wǎng)絡(luò)請(qǐng)求 networkRequest,訪問
需要訪問的地址,然后通過網(wǎng)絡(luò)回應(yīng)對(duì)象 newReply 來接收網(wǎng)絡(luò)回復(fù)的結(jié)果。結(jié)果是 JSION 格
式的文本,筆者使用正則表達(dá)式提取回復(fù)的內(nèi)容,作為下一個(gè)地址的參數(shù),如此反復(fù),就可以
將原子云服務(wù)器的帳號(hào)下的設(shè)備信息提取出來。
第 159 行,提取出來的信息轉(zhuǎn)交 webSocket 對(duì)象,讓 webSocket 獲取原子云平臺(tái)的鑒權(quán),
就可以實(shí)現(xiàn)通信了。
流程都是按照原子云平臺(tái) API 文檔的走,剩下的就是 webSocket 通信了,與 TCP,UDP 的文章來源:http://www.zghlxwxcb.cn/news/detail-595104.html
socket 通信相似,這里就不多解釋了,和第十一章的 TCP/UDP Socket 通信內(nèi)容相似。重點(diǎn)是流
程,再參考代碼看。文章來源地址http://www.zghlxwxcb.cn/news/detail-595104.html
到了這里,關(guān)于QT學(xué)習(xí)開發(fā)筆記(項(xiàng)目實(shí)戰(zhàn)之智能家居物聯(lián) UI 界面開發(fā) )的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!