diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..df01be5 Binary files /dev/null and b/.DS_Store differ diff --git a/Widgets/.DS_Store b/Widgets/.DS_Store new file mode 100644 index 0000000..7055477 Binary files /dev/null and b/Widgets/.DS_Store differ diff --git a/Widgets/topology/topology.js b/Widgets/topology/topology.js new file mode 100644 index 0000000..1d884b2 --- /dev/null +++ b/Widgets/topology/topology.js @@ -0,0 +1,157 @@ +self.onInit = function () { + //self.onResize(); + //self.draw(); +}; + +self.onDataUpdated = function () { + //self.draw(); +}; + +//function for scaling a canvas +function fitToContainer(canvas) { + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.width = canvas.offsetWidth; + canvas.height = canvas.offsetHeight; +} + +class Node { + constructor(id, x, y, radius, data) { + this.id = id; + this.x = x; + this.y = y; + this.radius = Math.min(radius, 25); + this.lineLength = 25; + this.data = data; + } + draw() { + this.node = new Path2D(); + this.node.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); + self.ctx.canvas.textAlign = "center"; + self.ctx.canvas.textBaseline = "middle"; + self.ctx.canvas.strokeText(this.data, this.x, this.y); + self.ctx.canvas.fillStyle = "White"; + self.ctx.canvas.stroke(this.node); + self.ctx.canvas.fill(this.node); + } + setX(x) { + this.x = x; + } + setY(y) { + this.y = y; + } + setRadius(radius) { + this.radius = radius; + } +} +class Edge { + constructor(source, target) { + this.source = source; + this.target = target; + //this.draw(); + } + draw() { + self.ctx.canvas.moveTo(self.nodes[this.source].x, self.nodes[this.source].y); + self.ctx.canvas.lineTo(self.nodes[this.target].x, self.nodes[this.target].y); + self.ctx.canvas.stroke(); + } +} + +function getDepth(edges) { + var depth = 1; + console.log(edges); + if (Object.keys(edges).length > 0) { + Object.keys(edges).forEach((target) => { + console.log("Target: " + target); + Object.keys(edges).forEach((nTarget) => { + edges[nTarget].forEach((edge) => { + console.log("Source: " + edge.source); + if (edge.source == target) { + console.log("recurse"); + var slice = Object.fromEntries(Object.entries(edges).slice(0, Object.keys(edges).indexOf(target)) + Object.entries(edges).slice(Object.keys(edges).indexOf(target)) + 1); + depth = depth + getDepth(slice); + } + }); + }); + }); + } + console.log("Depth: " + depth); + return depth; +} + +function placeNodes() { + var numSourceNodes = Object.keys(self.nodes).length - Object.keys(self.edges).length; + var depth = getDepth(self.edges); + console.log(depth); + + if (Object.keys(self.nodes).length == 1) { + self.nodes[Object.keys(self.nodes)[0]].setX(self.canvas.width / 2); + self.nodes[Object.keys(self.nodes)[0]].setY(self.canvas.height / 2); + self.nodes[Object.keys(self.nodes)[0]].setRadius(self.canvas.height * 0.1); + } else { + var counter = 0; + Object.keys(self.edges).forEach((target) => { + self.edges[target].forEach((edge) => { + self.nodes[edge.source].setX(self.canvas.width * (1 / (Object.keys(self.edges).length + 2))); + self.nodes[edge.source].setY(self.canvas.height * ((counter + 1) / (numSourceNodes + 1))); + self.nodes[edge.source].setRadius(self.canvas.height * (1 / (2 * (Object.keys(self.nodes).length + 1)))); + + self.nodes[edge.target].setX(self.canvas.width * (2 / (Object.keys(self.edges).length + 2))); + self.nodes[edge.target].setY(self.canvas.height * ((Object.keys(self.edges).indexOf(target) + 1) / (Object.keys(self.edges).length + 1))); + self.nodes[edge.target].setRadius(self.canvas.height * (1 / (2 * (Object.keys(self.nodes).length + 1)))); + counter++; + }); + }); + } +} + +self.draw = function () { + self.canvas = document.getElementById("topology"); + self.ctx.canvas = self.canvas.getContext("2d"); + fitToContainer(self.canvas); + self.ctx.canvas.globalCompositeOperation = "destination-over"; + var collapse = false; + self.nodes = {}; + let numNodes = 9; + //var centerNode = new Node(100, self.canvas.width * 0.11, self.canvas.height * 0.5, self.canvas.height * (1 / (2 * (numNodes + 1))), 1); + for (let i = 0; i < numNodes; i++) { + self.nodes[i] = new Node(i, 0, 0, 0, i); + //self.nodes[i] = new Node(i, self.canvas.width * 0.11, self.canvas.height * ((i + 1) / (numNodes + 1)), self.canvas.height * (1 / (2 * (numNodes + 1))), (Math.random() * 10).toFixed(2)); + //self.nodes[i].draw(); + } + + self.edges = { 0: [new Edge(3, 0), new Edge(4, 0), new Edge(5, 0), new Edge(6, 0)], 8: [new Edge(0, 8)], 7: [new Edge(8, 7)], 2: [new Edge(1, 2)] }; + + placeNodes(); + Object.keys(self.nodes).forEach((key) => { + self.nodes[key].draw(); + }); + Object.keys(self.edges).forEach((target) => { + self.edges[target].forEach((edge) => { + edge.draw(); + }); + }); + //self.edges.push(new Edge(0,0)); + /*let rNodes = Math.round(numNodes * 0.5); + for (let i = 0; i < rNodes; i++) { + self.nodes[i + numNodes] = new Node(i + numNodes, self.canvas.width * 0.89, self.canvas.height * ((i + 1) / (rNodes + 1)), self.canvas.height * (1 / (2 * (rNodes + 1))), (Math.random() * 20).toFixed(2)); + self.nodes[i + numNodes].draw(); + } + //self.nodes["hidden1"] = new Node("hidden1", self.canvas.width * 0.3, self.canvas.height * 0.5, 0, ""); + //self.nodes["hidden2"] = new Node("hidden2", self.canvas.width * 0.6, self.canvas.height * 0.5, 0, ""); + + for (i = 0; i < numNodes; i++) { + new Edge(i, "hidden1"); + } + new Edge("hidden1", "hidden2"); + for (i = numNodes; i < numNodes + rNodes; i++) { + new Edge("hidden2", i); + } + */ +}; + +self.onResize = function () { + self.draw(); +}; + +self.onDestroy = function () {}; diff --git a/Widgets/vessels/vessels.js b/Widgets/vessels/vessels.js new file mode 100644 index 0000000..fe6190e --- /dev/null +++ b/Widgets/vessels/vessels.js @@ -0,0 +1,205 @@ +self.onInit = function () { + volume = 0; + fluidLevel = 0; + self.ctx.$container.append( + "
" + ); + }; + self.onResize = function () { + self.draw(); + }; + + //function for scaling a canvas + function fitToContainer(canvas) { + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.width = canvas.offsetWidth; + canvas.height = canvas.offsetHeight; + } + + self.onDataUpdated = function () { + self.ctx.detectChanges(); + //setup variables + self.volume = 0; + self.fluidLevel = 0; + for (let i = 0; i < self.ctx.defaultSubscription.data.length; i++) { + if (self.ctx.defaultSubscription.data[i].dataKey.name === self.ctx.settings.vessels[0].volumeKey) { + self.volume = typeof self.ctx.defaultSubscription.data[i].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[i].data[0][1] : 0; + } else if (self.ctx.defaultSubscription.data[i].dataKey.name === self.ctx.settings.vessels[0].fluidLevelKey) { + self.fluidLevel = typeof self.ctx.defaultSubscription.data[i].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[i].data[0][1] : 0; + } + } + self.maxHeight = typeof self.ctx.settings.maxHeight !== "undefined" ? self.ctx.settings.maxHeight : 10; + self.fluidLevelPercent = self.fluidLevel / self.maxHeight; + //draw a new canvas + self.draw(); + }; + + self.drawTank = function (ctx, compartmentWidth, compartmentHeight) { + ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05); + ctx.lineTo(compartmentWidth * 0.05, compartmentHeight * 0.95); + ctx.ellipse(compartmentWidth / 2, compartmentHeight * 0.95, compartmentWidth / 2 - compartmentWidth * 0.05, compartmentHeight * 0.025, 0, Math.PI, 0, true); + ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.05); + ctx.ellipse(compartmentWidth / 2, compartmentHeight * 0.05, compartmentWidth / 2 - compartmentWidth * 0.05, compartmentHeight * 0.025, 0, 0, 2 * Math.PI); + }; + + self.drawPond = function (ctx, compartmentWidth, compartmentHeight) { + ctx.moveTo(compartmentWidth * 0.05, compartmentHeight * 0.05); + ctx.lineTo(compartmentWidth * 0.15, compartmentHeight * 0.95); + ctx.lineTo(compartmentWidth * 0.85, compartmentHeight * 0.95); + ctx.lineTo(compartmentWidth * 0.95, compartmentHeight * 0.05); + }; + + self.drawVessels = function (ctx, canvas, numVessels, cols, rows) { + if (numVessels <= cols * rows) { + //vessel + ctx.beginPath(); + for (let i = 1; i <= numVessels; i++) { + for (let j = 0; j < self.ctx.defaultSubscription.data.length; j++) { + //console.log(self.ctx.settings.vessels[i - 1].volumeKey); + //console.log(self.ctx.settings.vessels[i - 1].fluidLevelKey); + if (self.ctx.defaultSubscription.data[j].dataKey.name === self.ctx.settings.vessels[i - 1].volumeKey) { + self.volume = typeof self.ctx.defaultSubscription.data[j].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[j].data[0][1] : 0; + } else if (self.ctx.defaultSubscription.data[j].dataKey.name === self.ctx.settings.vessels[i - 1].fluidLevelKey) { + self.fluidLevel = typeof self.ctx.defaultSubscription.data[j].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[j].data[0][1] : 0; + } + //console.log(self.volume); + //console.log(self.fluidLevel); + } + self.maxHeight = typeof self.ctx.settings.vessels[i - 1].maxHeight !== "undefined" ? self.ctx.settings.vessels[i - 1].maxHeight : 10; + self.fluidLevelPercent = self.fluidLevel / self.maxHeight; + var vesselType = typeof self.ctx.settings.vessels[i - 1].vesselType !== "undefined" ? self.ctx.settings.vessels[i - 1].vesselType : "Tank"; + var fluidColor = typeof self.ctx.settings.vessels[i - 1].fluidColor !== "undefined" ? self.ctx.settings.vessels[i - 1].fluidColor : "Water"; + var compartmentWidth = canvas.width / cols; + var compartmentHeight = canvas.height / rows; + //gradient used as water + const grad = ctx.createLinearGradient(0, compartmentHeight * 0.05, 0, compartmentHeight * 0.95); + switch(fluidColor){ + case "Water": grad.addColorStop(0, "rgba(70,220,210,0.75)");grad.addColorStop(0.4, "rgba(0,120,240,0.75)");break; + case "Produced Water": grad.addColorStop(0, "rgba(170, 100, 30,1)");grad.addColorStop(0.4, "rgba(85, 50, 15,1");break; + case "Oil": grad.addColorStop(0,"rgba(100,100,100,0.75)");grad.addColorStop(0.3, "rgba(25,25,25, 0.85");break; + default: grad.addColorStop(0,fluidColor);break; + } + + grad.addColorStop(1, "rgba(0,0,0, 0.8)"); + //draw the vessel + switch (vesselType) { + case "Pond": + self.drawPond(ctx, compartmentWidth, compartmentHeight); + break; + case "Tank": + self.drawTank(ctx, compartmentWidth, compartmentHeight); + break; + } + + //draw + ctx.stroke(); + + ctx.save(); + //setup clip area of container + ctx.clip(); + + ctx.save(); + //set fill style for water to gradient + ctx.fillStyle = grad; + //move grid for animation + if (self.fluidLevelPercent > 1) { + ctx.translate(0, compartmentHeight - (compartmentHeight * 0.95 - compartmentHeight * 0.05) * 1 - compartmentHeight * 0.1); + } else { + ctx.translate(0, compartmentHeight - (compartmentHeight * 0.95 - compartmentHeight * 0.05) * self.fluidLevelPercent - compartmentHeight * 0.1); + } + ctx.fillRect(0, compartmentHeight * 0.05, compartmentWidth, compartmentHeight * 0.95); + ctx.restore(); + ctx.restore(); + + self.drawTicks(ctx, compartmentWidth, compartmentHeight); + self.drawText(ctx, compartmentWidth, compartmentHeight); + + if (i % cols === 0) { + ctx.translate(-(cols - 1) * compartmentWidth, compartmentHeight); + } else { + ctx.translate(compartmentWidth, 0); + } + } + } else { + console.error("Not enough space for Vessels"); + } + }; + + self.drawText = function (ctx, compartmentWidth, compartmentHeight) { + fl = typeof self.fluidLevel === 'number' ? self.fluidLevel.toFixed(2) : "0.00"; + vl = typeof self.volume === 'number' ? self.volume.toFixed(2) : "0.00"; + ctx.textAlign = "left"; + ctx.fillStyle = "rgba(0,0,0,1)"; + var padding = Math.max(Math.sqrt(compartmentWidth), Math.sqrt(compartmentHeight)); + ctx.fillText(`${(self.fluidLevelPercent * 100).toFixed(2)} %`, compartmentWidth / 4 - padding, compartmentHeight / 2); + ctx.fillText(`${fl} Ft`, compartmentWidth / 4 - padding, compartmentHeight / 2 + padding); + ctx.fillText(`${vl} BBLs`, compartmentWidth / 4 - padding, compartmentHeight / 2 + 2 * padding); + }; + + self.drawTicks = function (ctx, compartmentWidth, compartmentHeight, guageAlignment) { + ctx.fillStyle = "rgba(0,0,0,1)"; + ctx.textBaseline = "middle"; + var widthOffset; + switch (guageAlignment) { + case "center": + widthOffset = compartmentWidth * 0.5; + break; + case "left": + widthOffset = compartmentWidth * 0.05; + break; + case "right": + widthOffset = compartmentWidth * 0.95; + break; + default: + widthOffset = compartmentWidth * 0.5; + } + var heightOffset = compartmentHeight * 0.05; + var heightRange = compartmentHeight * 0.9; + var numTicks = Math.min(Math.round(compartmentHeight / 40), 10); + ctx.moveTo(widthOffset, compartmentHeight - heightOffset); + ctx.lineTo(widthOffset, heightOffset); + + for (let i = 0; i < numTicks; i++) { + if (i % 2) { + ctx.moveTo(widthOffset - ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); + ctx.lineTo(widthOffset + 10 + ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); + ctx.textAlign = "left"; + ctx.fillText((self.maxHeight * ((numTicks - i) / numTicks)).toFixed(2), widthOffset + 15, heightOffset + (i * heightRange) / numTicks); + } else { + ctx.moveTo(widthOffset + ctx.lineWidth / 2, heightOffset + (i * heightRange) / numTicks); + ctx.lineTo(widthOffset - (10 + ctx.lineWidth / 2), heightOffset + (i * heightRange) / numTicks); + ctx.textAlign = "right"; + ctx.fillText((self.maxHeight * ((numTicks - i) / numTicks)).toFixed(2), widthOffset - 15, heightOffset + (i * heightRange) / numTicks); + } + } + ctx.stroke(); + }; + + self.draw = function () { + //self.fluidLevel = typeof self.ctx.defaultSubscription.data[0].data[0] !== "undefined" ? self.ctx.defaultSubscription.data[0].data[0][1] : 0; + var numVessels = typeof self.ctx.settings.vessels !== "undefined" ? self.ctx.settings.vessels.length : 1; + var vesselType = typeof self.ctx.settings.vessels[0].vesselType !== "undefined" ? self.ctx.settings.vessels[0].vesselType : "Tank"; + var fluidColor = typeof self.ctx.settings.vessels[0].color !== "undefined" ? self.ctx.settings.vessels[0].color : "Water"; + var numCols = typeof self.ctx.settings.numCols !== "undefined" ? self.ctx.settings.numCols : 1; + var numRows = typeof self.ctx.settings.numRows !== "undefined" ? self.ctx.settings.numRows : 1; + + const canvas = document.getElementById("vessel" + self.ctx.defaultSubscription.id); + if (canvas.getContext) { + const ctx = canvas.getContext("2d"); + fitToContainer(canvas); + //clear frame + ctx.clearRect(0, 0, canvas.width, canvas.height); + + //line style + ctx.lineWidth = Math.max((canvas.width * 0.005) / numCols, (canvas.height * 0.005) / numRows); + ctx.lineJoin = "round"; + ctx.strokeStyle = "black"; + //text style + ctx.font = `${Math.min(Math.sqrt(canvas.width / numCols), Math.sqrt(canvas.height / numRows))}px Times New Roman`; + self.drawVessels(ctx, canvas, numVessels, numCols, numRows); + } + }; + + self.onDestroy = function () {}; + \ No newline at end of file diff --git a/__pycache__/thingsboardAPI.cpython-39.pyc b/__pycache__/thingsboardAPI.cpython-39.pyc new file mode 100644 index 0000000..21c3f88 Binary files /dev/null and b/__pycache__/thingsboardAPI.cpython-39.pyc differ diff --git a/getImage.py b/getImage.py new file mode 100644 index 0000000..045f284 --- /dev/null +++ b/getImage.py @@ -0,0 +1,24 @@ +import base64 +import requests +from requests.auth import HTTPDigestAuth +import json +def getImage(res): + with open(f'./snapshot{res}.jpg', 'wb') as handle: + resp = requests.get(f"http://192.168.1.67:3067/cgi-bin/SnapshotJPEG?Resolution={res}", auth=HTTPDigestAuth("ASS", "amerus@1903"), stream=True) + for block in resp.iter_content(1024): + if not block: + break + + handle.write(block) + with open(f'./snapshot{res}.jpg', 'rb') as img: + encoded_string = base64.b64encode(img.read()) + + return encoded_string.decode("UTF-8") + + +resolutions = ["640x360","1280x720", "1920x1080"] +for res in resolutions: + with open(f'./snapshot{res}.json', 'w') as f: + json.dump({"snapshot": getImage(res)}, f) + + \ No newline at end of file diff --git a/get_telemetry.ipynb b/get_telemetry.ipynb index ffa9505..46f1cba 100644 --- a/get_telemetry.ipynb +++ b/get_telemetry.ipynb @@ -2,525 +2,12 @@ "cells": [ { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "import requests\n", - "import json" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "headers = {\"Content-Type\": \"application/json\", \"Accept\": \"application/json\"}\n", - "data = '{\"username\":\"nmelone@henry-pump.com\",\"password\":\"gzU6$26v42mU%3jDzTJf\"}'\n", - "url_base = \"https://hp.henrypump.cloud/api/\"\n", - "response = requests.post(url_base + 'auth/login', headers=headers,data=data)\n", - "token = json.loads(response.content.decode(\"UTF-8\"))\n", - "headers[\"X-Authorization\"] = \"Bearer \" + token['token']" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "#Get Customers\n", - "customers = requests.get(url_base + \"customers?page=0&pageSize=10\", headers=headers)\n", - "customers = json.loads(customers.content.decode(\"UTF-8\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"data\": [\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"06b6a450-aadd-11ec-8eb3-41740343dd9f\"\n", - " },\n", - " \"createdTime\": 1648062681877,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BV-602\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BV-602\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"0731e280-d163-11ec-9d28-f57ea5aee126\"\n", - " },\n", - " \"createdTime\": 1652298379432,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BZ-201\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BZ-201\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"16af78d0-ccb8-11ec-b0e8-ff45c37940c6\"\n", - " },\n", - " \"createdTime\": 1651785156829,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BW-72\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BW-72\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"4ac91370-cca3-11ec-9cdb-5b8ff28e4445\"\n", - " },\n", - " \"createdTime\": 1651776224807,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BX-101\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BX-101\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"9a37c410-d144-11ec-a502-79978f9d7342\"\n", - " },\n", - " \"createdTime\": 1652285311697,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BW-041\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BW-041\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"c50ec0b0-d150-11ec-9a8d-d3ef2e0c0a7d\"\n", - " },\n", - " \"createdTime\": 1652290537531,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"S-601\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"S-601\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"d8433890-d147-11ec-8e7b-4d9a3bf70f9d\"\n", - " },\n", - " \"createdTime\": 1652286704281,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BY-501\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BY-501\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"e9b87630-aadc-11ec-8eb3-41740343dd9f\"\n", - " },\n", - " \"createdTime\": 1648062633235,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"BP-601\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"BP-601\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " },\n", - " {\n", - " \"id\": {\n", - " \"entityType\": \"DEVICE\",\n", - " \"id\": \"ed19b7e0-d213-11ec-8c76-ad8278896f52\"\n", - " },\n", - " \"createdTime\": 1652374356574,\n", - " \"additionalInfo\": {\n", - " \"gateway\": false,\n", - " \"overwriteActivityTime\": false,\n", - " \"description\": \"\"\n", - " },\n", - " \"tenantId\": {\n", - " \"entityType\": \"TENANT\",\n", - " \"id\": \"a610ad00-52e2-11ec-89c2-2f343e6c262d\"\n", - " },\n", - " \"customerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " },\n", - " \"name\": \"CF-501\",\n", - " \"type\": \"advvfdipp\",\n", - " \"label\": \"CF-501\",\n", - " \"deviceProfileId\": {\n", - " \"entityType\": \"DEVICE_PROFILE\",\n", - " \"id\": \"4ea52d70-a0b7-11ec-8e7b-f34df8141ace\"\n", - " },\n", - " \"deviceData\": {\n", - " \"configuration\": {\n", - " \"type\": \"DEFAULT\"\n", - " },\n", - " \"transportConfiguration\": {\n", - " \"type\": \"DEFAULT\"\n", - " }\n", - " },\n", - " \"firmwareId\": null,\n", - " \"softwareId\": null,\n", - " \"ownerId\": {\n", - " \"entityType\": \"CUSTOMER\",\n", - " \"id\": \"4b856c50-a0b6-11ec-a588-ad8278896f52\"\n", - " }\n", - " }\n", - " ],\n", - " \"totalPages\": 1,\n", - " \"totalElements\": 9,\n", - " \"hasNext\": false\n", - "}\n" - ] - } - ], - "source": [ - "#Get Devices\n", - "for customer in customers['data']:\n", - " if customer[\"name\"] == \"Faskens\":\n", - " cid = customer[\"id\"][\"id\"]\n", - "devices = requests.get(url_base + f\"customer/{cid}/devices?page=0&pageSize=10\", headers=headers)\n", - "devices = json.loads(devices.content.decode(\"UTF-8\"))\n", - "print(json.dumps(devices,indent=4))" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[\n", - " \"flowrate\",\n", - " \"hartrevtotal\",\n", - " \"hartfwdtotal\",\n", - " \"hartnettotal\",\n", - " \"maxvfdfrequency\",\n", - " \"minvfdfrequency\",\n", - " \"controllerfault_program\",\n", - " \"controllerfault_io\",\n", - " \"vfd_fault\",\n", - " \"last_vfd_fault_code\",\n", - " \"sensorheight\",\n", - " \"temperaturestartuplimit\",\n", - " \"temperatureshutdownlimit\",\n", - " \"pressurestartuplimit\",\n", - " \"pressureshutdownlimit\",\n", - " \"alarmfluidlevel\",\n", - " \"tubingpressuresetpoint\",\n", - " \"manualfrequencysetpoint\",\n", - " \"fluidlevelsetpoint\",\n", - " \"flowsetpoint\",\n", - " \"stopcommand\",\n", - " \"startcommand\",\n", - " \"startpermissive\",\n", - " \"runpermissive\",\n", - " \"alarmlockout\",\n", - " \"alarmvfd\",\n", - " \"alarmtubingpressure\",\n", - " \"alarmintaketemperature\",\n", - " \"alarmintakepressure\",\n", - " \"alarmflowrate\",\n", - " \"energytotalyesterday\",\n", - " \"flowtotalyesterday\",\n", - " \"fluidspecificgravity\",\n", - " \"downholesensorstatus\",\n", - " \"vfdcurrent\",\n", - " \"energytotal\",\n", - " \"flowtotal\",\n", - " \"vfdfrequency\",\n", - " \"wellstatus\",\n", - " \"pidcontrolmode\",\n", - " \"tubingpressure\",\n", - " \"intaketemperature\",\n", - " \"intakepressure\",\n", - " \"fluidlevel\",\n", - " \"wellstatus_int\",\n", - " \"vfd_fault_int\",\n", - " \"last_vfd_fault_code_int\",\n", - " \"alarmfluidlevel_int\",\n", - " \"startpermissive_int\",\n", - " \"runpermissive_int\",\n", - " \"alarmlockout_int\",\n", - " \"alarmvfd_int\",\n", - " \"alarmtubingpressure_int\",\n", - " \"alarmintaketemperature_int\",\n", - " \"alarmintakepressure_int\",\n", - " \"alarmflowrate_int\",\n", - " \"downholesensorstatus_int\",\n", - " \"pidcontrolmode_int\",\n", - " \"resetalarms\",\n", - " \"flowrate_gpm\",\n", - " \"flowmeter_fault\",\n", - " \"flowmeter_fault_int\",\n", - " \"RuleNodeState_6e98a3f0-d6b3-11ec-b5bf-ff45c37940c6\",\n", - " \"sumFlowRate\"\n", - "]\n" - ] - } - ], - "source": [ - "#Get Keys\n", - "for device in devices['data']:\n", - " if device[\"name\"] == \"BV-602\":\n", - " did = device['id']['id']\n", - " eType = device['id']['entityType']\n", - "keys = requests.get(url_base + f\"plugins/telemetry/{eType}/{did}/keys/timeseries\", headers=headers)\n", - "keys = json.loads(keys.content.decode(\"UTF-8\"))\n", - "\n", - "print(json.dumps(keys,indent=4))" + "import thingsboardAPI\n", + "from datetime import datetime as dt" ] }, { @@ -529,7 +16,41 @@ "metadata": {}, "outputs": [], "source": [ - "#Get Telemetry" + "tb = thingsboardAPI(\"nmelone@henry-pump.com\", \"gzU6$26v42mU%3jDzTJf\", \"hp.henrypump.cloud\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#tb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "customers = tb.getCustomers()\n", + "devices = tb.getDevices(customers=customers, target_customer=\"Amerus Safety Solutions\")\n", + "eType, did, keys = tb.getDeviceKeys(devices=devices, target_device=\"Camera Trailer 106\")\n", + "telemetry = tb.getTelemetry(startTs=tb.convertDateTimeToMS(\"26 May 2022, 13:57:00\"), endTs=int(dt.now().timestamp() * 1000), keys=\",\".join(keys), eType=eType, did=did)\n", + "#telemetry" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import base64\n", + "for snap in telemetry[\"snapshot\"]:\n", + " with open('/Users/nico/Documents/tbTimelapse/'+ str(snap[\"ts\"]) + \".jpg\", 'wb') as img:\n", + " img.write(base64.b64decode(snap[\"value\"]))" ] } ], diff --git a/thingsboardAPI.py b/thingsboardAPI.py new file mode 100644 index 0000000..66f9c06 --- /dev/null +++ b/thingsboardAPI.py @@ -0,0 +1,62 @@ +import requests +import json +from datetime import datetime as dt + +class ThingsBoardAPI(): + def __init__(self, username, password,domain): + self.headers = {"Content-Type": "application/json", "Accept": "application/json"} + self.credentials = json.dumps({"username":f"{username}", "password":f"{password}"}) + self.url_base = f"https://{domain}/api/" + self.getJWT() + + def getJWT(self): + response = requests.post(self.url_base + 'auth/login', headers=self.headers,data=self.credentials) + self.token = response.json() + self.headers["X-Authorization"] = "Bearer " + self.token['token'] + + def getCustomers(self, page=0, pageSize=10): + customers = requests.get(self.url_base + f"customers?page={page}&pageSize={pageSize}", headers=self.headers) + return customers.json() + + def getAssets(self, customers, target_customer, page=0, pageSize=10): + for c in customers['data']: + if c["name"] == target_customer: + cid = c["id"]["id"] + assets = requests.get(self.url_base + f"customers/{cid}/assets?page={page}&pageSize={pageSize}") + return assets.json() + + def getDevices(self,customers,target_customer, page=0, pageSize=10): + for c in customers['data']: + if c["name"] == target_customer: + cid = c["id"]["id"] + devices = requests.get(self.url_base + f"customer/{cid}/devices?page={page}&pageSize={pageSize}", headers=self.headers) + return devices.json() + + def getDeviceKeys(self, devices,target_device): + for d in devices['data']: + if d["name"] == target_device: + did = d['id']['id'] + eType = d['id']['entityType'] + keys = requests.get(self.url_base + f"plugins/telemetry/{eType}/{did}/keys/timeseries", headers=self.headers) + return eType, did, keys.json() + + def getTelemetry(self, startTs, endTs, keys, eType, did): + telemetry = requests.get(self.url_base + f"plugins/telemetry/{eType}/{did}/values/timeseries?startTs={startTs}&endTs={endTs}&keys={keys}", headers=self.headers) + return telemetry.json() + + def deleteTimeSeriesAll(self, keys, eType, did): + resp = requests.delete(self.url_base + f"plugins/telemetry/{eType}/{did}/timeseries/delete?deleteAllDataForKeys=true&keys={keys}", headers=self.headers) + return resp.json() + + def deleteTimeSeriesScoped(self, startTs, endTs, keys, eType, did): + resp = requests.delete(self.url_base + f"plugins/telemetry/{eType}/{did}/timeseries/delete?startTs={startTs}&endTs={endTs}&keys={keys}", headers=self.headers) + return resp.json() + + def convertDateTimeToMS(self,datestring): + date = dt.strptime(datestring,"%d %b %Y, %H:%M:%S") + return int(date.timestamp() * 1000) + + def __repr__(self): + print(f"Base URL: {self.url_base}") + print(f"Token: {self.token['token']}") + return "" \ No newline at end of file diff --git a/timelapse.ipynb b/timelapse.ipynb new file mode 100644 index 0000000..08c2d51 --- /dev/null +++ b/timelapse.ipynb @@ -0,0 +1,90 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from tb_rest_client.rest_client_pe import *\n", + "from tb_rest_client.rest import ApiException\n", + "import base64\n", + "from datetime import datetime as dt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def convertDateTimeToMS(datestring):\n", + " date = dt.strptime(datestring,\"%d %b %Y, %H:%M:%S\")\n", + " return int(date.timestamp() * 1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "url = \"https://hp.henrypump.cloud\"\n", + "username = \"nmelone@henry-pump.com\"\n", + "password = \"gzU6$26v42mU%3jDzTJf\"\n", + "with RestClientPE(base_url=url) as rest_client:\n", + " try:\n", + " rest_client.login(username=username, password=password)\n", + " customer = rest_client.get_customers(page_size=10,page=0,text_search=\"Amerus Safety Solutions\")\n", + " #print(customer)\n", + " cid = customer.data[0].id.id\n", + " device = rest_client.get_customer_devices(customer_id=cid, page=0, page_size=10,text_search=\"Camera Trailer 104\")\n", + " #print(device)\n", + " eType = device.data[0].id.entity_type\n", + " eid = device.data[0].id.id\n", + " start = convertDateTimeToMS(\"28 Jun 2022, 00:00:00\")\n", + " end = int(dt.now().timestamp() * 1000)\n", + " telemetry = rest_client.get_timeseries(entity_type=eType, entity_id=eid , keys=\"snapshot\", start_ts=start, end_ts=end, limit=1000)\n", + " #print(telemetry)\n", + " except ApiException as e:\n", + " print(e)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "for snap in telemetry[\"snapshot\"]:\n", + " with open('/Users/nico/Documents/tbTimelapse/'+ str(snap[\"ts\"]) + \".jpg\", 'wb') as img:\n", + " img.write(base64.b64decode(snap[\"value\"]))" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "32b1684233d9748bd1bb5a29a1b19459c9564d6488d1324e633b9c48826c5d03" + }, + "kernelspec": { + "display_name": "Python 3.10.4 ('thingsboard')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +}