Skip to content

Instantly share code, notes, and snippets.

@MarcelIsrafilov
Last active September 6, 2022 08:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarcelIsrafilov/37b3979085f3fd27becfaa972336a4e5 to your computer and use it in GitHub Desktop.
Save MarcelIsrafilov/37b3979085f3fd27becfaa972336a4e5 to your computer and use it in GitHub Desktop.
Intrusion detection demo
{
"sensors": [
{
"label": "createAlarm",
"name": "createAlarm",
"version": "1.0.2",
"properties": {
"text": "Motion detected!",
"severity": "MAJOR",
"type": "motion",
"resource": "$"
},
"position": [
-39,
639
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "dataStream",
"name": "stream",
"version": "1.0.1",
"resource": "$",
"position": [
-479,
193
],
"dataTrigger": true,
"tickTrigger": false,
"timeout": "PT50S"
},
{
"label": "drawRectangles",
"name": "detectedObjectsOnImage",
"version": "1.0.9",
"properties": {
"fileName": "/sandbox/images/${nodes.dataStream.rawData.stream.image}",
"jsonBoxes": "$${nodes.objectDetection.rawData.result}"
},
"position": [
-39,
421
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "motionCheck",
"name": "condition",
"version": "1.1.6",
"properties": {
"condition": "${nodes.dataStream.rawData.stream.presence}==1"
},
"position": [
-268,
191
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "objectDetection",
"name": "tensorflowjsObjectDetection",
"version": "1.0.20",
"properties": {
"imagefile": "/sandbox/images/${nodes.dataStream.rawData.stream.image}",
"imageurl": "",
"modelfile": "file:///sandbox/plugs/coco-ssd/model.json"
},
"position": [
-17,
191
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "personCheck",
"name": "condition",
"version": "1.1.6",
"properties": {
"condition": "${nodes.personGet.rawData.numberOfObjects}>0"
},
"position": [
-341,
419
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "personGet",
"name": "script",
"version": "2.0.1",
"properties": {
"script": "function example () {\n const boxes=JSON.parse('$${nodes.objectDetection.rawData.result}')\n const filteredBoxes= boxes.filter(function (el) {\n return ((el.class=='person' || el.class=='motorcycle' || el.class=='bird' || el.class=='cat' || el.class=='dog'));\n });\n return { numberOfObjects: filteredBoxes.length }\n}"
},
"position": [
234,
210
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "postFileToDiscord",
"name": "postFileToDiscord",
"version": "1.0.4",
"properties": {
"accessToken": "${vault.discordAccessToken}",
"fileName": "${nodes.drawRectangles.rawData.finalFileName}",
"channel": "${vault.discordChannelID}"
},
"position": [
286,
578
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
},
{
"label": "postFileToSlack",
"name": "postFileToSlack",
"version": "1.0.5",
"properties": {
"accessToken": "${vault.slackAccessToken}",
"fileName": "${nodes.drawRectangles.rawData.finalFileName}",
"channel": "#bot"
},
"position": [
276,
454
],
"dataTrigger": false,
"tickTrigger": true,
"timeout": "PT50S"
}
],
"triggers": [
{
"sourceLabel": "personCheck",
"destinationLabel": "createAlarm",
"statesTrigger": [
"True"
]
},
{
"sourceLabel": "objectDetection",
"destinationLabel": "personGet",
"statesTrigger": [
"classified"
]
},
{
"sourceLabel": "motionCheck",
"destinationLabel": "objectDetection",
"statesTrigger": [
"True"
]
},
{
"sourceLabel": "drawRectangles",
"destinationLabel": "postFileToDiscord",
"statesTrigger": [
"success"
]
},
{
"sourceLabel": "dataStream",
"destinationLabel": "motionCheck",
"statesTrigger": [
"Data"
]
},
{
"sourceLabel": "personGet",
"destinationLabel": "personCheck",
"statesTrigger": [
"Success"
]
},
{
"sourceLabel": "drawRectangles",
"destinationLabel": "postFileToSlack",
"statesTrigger": [
"success"
]
},
{
"sourceLabel": "personCheck",
"destinationLabel": "drawRectangles",
"statesTrigger": [
"True"
]
}
],
"name": "demo motionsensor",
"user": "test@waylay.io",
"createTime": 1662376614862,
"lastUpdateTime": 1662450998524,
"discoveryTemplate": false,
"taskDefaults": {
"tags": {}
}
}
{
"name": "detectedObjectsOnImage",
"version": "1.0.6",
"type": "sensor",
"script": "/*\n * ⚠️ Plugins should never throw exceptions, instead send an error back.\n *\n * ref: https://nodejs.org/api/errors.html#errors_error_first_callbacks\n */\nconst sharp = require('sharp');\n\n\nconst fileName = options.requiredProperties.fileName;\nconst jsonBoxes = options.requiredProperties.jsonBoxes;\nconst MAX_WIDTH = 1920;\nconst MAX_HEIGHT = 1080;\n\nif (!fileName || !jsonBoxes) {\n send(new Error('Missing property: fileName or jsonBoxes'));\n}\nconst dir = fileName.substring(0,fileName.lastIndexOf(\"/\"))\nlet state = \"success\"\nconst finalFileName = fileName.substring(0,fileName.lastIndexOf(\".\"))+\".markedImage.jpg\";\nconst rectanglePrefix=fileName.substring(0,fileName.lastIndexOf(\".\"))+\".rectangle\";\n\nconst images = new Array();\n\nasync function createRectangle(x, y, width, height, text, idx) {\n try {\n const svgImage = `\n <svg width=\"${width}\" height=\"${height}\">\n <style>\n .title { fill: #ff0000; font-size: 30px; font-weight: normal;}\n </style>\n <text x=\"15\" y=\"30\" text-anchor=\"left\" class=\"title\">${text}</text>\n </svg>\n `;\n \n await sharp(Buffer.from(svgImage))\n .extract({\n left: 5,\n top: 5,\n width: Math.floor(width)-10,\n height: Math.floor(height)-10\n })\n .extend({\n top: 5,\n bottom: 5,\n left: 5,\n right: 5,\n background: 'red'\n })\n .toFile(`${rectanglePrefix}${idx}.png`);\n\n } catch (error) {\n console.log(error);\n }\n}\n \nasync function composite(images,finalImage) {\n try {\n await sharp(fileName)\n .composite(images)\n .toFile(finalImage);\n\n } catch (error) {\n console.log(error);\n }\n}\n\nasync function execute(){\n const json = JSON.parse(jsonBoxes);\n for (const [idx,obj] of json.entries()){\n const y = Math.floor(obj.bbox[0])\n const x = Math.floor(obj.bbox[1])\n let width = Math.floor(obj.bbox[2])\n if(width>MAX_WIDTH){\n width = MAX_WIDTH\n }\n let height = Math.floor(obj.bbox[3])\n if(height>MAX_HEIGHT){\n height = MAX_HEIGHT\n }\n const text = obj.class\n\n images.push({\n input: `${rectanglePrefix}${idx}.png`,\n top: Math.floor(x),\n left: Math.floor(y)\n });\n\n await createRectangle(x, y, width, height, text, idx);\n }\n await composite(images,finalFileName)\n \n}\n\nexecute().then(()=>{\n const value = {\n observedState: state,\n rawData: {\n finalFileName: finalFileName\n }\n};\n\nsend(null, value);\n});\n",
"metadata": {
"author": "John Doe",
"iconURL": "",
"description": "",
"rawData": [
{
"dataType": "string",
"parameter": "finalFileName"
}
],
"configuration": [
{
"name": "fileName",
"type": "string",
"mandatory": true
},
{
"name": "jsonBoxes",
"type": "string",
"mandatory": true
}
],
"requiredProperties": [
"fileName",
"jsonBoxes"
],
"supportedStates": [
"success",
"error"
]
},
"dependencies": {
"sharp": "0.30.7"
}
}
{
"name": "postFileToDiscord",
"version": "1.0.4",
"type": "sensor",
"script": "//\nconst FormData = require('form-data')\nconst fs = require('fs-extra');\nconst axios = require('axios');\nconst { channel, accessToken, fileName} = options.requiredProperties\n\nasync function execute () {\n\n if (!accessToken || !channel || !fileName) {\n return send(new Error('Missing property'))\n }\n try {\n const stream = fs.createReadStream(fileName);\n const form = new FormData();\n form.append('files', stream);\n let headers = form.getHeaders(); \n const response = await axios.post('https://discord.com/api/v9/channels/'+channel+'/messages',\n form,\n { headers: { Authorization: `Bot ${accessToken}`, ...headers } })\n send(null, { observedState: 'Success', rawData: { response: response.data } })\n } catch (error) {\n console.error(error)\n send(null, { observedState: 'Error', rawData: { errorMessage: 'Error posting to slack api: ' + error } })\n }\n}\n\nexecute()",
"metadata": {
"author": "John Doe",
"iconURL": "",
"description": "",
"rawData": [
{
"dataType": "object",
"parameter": "response"
},
{
"dataType": "string",
"parameter": "errorMessage"
}
],
"configuration": [
{
"name": "accessToken",
"type": "string",
"mandatory": true
},
{
"name": "fileName",
"type": "string",
"mandatory": true
},
{
"name": "channel",
"type": "string",
"mandatory": true
}
],
"requiredProperties": [
"accessToken",
"fileName",
"channel"
],
"supportedStates": [
"Success",
"Error"
]
},
"dependencies": {
"axios": "^0.26.1",
"form-data": "4.0.0",
"fs-extra": "10.1.0"
}
}
{
"name": "postFileToSlack",
"version": "1.0.5",
"type": "sensor",
"script": "//\nconst FormData = require('form-data')\nconst fs = require('fs-extra');\nconst axios = require('axios');\nconst { channel, accessToken, fileName} = options.requiredProperties\n\nasync function execute () {\n\n if (!accessToken || !channel || !fileName) {\n return send(new Error('Missing property'))\n }\n try {\n const stream = fs.createReadStream(fileName);\n const form = new FormData();\n form.append('file', stream);\n form.append('channels', channel);\n let headers = form.getHeaders(); \n const response = await axios.post('https://slack.com/api/files.upload',\n form,\n { headers: { Authorization: `Bearer ${accessToken}`, ...headers } })\n send(null, { observedState: 'Success', rawData: { response: response.data } })\n } catch (error) {\n console.error(error)\n send(null, { observedState: 'Error', rawData: { errorMessage: 'Error posting to slack api: ' + error } })\n }\n}\n\nexecute()",
"metadata": {
"author": "John Doe",
"iconURL": "",
"description": "",
"rawData": [
{
"dataType": "object",
"parameter": "response"
},
{
"dataType": "string",
"parameter": "errorMessage"
}
],
"configuration": [
{
"name": "accessToken",
"type": "string",
"mandatory": true
},
{
"name": "fileName",
"type": "string",
"mandatory": true
},
{
"name": "channel",
"type": "string",
"mandatory": true
}
],
"requiredProperties": [
"accessToken",
"fileName",
"channel"
],
"supportedStates": [
"Success",
"Error"
]
},
"dependencies": {
"axios": "^0.26.1",
"form-data": "4.0.0",
"fs-extra": "10.1.0"
}
}
{
"name": "tensorflowjsObjectDetection",
"version": "1.0.18",
"type": "sensor",
"script": "//\n\nconst axios = require('axios');\nconst fs = require('fs-extra');\nconst cocoSsd = require('@tensorflow-models/coco-ssd');\n\n\n// tslint:disable-next-line: no-floating-promises\n\nconst { modelfile,imagefile, imageurl } = options.requiredProperties\n\n\nfunction base64_encode(file) {\n // read binary data\n var bitmap = fs.readFileSync(file);\n // convert binary data to base64 encoded string\n return new Buffer(bitmap).toString('base64');\n}\n\n\nasync function execute () {\n let model;\ntry {\n \n // Load the model\n const config={}\n if(modelfile){\n config.modelUrl=modelfile\n }\n \n model = await cocoSsd.load(config);\n\n\n const data = fs.readFileSync(imagefile);\n let classifyRes={};\n let state='classified'\n let imgTensor = tfnode.tidy( () => {\n try{\n return tfnode.node.decodeImage(data, 3); \n } catch (error) {\n console.error(error)\n return undefined;\n }\n })\n if(imgTensor){\n classifyRes = await model.detect(imgTensor)\n await model.dispose();\n tfnode.dispose(imgTensor)\n }else{\n state='error'\n }\n \n const classificationResult={};\n let value = {\n observedState: state,\n rawData: {\n result: classifyRes,\n }\n };\n \n if(imageurl){\n try {\n const method='POST';\n const modifiedResult={\"bounding_boxes\":[]}\n for (key in classifyRes) {\n const box={\n \"label\":classifyRes[key].class,\n \"value\":classifyRes[key].score,\n \"x\":Math.trunc(classifyRes[key].bbox[0]),\n \"y\":Math.trunc(classifyRes[key].bbox[1]),\n \"width\":Math.trunc(classifyRes[key].bbox[2]),\n \"height\":Math.trunc(classifyRes[key].bbox[3])\n \n }\n modifiedResult.bounding_boxes.push(box)\n };\n console.log(modifiedResult)\n const imgdataOriginal='data:image/png;base64, '+base64_encode(imagefile);\n const body={\n \"img\": imgdataOriginal,\n \"imgoriginal\":imgdataOriginal,\n \"classificationResult\":modifiedResult\n }\n const response = await axios({ url: imageurl, data: body, method: method, timeout: 2000 })\n value.rawData.httpSend = {\n status: response.status\n }\n } catch (error) {\n console.error(error)\n value.rawData.httpSend = {\n error: error\n } \n }\n }\n send(null, value);\n \n \n \n } catch (error) {\n console.error(error)\n send(null, { observedState: 'error', rawData: { errorMessage: 'Failed to call url: ' + error } })\n } finally {\n if(model){\n await model.dispose();\n }\n }\n\n}\n\n\n\nexecute()\n\n",
"metadata": {
"author": "John Doe",
"iconURL": "",
"description": "",
"rawData": [],
"configuration": [
{
"name": "imagefile",
"type": "string",
"mandatory": true
},
{
"name": "imageurl",
"type": "string",
"mandatory": false
},
{
"name": "modelfile",
"type": "string",
"mandatory": false
}
],
"requiredProperties": [
"imagefile",
"imageurl",
"modelfile"
],
"supportedStates": [
"classified",
"error"
]
},
"dependencies": {
"axios": " ^0.20.0",
"fs-extra": "^10.1.0",
"@tensorflow-models/coco-ssd": "2.0.3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment