Thingsboard的数据订阅,使用websocket提供支持,用于完成数据可视和实时数据分析。
官方说明:https://thingsboard.io/docs/user-guide/telemetry/#websocket-api
ThingsBard Web UI正在积极的使用WebSocket。websocket api提供订阅设备数据变更的功能。
使用以下URL可以通过websocket链接到遥测服务。
ws(s)://host:port/api/ws/plugins/telemetry?token=$JWT_TOKEN
建立链接后,可以发送 [ 订阅命令 ],并接受 [ 订阅数据 ]。
后端对应的java实现:TelemetryPluginCmdsWrapper.java
发送的各种订阅命令为json格式数据,如下:
{
"attrSubCmds": [],
"tsSubCmds": [],
"historyCmds": [],
"entityDataCmds": [],
"entityDataUnsubscribeCmds": [],
"alarmDataCmds": [],
"alarmDataUnsubscribeCmds": []
}
其中tsSubCmds命令构成大致如下:
每种命令的数据格式可能会稍有不同,具体请参看其java实现。
后端对应的java实现:TelemetrySubscriptionUpdate.java
官方提供的sample:
token - JWT token,通过http://xxxxxx/api/auth/login登录后取得。
请求数据:{“username”:“[email protected]”, “password”:“tenant”}
返回数据:{“token”:"$YOUR_JWT_TOKEN", “refreshToken”:"$YOUR_JWT_REFRESH_TOKEN"}
entityId - 具体实体id,这里使用的是设备id
<html>
<head>
<script type="text/javascript">
function WebSocketAPIExample() {
var token = "YOUR_JWT_TOKEN";
var entityId = "YOUR_DEVICE_ID";
var webSocket = new WebSocket("ws://127.0.0.1:8080/api/ws/plugins/telemetry?token=" + token);
if (entityId === "YOUR_DEVICE_ID") {
alert("Invalid device id!");
webSocket.close();
}
if (token === "YOUR_JWT_TOKEN") {
alert("Invalid JWT token!");
webSocket.close();
}
webSocket.onopen = function () {
var object = {
tsSubCmds: [
{
entityType: "DEVICE",
entityId: entityId,
scope: "LATEST_TELEMETRY",
cmdId: 10
}
],
historyCmds: [],
attrSubCmds: []
};
var data = JSON.stringify(object);
webSocket.send(data);
alert("Message is sent: " + data);
};
webSocket.onmessage = function (event) {
var received_msg = event.data;
alert("Message is received: " + received_msg);
};
webSocket.onclose = function (event) {
alert("Connection is closed!");
};
}
script>
head>
<body>
<div id="sse">
<a href="javascript:WebSocketAPIExample()">Run WebSocketa>
div>
body>
html>
c#实现有关键两步,一是用户登录,二是ws链接订阅。
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace ThingsBoardTelemetry
{
// 用户登录工具类
public class AuthUtil
{
private static string AUTH_DATA= "{{\"username\":\"{0}\", \"password\":\"{1}\"}}";
public static LoginResponse doLogin(string url, string username, string password)
{
string postData = string.Format(AUTH_DATA, username, password); ;
//创建HTTP请求
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
//设置请求方法
req.Method = "POST";
req.Timeout = 800000;
req.ContentType = "application/json";
req.Accept = "application/json";
byte[] data = Encoding.UTF8.GetBytes(postData);
req.ContentLength = data.Length;
using (Stream reqStream = req.GetRequestStream())
{
reqStream.Write(data, 0, data.Length);
reqStream.Close();
}
//获取请求返回的响应
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
//读取来自API接口的响应中的内容
StreamReader read = new StreamReader(res.GetResponseStream());
//读取StreamReader中所有的流
string resResult = read.ReadToEnd().ToString();
//反序列化json字符串得到对象
LoginResponse jobj = JsonConvert.DeserializeObject<LoginResponse>(resResult);
return jobj;
}
}
public class LoginResponse
{
public int errorCode { get; set; }
public string message { get; set; }
public int status { get; set; }
public string timestamp { get; set; }
public string token { get; set; }
public string refreshToken { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WebSocketSharp;
namespace ThingsBoardTelemetry
{
public partial class FormMain : Form
{
private static string WSURL_TEMPLATE = "ws://{0}/api/ws/plugins/telemetry?token={1}";
private static string AUTHURL_TEMPLATE = "http://{0}/api/auth/login";
private static string SUB_CMD_TEMPLATE = "{{\"tsSubCmds\": [{{\"entityType\": \"DEVICE\",\"entityId\":\"{0}\",{1}\"scope\": \"LATEST_TELEMETRY\",\"cmdId\": 1}}], \"historyCmds\": [],\"attrSubCmds\": []}}";
private static string KEY_TEMPLATE = "\"keys\":\"{0}\",";
private LoginResponse loginRes = null;
private WebSocket ws = null;
public FormMain()
{
InitializeComponent();
}
public void AddToList(string msg)
{
string allmsg = this.textLog.Text + msg + "\r\n";
textLog.Text = allmsg;
Console.WriteLine(allmsg);
}
private void btnCon_Click(object sender, EventArgs e)
{
string serverAdd = this.txt_server.Text;
string url = string.Format(AUTHURL_TEMPLATE, serverAdd);
string user = this.txt_user.Text;
string pass = this.txt_pass.Text;
loginRes = AuthUtil.DoLogin(url, user, pass);
if (loginRes.errorCode > 0)
{
MessageBox.Show(loginRes.message);
loginRes = null;
return;
}
this.AddToList("用户登录成功!");
}
private void Ws_OnClose(object sender, CloseEventArgs e)
{
this.AddToList("WebSocket链接关闭!" + e.Reason);
if (ws != null)
{
ws.OnOpen -= Ws_OnOpen;
ws.OnMessage -= Ws_OnMessage;
ws.OnError -= Ws_OnError;
ws.OnClose -= Ws_OnClose;
ws.Close();
ws = null;
}
}
private void Ws_OnError(object sender, ErrorEventArgs e)
{
this.AddToList("出错!" + e.Message);
}
private void Ws_OnMessage(object sender, MessageEventArgs e)
{
CheckForIllegalCrossThreadCalls = false;
string msg = "↓ " + (!e.IsPing ? e.Data : "Received a ping.");
this.AddToList(msg);
}
private void Ws_OnOpen(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false;
this.AddToList("WebSocket链接成功!");
try
{
string deviceId = this.txt_deviceId.Text.Trim();
string key = this.txtKey.Text.Trim();
string keys = (key.Length == 0) ? "" : string.Format(KEY_TEMPLATE, key);
string subCmd = string.Format(SUB_CMD_TEMPLATE, deviceId, keys);
this.AddToList("↑ " + subCmd);
ws.Send(subCmd);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void buttonSub_Click(object sender, EventArgs e)
{
if (loginRes == null)
{
MessageBox.Show("请先登录");
return;
}
string deviceId = this.txt_deviceId.Text;
if (deviceId.Trim().Length==0)
{
MessageBox.Show("设备ID必须输入。");
return;
}
string serverAdd = this.txt_server.Text;
string wsUrl = string.Format(WSURL_TEMPLATE, serverAdd, loginRes.token);
ws = new WebSocket(wsUrl);
ws.OnOpen += Ws_OnOpen;
ws.OnMessage += Ws_OnMessage;
ws.OnError += Ws_OnError;
ws.OnClose += Ws_OnClose;
ws.Connect();
}
private void btnUnsub_Click(object sender, EventArgs e)
{
ws.Close();
}
}
}