352 changed files with 13598 additions and 2539 deletions
@ -1,700 +0,0 @@ |
|||
{ |
|||
"title": "Raspberry PI GPIO Demo Dashboard", |
|||
"configuration": { |
|||
"description": "Demo dashboard for Raspberry PI GPIO Demo", |
|||
"widgets": { |
|||
"602177f6-267b-cb87-4e8f-e23d7fb2f61c": { |
|||
"isSystemType": true, |
|||
"bundleAlias": "gpio_widgets", |
|||
"typeAlias": "raspberry_pi_gpio_control", |
|||
"type": "rpc", |
|||
"title": "New widget", |
|||
"sizeX": 6, |
|||
"sizeY": 10, |
|||
"config": { |
|||
"targetDeviceAliases": [], |
|||
"showTitle": true, |
|||
"backgroundColor": "#fff", |
|||
"color": "rgba(0, 0, 0, 0.87)", |
|||
"padding": "0px", |
|||
"settings": { |
|||
"parseGpioStatusFunction": "return body[pin] === true;", |
|||
"gpioStatusChangeRequest": { |
|||
"method": "setGpioStatus", |
|||
"paramsBody": "{\n \"pin\": \"{$pin}\",\n \"enabled\": \"{$enabled}\"\n}" |
|||
}, |
|||
"requestTimeout": 500, |
|||
"switchPanelBackgroundColor": "#008a00", |
|||
"gpioStatusRequest": { |
|||
"method": "getGpioStatus", |
|||
"paramsBody": "{}" |
|||
}, |
|||
"gpioList": [ |
|||
{ |
|||
"pin": 7, |
|||
"label": "GPIO 4 (GPCLK0)", |
|||
"row": 3, |
|||
"col": 0, |
|||
"_uniqueKey": 0 |
|||
}, |
|||
{ |
|||
"pin": 11, |
|||
"label": "GPIO 17", |
|||
"row": 5, |
|||
"col": 0, |
|||
"_uniqueKey": 1 |
|||
}, |
|||
{ |
|||
"pin": 12, |
|||
"label": "GPIO 18", |
|||
"row": 5, |
|||
"col": 1, |
|||
"_uniqueKey": 2 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 3, |
|||
"pin": 13, |
|||
"label": "GPIO 27", |
|||
"row": 6, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 4, |
|||
"pin": 15, |
|||
"label": "GPIO 22", |
|||
"row": 7, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 5, |
|||
"pin": 16, |
|||
"label": "GPIO 23", |
|||
"row": 7, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 6, |
|||
"pin": 18, |
|||
"label": "GPIO 24", |
|||
"row": 8, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 7, |
|||
"pin": 22, |
|||
"label": "GPIO 25", |
|||
"row": 10, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 8, |
|||
"pin": 29, |
|||
"label": "GPIO 5", |
|||
"row": 14, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 9, |
|||
"pin": 31, |
|||
"label": "GPIO 6", |
|||
"row": 15, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 10, |
|||
"pin": 32, |
|||
"label": "GPIO 12", |
|||
"row": 15, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 11, |
|||
"pin": 33, |
|||
"label": "GPIO 13", |
|||
"row": 16, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 12, |
|||
"pin": 35, |
|||
"label": "GPIO 19", |
|||
"row": 17, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 13, |
|||
"pin": 36, |
|||
"label": "GPIO 16", |
|||
"row": 17, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 14, |
|||
"pin": 37, |
|||
"label": "GPIO 26", |
|||
"row": 18, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 15, |
|||
"pin": 38, |
|||
"label": "GPIO 20", |
|||
"row": 18, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"_uniqueKey": 16, |
|||
"pin": 40, |
|||
"label": "GPIO 21", |
|||
"row": 19, |
|||
"col": 1 |
|||
} |
|||
] |
|||
}, |
|||
"title": "Raspberry Pi GPIO Control Panel", |
|||
"datasources": [], |
|||
"targetDeviceAliasIds": [ |
|||
"f26b12b6-6938-e1a0-85ec-d88a1f23e382" |
|||
] |
|||
}, |
|||
"row": 0, |
|||
"col": 0, |
|||
"id": "602177f6-267b-cb87-4e8f-e23d7fb2f61c" |
|||
}, |
|||
"3cca52a5-e874-eb43-b444-8efa01e663c8": { |
|||
"isSystemType": true, |
|||
"bundleAlias": "gpio_widgets", |
|||
"typeAlias": "raspberry_pi_gpio_panel", |
|||
"type": "latest", |
|||
"title": "New widget", |
|||
"sizeX": 7, |
|||
"sizeY": 10, |
|||
"config": { |
|||
"showTitle": true, |
|||
"backgroundColor": "#fff", |
|||
"color": "rgba(0, 0, 0, 0.87)", |
|||
"padding": "0px", |
|||
"settings": { |
|||
"gpioList": [ |
|||
{ |
|||
"pin": 1, |
|||
"label": "3.3V", |
|||
"row": 0, |
|||
"col": 0, |
|||
"color": "#fc9700", |
|||
"_uniqueKey": 0 |
|||
}, |
|||
{ |
|||
"pin": 2, |
|||
"label": "5V", |
|||
"row": 0, |
|||
"col": 1, |
|||
"color": "#fb0000", |
|||
"_uniqueKey": 1 |
|||
}, |
|||
{ |
|||
"pin": 3, |
|||
"label": "GPIO 2 (I2C1_SDA)", |
|||
"row": 1, |
|||
"col": 0, |
|||
"color": "#02fefb", |
|||
"_uniqueKey": 2 |
|||
}, |
|||
{ |
|||
"color": "#fb0000", |
|||
"pin": 4, |
|||
"label": "5V", |
|||
"row": 1, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#02fefb", |
|||
"pin": 5, |
|||
"label": "GPIO 3 (I2C1_SCL)", |
|||
"row": 2, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 6, |
|||
"label": "GND", |
|||
"row": 2, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 7, |
|||
"label": "GPIO 4 (GPCLK0)", |
|||
"row": 3, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#fdfb00", |
|||
"pin": 8, |
|||
"label": "GPIO 14 (UART_TXD)", |
|||
"row": 3, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 9, |
|||
"label": "GND", |
|||
"row": 4, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#fdfb00", |
|||
"pin": 10, |
|||
"label": "GPIO 15 (UART_RXD)", |
|||
"row": 4, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 11, |
|||
"label": "GPIO 17", |
|||
"row": 5, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 12, |
|||
"label": "GPIO 18", |
|||
"row": 5, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 13, |
|||
"label": "GPIO 27", |
|||
"row": 6, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 14, |
|||
"label": "GND", |
|||
"row": 6, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 15, |
|||
"label": "GPIO 22", |
|||
"row": 7, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 16, |
|||
"label": "GPIO 23", |
|||
"row": 7, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#fc9700", |
|||
"pin": 17, |
|||
"label": "3.3V", |
|||
"row": 8, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 18, |
|||
"label": "GPIO 24", |
|||
"row": 8, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#fd01fd", |
|||
"pin": 19, |
|||
"label": "GPIO 10 (SPI_MOSI)", |
|||
"row": 9, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 20, |
|||
"label": "GND", |
|||
"row": 9, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#fd01fd", |
|||
"pin": 21, |
|||
"label": "GPIO 9 (SPI_MISO)", |
|||
"row": 10, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 22, |
|||
"label": "GPIO 25", |
|||
"row": 10, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#fd01fd", |
|||
"pin": 23, |
|||
"label": "GPIO 11 (SPI_SCLK)", |
|||
"row": 11, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#fd01fd", |
|||
"pin": 24, |
|||
"label": "GPIO 8 (SPI_CE0)", |
|||
"row": 11, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 25, |
|||
"label": "GND", |
|||
"row": 12, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#fd01fd", |
|||
"pin": 26, |
|||
"label": "GPIO 7 (SPI_CE1)", |
|||
"row": 12, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#ffffff", |
|||
"pin": 27, |
|||
"label": "ID_SD", |
|||
"row": 13, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#ffffff", |
|||
"pin": 28, |
|||
"label": "ID_SC", |
|||
"row": 13, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 29, |
|||
"label": "GPIO 5", |
|||
"row": 14, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 30, |
|||
"label": "GND", |
|||
"row": 14, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 31, |
|||
"label": "GPIO 6", |
|||
"row": 15, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 32, |
|||
"label": "GPIO 12", |
|||
"row": 15, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 33, |
|||
"label": "GPIO 13", |
|||
"row": 16, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 34, |
|||
"label": "GND", |
|||
"row": 16, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 35, |
|||
"label": "GPIO 19", |
|||
"row": 17, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 36, |
|||
"label": "GPIO 16", |
|||
"row": 17, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 37, |
|||
"label": "GPIO 26", |
|||
"row": 18, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 38, |
|||
"label": "GPIO 20", |
|||
"row": 18, |
|||
"col": 1 |
|||
}, |
|||
{ |
|||
"color": "#000000", |
|||
"pin": 39, |
|||
"label": "GND", |
|||
"row": 19, |
|||
"col": 0 |
|||
}, |
|||
{ |
|||
"color": "#00fd00", |
|||
"pin": 40, |
|||
"label": "GPIO 21", |
|||
"row": 19, |
|||
"col": 1 |
|||
} |
|||
], |
|||
"ledPanelBackgroundColor": "#008a00" |
|||
}, |
|||
"title": "Raspberry Pi GPIO Status Panel", |
|||
"datasources": [ |
|||
{ |
|||
"type": "entity", |
|||
"dataKeys": [ |
|||
{ |
|||
"name": "7", |
|||
"type": "attribute", |
|||
"label": "7", |
|||
"color": "#2196f3", |
|||
"settings": {}, |
|||
"_hash": 0.20925966435886978 |
|||
}, |
|||
{ |
|||
"name": "11", |
|||
"type": "attribute", |
|||
"label": "11", |
|||
"color": "#4caf50", |
|||
"settings": {}, |
|||
"_hash": 0.330267349594344 |
|||
}, |
|||
{ |
|||
"name": "12", |
|||
"type": "attribute", |
|||
"label": "12", |
|||
"color": "#f44336", |
|||
"settings": {}, |
|||
"_hash": 0.5040578704481748 |
|||
}, |
|||
{ |
|||
"name": "13", |
|||
"type": "attribute", |
|||
"label": "13", |
|||
"color": "#ffc107", |
|||
"settings": {}, |
|||
"_hash": 0.588956328191639 |
|||
}, |
|||
{ |
|||
"name": "15", |
|||
"type": "attribute", |
|||
"label": "15", |
|||
"color": "#607d8b", |
|||
"settings": {}, |
|||
"_hash": 0.9229040530336119 |
|||
}, |
|||
{ |
|||
"name": "16", |
|||
"type": "attribute", |
|||
"label": "16", |
|||
"color": "#9c27b0", |
|||
"settings": {}, |
|||
"_hash": 0.8692315253041654 |
|||
}, |
|||
{ |
|||
"name": "18", |
|||
"type": "attribute", |
|||
"label": "18", |
|||
"color": "#8bc34a", |
|||
"settings": {}, |
|||
"_hash": 0.41465562857521543 |
|||
}, |
|||
{ |
|||
"name": "22", |
|||
"type": "attribute", |
|||
"label": "22", |
|||
"color": "#3f51b5", |
|||
"settings": {}, |
|||
"_hash": 0.36135260043112827 |
|||
}, |
|||
{ |
|||
"name": "29", |
|||
"type": "attribute", |
|||
"label": "29", |
|||
"color": "#e91e63", |
|||
"settings": {}, |
|||
"_hash": 0.9904592276182183 |
|||
}, |
|||
{ |
|||
"name": "31", |
|||
"type": "attribute", |
|||
"label": "31", |
|||
"color": "#ffeb3b", |
|||
"settings": {}, |
|||
"_hash": 0.038330985429919195 |
|||
}, |
|||
{ |
|||
"name": "32", |
|||
"type": "attribute", |
|||
"label": "32", |
|||
"color": "#03a9f4", |
|||
"settings": {}, |
|||
"_hash": 0.4334683890135089 |
|||
}, |
|||
{ |
|||
"name": "33", |
|||
"type": "attribute", |
|||
"label": "33", |
|||
"color": "#ff9800", |
|||
"settings": {}, |
|||
"_hash": 0.6487255992492305 |
|||
}, |
|||
{ |
|||
"name": "35", |
|||
"type": "attribute", |
|||
"label": "35", |
|||
"color": "#673ab7", |
|||
"settings": {}, |
|||
"_hash": 0.971555321150732 |
|||
}, |
|||
{ |
|||
"name": "36", |
|||
"type": "attribute", |
|||
"label": "36", |
|||
"color": "#cddc39", |
|||
"settings": {}, |
|||
"_hash": 0.7826129728424382 |
|||
}, |
|||
{ |
|||
"name": "37", |
|||
"type": "attribute", |
|||
"label": "37", |
|||
"color": "#009688", |
|||
"settings": {}, |
|||
"_hash": 0.44925676517537627 |
|||
}, |
|||
{ |
|||
"name": "38", |
|||
"type": "attribute", |
|||
"label": "38", |
|||
"color": "#795548", |
|||
"settings": {}, |
|||
"_hash": 0.051518155759787465 |
|||
}, |
|||
{ |
|||
"name": "40", |
|||
"type": "attribute", |
|||
"label": "40", |
|||
"color": "#00bcd4", |
|||
"settings": {}, |
|||
"_hash": 0.8733296686871144 |
|||
} |
|||
], |
|||
"name": "RPi", |
|||
"entityAliasId": "f26b12b6-6938-e1a0-85ec-d88a1f23e382" |
|||
} |
|||
], |
|||
"timewindow": { |
|||
"realtime": { |
|||
"timewindowMs": 60000 |
|||
} |
|||
} |
|||
}, |
|||
"row": 0, |
|||
"col": 6, |
|||
"id": "3cca52a5-e874-eb43-b444-8efa01e663c8" |
|||
} |
|||
}, |
|||
"states": { |
|||
"default": { |
|||
"name": "Default", |
|||
"root": true, |
|||
"layouts": { |
|||
"main": { |
|||
"widgets": { |
|||
"602177f6-267b-cb87-4e8f-e23d7fb2f61c": { |
|||
"sizeX": 6, |
|||
"sizeY": 10, |
|||
"row": 0, |
|||
"col": 0 |
|||
}, |
|||
"3cca52a5-e874-eb43-b444-8efa01e663c8": { |
|||
"sizeX": 7, |
|||
"sizeY": 10, |
|||
"row": 0, |
|||
"col": 6 |
|||
} |
|||
}, |
|||
"gridSettings": { |
|||
"backgroundColor": "#eeeeee", |
|||
"color": "rgba(0,0,0,0.870588)", |
|||
"columns": 24, |
|||
"margins": [ |
|||
10, |
|||
10 |
|||
], |
|||
"backgroundSizeMode": "100%" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"entityAliases": { |
|||
"f26b12b6-6938-e1a0-85ec-d88a1f23e382": { |
|||
"id": "f26b12b6-6938-e1a0-85ec-d88a1f23e382", |
|||
"alias": "RPi", |
|||
"filter": { |
|||
"type": "entityName", |
|||
"resolveMultiple": false, |
|||
"entityType": "DEVICE", |
|||
"entityNameFilter": "Raspberry Pi Demo Device" |
|||
} |
|||
} |
|||
}, |
|||
"timewindow": { |
|||
"displayValue": "", |
|||
"selectedTab": 0, |
|||
"realtime": { |
|||
"interval": 1000, |
|||
"timewindowMs": 60000 |
|||
}, |
|||
"history": { |
|||
"historyType": 0, |
|||
"interval": 1000, |
|||
"timewindowMs": 60000, |
|||
"fixedTimewindow": { |
|||
"startTimeMs": 1498653734150, |
|||
"endTimeMs": 1498740134150 |
|||
} |
|||
}, |
|||
"aggregation": { |
|||
"type": "AVG", |
|||
"limit": 200 |
|||
} |
|||
}, |
|||
"settings": { |
|||
"stateControllerId": "default", |
|||
"showTitle": true, |
|||
"showDashboardsSelect": true, |
|||
"showEntitiesSelect": true, |
|||
"showDashboardTimewindow": true, |
|||
"showDashboardExport": true, |
|||
"toolbarAlwaysOpen": false |
|||
} |
|||
}, |
|||
"name": "Raspberry PI GPIO Demo Dashboard" |
|||
} |
|||
@ -1,264 +0,0 @@ |
|||
{ |
|||
"title": "Temperature & Humidity Demo Dashboard", |
|||
"configuration": { |
|||
"description": "Demo dashboard for sample applications that upload temperature and humidity received from DHT11 or DHT22 sensors", |
|||
"widgets": { |
|||
"03e06986-1c50-e9e4-267c-2bae930ad9a2": { |
|||
"isSystemType": true, |
|||
"bundleAlias": "digital_gauges", |
|||
"typeAlias": "digital_thermometer", |
|||
"type": "latest", |
|||
"title": "New widget", |
|||
"sizeX": 5, |
|||
"sizeY": 5, |
|||
"config": { |
|||
"datasources": [ |
|||
{ |
|||
"type": "entity", |
|||
"dataKeys": [ |
|||
{ |
|||
"name": "temperature", |
|||
"type": "timeseries", |
|||
"label": "temperature", |
|||
"color": "#2196f3", |
|||
"settings": {}, |
|||
"_hash": 0.3720839051412099 |
|||
} |
|||
], |
|||
"name": "DHT11", |
|||
"entityAliasId": "63a93238-c13f-4403-4bcc-9ccc86bd6a62" |
|||
} |
|||
], |
|||
"timewindow": { |
|||
"realtime": { |
|||
"timewindowMs": 60000 |
|||
} |
|||
}, |
|||
"showTitle": false, |
|||
"backgroundColor": "#000000", |
|||
"color": "rgba(0, 0, 0, 0.87)", |
|||
"padding": "0px", |
|||
"settings": { |
|||
"maxValue": 50, |
|||
"donutStartAngle": 90, |
|||
"showValue": true, |
|||
"showMinMax": true, |
|||
"gaugeWidthScale": 1, |
|||
"levelColors": [ |
|||
"#304ffe", |
|||
"#7e57c2", |
|||
"#ff4081", |
|||
"#d32f2f" |
|||
], |
|||
"refreshAnimationType": "<>", |
|||
"refreshAnimationTime": 700, |
|||
"startAnimationType": "<>", |
|||
"startAnimationTime": 700, |
|||
"titleFont": { |
|||
"family": "RobotoDraft", |
|||
"size": 12, |
|||
"style": "normal", |
|||
"weight": "500" |
|||
}, |
|||
"labelFont": { |
|||
"family": "RobotoDraft", |
|||
"size": 8, |
|||
"style": "normal", |
|||
"weight": "500" |
|||
}, |
|||
"valueFont": { |
|||
"family": "Segment7Standard", |
|||
"style": "normal", |
|||
"weight": "500", |
|||
"size": 18 |
|||
}, |
|||
"minMaxFont": { |
|||
"family": "Segment7Standard", |
|||
"size": 12, |
|||
"style": "normal", |
|||
"weight": "500" |
|||
}, |
|||
"dashThickness": 1.5, |
|||
"decimals": 0, |
|||
"minValue": 0, |
|||
"units": "°C", |
|||
"gaugeColor": "#333333", |
|||
"neonGlowBrightness": 35, |
|||
"gaugeType": "donut", |
|||
"showTitle": false |
|||
}, |
|||
"title": "Temperature" |
|||
}, |
|||
"row": 0, |
|||
"col": 0, |
|||
"id": "03e06986-1c50-e9e4-267c-2bae930ad9a2" |
|||
}, |
|||
"88808eb1-d381-9970-c852-e3499df68bd8": { |
|||
"isSystemType": true, |
|||
"bundleAlias": "digital_gauges", |
|||
"typeAlias": "digital_vertical_bar", |
|||
"type": "latest", |
|||
"title": "New widget", |
|||
"sizeX": 3, |
|||
"sizeY": 5, |
|||
"config": { |
|||
"datasources": [ |
|||
{ |
|||
"type": "entity", |
|||
"dataKeys": [ |
|||
{ |
|||
"name": "humidity", |
|||
"type": "timeseries", |
|||
"label": "humidity", |
|||
"color": "#2196f3", |
|||
"settings": {}, |
|||
"_hash": 0.9492802776509441 |
|||
} |
|||
], |
|||
"name": "DHT11", |
|||
"entityAliasId": "63a93238-c13f-4403-4bcc-9ccc86bd6a62" |
|||
} |
|||
], |
|||
"timewindow": { |
|||
"realtime": { |
|||
"timewindowMs": 60000 |
|||
} |
|||
}, |
|||
"showTitle": false, |
|||
"backgroundColor": "#000000", |
|||
"color": "rgba(0, 0, 0, 0.87)", |
|||
"padding": "0px", |
|||
"settings": { |
|||
"maxValue": 100, |
|||
"donutStartAngle": 90, |
|||
"showValue": true, |
|||
"showMinMax": true, |
|||
"gaugeWidthScale": 0.75, |
|||
"levelColors": [ |
|||
"#3d5afe", |
|||
"#f44336" |
|||
], |
|||
"refreshAnimationType": "<>", |
|||
"refreshAnimationTime": 700, |
|||
"startAnimationType": "<>", |
|||
"startAnimationTime": 700, |
|||
"titleFont": { |
|||
"family": "RobotoDraft", |
|||
"size": 12, |
|||
"style": "normal", |
|||
"weight": "500" |
|||
}, |
|||
"labelFont": { |
|||
"family": "RobotoDraft", |
|||
"size": 8, |
|||
"style": "normal", |
|||
"weight": "500" |
|||
}, |
|||
"valueFont": { |
|||
"family": "Segment7Standard", |
|||
"style": "normal", |
|||
"weight": "500", |
|||
"size": 14 |
|||
}, |
|||
"minMaxFont": { |
|||
"family": "Segment7Standard", |
|||
"size": 8, |
|||
"style": "normal", |
|||
"weight": "normal", |
|||
"color": "#cccccc" |
|||
}, |
|||
"neonGlowBrightness": 20, |
|||
"decimals": 0, |
|||
"showUnitTitle": true, |
|||
"gaugeColor": "#171a1c", |
|||
"gaugeType": "verticalBar", |
|||
"showTitle": false, |
|||
"minValue": 0, |
|||
"dashThickness": 1.2 |
|||
}, |
|||
"title": "Humidity" |
|||
}, |
|||
"row": 0, |
|||
"col": 5, |
|||
"id": "88808eb1-d381-9970-c852-e3499df68bd8" |
|||
} |
|||
}, |
|||
"states": { |
|||
"default": { |
|||
"name": "Default", |
|||
"root": true, |
|||
"layouts": { |
|||
"main": { |
|||
"widgets": { |
|||
"03e06986-1c50-e9e4-267c-2bae930ad9a2": { |
|||
"sizeX": 5, |
|||
"sizeY": 5, |
|||
"row": 0, |
|||
"col": 0 |
|||
}, |
|||
"88808eb1-d381-9970-c852-e3499df68bd8": { |
|||
"sizeX": 3, |
|||
"sizeY": 5, |
|||
"row": 0, |
|||
"col": 5 |
|||
} |
|||
}, |
|||
"gridSettings": { |
|||
"backgroundColor": "#eeeeee", |
|||
"color": "rgba(0,0,0,0.870588)", |
|||
"columns": 24, |
|||
"margins": [ |
|||
10, |
|||
10 |
|||
], |
|||
"backgroundSizeMode": "100%" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"entityAliases": { |
|||
"63a93238-c13f-4403-4bcc-9ccc86bd6a62": { |
|||
"id": "63a93238-c13f-4403-4bcc-9ccc86bd6a62", |
|||
"alias": "DHT11", |
|||
"filter": { |
|||
"type": "entityName", |
|||
"resolveMultiple": false, |
|||
"entityType": "DEVICE", |
|||
"entityNameFilter": "DHT11 Demo Device" |
|||
} |
|||
} |
|||
}, |
|||
"timewindow": { |
|||
"displayValue": "", |
|||
"selectedTab": 0, |
|||
"realtime": { |
|||
"interval": 1000, |
|||
"timewindowMs": 60000 |
|||
}, |
|||
"history": { |
|||
"historyType": 0, |
|||
"interval": 1000, |
|||
"timewindowMs": 60000, |
|||
"fixedTimewindow": { |
|||
"startTimeMs": 1498653790019, |
|||
"endTimeMs": 1498740190019 |
|||
} |
|||
}, |
|||
"aggregation": { |
|||
"type": "AVG", |
|||
"limit": 200 |
|||
} |
|||
}, |
|||
"settings": { |
|||
"stateControllerId": "default", |
|||
"showTitle": true, |
|||
"showDashboardsSelect": true, |
|||
"showEntitiesSelect": true, |
|||
"showDashboardTimewindow": true, |
|||
"showDashboardExport": true, |
|||
"toolbarAlwaysOpen": false |
|||
} |
|||
}, |
|||
"name": "Temperature & Humidity Demo Dashboard" |
|||
} |
|||
@ -1,189 +0,0 @@ |
|||
{ |
|||
"ruleChain": { |
|||
"additionalInfo": null, |
|||
"name": "Root Rule Chain", |
|||
"firstRuleNodeId": null, |
|||
"root": true, |
|||
"debugMode": false, |
|||
"configuration": null |
|||
}, |
|||
"metadata": { |
|||
"firstNodeIndex": 3, |
|||
"nodes": [ |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 1069, |
|||
"layoutY": 267 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", |
|||
"name": "Is Thermostat?", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return msg.id.entityType === \"DEVICE\" && msg.type === \"thermostat\";" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 824, |
|||
"layoutY": 156 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
|||
"name": "Save Timeseries", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"defaultTTL": 0 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 825, |
|||
"layoutY": 52 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
|||
"name": "Save Client Attributes", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"scope": "CLIENT_SCOPE", |
|||
"notifyDevice": "false" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 347, |
|||
"layoutY": 149 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", |
|||
"name": "Message Type Switch", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"version": 0 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 839, |
|||
"layoutY": 345 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
|||
"name": "Log RPC from Device", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 832, |
|||
"layoutY": 407 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
|||
"name": "Log Other", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 825, |
|||
"layoutY": 468 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", |
|||
"name": "RPC Call Request", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"timeoutInSeconds": 60 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 1069, |
|||
"layoutY": 90 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", |
|||
"name": "Is Thermostat?", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return metadata[\"deviceType\"] === \"thermostat\";" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 1090, |
|||
"layoutY": 360 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbCreateRelationNode", |
|||
"name": "Relate to Asset", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"direction": "FROM", |
|||
"relationType": "ToAlarmPropagationAsset", |
|||
"entityType": "ASSET", |
|||
"entityNamePattern": "Thermostat Alarms", |
|||
"entityTypePattern": "AlarmPropagationAsset", |
|||
"entityCacheExpiration": 300, |
|||
"createEntityIfNotExists": true, |
|||
"changeOriginatorToRelatedEntity": false, |
|||
"removeCurrentRelations": false |
|||
} |
|||
} |
|||
], |
|||
"connections": [ |
|||
{ |
|||
"fromIndex": 0, |
|||
"toIndex": 8, |
|||
"type": "True" |
|||
}, |
|||
{ |
|||
"fromIndex": 1, |
|||
"toIndex": 7, |
|||
"type": "Success" |
|||
}, |
|||
{ |
|||
"fromIndex": 3, |
|||
"toIndex": 5, |
|||
"type": "Other" |
|||
}, |
|||
{ |
|||
"fromIndex": 3, |
|||
"toIndex": 2, |
|||
"type": "Post attributes" |
|||
}, |
|||
{ |
|||
"fromIndex": 3, |
|||
"toIndex": 1, |
|||
"type": "Post telemetry" |
|||
}, |
|||
{ |
|||
"fromIndex": 3, |
|||
"toIndex": 4, |
|||
"type": "RPC Request from Device" |
|||
}, |
|||
{ |
|||
"fromIndex": 3, |
|||
"toIndex": 6, |
|||
"type": "RPC Request to Device" |
|||
}, |
|||
{ |
|||
"fromIndex": 3, |
|||
"toIndex": 0, |
|||
"type": "Entity Created" |
|||
} |
|||
], |
|||
"ruleChainConnections": [ |
|||
{ |
|||
"fromIndex": 7, |
|||
"targetRuleChainId": { |
|||
"entityType": "RULE_CHAIN", |
|||
"id": "25e26570-89ed-11ea-a650-cd6e14e633bd" |
|||
}, |
|||
"additionalInfo": { |
|||
"layoutX": 1109, |
|||
"layoutY": 182, |
|||
"ruleChainNodeId": "rule-chain-node-10" |
|||
}, |
|||
"type": "True" |
|||
} |
|||
] |
|||
} |
|||
} |
|||
@ -1,141 +0,0 @@ |
|||
{ |
|||
"ruleChain": { |
|||
"additionalInfo": null, |
|||
"name": "Thermostat Alarms", |
|||
"firstRuleNodeId": null, |
|||
"root": false, |
|||
"debugMode": false, |
|||
"configuration": null |
|||
}, |
|||
"metadata": { |
|||
"firstNodeIndex": 5, |
|||
"nodes": [ |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 929, |
|||
"layoutY": 67 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode", |
|||
"name": "Create Temp Alarm", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"alarmType": "High Temperature", |
|||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.temperature;\nreturn details;", |
|||
"severity": "MAJOR", |
|||
"propagate": true, |
|||
"useMessageAlarmData": false, |
|||
"relationTypes": [ |
|||
"ToAlarmPropagationAsset" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 930, |
|||
"layoutY": 201 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbClearAlarmNode", |
|||
"name": "Clear Temp Alarm", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"alarmType": "High Temperature", |
|||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 930, |
|||
"layoutY": 131 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode", |
|||
"name": "Create Humidity Alarm", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"alarmType": "Low Humidity", |
|||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.humidity;\nreturn details;", |
|||
"severity": "MINOR", |
|||
"propagate": true, |
|||
"useMessageAlarmData": false, |
|||
"relationTypes": [ |
|||
"ToAlarmPropagationAsset" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 929, |
|||
"layoutY": 275 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbClearAlarmNode", |
|||
"name": "Clear Humidity Alarm", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"alarmType": "Low Humidity", |
|||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 586, |
|||
"layoutY": 148 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode", |
|||
"name": "Check Alarms", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "var relations = [];\nif(metadata[\"ss_alarmTemperature\"] === \"true\"){\n if(msg.temperature > metadata[\"ss_thresholdTemperature\"]){\n relations.push(\"NewTempAlarm\");\n } else {\n relations.push(\"ClearTempAlarm\");\n }\n}\nif(metadata[\"ss_alarmHumidity\"] === \"true\"){\n if(msg.humidity < metadata[\"ss_thresholdHumidity\"]){\n relations.push(\"NewHumidityAlarm\");\n } else {\n relations.push(\"ClearHumidityAlarm\");\n }\n}\n\nreturn relations;" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 321, |
|||
"layoutY": 149 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode", |
|||
"name": "Fetch Configuration", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"clientAttributeNames": [], |
|||
"sharedAttributeNames": [], |
|||
"serverAttributeNames": [ |
|||
"alarmTemperature", |
|||
"thresholdTemperature", |
|||
"alarmHumidity", |
|||
"thresholdHumidity" |
|||
], |
|||
"latestTsKeyNames": [], |
|||
"tellFailureIfAbsent": false, |
|||
"getLatestValueWithTs": false |
|||
} |
|||
} |
|||
], |
|||
"connections": [ |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 0, |
|||
"type": "NewTempAlarm" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 1, |
|||
"type": "ClearTempAlarm" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 2, |
|||
"type": "NewHumidityAlarm" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 3, |
|||
"type": "ClearHumidityAlarm" |
|||
}, |
|||
{ |
|||
"fromIndex": 5, |
|||
"toIndex": 4, |
|||
"type": "Success" |
|||
} |
|||
], |
|||
"ruleChainConnections": null |
|||
} |
|||
} |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,53 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sms; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.rule.engine.api.sms.SmsSender; |
|||
import org.thingsboard.rule.engine.api.sms.exception.SmsParseException; |
|||
|
|||
import java.util.regex.Pattern; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractSmsSender implements SmsSender { |
|||
|
|||
private static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); |
|||
|
|||
private static final int MAX_SMS_MESSAGE_LENGTH = 1600; |
|||
private static final int MAX_SMS_SEGMENT_LENGTH = 70; |
|||
|
|||
protected String validatePhoneNumber(String phoneNumber) throws SmsParseException { |
|||
phoneNumber = phoneNumber.trim(); |
|||
if (!E_164_PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches()) { |
|||
throw new SmsParseException("Invalid phone number format. Phone number must be in E.164 format."); |
|||
} |
|||
return phoneNumber; |
|||
} |
|||
|
|||
protected String prepareMessage(String message) { |
|||
message = message.replaceAll("^\"|\"$", "").replaceAll("\\\\n", "\n"); |
|||
if (message.length() > MAX_SMS_MESSAGE_LENGTH) { |
|||
log.warn("SMS message exceeds maximum symbols length and will be truncated"); |
|||
message = message.substring(0, MAX_SMS_MESSAGE_LENGTH); |
|||
} |
|||
return message; |
|||
} |
|||
|
|||
protected int countMessageSegments(String message) { |
|||
return (int)Math.ceil((double) message.length() / (double) MAX_SMS_SEGMENT_LENGTH); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sms; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.rule.engine.api.sms.SmsSender; |
|||
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; |
|||
import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; |
|||
import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; |
|||
import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; |
|||
import org.thingsboard.server.service.sms.aws.AwsSmsSender; |
|||
import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; |
|||
|
|||
@Component |
|||
public class DefaultSmsSenderFactory implements SmsSenderFactory { |
|||
|
|||
@Override |
|||
public SmsSender createSmsSender(SmsProviderConfiguration config) { |
|||
switch (config.getType()) { |
|||
case AWS_SNS: |
|||
return new AwsSmsSender((AwsSnsSmsProviderConfiguration)config); |
|||
case TWILIO: |
|||
return new TwilioSmsSender((TwilioSmsProviderConfiguration)config); |
|||
default: |
|||
throw new RuntimeException("Unknown SMS provider type " + config.getType()); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sms; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.core.NestedRuntimeException; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.rule.engine.api.SmsService; |
|||
import org.thingsboard.rule.engine.api.sms.SmsSender; |
|||
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; |
|||
import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; |
|||
import org.thingsboard.server.common.data.sms.config.TestSmsRequest; |
|||
import org.thingsboard.server.common.data.AdminSettings; |
|||
import org.thingsboard.server.common.data.ApiUsageRecordKey; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.settings.AdminSettingsService; |
|||
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
|||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
|||
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class DefaultSmsService implements SmsService { |
|||
|
|||
private final SmsSenderFactory smsSenderFactory; |
|||
private final AdminSettingsService adminSettingsService; |
|||
private final TbApiUsageStateService apiUsageStateService; |
|||
private final TbApiUsageClient apiUsageClient; |
|||
|
|||
private SmsSender smsSender; |
|||
|
|||
public DefaultSmsService(SmsSenderFactory smsSenderFactory, AdminSettingsService adminSettingsService, TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) { |
|||
this.smsSenderFactory = smsSenderFactory; |
|||
this.adminSettingsService = adminSettingsService; |
|||
this.apiUsageStateService = apiUsageStateService; |
|||
this.apiUsageClient = apiUsageClient; |
|||
} |
|||
|
|||
@PostConstruct |
|||
private void init() { |
|||
updateSmsConfiguration(); |
|||
} |
|||
|
|||
@PreDestroy |
|||
private void destroy() { |
|||
if (this.smsSender != null) { |
|||
this.smsSender.destroy(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void updateSmsConfiguration() { |
|||
AdminSettings settings = adminSettingsService.findAdminSettingsByKey(new TenantId(EntityId.NULL_UUID), "sms"); |
|||
if (settings != null) { |
|||
try { |
|||
JsonNode jsonConfig = settings.getJsonValue(); |
|||
SmsProviderConfiguration configuration = JacksonUtil.convertValue(jsonConfig, SmsProviderConfiguration.class); |
|||
SmsSender newSmsSender = this.smsSenderFactory.createSmsSender(configuration); |
|||
if (this.smsSender != null) { |
|||
this.smsSender.destroy(); |
|||
} |
|||
this.smsSender = newSmsSender; |
|||
} catch (Exception e) { |
|||
log.error("Failed to create SMS sender", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private int sendSms(String numberTo, String message) throws ThingsboardException { |
|||
if (this.smsSender == null) { |
|||
throw new ThingsboardException("Unable to send SMS: no SMS provider configured!", ThingsboardErrorCode.GENERAL); |
|||
} |
|||
return this.sendSms(this.smsSender, numberTo, message); |
|||
} |
|||
|
|||
@Override |
|||
public void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException { |
|||
if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) { |
|||
int smsCount = 0; |
|||
try { |
|||
for (String numberTo : numbersTo) { |
|||
smsCount += this.sendSms(numberTo, message); |
|||
} |
|||
} finally { |
|||
if (smsCount > 0) { |
|||
apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount); |
|||
} |
|||
} |
|||
} else { |
|||
throw new RuntimeException("SMS sending is disabled due to API limits!"); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException { |
|||
SmsSender testSmsSender; |
|||
try { |
|||
testSmsSender = this.smsSenderFactory.createSmsSender(testSmsRequest.getProviderConfiguration()); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
this.sendSms(testSmsSender, testSmsRequest.getNumberTo(), testSmsRequest.getMessage()); |
|||
testSmsSender.destroy(); |
|||
} |
|||
|
|||
private int sendSms(SmsSender smsSender, String numberTo, String message) throws ThingsboardException { |
|||
try { |
|||
return smsSender.sendSms(numberTo, message); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
private ThingsboardException handleException(Exception exception) { |
|||
String message; |
|||
if (exception instanceof NestedRuntimeException) { |
|||
message = ((NestedRuntimeException) exception).getMostSpecificCause().getMessage(); |
|||
} else { |
|||
message = exception.getMessage(); |
|||
} |
|||
log.warn("Unable to send SMS: {}", message); |
|||
return new ThingsboardException(String.format("Unable to send SMS: %s", message), |
|||
ThingsboardErrorCode.GENERAL); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sms; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.common.util.AbstractListeningExecutor; |
|||
|
|||
@Component |
|||
public class SmsExecutorService extends AbstractListeningExecutor { |
|||
|
|||
@Value("${actors.rule.sms_thread_pool_size}") |
|||
private int smsExecutorThreadPoolSize; |
|||
|
|||
@Override |
|||
protected int getThreadPollSize() { |
|||
return smsExecutorThreadPoolSize; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sms.aws; |
|||
|
|||
import com.amazonaws.auth.AWSCredentials; |
|||
import com.amazonaws.auth.AWSStaticCredentialsProvider; |
|||
import com.amazonaws.auth.BasicAWSCredentials; |
|||
import com.amazonaws.services.sns.AmazonSNS; |
|||
import com.amazonaws.services.sns.AmazonSNSClient; |
|||
import com.amazonaws.services.sns.model.MessageAttributeValue; |
|||
import com.amazonaws.services.sns.model.PublishRequest; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; |
|||
import org.thingsboard.rule.engine.api.sms.exception.SmsException; |
|||
import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; |
|||
import org.thingsboard.server.service.sms.AbstractSmsSender; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Slf4j |
|||
public class AwsSmsSender extends AbstractSmsSender { |
|||
|
|||
private static final Map<String, MessageAttributeValue> SMS_ATTRIBUTES = new HashMap<>(); |
|||
|
|||
static { |
|||
SMS_ATTRIBUTES.put("AWS.SNS.SMS.SMSType", new MessageAttributeValue() |
|||
.withStringValue("Transactional") |
|||
.withDataType("String")); |
|||
} |
|||
|
|||
private AmazonSNS snsClient; |
|||
|
|||
public AwsSmsSender(AwsSnsSmsProviderConfiguration config) { |
|||
if (StringUtils.isEmpty(config.getAccessKeyId()) || StringUtils.isEmpty(config.getSecretAccessKey()) || StringUtils.isEmpty(config.getRegion())) { |
|||
throw new IllegalArgumentException("Invalid AWS sms provider configuration: aws accessKeyId, aws secretAccessKey and aws region should be specified!"); |
|||
} |
|||
AWSCredentials awsCredentials = new BasicAWSCredentials(config.getAccessKeyId(), config.getSecretAccessKey()); |
|||
AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials); |
|||
this.snsClient = AmazonSNSClient.builder() |
|||
.withCredentials(credProvider) |
|||
.withRegion(config.getRegion()) |
|||
.build(); |
|||
} |
|||
|
|||
@Override |
|||
public int sendSms(String numberTo, String message) throws SmsException { |
|||
numberTo = this.validatePhoneNumber(numberTo); |
|||
message = this.prepareMessage(message); |
|||
try { |
|||
PublishRequest publishRequest = new PublishRequest() |
|||
.withMessageAttributes(SMS_ATTRIBUTES) |
|||
.withPhoneNumber(numberTo) |
|||
.withMessage(message); |
|||
this.snsClient.publish(publishRequest); |
|||
return this.countMessageSegments(message); |
|||
} catch (Exception e) { |
|||
throw new SmsSendException("Failed to send SMS message - " + e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
if (this.snsClient != null) { |
|||
try { |
|||
this.snsClient.shutdown(); |
|||
} catch (Exception e) { |
|||
log.error("Failed to shutdown SNS client during destroy()", e); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sms.twilio; |
|||
|
|||
import com.twilio.http.TwilioRestClient; |
|||
import com.twilio.rest.api.v2010.account.Message; |
|||
import com.twilio.type.PhoneNumber; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; |
|||
import org.thingsboard.rule.engine.api.sms.exception.SmsException; |
|||
import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; |
|||
import org.thingsboard.server.service.sms.AbstractSmsSender; |
|||
|
|||
public class TwilioSmsSender extends AbstractSmsSender { |
|||
|
|||
private TwilioRestClient twilioRestClient; |
|||
private String numberFrom; |
|||
|
|||
public TwilioSmsSender(TwilioSmsProviderConfiguration config) { |
|||
if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) { |
|||
throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!"); |
|||
} |
|||
this.numberFrom = this.validatePhoneNumber(config.getNumberFrom()); |
|||
this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); |
|||
} |
|||
|
|||
@Override |
|||
public int sendSms(String numberTo, String message) throws SmsException { |
|||
numberTo = this.validatePhoneNumber(numberTo); |
|||
message = this.prepareMessage(message); |
|||
try { |
|||
String numSegments = Message.creator(new PhoneNumber(numberTo), new PhoneNumber(this.numberFrom), message).create(this.twilioRestClient).getNumSegments(); |
|||
return Integer.valueOf(numSegments); |
|||
} catch (Exception e) { |
|||
throw new SmsSendException("Failed to send SMS message - " + e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
<#-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
|||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" |
|||
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<head> |
|||
<meta name="viewport" content="width=device-width"/> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> |
|||
<title>Thingsboard - Api Usage State</title> |
|||
|
|||
|
|||
<style type="text/css"> |
|||
img { |
|||
max-width: 100%; |
|||
} |
|||
|
|||
body { |
|||
-webkit-font-smoothing: antialiased; |
|||
-webkit-text-size-adjust: none; |
|||
width: 100% !important; |
|||
height: 100%; |
|||
line-height: 1.6em; |
|||
} |
|||
|
|||
body { |
|||
background-color: #f6f6f6; |
|||
} |
|||
|
|||
@media only screen and (max-width: 640px) { |
|||
body { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
h1 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h2 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h3 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h4 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h1 { |
|||
font-size: 22px !important; |
|||
} |
|||
|
|||
h2 { |
|||
font-size: 18px !important; |
|||
} |
|||
|
|||
h3 { |
|||
font-size: 16px !important; |
|||
} |
|||
|
|||
.container { |
|||
padding: 0 !important; |
|||
width: 100% !important; |
|||
} |
|||
|
|||
.content { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
.content-wrap { |
|||
padding: 10px !important; |
|||
} |
|||
|
|||
.invoice { |
|||
width: 100% !important; |
|||
} |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body itemscope itemtype="http://schema.org/EmailMessage" |
|||
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" |
|||
bgcolor="#f6f6f6"> |
|||
|
|||
<table class="main" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; border-radius: 3px; width: 100%; background-color: #f6f6f6; margin: 0px auto;" cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> |
|||
<tbody> |
|||
<tr style="box-sizing: border-box; margin: 0px;"> |
|||
<td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" align="center" valign="top"> |
|||
<table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 367px; background-color: #ffffff; width: 600px; max-width: 600px !important;" width="600" cellspacing="0" cellpadding="0"> |
|||
<tbody> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px; height: 110px;" valign="top"><img src="https://media.thingsboard.io/email/head.png" alt="" width="598" height="91" /></td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #000000; box-sizing: border-box; font-size: 16px; margin: 0px; padding: 0px 32px; height: 66px; vertical-align: middle;" valign="middle">Your ThingsBoard account feature was <strong>disabled</strong></td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; line-height: 24px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0px; padding: 0px 32px; height: 93px; vertical-align: top;" valign="top"> |
|||
<div style="padding: 16px; margin-bottom: 24px; border: solid 2px #EB5757; border-radius: 6px; background: rgba(235, 87, 87, 0.05);"><img style="vertical-align: middle; padding-right: 6px;" src="https://media.thingsboard.io/email/alarm.png" alt="" width="20" height="20" /> |
|||
<div style="display: inline; vertical-align: middle;">We have <strong>disabled</strong> the ${apiFeature} for your account because ThingsBoard has already <strong>${apiLimitValueLabel}.</strong></div> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 32px; height: 59px;" valign="top">Please contact your system administrator to resolve the issue.</td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 32px; height: 40px;" valign="top">— The ThingsBoard</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<table style="color: #999999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; margin: 0px auto; height: 64px; background-color: #f6f6f6; width: 100%;" cellpadding="0px 0px 20px"> |
|||
<tbody> |
|||
<tr style="box-sizing: border-box; margin: 0px;"> |
|||
<td class="aligncenter content-block" style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" align="center" valign="top">This email was sent to <a style="box-sizing: border-box; color: #999999; margin: 0px;" href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard.</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,142 @@ |
|||
<#-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
|||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" |
|||
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<head> |
|||
<meta name="viewport" content="width=device-width"/> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> |
|||
<title>Thingsboard - Api Usage State</title> |
|||
|
|||
|
|||
<style type="text/css"> |
|||
img { |
|||
max-width: 100%; |
|||
} |
|||
|
|||
body { |
|||
-webkit-font-smoothing: antialiased; |
|||
-webkit-text-size-adjust: none; |
|||
width: 100% !important; |
|||
height: 100%; |
|||
line-height: 1.6em; |
|||
} |
|||
|
|||
body { |
|||
background-color: #f6f6f6; |
|||
} |
|||
|
|||
@media only screen and (max-width: 640px) { |
|||
body { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
h1 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h2 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h3 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h4 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h1 { |
|||
font-size: 22px !important; |
|||
} |
|||
|
|||
h2 { |
|||
font-size: 18px !important; |
|||
} |
|||
|
|||
h3 { |
|||
font-size: 16px !important; |
|||
} |
|||
|
|||
.container { |
|||
padding: 0 !important; |
|||
width: 100% !important; |
|||
} |
|||
|
|||
.content { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
.content-wrap { |
|||
padding: 10px !important; |
|||
} |
|||
|
|||
.invoice { |
|||
width: 100% !important; |
|||
} |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body itemscope itemtype="http://schema.org/EmailMessage" |
|||
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" |
|||
bgcolor="#f6f6f6"> |
|||
|
|||
<table class="main" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; border-radius: 3px; width: 100%; background-color: #f6f6f6; margin: 0px auto;" cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> |
|||
<tbody> |
|||
<tr style="box-sizing: border-box; margin: 0px;"> |
|||
<td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" align="center" valign="top"> |
|||
<table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 310px; background-color: #ffffff; width: 600px; max-width: 600px !important;" width="600" cellspacing="0" cellpadding="0"> |
|||
<tbody> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px; height: 110px;" valign="top"><img src="https://media.thingsboard.io/email/head.png" alt="" width="598" height="91" /></td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #000000; box-sizing: border-box; font-size: 16px; margin: 0px; padding: 0px 32px; height: 66px; vertical-align: middle;" valign="middle">Your ThingsBoard account feature was <strong>enabled</strong></td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; line-height: 24px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0px; padding: 0px 32px; height: 93px; vertical-align: top;" valign="top"> |
|||
<div style="padding: 16px; margin-bottom: 24px; border: solid 2px #27AE60; border-radius: 6px; background: rgba(39, 174, 96, 0.05);"><img style="vertical-align: middle; padding-right: 6px;" src="https://media.thingsboard.io/email/confirm.png" alt="" width="20" height="20" /> |
|||
<div style="display: inline; vertical-align: middle;">We have enabled the ${apiFeature} for your account and ThingsBoard is already able to ${apiLabel} messages.</div> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 32px; height: 40px;" valign="top">— The ThingsBoard</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<table style="color: #999999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; margin: 0px auto; height: 64px; background-color: #f6f6f6; width: 100%;" cellpadding="0px 0px 20px"> |
|||
<tbody> |
|||
<tr style="box-sizing: border-box; margin: 0px;"> |
|||
<td class="aligncenter content-block" style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" align="center" valign="top">This email was sent to <a style="box-sizing: border-box; color: #999999; margin: 0px;" href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard.</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,145 @@ |
|||
<#-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
|||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" |
|||
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<head> |
|||
<meta name="viewport" content="width=device-width"/> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> |
|||
<title>Thingsboard - Api Usage State</title> |
|||
|
|||
|
|||
<style type="text/css"> |
|||
img { |
|||
max-width: 100%; |
|||
} |
|||
|
|||
body { |
|||
-webkit-font-smoothing: antialiased; |
|||
-webkit-text-size-adjust: none; |
|||
width: 100% !important; |
|||
height: 100%; |
|||
line-height: 1.6em; |
|||
} |
|||
|
|||
body { |
|||
background-color: #f6f6f6; |
|||
} |
|||
|
|||
@media only screen and (max-width: 640px) { |
|||
body { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
h1 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h2 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h3 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h4 { |
|||
font-weight: 800 !important; |
|||
margin: 20px 0 5px !important; |
|||
} |
|||
|
|||
h1 { |
|||
font-size: 22px !important; |
|||
} |
|||
|
|||
h2 { |
|||
font-size: 18px !important; |
|||
} |
|||
|
|||
h3 { |
|||
font-size: 16px !important; |
|||
} |
|||
|
|||
.container { |
|||
padding: 0 !important; |
|||
width: 100% !important; |
|||
} |
|||
|
|||
.content { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
.content-wrap { |
|||
padding: 10px !important; |
|||
} |
|||
|
|||
.invoice { |
|||
width: 100% !important; |
|||
} |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body itemscope itemtype="http://schema.org/EmailMessage" |
|||
style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" |
|||
bgcolor="#f6f6f6"> |
|||
|
|||
<table class="main" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; border-radius: 3px; width: 100%; background-color: #f6f6f6; margin: 0px auto;" cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> |
|||
<tbody> |
|||
<tr style="box-sizing: border-box; margin: 0px;"> |
|||
<td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" align="center" valign="top"> |
|||
<table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 367px; background-color: #ffffff; width: 600px; max-width: 600px !important;" width="600" cellspacing="0" cellpadding="0"> |
|||
<tbody> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px; height: 110px;" valign="top"><img src="https://media.thingsboard.io/email/head.png" alt="" width="598" height="91" /></td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #000000; box-sizing: border-box; font-size: 16px; margin: 0px; padding: 0px 32px; height: 66px; vertical-align: middle;" valign="middle"><strong>Warning:</strong> your ThingsBoard account feature may be disabled soon</td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; line-height: 24px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0px; padding: 0px 32px; height: 93px; vertical-align: top;" valign="top"> |
|||
<div style="padding: 16px; margin-bottom: 24px; border: solid 2px #F2994A; border-radius: 6px; background: rgba(242, 153, 74, 0.05);"><img style="vertical-align: middle; padding-right: 6px;" src="https://media.thingsboard.io/email/warning.png" alt="" width="20" height="20" /> |
|||
<div style="display: inline; vertical-align: middle;">ThingsBoard has already ${apiValueLabel}.<br />${apiFeature} will be <strong>disabled</strong> for your account once the limit will be reached.</div> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 32px; height: 59px;" valign="top">Please contact your system administrator to resolve the issue.</td> |
|||
</tr> |
|||
<tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
|||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 32px; height: 40px;" valign="top">— The ThingsBoard</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<table style="color: #999999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; margin: 0px auto; height: 64px; background-color: #f6f6f6; width: 100%;" cellpadding="0px 0px 20px"> |
|||
<tbody> |
|||
<tr style="box-sizing: border-box; margin: 0px;"> |
|||
<td class="aligncenter content-block" style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" align="center" valign="top">This email was sent to <a style="box-sizing: border-box; color: #999999; margin: 0px;" href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard.</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ApiUsageStateMailMessage { |
|||
private final ApiUsageRecordKey key; |
|||
private final long threshold; |
|||
private final long value; |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.device.profile; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
@Data |
|||
public class JsonTransportPayloadConfiguration implements TransportPayloadTypeConfiguration { |
|||
|
|||
@Override |
|||
public TransportPayloadType getTransportPayloadType() { |
|||
return TransportPayloadType.JSON; |
|||
} |
|||
} |
|||
@ -0,0 +1,200 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.device.profile; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.github.os72.protobuf.dynamic.DynamicSchema; |
|||
import com.github.os72.protobuf.dynamic.EnumDefinition; |
|||
import com.github.os72.protobuf.dynamic.MessageDefinition; |
|||
import com.google.protobuf.Descriptors; |
|||
import com.google.protobuf.DynamicMessage; |
|||
import com.squareup.wire.schema.Location; |
|||
import com.squareup.wire.schema.internal.parser.EnumConstantElement; |
|||
import com.squareup.wire.schema.internal.parser.EnumElement; |
|||
import com.squareup.wire.schema.internal.parser.FieldElement; |
|||
import com.squareup.wire.schema.internal.parser.MessageElement; |
|||
import com.squareup.wire.schema.internal.parser.OneOfElement; |
|||
import com.squareup.wire.schema.internal.parser.ProtoFileElement; |
|||
import com.squareup.wire.schema.internal.parser.ProtoParser; |
|||
import com.squareup.wire.schema.internal.parser.TypeElement; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeConfiguration { |
|||
|
|||
public static final Location LOCATION = new Location("", "", -1, -1); |
|||
public static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; |
|||
public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; |
|||
|
|||
private String deviceTelemetryProtoSchema; |
|||
private String deviceAttributesProtoSchema; |
|||
|
|||
@Override |
|||
public TransportPayloadType getTransportPayloadType() { |
|||
return TransportPayloadType.PROTOBUF; |
|||
} |
|||
|
|||
public Descriptors.Descriptor getTelemetryDynamicMessageDescriptor(String deviceTelemetryProtoSchema) { |
|||
return getDescriptor(deviceTelemetryProtoSchema, TELEMETRY_PROTO_SCHEMA); |
|||
} |
|||
|
|||
public Descriptors.Descriptor getAttributesDynamicMessageDescriptor(String deviceAttributesProtoSchema) { |
|||
return getDescriptor(deviceAttributesProtoSchema, ATTRIBUTES_PROTO_SCHEMA); |
|||
} |
|||
|
|||
private Descriptors.Descriptor getDescriptor(String protoSchema, String schemaName) { |
|||
try { |
|||
ProtoFileElement protoFileElement = getTransportProtoSchema(protoSchema); |
|||
DynamicSchema dynamicSchema = getDynamicSchema(protoFileElement, schemaName); |
|||
String lastMsgName = getMessageTypes(protoFileElement.getTypes()).stream() |
|||
.map(MessageElement::getName).reduce((previous, last) -> last).get(); |
|||
DynamicMessage.Builder builder = dynamicSchema.newMessageBuilder(lastMsgName); |
|||
return builder.getDescriptorForType(); |
|||
} catch (Exception e) { |
|||
log.warn("Failed to get Message Descriptor due to {}", e.getMessage()); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) { |
|||
DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); |
|||
schemaBuilder.setName(schemaName); |
|||
schemaBuilder.setPackage(!isEmptyStr(protoFileElement.getPackageName()) ? |
|||
protoFileElement.getPackageName() : schemaName.toLowerCase()); |
|||
List<TypeElement> types = protoFileElement.getTypes(); |
|||
List<MessageElement> messageTypes = getMessageTypes(types); |
|||
|
|||
if (!messageTypes.isEmpty()) { |
|||
List<EnumElement> enumTypes = getEnumElements(types); |
|||
if (!enumTypes.isEmpty()) { |
|||
enumTypes.forEach(enumElement -> { |
|||
EnumDefinition enumDefinition = getEnumDefinition(enumElement); |
|||
schemaBuilder.addEnumDefinition(enumDefinition); |
|||
}); |
|||
} |
|||
List<MessageDefinition> messageDefinitions = getMessageDefinitions(messageTypes); |
|||
messageDefinitions.forEach(schemaBuilder::addMessageDefinition); |
|||
try { |
|||
return schemaBuilder.build(); |
|||
} catch (Descriptors.DescriptorValidationException e) { |
|||
throw new RuntimeException("Failed to create dynamic schema due to: " + e.getMessage()); |
|||
} |
|||
} else { |
|||
throw new RuntimeException("Failed to get Dynamic Schema! Message types is empty for schema:" + schemaName); |
|||
} |
|||
} |
|||
|
|||
public ProtoFileElement getTransportProtoSchema(String protoSchema) { |
|||
return new ProtoParser(LOCATION, protoSchema.toCharArray()).readProtoFile(); |
|||
} |
|||
|
|||
private List<MessageElement> getMessageTypes(List<TypeElement> types) { |
|||
return types.stream() |
|||
.filter(typeElement -> typeElement instanceof MessageElement) |
|||
.map(typeElement -> (MessageElement) typeElement) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
private List<EnumElement> getEnumElements(List<TypeElement> types) { |
|||
return types.stream() |
|||
.filter(typeElement -> typeElement instanceof EnumElement) |
|||
.map(typeElement -> (EnumElement) typeElement) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
private List<MessageDefinition> getMessageDefinitions(List<MessageElement> messageElementsList) { |
|||
if (!messageElementsList.isEmpty()) { |
|||
List<MessageDefinition> messageDefinitions = new ArrayList<>(); |
|||
messageElementsList.forEach(messageElement -> { |
|||
MessageDefinition.Builder messageDefinitionBuilder = MessageDefinition.newBuilder(messageElement.getName()); |
|||
|
|||
List<TypeElement> nestedTypes = messageElement.getNestedTypes(); |
|||
if (!nestedTypes.isEmpty()) { |
|||
List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes); |
|||
if (!nestedEnumTypes.isEmpty()) { |
|||
nestedEnumTypes.forEach(enumElement -> { |
|||
EnumDefinition nestedEnumDefinition = getEnumDefinition(enumElement); |
|||
messageDefinitionBuilder.addEnumDefinition(nestedEnumDefinition); |
|||
}); |
|||
} |
|||
List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes); |
|||
List<MessageDefinition> nestedMessageDefinitions = getMessageDefinitions(nestedMessageTypes); |
|||
nestedMessageDefinitions.forEach(messageDefinitionBuilder::addMessageDefinition); |
|||
} |
|||
List<FieldElement> messageElementFields = messageElement.getFields(); |
|||
List<OneOfElement> oneOfs = messageElement.getOneOfs(); |
|||
if (!oneOfs.isEmpty()) { |
|||
for (OneOfElement oneOfelement : oneOfs) { |
|||
MessageDefinition.OneofBuilder oneofBuilder = messageDefinitionBuilder.addOneof(oneOfelement.getName()); |
|||
addMessageFieldsToTheOneOfDefinition(oneOfelement.getFields(), oneofBuilder); |
|||
} |
|||
} |
|||
if (!messageElementFields.isEmpty()) { |
|||
addMessageFieldsToTheMessageDefinition(messageElementFields, messageDefinitionBuilder); |
|||
} |
|||
messageDefinitions.add(messageDefinitionBuilder.build()); |
|||
}); |
|||
return messageDefinitions; |
|||
} else { |
|||
return Collections.emptyList(); |
|||
} |
|||
} |
|||
|
|||
private EnumDefinition getEnumDefinition(EnumElement enumElement) { |
|||
List<EnumConstantElement> enumElementTypeConstants = enumElement.getConstants(); |
|||
EnumDefinition.Builder enumDefinitionBuilder = EnumDefinition.newBuilder(enumElement.getName()); |
|||
if (!enumElementTypeConstants.isEmpty()) { |
|||
enumElementTypeConstants.forEach(constantElement -> enumDefinitionBuilder.addValue(constantElement.getName(), constantElement.getTag())); |
|||
} |
|||
return enumDefinitionBuilder.build(); |
|||
} |
|||
|
|||
|
|||
private void addMessageFieldsToTheMessageDefinition(List<FieldElement> messageElementFields, MessageDefinition.Builder messageDefinitionBuilder) { |
|||
messageElementFields.forEach(fieldElement -> { |
|||
String labelStr = null; |
|||
if (fieldElement.getLabel() != null) { |
|||
labelStr = fieldElement.getLabel().name().toLowerCase(); |
|||
} |
|||
messageDefinitionBuilder.addField( |
|||
labelStr, |
|||
fieldElement.getType(), |
|||
fieldElement.getName(), |
|||
fieldElement.getTag()); |
|||
}); |
|||
} |
|||
|
|||
private void addMessageFieldsToTheOneOfDefinition(List<FieldElement> oneOfsElementFields, MessageDefinition.OneofBuilder oneofBuilder) { |
|||
oneOfsElementFields.forEach(fieldElement -> oneofBuilder.addField( |
|||
fieldElement.getType(), |
|||
fieldElement.getName(), |
|||
fieldElement.getTag())); |
|||
oneofBuilder.msgDefBuilder(); |
|||
} |
|||
|
|||
private boolean isEmptyStr(String str) { |
|||
return str == null || "".equals(str); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.device.profile; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
@JsonTypeInfo( |
|||
use = JsonTypeInfo.Id.NAME, |
|||
include = JsonTypeInfo.As.PROPERTY, |
|||
property = "transportPayloadType") |
|||
@JsonSubTypes({ |
|||
@JsonSubTypes.Type(value = JsonTransportPayloadConfiguration.class, name = "JSON"), |
|||
@JsonSubTypes.Type(value = ProtoTransportPayloadConfiguration.class, name = "PROTOBUF")}) |
|||
public interface TransportPayloadTypeConfiguration { |
|||
|
|||
@JsonIgnore |
|||
TransportPayloadType getTransportPayloadType(); |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sms.config; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class AwsSnsSmsProviderConfiguration implements SmsProviderConfiguration { |
|||
|
|||
private String accessKeyId; |
|||
private String secretAccessKey; |
|||
private String region; |
|||
|
|||
@Override |
|||
public SmsProviderType getType() { |
|||
return SmsProviderType.AWS_SNS; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sms.config; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
|
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
@JsonTypeInfo( |
|||
use = JsonTypeInfo.Id.NAME, |
|||
include = JsonTypeInfo.As.PROPERTY, |
|||
property = "type") |
|||
@JsonSubTypes({ |
|||
@JsonSubTypes.Type(value = AwsSnsSmsProviderConfiguration.class, name = "AWS_SNS"), |
|||
@JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO")}) |
|||
public interface SmsProviderConfiguration { |
|||
|
|||
@JsonIgnore |
|||
SmsProviderType getType(); |
|||
|
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sms.config; |
|||
|
|||
public enum SmsProviderType { |
|||
AWS_SNS, |
|||
TWILIO |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sms.config; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class TestSmsRequest { |
|||
|
|||
private SmsProviderConfiguration providerConfiguration; |
|||
private String numberTo; |
|||
private String message; |
|||
|
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue