From b7eb0e30e47b03fc30aa0e8e90fb9d6873291bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Penas=20D=C3=ADaz?= Date: Thu, 15 Jan 2026 15:57:00 +0100 Subject: [PATCH 1/5] Create Changelog for bosch-tps110 --- bosch-tps110/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 bosch-tps110/CHANGELOG.md diff --git a/bosch-tps110/CHANGELOG.md b/bosch-tps110/CHANGELOG.md new file mode 100644 index 00000000..dda6ac59 --- /dev/null +++ b/bosch-tps110/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 1.0.0 - 2026-01-15 + +- First version of plugin \ No newline at end of file From 72ef00e64357a68adb4217cb180609738db5f2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Penas=20D=C3=ADaz?= Date: Thu, 15 Jan 2026 15:57:01 +0100 Subject: [PATCH 2/5] Create LICENSE for bosch-tps110 --- bosch-tps110/LICENSE.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 bosch-tps110/LICENSE.md diff --git a/bosch-tps110/LICENSE.md b/bosch-tps110/LICENSE.md new file mode 100644 index 00000000..bffeef34 --- /dev/null +++ b/bosch-tps110/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2026 Thinger.io + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From cee5c8710cf32e2157146ec3ec960e6fc85a0d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Penas=20D=C3=ADaz?= Date: Thu, 15 Jan 2026 15:57:02 +0100 Subject: [PATCH 3/5] Upload image bosch-tps110 --- bosch-tps110/assets/bosch-tps110.png | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 bosch-tps110/assets/bosch-tps110.png diff --git a/bosch-tps110/assets/bosch-tps110.png b/bosch-tps110/assets/bosch-tps110.png new file mode 100644 index 00000000..e69de29b From 06a0167f9cab7e2d1b1a146affb160d8e9bd67f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Penas=20D=C3=ADaz?= Date: Thu, 15 Jan 2026 15:57:25 +0100 Subject: [PATCH 4/5] Create README for bosch-tps110 --- bosch-tps110/README.md | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 bosch-tps110/README.md diff --git a/bosch-tps110/README.md b/bosch-tps110/README.md new file mode 100644 index 00000000..261a29f3 --- /dev/null +++ b/bosch-tps110/README.md @@ -0,0 +1,71 @@ +Based on the device data provided, I will create the README.md documentation: + +```markdown +# BOSCH Parking Lot Sensor + +The BOSCH wireless smart parking sensor is a LoRaWAN® end device designed for parking space occupancy detection. It utilizes dual-sensor technology combining a magnetometer and radar for highly reliable vehicle detection. The sensor enables active parking lot management features including search, navigation, and reservation capabilities. Additionally, the BOSCH parking lot sensor integrates a GPS receiver for precise location tracking and a temperature sensor for environmental monitoring. + +## Key Features + +- **Dual-sensor detection**: Magnetometer and radar for accurate occupancy detection +- **GPS receiver**: Precise geolocation of parking spaces +- **Temperature sensor**: Environmental monitoring capability +- **LoRaWAN® connectivity**: Long-range, low-power wireless communication +- **Robust design**: Suitable for outdoor parking lot installations + +## Sensors and Measurements + +| Sensor | Measurement | Description | +|--------|-------------|-------------| +| Magnetometer | Magnetic field variation | Detects vehicle presence by measuring magnetic field changes | +| Radar | Object detection | Provides secondary occupancy verification | +| GPS | Latitude/Longitude | Enables precise parking space positioning | +| Temperature | °C | Monitors ambient temperature conditions | + +## Use Cases + +- Smart city parking management +- Shopping center and retail parking guidance +- Airport and transportation hub parking systems +- Street parking monitoring +- Commercial and industrial parking facilities +- Parking search and navigation applications +- Parking space reservation systems + +## Thinger.io Integration + +### Requirements + +A LoRaWAN server is required to connect the BOSCH Parking Lot Sensor to Thinger.io. Supported options include: + +- [The Things Stack](https://www.thethingsindustries.com/stack/) +- [LORIOT](https://loriot.io/) +- [ChirpStack](https://www.chirpstack.io/) + +The corresponding plugin for the selected LoRaWAN server must be installed in your Thinger.io instance. + +### Get Started + +#### Installation + +Look for the plugin in the [Thinger.io Plugin Store](https://marketplace.thinger.io/) and install it in your Thinger.io instance. Once the plugin is installed, a new Product will be created for this device. + +#### Configuration + +The Product is preconfigured for immediate use. Verify that the auto provision prefix matches the one selected in your LoRaWAN server plugin in Thinger.io, or adjust it as needed. + +#### Usage + +Start sending uplinks for automatic device and bucket provisioning. + +This product provides: +- Parking occupancy status tracking +- GPS position data storage +- Temperature monitoring +- Predefined dashboard for visualization + +## Additional Resources + +- [BOSCH Connectivity](https://www.bosch-connectivity.com/) +- [Thinger.io Documentation](https://docs.thinger.io) +``` \ No newline at end of file From de6659809a0f5147c923e379d72baef31bb6be9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Penas=20D=C3=ADaz?= Date: Thu, 15 Jan 2026 15:59:56 +0100 Subject: [PATCH 5/5] Create plugin.json for bosch-tps110 --- bosch-tps110/plugin.json | 599 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 bosch-tps110/plugin.json diff --git a/bosch-tps110/plugin.json b/bosch-tps110/plugin.json new file mode 100644 index 00000000..82175305 --- /dev/null +++ b/bosch-tps110/plugin.json @@ -0,0 +1,599 @@ +{ + "name": "bosch_tps110", + "version": "1.0.0", + "description": "BOSCH wireless smart parking sensor with magnetometer, radar, GPS, and temperature sensors for parking lot management", + "author": "Thinger.io", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/thinger-io/plugins.git", + "directory": "bosch-tps110" + }, + "metadata": { + "name": "BOSCH TPS110", + "description": "BOSCH wireless smart parking sensor with magnetometer, radar, GPS, and temperature sensors for parking lot management", + "image": "assets/tps110.png", + "category": "devices", + "vendor": "bosch" + }, + "resources": { + "products": [ + { + "description": "BOSCH wireless smart parking sensor with magnetometer, radar, GPS, and temperature sensors for parking lot management", + "enabled": true, + "name": "BOSCH TPS110", + "product": "bosch_tps110", + "profile": { + "api": { + "downlink": { + "enabled": true, + "handle_connectivity": false, + "request": { + "data": { + "path": "/downlink", + "payload": "{\n \"data\" : \"{{payload.data=\"\"}}\",\n \"port\" : {{payload.port=85}},\n \"priority\": {{payload.priority=3}},\n \"confirmed\" : {{payload.confirmed=false}},\n \"uplink\" : {{property.uplink}} \n}", + "payload_function": "", + "payload_type": "source_payload", + "plugin": "{{property.uplink.source}}", + "target": "plugin_endpoint" + } + } + }, + "uplink": { + "device_id_resolver": "getId", + "enabled": true, + "handle_connectivity": true, + "request": { + "data": { + "payload": "{{payload}}", + "payload_function": "", + "payload_type": "source_payload", + "resource_stream": "uplink", + "target": "resource_stream" + } + } + } + }, + "autoprovisions": { + "bosch_tps110_autoprovision": { + "config": { + "mode": "pattern", + "pattern": "bosch-tps110-.*" + }, + "enabled": true + } + }, + "buckets": { + "bosch_tps110_data_bucket": { + "backend": "mongodb", + "data": { + "payload": "{{payload}}", + "payload_function": "decodeThingerUplink", + "payload_type": "source_payload", + "resource": "uplink", + "source": "resource", + "update": "events" + }, + "enabled": true, + "retention": { + "period": 6, + "unit": "months" + }, + "tags": [] + } + }, + "code": { + "code": "function decodeThingerUplink(thingerData) {\n // 0. If data has already been decoded, we will return it\n if (thingerData.decodedPayload) return thingerData.decodedPayload;\n \n // 1. Extract and Validate Input\n // We need 'payload' (hex string) and 'fPort' (integer)\n const hexPayload = thingerData.payload || \"\";\n const port = thingerData.fPort || 1;\n\n // 2. Convert Hex String to Byte Array\n const bytes = [];\n for (let i = 0; i < hexPayload.length; i += 2) {\n bytes.push(parseInt(hexPayload.substr(i, 2), 16));\n }\n\n // 3. Dynamic Function Detection and Execution\n \n // CASE A: (The Things Stack v3)\n if (typeof decodeUplink === 'function') {\n try {\n const input = {\n bytes: bytes,\n fPort: port\n };\n var result = decodeUplink(input);\n \n if (result.data) return result.data;\n\n return result; \n } catch (e) {\n console.error(\"Error inside decodeUplink:\", e);\n throw e;\n }\n }\n\n // CASE B: Legacy TTN (v2)\n else if (typeof Decoder === 'function') {\n try {\n return Decoder(bytes, port);\n } catch (e) {\n console.error(\"Error inside Decoder:\", e);\n throw e;\n }\n }\n\n // CASE C: No decoder found\n else {\n throw new Error(\"No compatible TTN decoder function (decodeUplink or Decoder) found in scope.\");\n }\n}\n\n\n// TTN decoder\nfunction decodeUplink(input) {\n var data = {};\n switch (input.fPort) {\n case 1: // Parking status\n data.type = 'parking status';\n data.occupied = (input.bytes[0] & 0x1) === 0x1;\n break;\n\n case 2: // Heartbeat\n data.type = 'heartbeat';\n data.occupied = (input.bytes[0] & 0x1) === 0x1;\n if (input.bytes.length >= 2) {\n data.temperature =\n input.bytes[1] & 0x80 ? input.bytes[1] - 0x100 : input.bytes[1];\n }\n break;\n\n case 3: // Start-up\n data.type = 'startup';\n data.debugCodes = [];\n for (var i = 0; i <= 8; i += 4) {\n var debugCode = ((input.bytes[i + 1] & 0xf) << 8) | input.bytes[i];\n if (debugCode) {\n data.debugCodes.push(debugCode);\n }\n }\n data.firmwareVersion =\n input.bytes[12] + '.' + input.bytes[13] + '.' + input.bytes[14];\n data.resetCause = [\n undefined,\n 'watchdog',\n 'power on',\n 'system request',\n 'other',\n ][input.bytes[15]];\n data.occupied = (input.bytes[16] & 0x1) == 0x1;\n break;\n\n case 4: // Device information\n data.type = 'device information';\n data.bytes = input.bytes;\n break;\n\n case 5: // Device usage\n data.type = 'device usage';\n data.bytes = input.bytes;\n break;\n\n case 6: // Debug\n data.type = 'debug';\n data.timestamp =\n (input.bytes[3] << 24) |\n (input.bytes[2] << 16) |\n (input.bytes[1] << 8) |\n input.bytes[0];\n data.debugCode = ((input.bytes[5] & 0xf) << 8) | input.bytes[4];\n data.sequenceNumber = (input.bytes[9] << 8) | input.bytes[8];\n break;\n }\n\n return {\n data: data,\n };\n}\n", + "environment": "javascript", + "storage": "", + "version": "1.0" + }, + "properties": { + "bosch_tps110_location": { + "data": { + "payload": "{\"latitude\": {{payload.latitude}}, \"longitude\": {{payload.longitude}}}", + "payload_function": "decodeThingerUplink", + "payload_type": "source_payload", + "resource": "uplink", + "source": "resource", + "update": "events" + }, + "default": { + "latitude": 0, + "longitude": 0 + }, + "enabled": true + }, + "bosch_tps110_occupancy": { + "data": { + "payload": "{{payload.occupied}}", + "payload_function": "decodeThingerUplink", + "payload_type": "source_payload", + "resource": "uplink", + "source": "resource", + "update": "events" + }, + "default": { + "occupied": false + }, + "enabled": true + }, + "uplink": { + "data": { + "payload": "{{payload}}", + "payload_function": "", + "payload_type": "source_payload", + "resource": "uplink", + "source": "resource", + "update": "events" + }, + "default": { + "source": "value" + }, + "enabled": true + } + } + }, + "_resources": { + "properties": [ + { + "property": "dashboard", + "value": { + "tabs": [ + { + "name": "Parking Monitor", + "widgets": [ + { + "layout": { + "col": 0, + "row": 0, + "sizeX": 3, + "sizeY": 6 + }, + "panel": { + "color": "#1976D2", + "currentColor": "#1976D2", + "showOffline": { + "type": "last_sample" + }, + "title": "Parking Status" + }, + "properties": { + "color": "#4CAF50", + "decimal_places": 0, + "icon": "fa fa-car", + "text": "Occupied", + "unit": "" + }, + "sources": [ + { + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "occupied", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#4CAF50", + "name": "Occupancy", + "source": "bucket", + "timespan": { + "mode": "latest" + } + } + ], + "type": "text" + }, + { + "layout": { + "col": 3, + "row": 0, + "sizeX": 3, + "sizeY": 6 + }, + "panel": { + "color": "#FF9800", + "currentColor": "#FF9800", + "showOffline": { + "type": "none" + }, + "title": "Temperature" + }, + "properties": { + "color": "#FF5722", + "max": 50, + "min": -20, + "unit": "°C" + }, + "sources": [ + { + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "temperature", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#FF5722", + "name": "Temperature", + "source": "bucket", + "timespan": { + "mode": "latest" + } + } + ], + "type": "donutchart" + }, + { + "layout": { + "col": 6, + "row": 0, + "sizeX": 3, + "sizeY": 6 + }, + "panel": { + "color": "#FFC107", + "currentColor": "#FFC107", + "showOffline": { + "type": "none" + }, + "title": "Battery" + }, + "properties": { + "color": "#4CAF50", + "max": 4.2, + "min": 2.5, + "unit": "V" + }, + "sources": [ + { + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "battery", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#4CAF50", + "name": "Battery Voltage", + "source": "bucket", + "timespan": { + "mode": "latest" + } + } + ], + "type": "donutchart" + }, + { + "layout": { + "col": 9, + "row": 0, + "sizeX": 3, + "sizeY": 6 + }, + "panel": { + "color": "#9C27B0", + "currentColor": "#9C27B0", + "showOffline": { + "type": "none" + }, + "title": "Radar Confidence" + }, + "properties": { + "color": "#E91E63", + "max": 100, + "min": 0, + "unit": "%" + }, + "sources": [ + { + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "radar_confidence", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#E91E63", + "name": "Confidence", + "source": "bucket", + "timespan": { + "mode": "latest" + } + } + ], + "type": "donutchart" + }, + { + "layout": { + "col": 0, + "row": 6, + "sizeX": 6, + "sizeY": 12 + }, + "panel": { + "color": "#424242", + "currentColor": "#424242", + "showFullscreen": true, + "showOffline": { + "type": "none" + }, + "subtitle": "Last 7 days", + "title": "Occupancy History" + }, + "properties": { + "alignTimeSeries": true, + "dataAppend": false, + "options": "var options = {\n series: series,\n chart: {\n type: 'area',\n background: '#424242',\n toolbar: {\n show: true,\n tools: {\n download: true,\n selection: true,\n zoom: true,\n zoomin: true,\n zoomout: true,\n pan: true,\n reset: true\n },\n autoSelected: 'zoom'\n },\n zoom: {\n enabled: true,\n type: 'x',\n autoScaleYaxis: true\n }\n },\n stroke: {\n curve: 'stepline',\n width: 2\n },\n fill: {\n type: 'solid',\n opacity: 0.6\n },\n grid: {\n row: {\n colors: ['#555555', 'transparent'],\n opacity: 0.3\n }\n },\n xaxis: {\n type: 'datetime',\n labels: {\n datetimeUTC: false,\n style: {\n colors: '#FFFFFF'\n }\n }\n },\n yaxis: {\n min: 0,\n max: 1,\n tickAmount: 1,\n labels: {\n formatter: function(val) {\n return val === 1 ? 'Occupied' : 'Free';\n },\n style: {\n colors: '#FFFFFF'\n }\n }\n },\n tooltip: {\n x: {\n format: 'dd/MM/yyyy HH:mm:ss'\n },\n y: {\n formatter: function(val) {\n return val === 1 ? 'Occupied' : 'Free';\n }\n },\n shared: true\n },\n legend: {\n position: 'bottom',\n labels: {\n colors: '#FFFFFF'\n }\n },\n colors: ['#4CAF50']\n};", + "realTimeUpdate": false + }, + "sources": [ + { + "aggregation": { + "period": "hour", + "type": "avg" + }, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "occupied", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#4CAF50", + "name": "Parking Space", + "source": "bucket", + "timespan": { + "magnitude": "day", + "mode": "relative", + "period": "latest", + "value": 7 + }, + "transform": "" + } + ], + "type": "apex_charts" + }, + { + "layout": { + "col": 6, + "row": 6, + "sizeX": 6, + "sizeY": 12 + }, + "panel": { + "color": "#1565C0", + "currentColor": "#1565C0", + "showFullscreen": true, + "showOffline": { + "type": "none" + }, + "subtitle": "Temperature trends", + "title": "Environmental Monitoring" + }, + "properties": { + "alignTimeSeries": true, + "dataAppend": false, + "options": "var options = {\n series: series,\n chart: {\n background: '#1565C0',\n toolbar: {\n show: true,\n autoSelected: 'zoom'\n },\n zoom: {\n enabled: true,\n type: 'x',\n autoScaleYaxis: true\n }\n },\n stroke: {\n curve: 'smooth',\n width: 2\n },\n grid: {\n row: {\n colors: ['#1976D2', 'transparent'],\n opacity: 0.3\n }\n },\n xaxis: {\n type: 'datetime',\n labels: {\n datetimeUTC: false,\n style: {\n colors: '#FFFFFF'\n }\n }\n },\n yaxis: {\n labels: {\n formatter: function(val) {\n return val.toFixed(1) + '°C';\n },\n style: {\n colors: '#FFFFFF'\n }\n }\n },\n tooltip: {\n x: {\n format: 'dd/MM/yyyy HH:mm'\n },\n shared: true\n },\n legend: {\n position: 'bottom',\n labels: {\n colors: '#FFFFFF'\n }\n },\n colors: ['#FF5722']\n};", + "realTimeUpdate": false + }, + "sources": [ + { + "aggregation": { + "period": "hour", + "type": "avg" + }, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "temperature", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#FF5722", + "name": "Temperature", + "source": "bucket", + "timespan": { + "magnitude": "day", + "mode": "relative", + "period": "latest", + "value": 7 + }, + "transform": "" + } + ], + "type": "apex_charts" + }, + { + "layout": { + "col": 0, + "row": 18, + "sizeX": 12, + "sizeY": 10 + }, + "panel": { + "color": "#ffffff", + "currentColor": "#ffffff", + "showOffline": { + "type": "none" + }, + "title": "Recent Events" + }, + "properties": { + "source": "code", + "template": "
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TimestampStatusTemperature (°C)Battery (V)Radar Confidence (%)GPS
{{ entry.ts | date:'medium' }}{{ entry.occupied ? 'Occupied' : 'Free' }}{{ entry.temperature || '—' }}{{ entry.battery || '—' }}{{ entry.radar_confidence || '—' }}{{ entry.latitude && entry.longitude ? (entry.latitude + ', ' + entry.longitude) : '—' }}
\n
\n" + }, + "sources": [ + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "ts", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#1abc9c", + "name": "timestamp", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + }, + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "occupied", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#4CAF50", + "name": "occupancy", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + }, + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "temperature", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#FF5722", + "name": "temperature", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + }, + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "battery", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#FFC107", + "name": "battery", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + }, + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "radar_confidence", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#E91E63", + "name": "radar", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + }, + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "latitude", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#2196F3", + "name": "latitude", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + }, + { + "aggregation": {}, + "bucket": { + "backend": "mongodb", + "id": "bosch_tps110_data_bucket", + "mapping": "longitude", + "tags": { + "device": [], + "group": [] + } + }, + "color": "#2196F3", + "name": "longitude", + "source": "bucket", + "timespan": { + "magnitude": "hour", + "mode": "relative", + "period": "latest", + "value": 24 + } + } + ], + "type": "html_time" + } + ] + } + ] + } + } + ] + } + } + ] + } +} \ No newline at end of file