Commit a583f9fa8e9cd123bc9852d3a5c0138928afea6f
Merge branch 'master' of github.com:thingsboard/thingsboard
Showing
100 changed files
with
435 additions
and
1587 deletions
Too many changes to show.
To preserve performance only 100 of 130 files are displayed.
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>application</artifactId> | 26 | <artifactId>application</artifactId> |
@@ -955,7 +955,7 @@ | @@ -955,7 +955,7 @@ | ||
955 | }, | 955 | }, |
956 | "methodName": "gateway_restart", | 956 | "methodName": "gateway_restart", |
957 | "methodParams": "{}", | 957 | "methodParams": "{}", |
958 | - "buttonText": "gateway restart" | 958 | + "buttonText": "GATEWAY RESTART" |
959 | }, | 959 | }, |
960 | "title": "New RPC Button", | 960 | "title": "New RPC Button", |
961 | "dropShadow": true, | 961 | "dropShadow": true, |
application/src/main/data/json/demo/dashboards/raspberry_pi_gpio_demo_dashboard.json
deleted
100644 → 0
1 | -{ | ||
2 | - "title": "Raspberry PI GPIO Demo Dashboard", | ||
3 | - "configuration": { | ||
4 | - "description": "Demo dashboard for Raspberry PI GPIO Demo", | ||
5 | - "widgets": { | ||
6 | - "602177f6-267b-cb87-4e8f-e23d7fb2f61c": { | ||
7 | - "isSystemType": true, | ||
8 | - "bundleAlias": "gpio_widgets", | ||
9 | - "typeAlias": "raspberry_pi_gpio_control", | ||
10 | - "type": "rpc", | ||
11 | - "title": "New widget", | ||
12 | - "sizeX": 6, | ||
13 | - "sizeY": 10, | ||
14 | - "config": { | ||
15 | - "targetDeviceAliases": [], | ||
16 | - "showTitle": true, | ||
17 | - "backgroundColor": "#fff", | ||
18 | - "color": "rgba(0, 0, 0, 0.87)", | ||
19 | - "padding": "0px", | ||
20 | - "settings": { | ||
21 | - "parseGpioStatusFunction": "return body[pin] === true;", | ||
22 | - "gpioStatusChangeRequest": { | ||
23 | - "method": "setGpioStatus", | ||
24 | - "paramsBody": "{\n \"pin\": \"{$pin}\",\n \"enabled\": \"{$enabled}\"\n}" | ||
25 | - }, | ||
26 | - "requestTimeout": 500, | ||
27 | - "switchPanelBackgroundColor": "#008a00", | ||
28 | - "gpioStatusRequest": { | ||
29 | - "method": "getGpioStatus", | ||
30 | - "paramsBody": "{}" | ||
31 | - }, | ||
32 | - "gpioList": [ | ||
33 | - { | ||
34 | - "pin": 7, | ||
35 | - "label": "GPIO 4 (GPCLK0)", | ||
36 | - "row": 3, | ||
37 | - "col": 0, | ||
38 | - "_uniqueKey": 0 | ||
39 | - }, | ||
40 | - { | ||
41 | - "pin": 11, | ||
42 | - "label": "GPIO 17", | ||
43 | - "row": 5, | ||
44 | - "col": 0, | ||
45 | - "_uniqueKey": 1 | ||
46 | - }, | ||
47 | - { | ||
48 | - "pin": 12, | ||
49 | - "label": "GPIO 18", | ||
50 | - "row": 5, | ||
51 | - "col": 1, | ||
52 | - "_uniqueKey": 2 | ||
53 | - }, | ||
54 | - { | ||
55 | - "_uniqueKey": 3, | ||
56 | - "pin": 13, | ||
57 | - "label": "GPIO 27", | ||
58 | - "row": 6, | ||
59 | - "col": 0 | ||
60 | - }, | ||
61 | - { | ||
62 | - "_uniqueKey": 4, | ||
63 | - "pin": 15, | ||
64 | - "label": "GPIO 22", | ||
65 | - "row": 7, | ||
66 | - "col": 0 | ||
67 | - }, | ||
68 | - { | ||
69 | - "_uniqueKey": 5, | ||
70 | - "pin": 16, | ||
71 | - "label": "GPIO 23", | ||
72 | - "row": 7, | ||
73 | - "col": 1 | ||
74 | - }, | ||
75 | - { | ||
76 | - "_uniqueKey": 6, | ||
77 | - "pin": 18, | ||
78 | - "label": "GPIO 24", | ||
79 | - "row": 8, | ||
80 | - "col": 1 | ||
81 | - }, | ||
82 | - { | ||
83 | - "_uniqueKey": 7, | ||
84 | - "pin": 22, | ||
85 | - "label": "GPIO 25", | ||
86 | - "row": 10, | ||
87 | - "col": 1 | ||
88 | - }, | ||
89 | - { | ||
90 | - "_uniqueKey": 8, | ||
91 | - "pin": 29, | ||
92 | - "label": "GPIO 5", | ||
93 | - "row": 14, | ||
94 | - "col": 0 | ||
95 | - }, | ||
96 | - { | ||
97 | - "_uniqueKey": 9, | ||
98 | - "pin": 31, | ||
99 | - "label": "GPIO 6", | ||
100 | - "row": 15, | ||
101 | - "col": 0 | ||
102 | - }, | ||
103 | - { | ||
104 | - "_uniqueKey": 10, | ||
105 | - "pin": 32, | ||
106 | - "label": "GPIO 12", | ||
107 | - "row": 15, | ||
108 | - "col": 1 | ||
109 | - }, | ||
110 | - { | ||
111 | - "_uniqueKey": 11, | ||
112 | - "pin": 33, | ||
113 | - "label": "GPIO 13", | ||
114 | - "row": 16, | ||
115 | - "col": 0 | ||
116 | - }, | ||
117 | - { | ||
118 | - "_uniqueKey": 12, | ||
119 | - "pin": 35, | ||
120 | - "label": "GPIO 19", | ||
121 | - "row": 17, | ||
122 | - "col": 0 | ||
123 | - }, | ||
124 | - { | ||
125 | - "_uniqueKey": 13, | ||
126 | - "pin": 36, | ||
127 | - "label": "GPIO 16", | ||
128 | - "row": 17, | ||
129 | - "col": 1 | ||
130 | - }, | ||
131 | - { | ||
132 | - "_uniqueKey": 14, | ||
133 | - "pin": 37, | ||
134 | - "label": "GPIO 26", | ||
135 | - "row": 18, | ||
136 | - "col": 0 | ||
137 | - }, | ||
138 | - { | ||
139 | - "_uniqueKey": 15, | ||
140 | - "pin": 38, | ||
141 | - "label": "GPIO 20", | ||
142 | - "row": 18, | ||
143 | - "col": 1 | ||
144 | - }, | ||
145 | - { | ||
146 | - "_uniqueKey": 16, | ||
147 | - "pin": 40, | ||
148 | - "label": "GPIO 21", | ||
149 | - "row": 19, | ||
150 | - "col": 1 | ||
151 | - } | ||
152 | - ] | ||
153 | - }, | ||
154 | - "title": "Raspberry Pi GPIO Control Panel", | ||
155 | - "datasources": [], | ||
156 | - "targetDeviceAliasIds": [ | ||
157 | - "f26b12b6-6938-e1a0-85ec-d88a1f23e382" | ||
158 | - ] | ||
159 | - }, | ||
160 | - "row": 0, | ||
161 | - "col": 0, | ||
162 | - "id": "602177f6-267b-cb87-4e8f-e23d7fb2f61c" | ||
163 | - }, | ||
164 | - "3cca52a5-e874-eb43-b444-8efa01e663c8": { | ||
165 | - "isSystemType": true, | ||
166 | - "bundleAlias": "gpio_widgets", | ||
167 | - "typeAlias": "raspberry_pi_gpio_panel", | ||
168 | - "type": "latest", | ||
169 | - "title": "New widget", | ||
170 | - "sizeX": 7, | ||
171 | - "sizeY": 10, | ||
172 | - "config": { | ||
173 | - "showTitle": true, | ||
174 | - "backgroundColor": "#fff", | ||
175 | - "color": "rgba(0, 0, 0, 0.87)", | ||
176 | - "padding": "0px", | ||
177 | - "settings": { | ||
178 | - "gpioList": [ | ||
179 | - { | ||
180 | - "pin": 1, | ||
181 | - "label": "3.3V", | ||
182 | - "row": 0, | ||
183 | - "col": 0, | ||
184 | - "color": "#fc9700", | ||
185 | - "_uniqueKey": 0 | ||
186 | - }, | ||
187 | - { | ||
188 | - "pin": 2, | ||
189 | - "label": "5V", | ||
190 | - "row": 0, | ||
191 | - "col": 1, | ||
192 | - "color": "#fb0000", | ||
193 | - "_uniqueKey": 1 | ||
194 | - }, | ||
195 | - { | ||
196 | - "pin": 3, | ||
197 | - "label": "GPIO 2 (I2C1_SDA)", | ||
198 | - "row": 1, | ||
199 | - "col": 0, | ||
200 | - "color": "#02fefb", | ||
201 | - "_uniqueKey": 2 | ||
202 | - }, | ||
203 | - { | ||
204 | - "color": "#fb0000", | ||
205 | - "pin": 4, | ||
206 | - "label": "5V", | ||
207 | - "row": 1, | ||
208 | - "col": 1 | ||
209 | - }, | ||
210 | - { | ||
211 | - "color": "#02fefb", | ||
212 | - "pin": 5, | ||
213 | - "label": "GPIO 3 (I2C1_SCL)", | ||
214 | - "row": 2, | ||
215 | - "col": 0 | ||
216 | - }, | ||
217 | - { | ||
218 | - "color": "#000000", | ||
219 | - "pin": 6, | ||
220 | - "label": "GND", | ||
221 | - "row": 2, | ||
222 | - "col": 1 | ||
223 | - }, | ||
224 | - { | ||
225 | - "color": "#00fd00", | ||
226 | - "pin": 7, | ||
227 | - "label": "GPIO 4 (GPCLK0)", | ||
228 | - "row": 3, | ||
229 | - "col": 0 | ||
230 | - }, | ||
231 | - { | ||
232 | - "color": "#fdfb00", | ||
233 | - "pin": 8, | ||
234 | - "label": "GPIO 14 (UART_TXD)", | ||
235 | - "row": 3, | ||
236 | - "col": 1 | ||
237 | - }, | ||
238 | - { | ||
239 | - "color": "#000000", | ||
240 | - "pin": 9, | ||
241 | - "label": "GND", | ||
242 | - "row": 4, | ||
243 | - "col": 0 | ||
244 | - }, | ||
245 | - { | ||
246 | - "color": "#fdfb00", | ||
247 | - "pin": 10, | ||
248 | - "label": "GPIO 15 (UART_RXD)", | ||
249 | - "row": 4, | ||
250 | - "col": 1 | ||
251 | - }, | ||
252 | - { | ||
253 | - "color": "#00fd00", | ||
254 | - "pin": 11, | ||
255 | - "label": "GPIO 17", | ||
256 | - "row": 5, | ||
257 | - "col": 0 | ||
258 | - }, | ||
259 | - { | ||
260 | - "color": "#00fd00", | ||
261 | - "pin": 12, | ||
262 | - "label": "GPIO 18", | ||
263 | - "row": 5, | ||
264 | - "col": 1 | ||
265 | - }, | ||
266 | - { | ||
267 | - "color": "#00fd00", | ||
268 | - "pin": 13, | ||
269 | - "label": "GPIO 27", | ||
270 | - "row": 6, | ||
271 | - "col": 0 | ||
272 | - }, | ||
273 | - { | ||
274 | - "color": "#000000", | ||
275 | - "pin": 14, | ||
276 | - "label": "GND", | ||
277 | - "row": 6, | ||
278 | - "col": 1 | ||
279 | - }, | ||
280 | - { | ||
281 | - "color": "#00fd00", | ||
282 | - "pin": 15, | ||
283 | - "label": "GPIO 22", | ||
284 | - "row": 7, | ||
285 | - "col": 0 | ||
286 | - }, | ||
287 | - { | ||
288 | - "color": "#00fd00", | ||
289 | - "pin": 16, | ||
290 | - "label": "GPIO 23", | ||
291 | - "row": 7, | ||
292 | - "col": 1 | ||
293 | - }, | ||
294 | - { | ||
295 | - "color": "#fc9700", | ||
296 | - "pin": 17, | ||
297 | - "label": "3.3V", | ||
298 | - "row": 8, | ||
299 | - "col": 0 | ||
300 | - }, | ||
301 | - { | ||
302 | - "color": "#00fd00", | ||
303 | - "pin": 18, | ||
304 | - "label": "GPIO 24", | ||
305 | - "row": 8, | ||
306 | - "col": 1 | ||
307 | - }, | ||
308 | - { | ||
309 | - "color": "#fd01fd", | ||
310 | - "pin": 19, | ||
311 | - "label": "GPIO 10 (SPI_MOSI)", | ||
312 | - "row": 9, | ||
313 | - "col": 0 | ||
314 | - }, | ||
315 | - { | ||
316 | - "color": "#000000", | ||
317 | - "pin": 20, | ||
318 | - "label": "GND", | ||
319 | - "row": 9, | ||
320 | - "col": 1 | ||
321 | - }, | ||
322 | - { | ||
323 | - "color": "#fd01fd", | ||
324 | - "pin": 21, | ||
325 | - "label": "GPIO 9 (SPI_MISO)", | ||
326 | - "row": 10, | ||
327 | - "col": 0 | ||
328 | - }, | ||
329 | - { | ||
330 | - "color": "#00fd00", | ||
331 | - "pin": 22, | ||
332 | - "label": "GPIO 25", | ||
333 | - "row": 10, | ||
334 | - "col": 1 | ||
335 | - }, | ||
336 | - { | ||
337 | - "color": "#fd01fd", | ||
338 | - "pin": 23, | ||
339 | - "label": "GPIO 11 (SPI_SCLK)", | ||
340 | - "row": 11, | ||
341 | - "col": 0 | ||
342 | - }, | ||
343 | - { | ||
344 | - "color": "#fd01fd", | ||
345 | - "pin": 24, | ||
346 | - "label": "GPIO 8 (SPI_CE0)", | ||
347 | - "row": 11, | ||
348 | - "col": 1 | ||
349 | - }, | ||
350 | - { | ||
351 | - "color": "#000000", | ||
352 | - "pin": 25, | ||
353 | - "label": "GND", | ||
354 | - "row": 12, | ||
355 | - "col": 0 | ||
356 | - }, | ||
357 | - { | ||
358 | - "color": "#fd01fd", | ||
359 | - "pin": 26, | ||
360 | - "label": "GPIO 7 (SPI_CE1)", | ||
361 | - "row": 12, | ||
362 | - "col": 1 | ||
363 | - }, | ||
364 | - { | ||
365 | - "color": "#ffffff", | ||
366 | - "pin": 27, | ||
367 | - "label": "ID_SD", | ||
368 | - "row": 13, | ||
369 | - "col": 0 | ||
370 | - }, | ||
371 | - { | ||
372 | - "color": "#ffffff", | ||
373 | - "pin": 28, | ||
374 | - "label": "ID_SC", | ||
375 | - "row": 13, | ||
376 | - "col": 1 | ||
377 | - }, | ||
378 | - { | ||
379 | - "color": "#00fd00", | ||
380 | - "pin": 29, | ||
381 | - "label": "GPIO 5", | ||
382 | - "row": 14, | ||
383 | - "col": 0 | ||
384 | - }, | ||
385 | - { | ||
386 | - "color": "#000000", | ||
387 | - "pin": 30, | ||
388 | - "label": "GND", | ||
389 | - "row": 14, | ||
390 | - "col": 1 | ||
391 | - }, | ||
392 | - { | ||
393 | - "color": "#00fd00", | ||
394 | - "pin": 31, | ||
395 | - "label": "GPIO 6", | ||
396 | - "row": 15, | ||
397 | - "col": 0 | ||
398 | - }, | ||
399 | - { | ||
400 | - "color": "#00fd00", | ||
401 | - "pin": 32, | ||
402 | - "label": "GPIO 12", | ||
403 | - "row": 15, | ||
404 | - "col": 1 | ||
405 | - }, | ||
406 | - { | ||
407 | - "color": "#00fd00", | ||
408 | - "pin": 33, | ||
409 | - "label": "GPIO 13", | ||
410 | - "row": 16, | ||
411 | - "col": 0 | ||
412 | - }, | ||
413 | - { | ||
414 | - "color": "#000000", | ||
415 | - "pin": 34, | ||
416 | - "label": "GND", | ||
417 | - "row": 16, | ||
418 | - "col": 1 | ||
419 | - }, | ||
420 | - { | ||
421 | - "color": "#00fd00", | ||
422 | - "pin": 35, | ||
423 | - "label": "GPIO 19", | ||
424 | - "row": 17, | ||
425 | - "col": 0 | ||
426 | - }, | ||
427 | - { | ||
428 | - "color": "#00fd00", | ||
429 | - "pin": 36, | ||
430 | - "label": "GPIO 16", | ||
431 | - "row": 17, | ||
432 | - "col": 1 | ||
433 | - }, | ||
434 | - { | ||
435 | - "color": "#00fd00", | ||
436 | - "pin": 37, | ||
437 | - "label": "GPIO 26", | ||
438 | - "row": 18, | ||
439 | - "col": 0 | ||
440 | - }, | ||
441 | - { | ||
442 | - "color": "#00fd00", | ||
443 | - "pin": 38, | ||
444 | - "label": "GPIO 20", | ||
445 | - "row": 18, | ||
446 | - "col": 1 | ||
447 | - }, | ||
448 | - { | ||
449 | - "color": "#000000", | ||
450 | - "pin": 39, | ||
451 | - "label": "GND", | ||
452 | - "row": 19, | ||
453 | - "col": 0 | ||
454 | - }, | ||
455 | - { | ||
456 | - "color": "#00fd00", | ||
457 | - "pin": 40, | ||
458 | - "label": "GPIO 21", | ||
459 | - "row": 19, | ||
460 | - "col": 1 | ||
461 | - } | ||
462 | - ], | ||
463 | - "ledPanelBackgroundColor": "#008a00" | ||
464 | - }, | ||
465 | - "title": "Raspberry Pi GPIO Status Panel", | ||
466 | - "datasources": [ | ||
467 | - { | ||
468 | - "type": "entity", | ||
469 | - "dataKeys": [ | ||
470 | - { | ||
471 | - "name": "7", | ||
472 | - "type": "attribute", | ||
473 | - "label": "7", | ||
474 | - "color": "#2196f3", | ||
475 | - "settings": {}, | ||
476 | - "_hash": 0.20925966435886978 | ||
477 | - }, | ||
478 | - { | ||
479 | - "name": "11", | ||
480 | - "type": "attribute", | ||
481 | - "label": "11", | ||
482 | - "color": "#4caf50", | ||
483 | - "settings": {}, | ||
484 | - "_hash": 0.330267349594344 | ||
485 | - }, | ||
486 | - { | ||
487 | - "name": "12", | ||
488 | - "type": "attribute", | ||
489 | - "label": "12", | ||
490 | - "color": "#f44336", | ||
491 | - "settings": {}, | ||
492 | - "_hash": 0.5040578704481748 | ||
493 | - }, | ||
494 | - { | ||
495 | - "name": "13", | ||
496 | - "type": "attribute", | ||
497 | - "label": "13", | ||
498 | - "color": "#ffc107", | ||
499 | - "settings": {}, | ||
500 | - "_hash": 0.588956328191639 | ||
501 | - }, | ||
502 | - { | ||
503 | - "name": "15", | ||
504 | - "type": "attribute", | ||
505 | - "label": "15", | ||
506 | - "color": "#607d8b", | ||
507 | - "settings": {}, | ||
508 | - "_hash": 0.9229040530336119 | ||
509 | - }, | ||
510 | - { | ||
511 | - "name": "16", | ||
512 | - "type": "attribute", | ||
513 | - "label": "16", | ||
514 | - "color": "#9c27b0", | ||
515 | - "settings": {}, | ||
516 | - "_hash": 0.8692315253041654 | ||
517 | - }, | ||
518 | - { | ||
519 | - "name": "18", | ||
520 | - "type": "attribute", | ||
521 | - "label": "18", | ||
522 | - "color": "#8bc34a", | ||
523 | - "settings": {}, | ||
524 | - "_hash": 0.41465562857521543 | ||
525 | - }, | ||
526 | - { | ||
527 | - "name": "22", | ||
528 | - "type": "attribute", | ||
529 | - "label": "22", | ||
530 | - "color": "#3f51b5", | ||
531 | - "settings": {}, | ||
532 | - "_hash": 0.36135260043112827 | ||
533 | - }, | ||
534 | - { | ||
535 | - "name": "29", | ||
536 | - "type": "attribute", | ||
537 | - "label": "29", | ||
538 | - "color": "#e91e63", | ||
539 | - "settings": {}, | ||
540 | - "_hash": 0.9904592276182183 | ||
541 | - }, | ||
542 | - { | ||
543 | - "name": "31", | ||
544 | - "type": "attribute", | ||
545 | - "label": "31", | ||
546 | - "color": "#ffeb3b", | ||
547 | - "settings": {}, | ||
548 | - "_hash": 0.038330985429919195 | ||
549 | - }, | ||
550 | - { | ||
551 | - "name": "32", | ||
552 | - "type": "attribute", | ||
553 | - "label": "32", | ||
554 | - "color": "#03a9f4", | ||
555 | - "settings": {}, | ||
556 | - "_hash": 0.4334683890135089 | ||
557 | - }, | ||
558 | - { | ||
559 | - "name": "33", | ||
560 | - "type": "attribute", | ||
561 | - "label": "33", | ||
562 | - "color": "#ff9800", | ||
563 | - "settings": {}, | ||
564 | - "_hash": 0.6487255992492305 | ||
565 | - }, | ||
566 | - { | ||
567 | - "name": "35", | ||
568 | - "type": "attribute", | ||
569 | - "label": "35", | ||
570 | - "color": "#673ab7", | ||
571 | - "settings": {}, | ||
572 | - "_hash": 0.971555321150732 | ||
573 | - }, | ||
574 | - { | ||
575 | - "name": "36", | ||
576 | - "type": "attribute", | ||
577 | - "label": "36", | ||
578 | - "color": "#cddc39", | ||
579 | - "settings": {}, | ||
580 | - "_hash": 0.7826129728424382 | ||
581 | - }, | ||
582 | - { | ||
583 | - "name": "37", | ||
584 | - "type": "attribute", | ||
585 | - "label": "37", | ||
586 | - "color": "#009688", | ||
587 | - "settings": {}, | ||
588 | - "_hash": 0.44925676517537627 | ||
589 | - }, | ||
590 | - { | ||
591 | - "name": "38", | ||
592 | - "type": "attribute", | ||
593 | - "label": "38", | ||
594 | - "color": "#795548", | ||
595 | - "settings": {}, | ||
596 | - "_hash": 0.051518155759787465 | ||
597 | - }, | ||
598 | - { | ||
599 | - "name": "40", | ||
600 | - "type": "attribute", | ||
601 | - "label": "40", | ||
602 | - "color": "#00bcd4", | ||
603 | - "settings": {}, | ||
604 | - "_hash": 0.8733296686871144 | ||
605 | - } | ||
606 | - ], | ||
607 | - "name": "RPi", | ||
608 | - "entityAliasId": "f26b12b6-6938-e1a0-85ec-d88a1f23e382" | ||
609 | - } | ||
610 | - ], | ||
611 | - "timewindow": { | ||
612 | - "realtime": { | ||
613 | - "timewindowMs": 60000 | ||
614 | - } | ||
615 | - } | ||
616 | - }, | ||
617 | - "row": 0, | ||
618 | - "col": 6, | ||
619 | - "id": "3cca52a5-e874-eb43-b444-8efa01e663c8" | ||
620 | - } | ||
621 | - }, | ||
622 | - "states": { | ||
623 | - "default": { | ||
624 | - "name": "Default", | ||
625 | - "root": true, | ||
626 | - "layouts": { | ||
627 | - "main": { | ||
628 | - "widgets": { | ||
629 | - "602177f6-267b-cb87-4e8f-e23d7fb2f61c": { | ||
630 | - "sizeX": 6, | ||
631 | - "sizeY": 10, | ||
632 | - "row": 0, | ||
633 | - "col": 0 | ||
634 | - }, | ||
635 | - "3cca52a5-e874-eb43-b444-8efa01e663c8": { | ||
636 | - "sizeX": 7, | ||
637 | - "sizeY": 10, | ||
638 | - "row": 0, | ||
639 | - "col": 6 | ||
640 | - } | ||
641 | - }, | ||
642 | - "gridSettings": { | ||
643 | - "backgroundColor": "#eeeeee", | ||
644 | - "color": "rgba(0,0,0,0.870588)", | ||
645 | - "columns": 24, | ||
646 | - "margins": [ | ||
647 | - 10, | ||
648 | - 10 | ||
649 | - ], | ||
650 | - "backgroundSizeMode": "100%" | ||
651 | - } | ||
652 | - } | ||
653 | - } | ||
654 | - } | ||
655 | - }, | ||
656 | - "entityAliases": { | ||
657 | - "f26b12b6-6938-e1a0-85ec-d88a1f23e382": { | ||
658 | - "id": "f26b12b6-6938-e1a0-85ec-d88a1f23e382", | ||
659 | - "alias": "RPi", | ||
660 | - "filter": { | ||
661 | - "type": "entityName", | ||
662 | - "resolveMultiple": false, | ||
663 | - "entityType": "DEVICE", | ||
664 | - "entityNameFilter": "Raspberry Pi Demo Device" | ||
665 | - } | ||
666 | - } | ||
667 | - }, | ||
668 | - "timewindow": { | ||
669 | - "displayValue": "", | ||
670 | - "selectedTab": 0, | ||
671 | - "realtime": { | ||
672 | - "interval": 1000, | ||
673 | - "timewindowMs": 60000 | ||
674 | - }, | ||
675 | - "history": { | ||
676 | - "historyType": 0, | ||
677 | - "interval": 1000, | ||
678 | - "timewindowMs": 60000, | ||
679 | - "fixedTimewindow": { | ||
680 | - "startTimeMs": 1498653734150, | ||
681 | - "endTimeMs": 1498740134150 | ||
682 | - } | ||
683 | - }, | ||
684 | - "aggregation": { | ||
685 | - "type": "AVG", | ||
686 | - "limit": 200 | ||
687 | - } | ||
688 | - }, | ||
689 | - "settings": { | ||
690 | - "stateControllerId": "default", | ||
691 | - "showTitle": true, | ||
692 | - "showDashboardsSelect": true, | ||
693 | - "showEntitiesSelect": true, | ||
694 | - "showDashboardTimewindow": true, | ||
695 | - "showDashboardExport": true, | ||
696 | - "toolbarAlwaysOpen": false | ||
697 | - } | ||
698 | - }, | ||
699 | - "name": "Raspberry PI GPIO Demo Dashboard" | ||
700 | -} |
application/src/main/data/json/demo/dashboards/temperature___humidity_demo_dashboard.json
deleted
100644 → 0
1 | -{ | ||
2 | - "title": "Temperature & Humidity Demo Dashboard", | ||
3 | - "configuration": { | ||
4 | - "description": "Demo dashboard for sample applications that upload temperature and humidity received from DHT11 or DHT22 sensors", | ||
5 | - "widgets": { | ||
6 | - "03e06986-1c50-e9e4-267c-2bae930ad9a2": { | ||
7 | - "isSystemType": true, | ||
8 | - "bundleAlias": "digital_gauges", | ||
9 | - "typeAlias": "digital_thermometer", | ||
10 | - "type": "latest", | ||
11 | - "title": "New widget", | ||
12 | - "sizeX": 5, | ||
13 | - "sizeY": 5, | ||
14 | - "config": { | ||
15 | - "datasources": [ | ||
16 | - { | ||
17 | - "type": "entity", | ||
18 | - "dataKeys": [ | ||
19 | - { | ||
20 | - "name": "temperature", | ||
21 | - "type": "timeseries", | ||
22 | - "label": "temperature", | ||
23 | - "color": "#2196f3", | ||
24 | - "settings": {}, | ||
25 | - "_hash": 0.3720839051412099 | ||
26 | - } | ||
27 | - ], | ||
28 | - "name": "DHT11", | ||
29 | - "entityAliasId": "63a93238-c13f-4403-4bcc-9ccc86bd6a62" | ||
30 | - } | ||
31 | - ], | ||
32 | - "timewindow": { | ||
33 | - "realtime": { | ||
34 | - "timewindowMs": 60000 | ||
35 | - } | ||
36 | - }, | ||
37 | - "showTitle": false, | ||
38 | - "backgroundColor": "#000000", | ||
39 | - "color": "rgba(0, 0, 0, 0.87)", | ||
40 | - "padding": "0px", | ||
41 | - "settings": { | ||
42 | - "maxValue": 50, | ||
43 | - "donutStartAngle": 90, | ||
44 | - "showValue": true, | ||
45 | - "showMinMax": true, | ||
46 | - "gaugeWidthScale": 1, | ||
47 | - "levelColors": [ | ||
48 | - "#304ffe", | ||
49 | - "#7e57c2", | ||
50 | - "#ff4081", | ||
51 | - "#d32f2f" | ||
52 | - ], | ||
53 | - "refreshAnimationType": "<>", | ||
54 | - "refreshAnimationTime": 700, | ||
55 | - "startAnimationType": "<>", | ||
56 | - "startAnimationTime": 700, | ||
57 | - "titleFont": { | ||
58 | - "family": "RobotoDraft", | ||
59 | - "size": 12, | ||
60 | - "style": "normal", | ||
61 | - "weight": "500" | ||
62 | - }, | ||
63 | - "labelFont": { | ||
64 | - "family": "RobotoDraft", | ||
65 | - "size": 8, | ||
66 | - "style": "normal", | ||
67 | - "weight": "500" | ||
68 | - }, | ||
69 | - "valueFont": { | ||
70 | - "family": "Segment7Standard", | ||
71 | - "style": "normal", | ||
72 | - "weight": "500", | ||
73 | - "size": 18 | ||
74 | - }, | ||
75 | - "minMaxFont": { | ||
76 | - "family": "Segment7Standard", | ||
77 | - "size": 12, | ||
78 | - "style": "normal", | ||
79 | - "weight": "500" | ||
80 | - }, | ||
81 | - "dashThickness": 1.5, | ||
82 | - "decimals": 0, | ||
83 | - "minValue": 0, | ||
84 | - "units": "°C", | ||
85 | - "gaugeColor": "#333333", | ||
86 | - "neonGlowBrightness": 35, | ||
87 | - "gaugeType": "donut", | ||
88 | - "showTitle": false | ||
89 | - }, | ||
90 | - "title": "Temperature" | ||
91 | - }, | ||
92 | - "row": 0, | ||
93 | - "col": 0, | ||
94 | - "id": "03e06986-1c50-e9e4-267c-2bae930ad9a2" | ||
95 | - }, | ||
96 | - "88808eb1-d381-9970-c852-e3499df68bd8": { | ||
97 | - "isSystemType": true, | ||
98 | - "bundleAlias": "digital_gauges", | ||
99 | - "typeAlias": "digital_vertical_bar", | ||
100 | - "type": "latest", | ||
101 | - "title": "New widget", | ||
102 | - "sizeX": 3, | ||
103 | - "sizeY": 5, | ||
104 | - "config": { | ||
105 | - "datasources": [ | ||
106 | - { | ||
107 | - "type": "entity", | ||
108 | - "dataKeys": [ | ||
109 | - { | ||
110 | - "name": "humidity", | ||
111 | - "type": "timeseries", | ||
112 | - "label": "humidity", | ||
113 | - "color": "#2196f3", | ||
114 | - "settings": {}, | ||
115 | - "_hash": 0.9492802776509441 | ||
116 | - } | ||
117 | - ], | ||
118 | - "name": "DHT11", | ||
119 | - "entityAliasId": "63a93238-c13f-4403-4bcc-9ccc86bd6a62" | ||
120 | - } | ||
121 | - ], | ||
122 | - "timewindow": { | ||
123 | - "realtime": { | ||
124 | - "timewindowMs": 60000 | ||
125 | - } | ||
126 | - }, | ||
127 | - "showTitle": false, | ||
128 | - "backgroundColor": "#000000", | ||
129 | - "color": "rgba(0, 0, 0, 0.87)", | ||
130 | - "padding": "0px", | ||
131 | - "settings": { | ||
132 | - "maxValue": 100, | ||
133 | - "donutStartAngle": 90, | ||
134 | - "showValue": true, | ||
135 | - "showMinMax": true, | ||
136 | - "gaugeWidthScale": 0.75, | ||
137 | - "levelColors": [ | ||
138 | - "#3d5afe", | ||
139 | - "#f44336" | ||
140 | - ], | ||
141 | - "refreshAnimationType": "<>", | ||
142 | - "refreshAnimationTime": 700, | ||
143 | - "startAnimationType": "<>", | ||
144 | - "startAnimationTime": 700, | ||
145 | - "titleFont": { | ||
146 | - "family": "RobotoDraft", | ||
147 | - "size": 12, | ||
148 | - "style": "normal", | ||
149 | - "weight": "500" | ||
150 | - }, | ||
151 | - "labelFont": { | ||
152 | - "family": "RobotoDraft", | ||
153 | - "size": 8, | ||
154 | - "style": "normal", | ||
155 | - "weight": "500" | ||
156 | - }, | ||
157 | - "valueFont": { | ||
158 | - "family": "Segment7Standard", | ||
159 | - "style": "normal", | ||
160 | - "weight": "500", | ||
161 | - "size": 14 | ||
162 | - }, | ||
163 | - "minMaxFont": { | ||
164 | - "family": "Segment7Standard", | ||
165 | - "size": 8, | ||
166 | - "style": "normal", | ||
167 | - "weight": "normal", | ||
168 | - "color": "#cccccc" | ||
169 | - }, | ||
170 | - "neonGlowBrightness": 20, | ||
171 | - "decimals": 0, | ||
172 | - "showUnitTitle": true, | ||
173 | - "gaugeColor": "#171a1c", | ||
174 | - "gaugeType": "verticalBar", | ||
175 | - "showTitle": false, | ||
176 | - "minValue": 0, | ||
177 | - "dashThickness": 1.2 | ||
178 | - }, | ||
179 | - "title": "Humidity" | ||
180 | - }, | ||
181 | - "row": 0, | ||
182 | - "col": 5, | ||
183 | - "id": "88808eb1-d381-9970-c852-e3499df68bd8" | ||
184 | - } | ||
185 | - }, | ||
186 | - "states": { | ||
187 | - "default": { | ||
188 | - "name": "Default", | ||
189 | - "root": true, | ||
190 | - "layouts": { | ||
191 | - "main": { | ||
192 | - "widgets": { | ||
193 | - "03e06986-1c50-e9e4-267c-2bae930ad9a2": { | ||
194 | - "sizeX": 5, | ||
195 | - "sizeY": 5, | ||
196 | - "row": 0, | ||
197 | - "col": 0 | ||
198 | - }, | ||
199 | - "88808eb1-d381-9970-c852-e3499df68bd8": { | ||
200 | - "sizeX": 3, | ||
201 | - "sizeY": 5, | ||
202 | - "row": 0, | ||
203 | - "col": 5 | ||
204 | - } | ||
205 | - }, | ||
206 | - "gridSettings": { | ||
207 | - "backgroundColor": "#eeeeee", | ||
208 | - "color": "rgba(0,0,0,0.870588)", | ||
209 | - "columns": 24, | ||
210 | - "margins": [ | ||
211 | - 10, | ||
212 | - 10 | ||
213 | - ], | ||
214 | - "backgroundSizeMode": "100%" | ||
215 | - } | ||
216 | - } | ||
217 | - } | ||
218 | - } | ||
219 | - }, | ||
220 | - "entityAliases": { | ||
221 | - "63a93238-c13f-4403-4bcc-9ccc86bd6a62": { | ||
222 | - "id": "63a93238-c13f-4403-4bcc-9ccc86bd6a62", | ||
223 | - "alias": "DHT11", | ||
224 | - "filter": { | ||
225 | - "type": "entityName", | ||
226 | - "resolveMultiple": false, | ||
227 | - "entityType": "DEVICE", | ||
228 | - "entityNameFilter": "DHT11 Demo Device" | ||
229 | - } | ||
230 | - } | ||
231 | - }, | ||
232 | - "timewindow": { | ||
233 | - "displayValue": "", | ||
234 | - "selectedTab": 0, | ||
235 | - "realtime": { | ||
236 | - "interval": 1000, | ||
237 | - "timewindowMs": 60000 | ||
238 | - }, | ||
239 | - "history": { | ||
240 | - "historyType": 0, | ||
241 | - "interval": 1000, | ||
242 | - "timewindowMs": 60000, | ||
243 | - "fixedTimewindow": { | ||
244 | - "startTimeMs": 1498653790019, | ||
245 | - "endTimeMs": 1498740190019 | ||
246 | - } | ||
247 | - }, | ||
248 | - "aggregation": { | ||
249 | - "type": "AVG", | ||
250 | - "limit": 200 | ||
251 | - } | ||
252 | - }, | ||
253 | - "settings": { | ||
254 | - "stateControllerId": "default", | ||
255 | - "showTitle": true, | ||
256 | - "showDashboardsSelect": true, | ||
257 | - "showEntitiesSelect": true, | ||
258 | - "showDashboardTimewindow": true, | ||
259 | - "showDashboardExport": true, | ||
260 | - "toolbarAlwaysOpen": false | ||
261 | - } | ||
262 | - }, | ||
263 | - "name": "Temperature & Humidity Demo Dashboard" | ||
264 | -} |
@@ -147,9 +147,9 @@ | @@ -147,9 +147,9 @@ | ||
147 | "name": "Add", | 147 | "name": "Add", |
148 | "icon": "add", | 148 | "icon": "add", |
149 | "type": "customPretty", | 149 | "type": "customPretty", |
150 | - "customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"add-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Add thermostat</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" required>\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n Thermostat name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"alarmTemperature\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('alarmTemperature').value\"\n formControlName=\"thresholdTemperature\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('thresholdTemperature').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n \n <mat-slide-toggle formControlName=\"alarmHumidity\">\n Low humidity alarm\n </mat-slide-toggle>\n \n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('alarmHumidity').value\"\n formControlName=\"thresholdHumidity\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('thresholdHumidity').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n Create\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>", | 150 | + "customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"add-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Add thermostat</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" required>\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n Thermostat name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n formControlName=\"temperatureAlarmThreshold\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n \n <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n Low humidity alarm\n </mat-slide-toggle>\n \n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n formControlName=\"humidityAlarmThreshold\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n Create\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>", |
151 | "customCss": ".add-entity-form{\n width: 300px;\n}\n", | 151 | "customCss": ".add-entity-form{\n width: 300px;\n}\n", |
152 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n let vm = instance;\n \n vm.addEntityFormGroup = vm.fb.group({\n entityName: ['', [vm.validators.required]],\n attributes: vm.fb.group({\n alarmTemperature: [false],\n thresholdTemperature: [{value: null, disabled: true}],\n alarmHumidity: [false],\n thresholdHumidity: [{value: null, disabled: true}]\n })\n });\n \n vm.addEntityFormGroup.get('attributes').get('alarmTemperature').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('thresholdTemperature').disable();\n }\n });\n \n vm.addEntityFormGroup.get('attributes').get('alarmHumidity').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('thresholdHumidity').disable();\n }\n });\n\n vm.save = function() {\n vm.addEntityFormGroup.markAsPristine();\n saveEntityObservable().subscribe(\n function (entity) {\n saveAttributes(entity.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function saveEntityObservable() {\n const formValues = vm.addEntityFormGroup.value;\n let entity = {\n name: formValues.entityName,\n type: \"thermostat\"\n };\n return deviceService.saveDevice(entity);\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.addEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if(attributes[key] !== null) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}", | 152 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n let vm = instance;\n \n vm.addEntityFormGroup = vm.fb.group({\n entityName: ['', [vm.validators.required]],\n attributes: vm.fb.group({\n temperatureAlarmFlag: [false],\n temperatureAlarmThreshold: [{value: null, disabled: true}],\n humidityAlarmFlag: [false],\n humidityAlarmThreshold: [{value: null, disabled: true}]\n })\n });\n \n vm.addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').disable();\n }\n });\n \n vm.addEntityFormGroup.get('attributes').get('humidityAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').disable();\n }\n });\n\n vm.save = function() {\n vm.addEntityFormGroup.markAsPristine();\n saveEntityObservable().subscribe(\n function (entity) {\n saveAttributes(entity.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function saveEntityObservable() {\n const formValues = vm.addEntityFormGroup.value;\n let entity = {\n name: formValues.entityName,\n type: \"thermostat\"\n };\n return deviceService.saveDevice(entity);\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.addEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if(attributes[key] !== null) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}", |
153 | "customResources": [], | 153 | "customResources": [], |
154 | "id": "8ab5a518-67d2-b6a2-956d-81fd512294b2" | 154 | "id": "8ab5a518-67d2-b6a2-956d-81fd512294b2" |
155 | } | 155 | } |
@@ -167,9 +167,9 @@ | @@ -167,9 +167,9 @@ | ||
167 | "name": "Edit", | 167 | "name": "Edit", |
168 | "icon": "edit", | 168 | "icon": "edit", |
169 | "type": "customPretty", | 169 | "type": "customPretty", |
170 | - "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Edit thermostat {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" readonly>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"alarmTemperature\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('alarmTemperature').value\"\n formControlName=\"thresholdTemperature\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('thresholdTemperature').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n\n <mat-slide-toggle formControlName=\"alarmHumidity\">\n Low humidity alarm\n </mat-slide-toggle>\n\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('alarmHumidity').value\"\n formControlName=\"thresholdHumidity\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('thresholdHumidity').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>", | 170 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Edit thermostat {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" readonly>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n formControlName=\"temperatureAlarmThreshold\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n\n <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n Low humidity alarm\n </mat-slide-toggle>\n\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n formControlName=\"humidityAlarmThreshold\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>", |
171 | "customCss": ".edit-entity-form{\n width: 300px;\n}", | 171 | "customCss": ".edit-entity-form{\n width: 300px;\n}", |
172 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId;\n vm.entityName = entityName;\n vm.attributes = {};\n \n vm.editEntityFormGroup = vm.fb.group({\n entityName: [''],\n attributes: vm.fb.group({\n alarmTemperature: [false],\n thresholdTemperature: [{value: null, disabled: true}],\n alarmHumidity: [false],\n thresholdHumidity: [{value: null, disabled: true}]\n })\n });\n \n vm.editEntityFormGroup.get('attributes').get('alarmTemperature').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').disable();\n }\n });\n \n vm.editEntityFormGroup.get('attributes').get('alarmHumidity').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').disable();\n }\n });\n \n \n getEntityInfo();\n \n \n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveAttributes(entityId).subscribe(\n function () {\n vm.dialogRef.close(null);\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function getEntityAttributes(attributes) {\n for (var i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value;\n }\n }\n \n function getEntityInfo() {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE').subscribe(\n function (attributes) {\n getEntityAttributes(attributes);\n vm.editEntityFormGroup.patchValue({\n entityName: vm.entityName,\n attributes: vm.attributes\n });\n // if(vm.attributes.alarmTemperature) {\n // vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n // }\n // if(vm.attributes.alarmHumidity) {\n // vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n // }\n }\n );\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.editEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if (attributes[key] !== vm.attributes[key]) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}", | 172 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId;\n vm.entityName = entityName;\n vm.attributes = {};\n \n vm.editEntityFormGroup = vm.fb.group({\n entityName: [''],\n attributes: vm.fb.group({\n temperatureAlarmFlag: [false],\n temperatureAlarmThreshold: [{value: null, disabled: true}],\n humidityAlarmFlag: [false],\n humidityAlarmThreshold: [{value: null, disabled: true}]\n })\n });\n \n vm.editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').disable();\n }\n });\n \n vm.editEntityFormGroup.get('attributes').get('humidityAlarmFlag').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').disable();\n }\n });\n \n \n getEntityInfo();\n \n \n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveAttributes(entityId).subscribe(\n function () {\n vm.dialogRef.close(null);\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function getEntityAttributes(attributes) {\n for (var i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value;\n }\n }\n \n function getEntityInfo() {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE').subscribe(\n function (attributes) {\n getEntityAttributes(attributes);\n vm.editEntityFormGroup.patchValue({\n entityName: vm.entityName,\n attributes: vm.attributes\n });\n // if(vm.attributes.temperatureAlarmFlag) {\n // vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n // }\n // if(vm.attributes.humidityAlarmFlag) {\n // vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n // }\n }\n );\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.editEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if (attributes[key] !== vm.attributes[key]) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}", |
173 | "customResources": [], | 173 | "customResources": [], |
174 | "id": "7506576f-87ba-d3a0-88fb-e304d451776d" | 174 | "id": "7506576f-87ba-d3a0-88fb-e304d451776d" |
175 | }, | 175 | }, |
@@ -550,7 +550,7 @@ | @@ -550,7 +550,7 @@ | ||
550 | "type": "entity", | 550 | "type": "entity", |
551 | "dataKeys": [ | 551 | "dataKeys": [ |
552 | { | 552 | { |
553 | - "name": "alarmTemperature", | 553 | + "name": "temperatureAlarmFlag", |
554 | "type": "attribute", | 554 | "type": "attribute", |
555 | "label": "High temperature alarm", | 555 | "label": "High temperature alarm", |
556 | "color": "#4caf50", | 556 | "color": "#4caf50", |
@@ -565,7 +565,7 @@ | @@ -565,7 +565,7 @@ | ||
565 | "_hash": 0.8725278440159361 | 565 | "_hash": 0.8725278440159361 |
566 | }, | 566 | }, |
567 | { | 567 | { |
568 | - "name": "thresholdTemperature", | 568 | + "name": "temperatureAlarmThreshold", |
569 | "type": "attribute", | 569 | "type": "attribute", |
570 | "label": "High temperature threshold, °C", | 570 | "label": "High temperature threshold, °C", |
571 | "color": "#f44336", | 571 | "color": "#f44336", |
@@ -576,12 +576,12 @@ | @@ -576,12 +576,12 @@ | ||
576 | "isEditable": "editable", | 576 | "isEditable": "editable", |
577 | "dataKeyHidden": false, | 577 | "dataKeyHidden": false, |
578 | "step": 1, | 578 | "step": 1, |
579 | - "disabledOnDataKey": "alarmTemperature" | 579 | + "disabledOnDataKey": "temperatureAlarmFlag" |
580 | }, | 580 | }, |
581 | "_hash": 0.7316078472857874 | 581 | "_hash": 0.7316078472857874 |
582 | }, | 582 | }, |
583 | { | 583 | { |
584 | - "name": "alarmHumidity", | 584 | + "name": "humidityAlarmFlag", |
585 | "type": "attribute", | 585 | "type": "attribute", |
586 | "label": "Low humidity alarm", | 586 | "label": "Low humidity alarm", |
587 | "color": "#ffc107", | 587 | "color": "#ffc107", |
@@ -596,7 +596,7 @@ | @@ -596,7 +596,7 @@ | ||
596 | "_hash": 0.5339673667431057 | 596 | "_hash": 0.5339673667431057 |
597 | }, | 597 | }, |
598 | { | 598 | { |
599 | - "name": "thresholdHumidity", | 599 | + "name": "humidityAlarmThreshold", |
600 | "type": "attribute", | 600 | "type": "attribute", |
601 | "label": "Low humidity threshold, %", | 601 | "label": "Low humidity threshold, %", |
602 | "color": "#607d8b", | 602 | "color": "#607d8b", |
@@ -607,7 +607,7 @@ | @@ -607,7 +607,7 @@ | ||
607 | "isEditable": "editable", | 607 | "isEditable": "editable", |
608 | "dataKeyHidden": false, | 608 | "dataKeyHidden": false, |
609 | "step": 1, | 609 | "step": 1, |
610 | - "disabledOnDataKey": "alarmHumidity" | 610 | + "disabledOnDataKey": "humidityAlarmFlag" |
611 | }, | 611 | }, |
612 | "_hash": 0.2687091190358901 | 612 | "_hash": 0.2687091190358901 |
613 | } | 613 | } |
application/src/main/data/json/demo/rule_chains/root_rule_chain.json
deleted
100644 → 0
1 | -{ | ||
2 | - "ruleChain": { | ||
3 | - "additionalInfo": null, | ||
4 | - "name": "Root Rule Chain", | ||
5 | - "firstRuleNodeId": null, | ||
6 | - "root": true, | ||
7 | - "debugMode": false, | ||
8 | - "configuration": null | ||
9 | - }, | ||
10 | - "metadata": { | ||
11 | - "firstNodeIndex": 3, | ||
12 | - "nodes": [ | ||
13 | - { | ||
14 | - "additionalInfo": { | ||
15 | - "layoutX": 1069, | ||
16 | - "layoutY": 267 | ||
17 | - }, | ||
18 | - "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", | ||
19 | - "name": "Is Thermostat?", | ||
20 | - "debugMode": false, | ||
21 | - "configuration": { | ||
22 | - "jsScript": "return msg.id.entityType === \"DEVICE\" && msg.type === \"thermostat\";" | ||
23 | - } | ||
24 | - }, | ||
25 | - { | ||
26 | - "additionalInfo": { | ||
27 | - "layoutX": 824, | ||
28 | - "layoutY": 156 | ||
29 | - }, | ||
30 | - "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", | ||
31 | - "name": "Save Timeseries", | ||
32 | - "debugMode": false, | ||
33 | - "configuration": { | ||
34 | - "defaultTTL": 0 | ||
35 | - } | ||
36 | - }, | ||
37 | - { | ||
38 | - "additionalInfo": { | ||
39 | - "layoutX": 825, | ||
40 | - "layoutY": 52 | ||
41 | - }, | ||
42 | - "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", | ||
43 | - "name": "Save Client Attributes", | ||
44 | - "debugMode": false, | ||
45 | - "configuration": { | ||
46 | - "scope": "CLIENT_SCOPE", | ||
47 | - "notifyDevice": "false" | ||
48 | - } | ||
49 | - }, | ||
50 | - { | ||
51 | - "additionalInfo": { | ||
52 | - "layoutX": 347, | ||
53 | - "layoutY": 149 | ||
54 | - }, | ||
55 | - "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", | ||
56 | - "name": "Message Type Switch", | ||
57 | - "debugMode": false, | ||
58 | - "configuration": { | ||
59 | - "version": 0 | ||
60 | - } | ||
61 | - }, | ||
62 | - { | ||
63 | - "additionalInfo": { | ||
64 | - "layoutX": 839, | ||
65 | - "layoutY": 345 | ||
66 | - }, | ||
67 | - "type": "org.thingsboard.rule.engine.action.TbLogNode", | ||
68 | - "name": "Log RPC from Device", | ||
69 | - "debugMode": false, | ||
70 | - "configuration": { | ||
71 | - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | ||
72 | - } | ||
73 | - }, | ||
74 | - { | ||
75 | - "additionalInfo": { | ||
76 | - "layoutX": 832, | ||
77 | - "layoutY": 407 | ||
78 | - }, | ||
79 | - "type": "org.thingsboard.rule.engine.action.TbLogNode", | ||
80 | - "name": "Log Other", | ||
81 | - "debugMode": false, | ||
82 | - "configuration": { | ||
83 | - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | ||
84 | - } | ||
85 | - }, | ||
86 | - { | ||
87 | - "additionalInfo": { | ||
88 | - "layoutX": 825, | ||
89 | - "layoutY": 468 | ||
90 | - }, | ||
91 | - "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", | ||
92 | - "name": "RPC Call Request", | ||
93 | - "debugMode": false, | ||
94 | - "configuration": { | ||
95 | - "timeoutInSeconds": 60 | ||
96 | - } | ||
97 | - }, | ||
98 | - { | ||
99 | - "additionalInfo": { | ||
100 | - "layoutX": 1069, | ||
101 | - "layoutY": 90 | ||
102 | - }, | ||
103 | - "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", | ||
104 | - "name": "Is Thermostat?", | ||
105 | - "debugMode": false, | ||
106 | - "configuration": { | ||
107 | - "jsScript": "return metadata[\"deviceType\"] === \"thermostat\";" | ||
108 | - } | ||
109 | - }, | ||
110 | - { | ||
111 | - "additionalInfo": { | ||
112 | - "layoutX": 1090, | ||
113 | - "layoutY": 360 | ||
114 | - }, | ||
115 | - "type": "org.thingsboard.rule.engine.action.TbCreateRelationNode", | ||
116 | - "name": "Relate to Asset", | ||
117 | - "debugMode": false, | ||
118 | - "configuration": { | ||
119 | - "direction": "FROM", | ||
120 | - "relationType": "ToAlarmPropagationAsset", | ||
121 | - "entityType": "ASSET", | ||
122 | - "entityNamePattern": "Thermostat Alarms", | ||
123 | - "entityTypePattern": "AlarmPropagationAsset", | ||
124 | - "entityCacheExpiration": 300, | ||
125 | - "createEntityIfNotExists": true, | ||
126 | - "changeOriginatorToRelatedEntity": false, | ||
127 | - "removeCurrentRelations": false | ||
128 | - } | ||
129 | - } | ||
130 | - ], | ||
131 | - "connections": [ | ||
132 | - { | ||
133 | - "fromIndex": 0, | ||
134 | - "toIndex": 8, | ||
135 | - "type": "True" | ||
136 | - }, | ||
137 | - { | ||
138 | - "fromIndex": 1, | ||
139 | - "toIndex": 7, | ||
140 | - "type": "Success" | ||
141 | - }, | ||
142 | - { | ||
143 | - "fromIndex": 3, | ||
144 | - "toIndex": 5, | ||
145 | - "type": "Other" | ||
146 | - }, | ||
147 | - { | ||
148 | - "fromIndex": 3, | ||
149 | - "toIndex": 2, | ||
150 | - "type": "Post attributes" | ||
151 | - }, | ||
152 | - { | ||
153 | - "fromIndex": 3, | ||
154 | - "toIndex": 1, | ||
155 | - "type": "Post telemetry" | ||
156 | - }, | ||
157 | - { | ||
158 | - "fromIndex": 3, | ||
159 | - "toIndex": 4, | ||
160 | - "type": "RPC Request from Device" | ||
161 | - }, | ||
162 | - { | ||
163 | - "fromIndex": 3, | ||
164 | - "toIndex": 6, | ||
165 | - "type": "RPC Request to Device" | ||
166 | - }, | ||
167 | - { | ||
168 | - "fromIndex": 3, | ||
169 | - "toIndex": 0, | ||
170 | - "type": "Entity Created" | ||
171 | - } | ||
172 | - ], | ||
173 | - "ruleChainConnections": [ | ||
174 | - { | ||
175 | - "fromIndex": 7, | ||
176 | - "targetRuleChainId": { | ||
177 | - "entityType": "RULE_CHAIN", | ||
178 | - "id": "25e26570-89ed-11ea-a650-cd6e14e633bd" | ||
179 | - }, | ||
180 | - "additionalInfo": { | ||
181 | - "layoutX": 1109, | ||
182 | - "layoutY": 182, | ||
183 | - "ruleChainNodeId": "rule-chain-node-10" | ||
184 | - }, | ||
185 | - "type": "True" | ||
186 | - } | ||
187 | - ] | ||
188 | - } | ||
189 | -} |
application/src/main/data/json/demo/rule_chains/thermostat_alarms.json
deleted
100644 → 0
1 | -{ | ||
2 | - "ruleChain": { | ||
3 | - "additionalInfo": { | ||
4 | - "description": "" | ||
5 | - }, | ||
6 | - "name": "Thermostat Alarms", | ||
7 | - "firstRuleNodeId": null, | ||
8 | - "root": false, | ||
9 | - "debugMode": false, | ||
10 | - "configuration": null | ||
11 | - }, | ||
12 | - "metadata": { | ||
13 | - "firstNodeIndex": 6, | ||
14 | - "nodes": [ | ||
15 | - { | ||
16 | - "additionalInfo": { | ||
17 | - "layoutX": 822, | ||
18 | - "layoutY": 294 | ||
19 | - }, | ||
20 | - "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", | ||
21 | - "name": "Save Timeseries", | ||
22 | - "debugMode": false, | ||
23 | - "configuration": { | ||
24 | - "defaultTTL": 0 | ||
25 | - } | ||
26 | - }, | ||
27 | - { | ||
28 | - "additionalInfo": { | ||
29 | - "description": null, | ||
30 | - "layoutX": 824, | ||
31 | - "layoutY": 221 | ||
32 | - }, | ||
33 | - "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", | ||
34 | - "name": "Save Client Attributes", | ||
35 | - "debugMode": false, | ||
36 | - "configuration": { | ||
37 | - "scope": "SERVER_SCOPE", | ||
38 | - "notifyDevice": null | ||
39 | - } | ||
40 | - }, | ||
41 | - { | ||
42 | - "additionalInfo": { | ||
43 | - "layoutX": 494, | ||
44 | - "layoutY": 309 | ||
45 | - }, | ||
46 | - "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", | ||
47 | - "name": "Message Type Switch", | ||
48 | - "debugMode": false, | ||
49 | - "configuration": { | ||
50 | - "version": 0 | ||
51 | - } | ||
52 | - }, | ||
53 | - { | ||
54 | - "additionalInfo": { | ||
55 | - "layoutX": 824, | ||
56 | - "layoutY": 383 | ||
57 | - }, | ||
58 | - "type": "org.thingsboard.rule.engine.action.TbLogNode", | ||
59 | - "name": "Log RPC from Device", | ||
60 | - "debugMode": false, | ||
61 | - "configuration": { | ||
62 | - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | ||
63 | - } | ||
64 | - }, | ||
65 | - { | ||
66 | - "additionalInfo": { | ||
67 | - "layoutX": 823, | ||
68 | - "layoutY": 444 | ||
69 | - }, | ||
70 | - "type": "org.thingsboard.rule.engine.action.TbLogNode", | ||
71 | - "name": "Log Other", | ||
72 | - "debugMode": false, | ||
73 | - "configuration": { | ||
74 | - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | ||
75 | - } | ||
76 | - }, | ||
77 | - { | ||
78 | - "additionalInfo": { | ||
79 | - "layoutX": 822, | ||
80 | - "layoutY": 507 | ||
81 | - }, | ||
82 | - "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", | ||
83 | - "name": "RPC Call Request", | ||
84 | - "debugMode": false, | ||
85 | - "configuration": { | ||
86 | - "timeoutInSeconds": 60 | ||
87 | - } | ||
88 | - }, | ||
89 | - { | ||
90 | - "additionalInfo": { | ||
91 | - "description": "", | ||
92 | - "layoutX": 209, | ||
93 | - "layoutY": 307 | ||
94 | - }, | ||
95 | - "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", | ||
96 | - "name": "Device Profile Node", | ||
97 | - "debugMode": false, | ||
98 | - "configuration": { | ||
99 | - "persistAlarmRulesState": false, | ||
100 | - "fetchAlarmRulesStateOnStart": false | ||
101 | - } | ||
102 | - } | ||
103 | - ], | ||
104 | - "connections": [ | ||
105 | - { | ||
106 | - "fromIndex": 2, | ||
107 | - "toIndex": 4, | ||
108 | - "type": "Other" | ||
109 | - }, | ||
110 | - { | ||
111 | - "fromIndex": 2, | ||
112 | - "toIndex": 1, | ||
113 | - "type": "Post attributes" | ||
114 | - }, | ||
115 | - { | ||
116 | - "fromIndex": 2, | ||
117 | - "toIndex": 0, | ||
118 | - "type": "Post telemetry" | ||
119 | - }, | ||
120 | - { | ||
121 | - "fromIndex": 2, | ||
122 | - "toIndex": 3, | ||
123 | - "type": "RPC Request from Device" | ||
124 | - }, | ||
125 | - { | ||
126 | - "fromIndex": 2, | ||
127 | - "toIndex": 5, | ||
128 | - "type": "RPC Request to Device" | ||
129 | - }, | ||
130 | - { | ||
131 | - "fromIndex": 6, | ||
132 | - "toIndex": 2, | ||
133 | - "type": "Success" | ||
134 | - } | ||
135 | - ], | ||
136 | - "ruleChainConnections": null | ||
137 | - } | ||
138 | -} |
@@ -33,7 +33,7 @@ | @@ -33,7 +33,7 @@ | ||
33 | "templateCss": ".tb-toast {\n min-width: 0;\n font-size: 14px !important;\n}", | 33 | "templateCss": ".tb-toast {\n min-width: 0;\n font-size: 14px !important;\n}", |
34 | "controllerScript": "self.onInit = function() {\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.multipleInputWidget.onDataUpdated();\r\n}\r\n", | 34 | "controllerScript": "self.onInit = function() {\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.multipleInputWidget.onDataUpdated();\r\n}\r\n", |
35 | "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showActionButtons\":{\n \"title\":\"Show action buttons\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"updateAllValues\": {\n \"title\":\"Update all values, not only modified\",\n \"type\":\"boolean\",\n \"default\": false\n },\n \"saveButtonLabel\": {\n \"title\": \"'SAVE' button label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"resetButtonLabel\": {\n \"title\": \"'UNDO' button label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"showGroupTitle\": {\n \"title\":\"Show title for group of fields, related to different entities\",\n \"type\":\"boolean\",\n \"default\": false\n },\n \"groupTitle\": {\n \"title\": \"Group title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"fieldsAlignment\": {\n \"title\": \"Fields alignment\",\n \"type\": \"string\",\n \"default\": \"row\"\n },\n \"fieldsInRow\": {\n \"title\": \"Number of fields in the row\",\n \"type\": \"number\",\n \"default\": \"2\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showActionButtons\",\n {\n \"key\": \"updateAllValues\",\n \"condition\": \"model.showActionButtons === true\"\n },\n {\n \"key\": \"saveButtonLabel\",\n \"condition\": \"model.showActionButtons === true\"\n },\n {\n \"key\": \"resetButtonLabel\",\n \"condition\": \"model.showActionButtons === true\"\n },\n \"showResultMessage\",\n \"showGroupTitle\",\n \"groupTitle\",\n {\n \"key\": \"fieldsAlignment\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"row\",\n \"label\": \"Row (default)\"\n },\n {\n \"value\": \"column\",\n \"label\": \"Column\"\n }\n ]\n },\n {\n \"key\": \"fieldsInRow\",\n \"condition\": \"model.fieldsAlignment === 'row'\"\n }\n ]\n}", | 35 | "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showActionButtons\":{\n \"title\":\"Show action buttons\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"updateAllValues\": {\n \"title\":\"Update all values, not only modified\",\n \"type\":\"boolean\",\n \"default\": false\n },\n \"saveButtonLabel\": {\n \"title\": \"'SAVE' button label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"resetButtonLabel\": {\n \"title\": \"'UNDO' button label\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"showGroupTitle\": {\n \"title\":\"Show title for group of fields, related to different entities\",\n \"type\":\"boolean\",\n \"default\": false\n },\n \"groupTitle\": {\n \"title\": \"Group title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"fieldsAlignment\": {\n \"title\": \"Fields alignment\",\n \"type\": \"string\",\n \"default\": \"row\"\n },\n \"fieldsInRow\": {\n \"title\": \"Number of fields in the row\",\n \"type\": \"number\",\n \"default\": \"2\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showActionButtons\",\n {\n \"key\": \"updateAllValues\",\n \"condition\": \"model.showActionButtons === true\"\n },\n {\n \"key\": \"saveButtonLabel\",\n \"condition\": \"model.showActionButtons === true\"\n },\n {\n \"key\": \"resetButtonLabel\",\n \"condition\": \"model.showActionButtons === true\"\n },\n \"showResultMessage\",\n \"showGroupTitle\",\n \"groupTitle\",\n {\n \"key\": \"fieldsAlignment\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"row\",\n \"label\": \"Row (default)\"\n },\n {\n \"value\": \"column\",\n \"label\": \"Column\"\n }\n ]\n },\n {\n \"key\": \"fieldsInRow\",\n \"condition\": \"model.fieldsAlignment === 'row'\"\n }\n ]\n}", |
36 | - "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"dataKeyType\": {\n \"title\": \"Datakey type\",\n \"type\": \"string\",\n \"default\": \"server\"\n },\n \"dataKeyValueType\": {\n \"title\": \"Datakey value type\",\n \"type\": \"string\",\n \"default\": \"string\"\n },\n \"step\": {\n \"title\": \"Step interval between values\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\"\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\"\n },\n \"required\": {\n \"title\": \"Value is required\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"minValueErrorMessage\": {\n \"title\": \"'Min Value' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"maxValueErrorMessage\": {\n \"title\": \"'Max Value' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"invalidDateErrorMessage\": {\n \"title\": \"'Invalid Date' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"selectOptionsType\": {\n \"title\": \"Select options type\",\n \"type\": \"string\",\n \"default\": \"valueWithLabel\"\n },\n \"selectOptionsList\": {\n \"title\": \"Select options list\",\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"value\": {\n \"title\": \"Value\",\n \"type\": \"string\"\n },\n \"label\": {\n \"title\": \"Label\",\n \"type\": \"string\"\n }\n },\n \"required\": [\"value\", \"label\"]\n }\n },\n \"isEditable\": {\n \"title\": \"Ability to edit attribute\",\n \"type\": \"string\",\n \"default\": \"editable\"\n },\n \"disabledOnDataKey\": {\n \"title\": \"Disable on false value of another datakey (specify datakey name)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"dataKeyHidden\": {\n \"title\": \"Hide input field\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"dataKeyType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"server\",\n \"label\": \"Server attribute (default)\"\n },\n {\n \"value\": \"shared\",\n \"label\": \"Shared attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Timeseries\"\n }\n ]\n },\n {\n \"key\": \"dataKeyValueType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"string\",\n \"label\": \"String\"\n },\n {\n \"value\": \"double\",\n \"label\": \"Double\"\n },\n {\n \"value\": \"integer\",\n \"label\": \"Integer\"\n },\n {\n \"value\": \"booleanCheckbox\",\n \"label\": \"Boolean (Checkbox)\"\n },\n {\n \"value\": \"booleanSwitch\",\n \"label\": \"Boolean (Switch)\"\n },\n {\n \"value\": \"dateTime\",\n \"label\": \"Date & Time\"\n },\n {\n \"value\": \"date\",\n \"label\": \"Date\"\n },\n {\n \"value\": \"time\",\n \"label\": \"Time\"\n },\n {\n \"value\": \"selectOption\",\n \"label\": \"Selectable option\"\n }\n ]\n },\n {\n \"key\": \"selectOptionsType\",\n \"condition\": \"model.dataKeyValueType === 'selectOption'\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"valueWithLabel\",\n \"label\": \"Values with labels\"\n },\n {\n \"value\": \"rawValue\",\n \"label\": \"Raw values\"\n }\n ]\n },\n {\n \"key\": \"selectOptionsList\",\n \"type\": \"array\",\n \"condition\": \"model.dataKeyValueType === 'selectOption'\",\n \"items\": [\n \"selectOptionsList[].value\",\n {\n \"key\": \"selectOptionsList[].label\",\n \"condition\": \"model.selectOptionsType === 'valueWithLabel'\"\n }\n ]\n },\n {\n \"key\": \"step\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"minValue\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"maxValue\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n \"required\",\n {\n \"key\": \"requiredErrorMessage\",\n \"condition\": \"model.required === true\"\n },\n {\n \"key\": \"invalidDateErrorMessage\",\n \"condition\": \"model.dataKeyValueType === 'dateTime' || model.dataKeyValueType === 'date' || model.dataKeyValueType === 'time'\"\n },\n {\n \"key\": \"minValueErrorMessage\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"maxValueErrorMessage\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"isEditable\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"editable\",\n \"label\": \"Editable (default)\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n },\n {\n \"value\": \"readonly\",\n \"label\": \"Read-only\"\n }\n ]\n },\n \"disabledOnDataKey\",\n \"dataKeyHidden\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t}\n ]\n}\n", | 36 | + "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"dataKeyType\": {\n \"title\": \"Datakey type\",\n \"type\": \"string\",\n \"default\": \"server\"\n },\n \"dataKeyValueType\": {\n \"title\": \"Datakey value type\",\n \"type\": \"string\",\n \"default\": \"string\"\n },\n \"step\": {\n \"title\": \"Step interval between values\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\"\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\"\n },\n \"required\": {\n \"title\": \"Value is required\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"minValueErrorMessage\": {\n \"title\": \"'Min Value' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"maxValueErrorMessage\": {\n \"title\": \"'Max Value' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"invalidDateErrorMessage\": {\n \"title\": \"'Invalid Date' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"isEditable\": {\n \"title\": \"Ability to edit attribute\",\n \"type\": \"string\",\n \"default\": \"editable\"\n },\n \"disabledOnDataKey\": {\n \"title\": \"Disable on false value of another datakey (specify datakey name)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"dataKeyHidden\": {\n \"title\": \"Hide input field\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"dataKeyType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"server\",\n \"label\": \"Server attribute (default)\"\n },\n {\n \"value\": \"shared\",\n \"label\": \"Shared attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Timeseries\"\n }\n ]\n },\n {\n \"key\": \"dataKeyValueType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"string\",\n \"label\": \"String\"\n },\n {\n \"value\": \"double\",\n \"label\": \"Double\"\n },\n {\n \"value\": \"integer\",\n \"label\": \"Integer\"\n },\n {\n \"value\": \"booleanCheckbox\",\n \"label\": \"Boolean (Checkbox)\"\n },\n {\n \"value\": \"booleanSwitch\",\n \"label\": \"Boolean (Switch)\"\n },\n {\n \"value\": \"dateTime\",\n \"label\": \"Date & Time\"\n },\n {\n \"value\": \"date\",\n \"label\": \"Date\"\n },\n {\n \"value\": \"time\",\n \"label\": \"Time\"\n }\n ]\n },\n {\n \"key\": \"step\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"minValue\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"maxValue\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n \"required\",\n {\n \"key\": \"requiredErrorMessage\",\n \"condition\": \"model.required === true\"\n },\n {\n \"key\": \"invalidDateErrorMessage\",\n \"condition\": \"model.dataKeyValueType === 'dateTime' || model.dataKeyValueType === 'date' || model.dataKeyValueType === 'time'\"\n },\n {\n \"key\": \"minValueErrorMessage\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"maxValueErrorMessage\",\n \"condition\": \"model.dataKeyValueType === 'double' || model.dataKeyValueType === 'integer'\"\n },\n {\n \"key\": \"isEditable\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"editable\",\n \"label\": \"Editable (default)\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n },\n {\n \"value\": \"readonly\",\n \"label\": \"Read-only\"\n }\n ]\n },\n \"disabledOnDataKey\",\n \"dataKeyHidden\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t}\n ]\n}\n", |
37 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" | 37 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
38 | } | 38 | } |
39 | }, | 39 | }, |
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.Customer; | @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.Customer; | ||
39 | import org.thingsboard.server.common.data.DataConstants; | 39 | import org.thingsboard.server.common.data.DataConstants; |
40 | import org.thingsboard.server.common.data.Device; | 40 | import org.thingsboard.server.common.data.Device; |
41 | import org.thingsboard.server.common.data.DeviceProfile; | 41 | import org.thingsboard.server.common.data.DeviceProfile; |
42 | +import org.thingsboard.server.common.data.TenantProfile; | ||
42 | import org.thingsboard.server.common.data.alarm.Alarm; | 43 | import org.thingsboard.server.common.data.alarm.Alarm; |
43 | import org.thingsboard.server.common.data.asset.Asset; | 44 | import org.thingsboard.server.common.data.asset.Asset; |
44 | import org.thingsboard.server.common.data.id.DeviceId; | 45 | import org.thingsboard.server.common.data.id.DeviceId; |
@@ -511,13 +512,24 @@ class DefaultTbContext implements TbContext { | @@ -511,13 +512,24 @@ class DefaultTbContext implements TbContext { | ||
511 | } | 512 | } |
512 | 513 | ||
513 | @Override | 514 | @Override |
515 | + public void addTenantProfileListener(Consumer<TenantProfile> listener) { | ||
516 | + mainCtx.getTenantProfileCache().addListener(getTenantId(), getSelfId(), listener); | ||
517 | + } | ||
518 | + | ||
519 | + @Override | ||
514 | public void addDeviceProfileListeners(Consumer<DeviceProfile> profileListener, BiConsumer<DeviceId, DeviceProfile> deviceListener) { | 520 | public void addDeviceProfileListeners(Consumer<DeviceProfile> profileListener, BiConsumer<DeviceId, DeviceProfile> deviceListener) { |
515 | mainCtx.getDeviceProfileCache().addListener(getTenantId(), getSelfId(), profileListener, deviceListener); | 521 | mainCtx.getDeviceProfileCache().addListener(getTenantId(), getSelfId(), profileListener, deviceListener); |
516 | } | 522 | } |
517 | 523 | ||
518 | @Override | 524 | @Override |
519 | - public void removeProfileListener() { | 525 | + public void removeListeners() { |
520 | mainCtx.getDeviceProfileCache().removeListener(getTenantId(), getSelfId()); | 526 | mainCtx.getDeviceProfileCache().removeListener(getTenantId(), getSelfId()); |
527 | + mainCtx.getTenantProfileCache().removeListener(getTenantId(), getSelfId()); | ||
528 | + } | ||
529 | + | ||
530 | + @Override | ||
531 | + public TenantProfile getTenantProfile() { | ||
532 | + return mainCtx.getTenantProfileCache().get(getTenantId()); | ||
521 | } | 533 | } |
522 | 534 | ||
523 | private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { | 535 | private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { |
@@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.ResponseBody; | @@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.ResponseBody; | ||
26 | import org.springframework.web.bind.annotation.RestController; | 26 | import org.springframework.web.bind.annotation.RestController; |
27 | import org.thingsboard.rule.engine.api.MailService; | 27 | import org.thingsboard.rule.engine.api.MailService; |
28 | import org.thingsboard.rule.engine.api.SmsService; | 28 | import org.thingsboard.rule.engine.api.SmsService; |
29 | -import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest; | 29 | +import org.thingsboard.server.common.data.sms.config.TestSmsRequest; |
30 | import org.thingsboard.server.common.data.AdminSettings; | 30 | import org.thingsboard.server.common.data.AdminSettings; |
31 | import org.thingsboard.server.common.data.UpdateMessage; | 31 | import org.thingsboard.server.common.data.UpdateMessage; |
32 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 32 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
@@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.asset.Asset; | @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.asset.Asset; | ||
33 | import org.thingsboard.server.common.data.asset.AssetInfo; | 33 | import org.thingsboard.server.common.data.asset.AssetInfo; |
34 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; | 34 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; |
35 | import org.thingsboard.server.common.data.audit.ActionType; | 35 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | ||
36 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 37 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | import org.thingsboard.server.common.data.id.AssetId; | 38 | import org.thingsboard.server.common.data.id.AssetId; |
38 | import org.thingsboard.server.common.data.id.CustomerId; | 39 | import org.thingsboard.server.common.data.id.CustomerId; |
@@ -51,6 +52,8 @@ import java.util.ArrayList; | @@ -51,6 +52,8 @@ import java.util.ArrayList; | ||
51 | import java.util.List; | 52 | import java.util.List; |
52 | import java.util.stream.Collectors; | 53 | import java.util.stream.Collectors; |
53 | 54 | ||
55 | +import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; | ||
56 | + | ||
54 | @RestController | 57 | @RestController |
55 | @TbCoreComponent | 58 | @TbCoreComponent |
56 | @RequestMapping("/api") | 59 | @RequestMapping("/api") |
@@ -89,6 +92,10 @@ public class AssetController extends BaseController { | @@ -89,6 +92,10 @@ public class AssetController extends BaseController { | ||
89 | @ResponseBody | 92 | @ResponseBody |
90 | public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException { | 93 | public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException { |
91 | try { | 94 | try { |
95 | + if (TB_SERVICE_QUEUE.equals(asset.getType())) { | ||
96 | + throw new ThingsboardException("Unable to save asset with type " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
97 | + } | ||
98 | + | ||
92 | asset.setTenantId(getCurrentUser().getTenantId()); | 99 | asset.setTenantId(getCurrentUser().getTenantId()); |
93 | 100 | ||
94 | checkEntity(asset.getId(), asset, Resource.ASSET); | 101 | checkEntity(asset.getId(), asset, Resource.ASSET); |
@@ -45,6 +45,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; | @@ -45,6 +45,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; | ||
45 | import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | 45 | import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; |
46 | import org.thingsboard.server.common.data.DataConstants; | 46 | import org.thingsboard.server.common.data.DataConstants; |
47 | import org.thingsboard.server.common.data.EntityType; | 47 | import org.thingsboard.server.common.data.EntityType; |
48 | +import org.thingsboard.server.common.data.TenantProfile; | ||
48 | import org.thingsboard.server.common.data.audit.ActionType; | 49 | import org.thingsboard.server.common.data.audit.ActionType; |
49 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 50 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
50 | import org.thingsboard.server.common.data.id.DeviceId; | 51 | import org.thingsboard.server.common.data.id.DeviceId; |
@@ -69,6 +70,7 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; | @@ -69,6 +70,7 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
69 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | 70 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
70 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 71 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
71 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 72 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
73 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | ||
72 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 74 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
73 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 75 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
74 | import org.thingsboard.server.queue.util.TbCoreComponent; | 76 | import org.thingsboard.server.queue.util.TbCoreComponent; |
@@ -93,6 +95,7 @@ import java.util.Map; | @@ -93,6 +95,7 @@ import java.util.Map; | ||
93 | import java.util.Set; | 95 | import java.util.Set; |
94 | import java.util.concurrent.ExecutorService; | 96 | import java.util.concurrent.ExecutorService; |
95 | import java.util.concurrent.Executors; | 97 | import java.util.concurrent.Executors; |
98 | +import java.util.concurrent.TimeUnit; | ||
96 | import java.util.stream.Collectors; | 99 | import java.util.stream.Collectors; |
97 | 100 | ||
98 | /** | 101 | /** |
@@ -205,7 +208,7 @@ public class TelemetryController extends BaseController { | @@ -205,7 +208,7 @@ public class TelemetryController extends BaseController { | ||
205 | @RequestParam(name = "interval", defaultValue = "0") Long interval, | 208 | @RequestParam(name = "interval", defaultValue = "0") Long interval, |
206 | @RequestParam(name = "limit", defaultValue = "100") Integer limit, | 209 | @RequestParam(name = "limit", defaultValue = "100") Integer limit, |
207 | @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, | 210 | @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, |
208 | - @RequestParam(name= "orderBy", defaultValue = "DESC") String orderBy, | 211 | + @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, |
209 | @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { | 212 | @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { |
210 | return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | 213 | return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, |
211 | (result, tenantId, entityId) -> { | 214 | (result, tenantId, entityId) -> { |
@@ -392,7 +395,7 @@ public class TelemetryController extends BaseController { | @@ -392,7 +395,7 @@ public class TelemetryController extends BaseController { | ||
392 | if (attributes.isEmpty()) { | 395 | if (attributes.isEmpty()) { |
393 | return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); | 396 | return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); |
394 | } | 397 | } |
395 | - for (AttributeKvEntry attributeKvEntry: attributes) { | 398 | + for (AttributeKvEntry attributeKvEntry : attributes) { |
396 | if (attributeKvEntry.getKey().isEmpty() || attributeKvEntry.getKey().trim().length() == 0) { | 399 | if (attributeKvEntry.getKey().isEmpty() || attributeKvEntry.getKey().trim().length() == 0) { |
397 | return getImmediateDeferredResult("Key cannot be empty or contains only spaces", HttpStatus.BAD_REQUEST); | 400 | return getImmediateDeferredResult("Key cannot be empty or contains only spaces", HttpStatus.BAD_REQUEST); |
398 | } | 401 | } |
@@ -440,9 +443,13 @@ public class TelemetryController extends BaseController { | @@ -440,9 +443,13 @@ public class TelemetryController extends BaseController { | ||
440 | if (entries.isEmpty()) { | 443 | if (entries.isEmpty()) { |
441 | return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST); | 444 | return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST); |
442 | } | 445 | } |
443 | - SecurityUser user = getCurrentUser(); | ||
444 | return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_TELEMETRY, entityIdSrc, (result, tenantId, entityId) -> { | 446 | return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_TELEMETRY, entityIdSrc, (result, tenantId, entityId) -> { |
445 | - tsSubService.saveAndNotify(tenantId, entityId, entries, ttl, new FutureCallback<Void>() { | 447 | + long tenantTtl = ttl; |
448 | + if (!TenantId.SYS_TENANT_ID.equals(tenantId) && tenantTtl == 0) { | ||
449 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | ||
450 | + tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); | ||
451 | + } | ||
452 | + tsSubService.saveAndNotify(tenantId, entityId, entries, tenantTtl, new FutureCallback<Void>() { | ||
446 | @Override | 453 | @Override |
447 | public void onSuccess(@Nullable Void tmp) { | 454 | public void onSuccess(@Nullable Void tmp) { |
448 | result.setResult(new ResponseEntity(HttpStatus.OK)); | 455 | result.setResult(new ResponseEntity(HttpStatus.OK)); |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -147,6 +147,11 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -147,6 +147,11 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
147 | public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { | 147 | public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { |
148 | ToUsageStatsServiceMsg statsMsg = msg.getValue(); | 148 | ToUsageStatsServiceMsg statsMsg = msg.getValue(); |
149 | TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); | 149 | TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); |
150 | + | ||
151 | + if (tenantProfileCache.get(tenantId) == null) { | ||
152 | + return; | ||
153 | + } | ||
154 | + | ||
150 | TenantApiUsageState tenantState; | 155 | TenantApiUsageState tenantState; |
151 | List<TsKvEntry> updatedEntries; | 156 | List<TsKvEntry> updatedEntries; |
152 | Map<ApiFeature, ApiUsageStateValue> result; | 157 | Map<ApiFeature, ApiUsageStateValue> result; |
@@ -166,7 +171,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -166,7 +171,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
166 | long newValue = tenantState.add(recordKey, kvProto.getValue()); | 171 | long newValue = tenantState.add(recordKey, kvProto.getValue()); |
167 | updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); | 172 | updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); |
168 | long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); | 173 | long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); |
169 | - updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); | 174 | + updatedEntries.add(new BasicTsKvEntry(newHourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); |
170 | apiFeatures.add(recordKey.getApiFeature()); | 175 | apiFeatures.add(recordKey.getApiFeature()); |
171 | } | 176 | } |
172 | result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures); | 177 | result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures); |
@@ -343,7 +348,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -343,7 +348,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
343 | long now = System.currentTimeMillis(); | 348 | long now = System.currentTimeMillis(); |
344 | myTenantStates.values().forEach(state -> { | 349 | myTenantStates.values().forEach(state -> { |
345 | if ((state.getNextCycleTs() > now) && (state.getNextCycleTs() - now < TimeUnit.HOURS.toMillis(1))) { | 350 | if ((state.getNextCycleTs() > now) && (state.getNextCycleTs() - now < TimeUnit.HOURS.toMillis(1))) { |
351 | + TenantId tenantId = state.getTenantId(); | ||
346 | state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); | 352 | state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); |
353 | + ToUsageStatsServiceMsg.Builder msg = ToUsageStatsServiceMsg.newBuilder(); | ||
354 | + msg.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); | ||
355 | + msg.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); | ||
356 | + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | ||
357 | + msg.addValues(UsageStatsKVProto.newBuilder().setKey(key.name()).setValue(0).build()); | ||
358 | + } | ||
359 | + process(new TbProtoQueueMsg<>(UUID.randomUUID(), msg.build()), TbCallback.EMPTY); | ||
347 | } | 360 | } |
348 | }); | 361 | }); |
349 | } finally { | 362 | } finally { |
@@ -18,9 +18,7 @@ package org.thingsboard.server.service.device; | @@ -18,9 +18,7 @@ package org.thingsboard.server.service.device; | ||
18 | import com.fasterxml.jackson.core.JsonProcessingException; | 18 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | import com.fasterxml.jackson.databind.JsonNode; | 19 | import com.fasterxml.jackson.databind.JsonNode; |
20 | import com.fasterxml.jackson.databind.node.ObjectNode; | 20 | import com.fasterxml.jackson.databind.node.ObjectNode; |
21 | -import com.google.common.util.concurrent.Futures; | ||
22 | import com.google.common.util.concurrent.ListenableFuture; | 21 | import com.google.common.util.concurrent.ListenableFuture; |
23 | -import com.google.common.util.concurrent.MoreExecutors; | ||
24 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
25 | import org.apache.commons.lang.RandomStringUtils; | 23 | import org.apache.commons.lang.RandomStringUtils; |
26 | import org.springframework.beans.factory.annotation.Autowired; | 24 | import org.springframework.beans.factory.annotation.Autowired; |
@@ -114,6 +112,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { | @@ -114,6 +112,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { | ||
114 | public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) { | 112 | public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) { |
115 | String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey(); | 113 | String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey(); |
116 | String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); | 114 | String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret(); |
115 | + if (!StringUtils.isEmpty(provisionRequest.getDeviceName())) { | ||
116 | + provisionRequest.setDeviceName(provisionRequest.getDeviceName().trim()); | ||
117 | + if (StringUtils.isEmpty(provisionRequest.getDeviceName())) { | ||
118 | + log.warn("Provision request contains empty device name!"); | ||
119 | + throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); | ||
120 | + } | ||
121 | + } | ||
117 | 122 | ||
118 | if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) { | 123 | if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) { |
119 | throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); | 124 | throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name()); |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -82,7 +82,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; | @@ -82,7 +82,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; | ||
82 | 82 | ||
83 | import java.util.Arrays; | 83 | import java.util.Arrays; |
84 | import java.util.Collections; | 84 | import java.util.Collections; |
85 | -import java.util.LinkedHashMap; | 85 | +import java.util.TreeMap; |
86 | 86 | ||
87 | @Service | 87 | @Service |
88 | @Profile("install") | 88 | @Profile("install") |
@@ -272,7 +272,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -272,7 +272,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
272 | thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | 272 | thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); |
273 | thermostatDeviceProfile.setDescription("Thermostat device profile"); | 273 | thermostatDeviceProfile.setDescription("Thermostat device profile"); |
274 | thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains( | 274 | thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains( |
275 | - demoTenant.getId(), new PageLink(1, 0, "Thermostat Alarms")).getData().get(0).getId()); | 275 | + demoTenant.getId(), new PageLink(1, 0, "Thermostat")).getData().get(0).getId()); |
276 | 276 | ||
277 | DeviceProfileData deviceProfileData = new DeviceProfileData(); | 277 | DeviceProfileData deviceProfileData = new DeviceProfileData(); |
278 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | 278 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
@@ -290,13 +290,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -290,13 +290,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
290 | AlarmCondition temperatureCondition = new AlarmCondition(); | 290 | AlarmCondition temperatureCondition = new AlarmCondition(); |
291 | temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | 291 | temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); |
292 | 292 | ||
293 | - KeyFilter alarmTemperatureAttributeFilter = new KeyFilter(); | ||
294 | - alarmTemperatureAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmTemperature")); | ||
295 | - alarmTemperatureAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | ||
296 | - BooleanFilterPredicate alarmTemperatureAttributePredicate = new BooleanFilterPredicate(); | ||
297 | - alarmTemperatureAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | ||
298 | - alarmTemperatureAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | ||
299 | - alarmTemperatureAttributeFilter.setPredicate(alarmTemperatureAttributePredicate); | 293 | + KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter(); |
294 | + temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag")); | ||
295 | + temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | ||
296 | + BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); | ||
297 | + temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | ||
298 | + temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | ||
299 | + temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate); | ||
300 | 300 | ||
301 | KeyFilter temperatureTimeseriesFilter = new KeyFilter(); | 301 | KeyFilter temperatureTimeseriesFilter = new KeyFilter(); |
302 | temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | 302 | temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); |
@@ -305,13 +305,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -305,13 +305,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
305 | temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); | 305 | temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
306 | FilterPredicateValue<Double> temperatureTimeseriesPredicateValue = | 306 | FilterPredicateValue<Double> temperatureTimeseriesPredicateValue = |
307 | new FilterPredicateValue<>(25.0, null, | 307 | new FilterPredicateValue<>(25.0, null, |
308 | - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature")); | 308 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "temperatureAlarmThreshold")); |
309 | temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue); | 309 | temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue); |
310 | temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate); | 310 | temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate); |
311 | - temperatureCondition.setCondition(Arrays.asList(alarmTemperatureAttributeFilter, temperatureTimeseriesFilter)); | 311 | + temperatureCondition.setCondition(Arrays.asList(temperatureAlarmFlagAttributeFilter, temperatureTimeseriesFilter)); |
312 | temperatureRule.setAlarmDetails("Current temperature = ${temperature}"); | 312 | temperatureRule.setAlarmDetails("Current temperature = ${temperature}"); |
313 | temperatureRule.setCondition(temperatureCondition); | 313 | temperatureRule.setCondition(temperatureCondition); |
314 | - highTemperature.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule))); | 314 | + highTemperature.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule))); |
315 | 315 | ||
316 | AlarmRule clearTemperatureRule = new AlarmRule(); | 316 | AlarmRule clearTemperatureRule = new AlarmRule(); |
317 | AlarmCondition clearTemperatureCondition = new AlarmCondition(); | 317 | AlarmCondition clearTemperatureCondition = new AlarmCondition(); |
@@ -324,7 +324,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -324,7 +324,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
324 | clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); | 324 | clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); |
325 | FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue = | 325 | FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue = |
326 | new FilterPredicateValue<>(25.0, null, | 326 | new FilterPredicateValue<>(25.0, null, |
327 | - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature")); | 327 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "temperatureAlarmThreshold")); |
328 | 328 | ||
329 | clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue); | 329 | clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue); |
330 | clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate); | 330 | clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate); |
@@ -340,13 +340,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -340,13 +340,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
340 | AlarmCondition humidityCondition = new AlarmCondition(); | 340 | AlarmCondition humidityCondition = new AlarmCondition(); |
341 | humidityCondition.setSpec(new SimpleAlarmConditionSpec()); | 341 | humidityCondition.setSpec(new SimpleAlarmConditionSpec()); |
342 | 342 | ||
343 | - KeyFilter alarmHumidityAttributeFilter = new KeyFilter(); | ||
344 | - alarmHumidityAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmHumidity")); | ||
345 | - alarmHumidityAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | ||
346 | - BooleanFilterPredicate alarmHumidityAttributePredicate = new BooleanFilterPredicate(); | ||
347 | - alarmHumidityAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | ||
348 | - alarmHumidityAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | ||
349 | - alarmHumidityAttributeFilter.setPredicate(alarmHumidityAttributePredicate); | 343 | + KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter(); |
344 | + humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag")); | ||
345 | + humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | ||
346 | + BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate(); | ||
347 | + humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | ||
348 | + humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | ||
349 | + humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate); | ||
350 | 350 | ||
351 | KeyFilter humidityTimeseriesFilter = new KeyFilter(); | 351 | KeyFilter humidityTimeseriesFilter = new KeyFilter(); |
352 | humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | 352 | humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); |
@@ -355,14 +355,14 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -355,14 +355,14 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
355 | humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); | 355 | humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); |
356 | FilterPredicateValue<Double> humidityTimeseriesPredicateValue = | 356 | FilterPredicateValue<Double> humidityTimeseriesPredicateValue = |
357 | new FilterPredicateValue<>(60.0, null, | 357 | new FilterPredicateValue<>(60.0, null, |
358 | - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity")); | 358 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "humidityAlarmThreshold")); |
359 | humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue); | 359 | humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue); |
360 | humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate); | 360 | humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate); |
361 | - humidityCondition.setCondition(Arrays.asList(alarmHumidityAttributeFilter, humidityTimeseriesFilter)); | 361 | + humidityCondition.setCondition(Arrays.asList(humidityAlarmFlagAttributeFilter, humidityTimeseriesFilter)); |
362 | 362 | ||
363 | humidityRule.setCondition(humidityCondition); | 363 | humidityRule.setCondition(humidityCondition); |
364 | humidityRule.setAlarmDetails("Current humidity = ${humidity}"); | 364 | humidityRule.setAlarmDetails("Current humidity = ${humidity}"); |
365 | - lowHumidity.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule))); | 365 | + lowHumidity.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule))); |
366 | 366 | ||
367 | AlarmRule clearHumidityRule = new AlarmRule(); | 367 | AlarmRule clearHumidityRule = new AlarmRule(); |
368 | AlarmCondition clearHumidityCondition = new AlarmCondition(); | 368 | AlarmCondition clearHumidityCondition = new AlarmCondition(); |
@@ -375,7 +375,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -375,7 +375,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
375 | clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); | 375 | clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); |
376 | FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue = | 376 | FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue = |
377 | new FilterPredicateValue<>(60.0, null, | 377 | new FilterPredicateValue<>(60.0, null, |
378 | - new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity")); | 378 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "humidityAlarmThreshold")); |
379 | 379 | ||
380 | clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue); | 380 | clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue); |
381 | clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate); | 381 | clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate); |
@@ -394,18 +394,18 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -394,18 +394,18 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
394 | attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE, | 394 | attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE, |
395 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)), | 395 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)), |
396 | new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -122.1503)), | 396 | new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -122.1503)), |
397 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmTemperature", true)), | ||
398 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmHumidity", true)), | ||
399 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdTemperature", (long) 20)), | ||
400 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdHumidity", (long) 50)))); | 397 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)), |
398 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("humidityAlarmFlag", true)), | ||
399 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperatureAlarmThreshold", (long) 20)), | ||
400 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 50)))); | ||
401 | 401 | ||
402 | attributesService.save(demoTenant.getId(), t2Id, DataConstants.SERVER_SCOPE, | 402 | attributesService.save(demoTenant.getId(), t2Id, DataConstants.SERVER_SCOPE, |
403 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.493801)), | 403 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.493801)), |
404 | new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -121.948769)), | 404 | new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -121.948769)), |
405 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmTemperature", true)), | ||
406 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmHumidity", true)), | ||
407 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdTemperature", (long) 25)), | ||
408 | - new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdHumidity", (long) 30)))); | 405 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)), |
406 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("humidityAlarmFlag", true)), | ||
407 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperatureAlarmThreshold", (long) 25)), | ||
408 | + new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 30)))); | ||
409 | 409 | ||
410 | installScripts.loadDashboards(demoTenant.getId(), null); | 410 | installScripts.loadDashboards(demoTenant.getId(), null); |
411 | } | 411 | } |
@@ -210,26 +210,9 @@ public class InstallScripts { | @@ -210,26 +210,9 @@ public class InstallScripts { | ||
210 | 210 | ||
211 | 211 | ||
212 | public void loadDemoRuleChains(TenantId tenantId) throws Exception { | 212 | public void loadDemoRuleChains(TenantId tenantId) throws Exception { |
213 | - Path ruleChainsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, RULE_CHAINS_DIR); | ||
214 | try { | 213 | try { |
215 | - JsonNode ruleChainJson = objectMapper.readTree(ruleChainsDir.resolve("thermostat_alarms.json").toFile()); | ||
216 | - RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); | ||
217 | - RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); | ||
218 | - ruleChain.setTenantId(tenantId); | ||
219 | - ruleChain = ruleChainService.saveRuleChain(ruleChain); | ||
220 | - ruleChainMetaData.setRuleChainId(ruleChain.getId()); | ||
221 | - ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData); | ||
222 | - | ||
223 | - JsonNode rootChainJson = objectMapper.readTree(ruleChainsDir.resolve("root_rule_chain.json").toFile()); | ||
224 | - RuleChain rootChain = objectMapper.treeToValue(rootChainJson.get("ruleChain"), RuleChain.class); | ||
225 | - RuleChainMetaData rootChainMetaData = objectMapper.treeToValue(rootChainJson.get("metadata"), RuleChainMetaData.class); | ||
226 | - | ||
227 | - RuleChainId thermostatsRuleChainId = ruleChain.getId(); | ||
228 | - rootChainMetaData.getRuleChainConnections().forEach(connection -> connection.setTargetRuleChainId(thermostatsRuleChainId)); | ||
229 | - rootChain.setTenantId(tenantId); | ||
230 | - rootChain = ruleChainService.saveRuleChain(rootChain); | ||
231 | - rootChainMetaData.setRuleChainId(rootChain.getId()); | ||
232 | - ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), rootChainMetaData); | 214 | + createDefaultRuleChains(tenantId); |
215 | + createDefaultRuleChain(tenantId, "Thermostat"); | ||
233 | } catch (Exception e) { | 216 | } catch (Exception e) { |
234 | log.error("Unable to load dashboard from json", e); | 217 | log.error("Unable to load dashboard from json", e); |
235 | throw new RuntimeException("Unable to load dashboard from json", e); | 218 | throw new RuntimeException("Unable to load dashboard from json", e); |
@@ -250,6 +250,8 @@ public class DefaultMailService implements MailService { | @@ -250,6 +250,8 @@ public class DefaultMailService implements MailService { | ||
250 | helper.setText(body); | 250 | helper.setText(body); |
251 | mailSender.send(helper.getMimeMessage()); | 251 | mailSender.send(helper.getMimeMessage()); |
252 | apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); | 252 | apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); |
253 | + } else { | ||
254 | + throw new RuntimeException("Email sending is disabled due to API limits!"); | ||
253 | } | 255 | } |
254 | } | 256 | } |
255 | 257 |
@@ -297,7 +297,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore | @@ -297,7 +297,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore | ||
297 | try { | 297 | try { |
298 | handleUsageStats(msg, callback); | 298 | handleUsageStats(msg, callback); |
299 | } catch (Throwable e) { | 299 | } catch (Throwable e) { |
300 | - log.warn("[{}] Failed to process usge stats: {}", id, msg, e); | 300 | + log.warn("[{}] Failed to process usage stats: {}", id, msg, e); |
301 | callback.onFailure(e); | 301 | callback.onFailure(e); |
302 | } | 302 | } |
303 | }); | 303 | }); |
@@ -18,9 +18,9 @@ package org.thingsboard.server.service.sms; | @@ -18,9 +18,9 @@ package org.thingsboard.server.service.sms; | ||
18 | import org.springframework.stereotype.Component; | 18 | import org.springframework.stereotype.Component; |
19 | import org.thingsboard.rule.engine.api.sms.SmsSender; | 19 | import org.thingsboard.rule.engine.api.sms.SmsSender; |
20 | import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; | 20 | import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; |
21 | -import org.thingsboard.rule.engine.api.sms.config.AwsSnsSmsProviderConfiguration; | ||
22 | -import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration; | ||
23 | -import org.thingsboard.rule.engine.api.sms.config.TwilioSmsProviderConfiguration; | 21 | +import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; |
22 | +import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; | ||
23 | +import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; | ||
24 | import org.thingsboard.server.service.sms.aws.AwsSmsSender; | 24 | import org.thingsboard.server.service.sms.aws.AwsSmsSender; |
25 | import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; | 25 | import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; |
26 | 26 |
@@ -22,8 +22,8 @@ import org.springframework.stereotype.Service; | @@ -22,8 +22,8 @@ import org.springframework.stereotype.Service; | ||
22 | import org.thingsboard.rule.engine.api.SmsService; | 22 | import org.thingsboard.rule.engine.api.SmsService; |
23 | import org.thingsboard.rule.engine.api.sms.SmsSender; | 23 | import org.thingsboard.rule.engine.api.sms.SmsSender; |
24 | import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; | 24 | import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; |
25 | -import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration; | ||
26 | -import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest; | 25 | +import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; |
26 | +import org.thingsboard.server.common.data.sms.config.TestSmsRequest; | ||
27 | import org.thingsboard.server.common.data.AdminSettings; | 27 | import org.thingsboard.server.common.data.AdminSettings; |
28 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 28 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
29 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | 29 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
@@ -106,6 +106,8 @@ public class DefaultSmsService implements SmsService { | @@ -106,6 +106,8 @@ public class DefaultSmsService implements SmsService { | ||
106 | apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount); | 106 | apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount); |
107 | } | 107 | } |
108 | } | 108 | } |
109 | + } else { | ||
110 | + throw new RuntimeException("SMS sending is disabled due to API limits!"); | ||
109 | } | 111 | } |
110 | } | 112 | } |
111 | 113 |
@@ -23,7 +23,7 @@ import com.amazonaws.services.sns.AmazonSNSClient; | @@ -23,7 +23,7 @@ import com.amazonaws.services.sns.AmazonSNSClient; | ||
23 | import com.amazonaws.services.sns.model.PublishRequest; | 23 | import com.amazonaws.services.sns.model.PublishRequest; |
24 | import lombok.extern.slf4j.Slf4j; | 24 | import lombok.extern.slf4j.Slf4j; |
25 | import org.apache.commons.lang3.StringUtils; | 25 | import org.apache.commons.lang3.StringUtils; |
26 | -import org.thingsboard.rule.engine.api.sms.config.AwsSnsSmsProviderConfiguration; | 26 | +import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; |
27 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; | 27 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; |
28 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; | 28 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; |
29 | import org.thingsboard.server.service.sms.AbstractSmsSender; | 29 | import org.thingsboard.server.service.sms.AbstractSmsSender; |
@@ -19,7 +19,7 @@ import com.twilio.http.TwilioRestClient; | @@ -19,7 +19,7 @@ import com.twilio.http.TwilioRestClient; | ||
19 | import com.twilio.rest.api.v2010.account.Message; | 19 | import com.twilio.rest.api.v2010.account.Message; |
20 | import com.twilio.type.PhoneNumber; | 20 | import com.twilio.type.PhoneNumber; |
21 | import org.apache.commons.lang3.StringUtils; | 21 | import org.apache.commons.lang3.StringUtils; |
22 | -import org.thingsboard.rule.engine.api.sms.config.TwilioSmsProviderConfiguration; | 22 | +import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; |
23 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; | 23 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; |
24 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; | 24 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; |
25 | import org.thingsboard.server.service.sms.AbstractSmsSender; | 25 | import org.thingsboard.server.service.sms.AbstractSmsSender; |
@@ -521,27 +521,27 @@ public class DefaultDeviceStateService implements DeviceStateService { | @@ -521,27 +521,27 @@ public class DefaultDeviceStateService implements DeviceStateService { | ||
521 | 521 | ||
522 | private void save(DeviceId deviceId, String key, long value) { | 522 | private void save(DeviceId deviceId, String key, long value) { |
523 | if (persistToTelemetry) { | 523 | if (persistToTelemetry) { |
524 | - tsSubService.saveAndNotify( | 524 | + tsSubService.saveAndNotifyInternal( |
525 | TenantId.SYS_TENANT_ID, deviceId, | 525 | TenantId.SYS_TENANT_ID, deviceId, |
526 | Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), | 526 | Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), |
527 | - new AttributeSaveCallback(deviceId, key, value)); | 527 | + new AttributeSaveCallback<>(deviceId, key, value)); |
528 | } else { | 528 | } else { |
529 | - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); | 529 | + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback<>(deviceId, key, value)); |
530 | } | 530 | } |
531 | } | 531 | } |
532 | 532 | ||
533 | private void save(DeviceId deviceId, String key, boolean value) { | 533 | private void save(DeviceId deviceId, String key, boolean value) { |
534 | if (persistToTelemetry) { | 534 | if (persistToTelemetry) { |
535 | - tsSubService.saveAndNotify( | 535 | + tsSubService.saveAndNotifyInternal( |
536 | TenantId.SYS_TENANT_ID, deviceId, | 536 | TenantId.SYS_TENANT_ID, deviceId, |
537 | Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), | 537 | Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), |
538 | - new AttributeSaveCallback(deviceId, key, value)); | 538 | + new AttributeSaveCallback<>(deviceId, key, value)); |
539 | } else { | 539 | } else { |
540 | - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); | 540 | + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback<>(deviceId, key, value)); |
541 | } | 541 | } |
542 | } | 542 | } |
543 | 543 | ||
544 | - private static class AttributeSaveCallback implements FutureCallback<Void> { | 544 | + private static class AttributeSaveCallback<T> implements FutureCallback<T> { |
545 | private final DeviceId deviceId; | 545 | private final DeviceId deviceId; |
546 | private final String key; | 546 | private final String key; |
547 | private final Object value; | 547 | private final Object value; |
@@ -553,7 +553,7 @@ public class DefaultDeviceStateService implements DeviceStateService { | @@ -553,7 +553,7 @@ public class DefaultDeviceStateService implements DeviceStateService { | ||
553 | } | 553 | } |
554 | 554 | ||
555 | @Override | 555 | @Override |
556 | - public void onSuccess(@Nullable Void result) { | 556 | + public void onSuccess(@Nullable T result) { |
557 | log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value); | 557 | log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value); |
558 | } | 558 | } |
559 | 559 |
@@ -25,6 +25,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; | @@ -25,6 +25,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; | ||
25 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 25 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
26 | import org.thingsboard.server.common.data.EntityType; | 26 | import org.thingsboard.server.common.data.EntityType; |
27 | import org.thingsboard.server.common.data.EntityView; | 27 | import org.thingsboard.server.common.data.EntityView; |
28 | +import org.thingsboard.server.common.data.TenantProfile; | ||
28 | import org.thingsboard.server.common.data.id.EntityId; | 29 | import org.thingsboard.server.common.data.id.EntityId; |
29 | import org.thingsboard.server.common.data.id.TenantId; | 30 | import org.thingsboard.server.common.data.id.TenantId; |
30 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 31 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
@@ -34,13 +35,14 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry; | @@ -34,13 +35,14 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry; | ||
34 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 35 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
35 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 36 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
36 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 37 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
38 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | ||
37 | import org.thingsboard.server.common.msg.queue.ServiceType; | 39 | import org.thingsboard.server.common.msg.queue.ServiceType; |
38 | import org.thingsboard.server.common.msg.queue.TbCallback; | 40 | import org.thingsboard.server.common.msg.queue.TbCallback; |
39 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | 41 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
40 | import org.thingsboard.server.dao.attributes.AttributesService; | 42 | import org.thingsboard.server.dao.attributes.AttributesService; |
41 | import org.thingsboard.server.dao.entityview.EntityViewService; | 43 | import org.thingsboard.server.dao.entityview.EntityViewService; |
44 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | ||
42 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 45 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
43 | -import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | ||
44 | import org.thingsboard.server.gen.transport.TransportProtos; | 46 | import org.thingsboard.server.gen.transport.TransportProtos; |
45 | import org.thingsboard.server.queue.discovery.PartitionService; | 47 | import org.thingsboard.server.queue.discovery.PartitionService; |
46 | import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | 48 | import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
@@ -119,11 +121,12 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | @@ -119,11 +121,12 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | ||
119 | @Override | 121 | @Override |
120 | public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { | 122 | public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { |
121 | checkInternalEntity(entityId); | 123 | checkInternalEntity(entityId); |
122 | - if (apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { | 124 | + boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; |
125 | + if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { | ||
123 | saveAndNotifyInternal(tenantId, entityId, ts, ttl, new FutureCallback<Integer>() { | 126 | saveAndNotifyInternal(tenantId, entityId, ts, ttl, new FutureCallback<Integer>() { |
124 | @Override | 127 | @Override |
125 | public void onSuccess(Integer result) { | 128 | public void onSuccess(Integer result) { |
126 | - if (result != null && result > 0) { | 129 | + if (!sysTenant && result != null && result > 0) { |
127 | apiUsageClient.report(tenantId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); | 130 | apiUsageClient.report(tenantId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); |
128 | } | 131 | } |
129 | callback.onSuccess(null); | 132 | callback.onSuccess(null); |
@@ -134,7 +137,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | @@ -134,7 +137,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | ||
134 | callback.onFailure(t); | 137 | callback.onFailure(t); |
135 | } | 138 | } |
136 | }); | 139 | }); |
137 | - } else{ | 140 | + } else { |
138 | callback.onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); | 141 | callback.onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); |
139 | } | 142 | } |
140 | } | 143 | } |
@@ -4,4 +4,4 @@ account.activated.subject=Thingsboard - your account has been activated | @@ -4,4 +4,4 @@ account.activated.subject=Thingsboard - your account has been activated | ||
4 | reset.password.subject=Thingsboard - Password reset has been requested | 4 | reset.password.subject=Thingsboard - Password reset has been requested |
5 | password.was.reset.subject=Thingsboard - your account password has been reset | 5 | password.was.reset.subject=Thingsboard - your account password has been reset |
6 | account.lockout.subject=Thingsboard - User account has been lockout | 6 | account.lockout.subject=Thingsboard - User account has been lockout |
7 | -api.usage.state=Thingsboard - Api Usage State for tenant has been updated | ||
7 | +api.usage.state=Thingsboard - Account limits |
@@ -103,40 +103,30 @@ | @@ -103,40 +103,30 @@ | ||
103 | 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;" | 103 | 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;" |
104 | bgcolor="#f6f6f6"> | 104 | bgcolor="#f6f6f6"> |
105 | 105 | ||
106 | -<table class="main" | ||
107 | - 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;" | ||
108 | - cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> | 106 | +<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"> |
109 | <tbody> | 107 | <tbody> |
110 | <tr style="box-sizing: border-box; margin: 0px;"> | 108 | <tr style="box-sizing: border-box; margin: 0px;"> |
111 | - <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" | ||
112 | - align="center" valign="top"> | ||
113 | - <table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 223px; padding: 20px; background-color: #ffffff; width: 600px; max-width: 600px !important;" | ||
114 | - width="600" cellspacing="0" cellpadding="0"> | 109 | + <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" align="center" valign="top"> |
110 | + <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"> | ||
115 | <tbody> | 111 | <tbody> |
116 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 112 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
117 | - <td class="content-block" | ||
118 | - 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 0px 20px; height: 84px;" | ||
119 | - valign="top"> | ||
120 | - <h2>Your ThingsBoard account feature was disabled</h2> | ||
121 | - </td> | 113 | + <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> |
122 | </tr> | 114 | </tr> |
123 | - <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
124 | - <td class="content-block" | ||
125 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | ||
126 | - valign="top">We have disabled the ${apiFeature} for your account because ThingsBoard has already ${apiLimitValueLabel}. | 115 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; margin: 0;"> |
116 | + <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> | ||
117 | + </tr> | ||
118 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; line-height: 24px; margin: 0;"> | ||
119 | + <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"> | ||
120 | + <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" /> | ||
121 | + <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> | ||
122 | + </div> | ||
127 | </td> | 123 | </td> |
128 | </tr> | 124 | </tr> |
129 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 125 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
130 | - <td class="content-block" | ||
131 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 59px;" | ||
132 | - valign="top">Please contact your system administrator to resolve the issue. | ||
133 | - </td> | 126 | + <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> |
134 | </tr> | 127 | </tr> |
135 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 128 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
136 | - <td class="content-block" | ||
137 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | ||
138 | - valign="top">— The ThingsBoard | ||
139 | - </td> | 129 | + <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> |
140 | </tr> | 130 | </tr> |
141 | </tbody> | 131 | </tbody> |
142 | </table> | 132 | </table> |
@@ -144,16 +134,10 @@ | @@ -144,16 +134,10 @@ | ||
144 | </tr> | 134 | </tr> |
145 | </tbody> | 135 | </tbody> |
146 | </table> | 136 | </table> |
147 | -<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%;" | ||
148 | - cellpadding="0px 0px 20px"> | 137 | +<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"> |
149 | <tbody> | 138 | <tbody> |
150 | <tr style="box-sizing: border-box; margin: 0px;"> | 139 | <tr style="box-sizing: border-box; margin: 0px;"> |
151 | - <td class="aligncenter content-block" | ||
152 | - style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" | ||
153 | - align="center" valign="top">This email was sent to <a | ||
154 | - style="box-sizing: border-box; color: #999999; margin: 0px;" | ||
155 | - href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard. | ||
156 | - </td> | 140 | + <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> |
157 | </tr> | 141 | </tr> |
158 | </tbody> | 142 | </tbody> |
159 | </table> | 143 | </table> |
@@ -103,34 +103,27 @@ | @@ -103,34 +103,27 @@ | ||
103 | 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;" | 103 | 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;" |
104 | bgcolor="#f6f6f6"> | 104 | bgcolor="#f6f6f6"> |
105 | 105 | ||
106 | -<table class="main" | ||
107 | - 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;" | ||
108 | - cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> | 106 | +<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"> |
109 | <tbody> | 107 | <tbody> |
110 | <tr style="box-sizing: border-box; margin: 0px;"> | 108 | <tr style="box-sizing: border-box; margin: 0px;"> |
111 | - <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" | ||
112 | - align="center" valign="top"> | ||
113 | - <table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 223px; padding: 20px; background-color: #ffffff; width: 600px; max-width: 600px !important;" | ||
114 | - width="600" cellspacing="0" cellpadding="0"> | 109 | + <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" align="center" valign="top"> |
110 | + <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"> | ||
115 | <tbody> | 111 | <tbody> |
116 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 112 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
117 | - <td class="content-block" | ||
118 | - 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 0px 20px; height: 84px;" | ||
119 | - valign="top"> | ||
120 | - <h2>Your ThingsBoard account feature was enabled</h2> | ||
121 | - </td> | 113 | + <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> |
122 | </tr> | 114 | </tr> |
123 | - <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
124 | - <td class="content-block" | ||
125 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | ||
126 | - valign="top">We have enabled the ${apiFeature} for your account and ThingsBoard is already able to ${apiLabel} messages. | 115 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; margin: 0;"> |
116 | + <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> | ||
117 | + </tr> | ||
118 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; line-height: 24px; margin: 0;"> | ||
119 | + <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"> | ||
120 | + <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" /> | ||
121 | + <div style="display: inline; vertical-align: middle;">We have enabled the ${apiFeature} for your account and ThingsBoard is already able to ${apiLabel} messages.</div> | ||
122 | + </div> | ||
127 | </td> | 123 | </td> |
128 | </tr> | 124 | </tr> |
129 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 125 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
130 | - <td class="content-block" | ||
131 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | ||
132 | - valign="top">— The ThingsBoard | ||
133 | - </td> | 126 | + <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> |
134 | </tr> | 127 | </tr> |
135 | </tbody> | 128 | </tbody> |
136 | </table> | 129 | </table> |
@@ -138,16 +131,10 @@ | @@ -138,16 +131,10 @@ | ||
138 | </tr> | 131 | </tr> |
139 | </tbody> | 132 | </tbody> |
140 | </table> | 133 | </table> |
141 | -<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%;" | ||
142 | - cellpadding="0px 0px 20px"> | 134 | +<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"> |
143 | <tbody> | 135 | <tbody> |
144 | <tr style="box-sizing: border-box; margin: 0px;"> | 136 | <tr style="box-sizing: border-box; margin: 0px;"> |
145 | - <td class="aligncenter content-block" | ||
146 | - style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" | ||
147 | - align="center" valign="top">This email was sent to <a | ||
148 | - style="box-sizing: border-box; color: #999999; margin: 0px;" | ||
149 | - href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard. | ||
150 | - </td> | 137 | + <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> |
151 | </tr> | 138 | </tr> |
152 | </tbody> | 139 | </tbody> |
153 | </table> | 140 | </table> |
@@ -103,41 +103,30 @@ | @@ -103,41 +103,30 @@ | ||
103 | 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;" | 103 | 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;" |
104 | bgcolor="#f6f6f6"> | 104 | bgcolor="#f6f6f6"> |
105 | 105 | ||
106 | -<table class="main" | ||
107 | - 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;" | ||
108 | - cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> | 106 | +<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"> |
109 | <tbody> | 107 | <tbody> |
110 | <tr style="box-sizing: border-box; margin: 0px;"> | 108 | <tr style="box-sizing: border-box; margin: 0px;"> |
111 | - <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" | ||
112 | - align="center" valign="top"> | ||
113 | - <table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 223px; padding: 20px; background-color: #ffffff; width: 600px; max-width: 600px !important;" | ||
114 | - width="600" cellspacing="0" cellpadding="0"> | 109 | + <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" align="center" valign="top"> |
110 | + <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"> | ||
115 | <tbody> | 111 | <tbody> |
116 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 112 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
117 | - <td class="content-block" | ||
118 | - 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 0px 20px; height: 84px;" | ||
119 | - valign="top"> | ||
120 | - <h2>Your ThingsBoard account feature may be disabled soon</h2> | ||
121 | - </td> | 113 | + <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> |
122 | </tr> | 114 | </tr> |
123 | - <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
124 | - <td class="content-block" | ||
125 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | ||
126 | - valign="top"> | ||
127 | - ThingsBoard has already ${apiValueLabel}.<br> ${apiFeature} will be disabled for your account once the limit will be reached. | 115 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; margin: 0;"> |
116 | + <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> | ||
117 | + </tr> | ||
118 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; line-height: 24px; margin: 0;"> | ||
119 | + <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"> | ||
120 | + <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" /> | ||
121 | + <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> | ||
122 | + </div> | ||
128 | </td> | 123 | </td> |
129 | </tr> | 124 | </tr> |
130 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 125 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
131 | - <td class="content-block" | ||
132 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 59px;" | ||
133 | - valign="top">Please contact your system administrator to resolve the issue. | ||
134 | - </td> | 126 | + <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> |
135 | </tr> | 127 | </tr> |
136 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | 128 | <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> |
137 | - <td class="content-block" | ||
138 | - style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | ||
139 | - valign="top">— The ThingsBoard | ||
140 | - </td> | 129 | + <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> |
141 | </tr> | 130 | </tr> |
142 | </tbody> | 131 | </tbody> |
143 | </table> | 132 | </table> |
@@ -145,16 +134,10 @@ | @@ -145,16 +134,10 @@ | ||
145 | </tr> | 134 | </tr> |
146 | </tbody> | 135 | </tbody> |
147 | </table> | 136 | </table> |
148 | -<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%;" | ||
149 | - cellpadding="0px 0px 20px"> | 137 | +<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"> |
150 | <tbody> | 138 | <tbody> |
151 | <tr style="box-sizing: border-box; margin: 0px;"> | 139 | <tr style="box-sizing: border-box; margin: 0px;"> |
152 | - <td class="aligncenter content-block" | ||
153 | - style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" | ||
154 | - align="center" valign="top">This email was sent to <a | ||
155 | - style="box-sizing: border-box; color: #999999; margin: 0px;" | ||
156 | - href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard. | ||
157 | - </td> | 140 | + <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> |
158 | </tr> | 141 | </tr> |
159 | </tbody> | 142 | </tbody> |
160 | </table> | 143 | </table> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -15,9 +15,6 @@ | @@ -15,9 +15,6 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.device; | 16 | package org.thingsboard.server.dao.device; |
17 | 17 | ||
18 | -import com.google.common.util.concurrent.ListenableFuture; | ||
19 | -import org.thingsboard.server.common.data.Device; | ||
20 | -import org.thingsboard.server.common.data.DeviceProfile; | ||
21 | import org.thingsboard.server.dao.device.provision.ProvisionFailedException; | 18 | import org.thingsboard.server.dao.device.provision.ProvisionFailedException; |
22 | import org.thingsboard.server.dao.device.provision.ProvisionRequest; | 19 | import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
23 | import org.thingsboard.server.dao.device.provision.ProvisionResponse; | 20 | import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
@@ -16,9 +16,12 @@ | @@ -16,9 +16,12 @@ | ||
16 | package org.thingsboard.server.dao.tenant; | 16 | package org.thingsboard.server.dao.tenant; |
17 | 17 | ||
18 | import org.thingsboard.server.common.data.TenantProfile; | 18 | import org.thingsboard.server.common.data.TenantProfile; |
19 | +import org.thingsboard.server.common.data.id.EntityId; | ||
19 | import org.thingsboard.server.common.data.id.TenantId; | 20 | import org.thingsboard.server.common.data.id.TenantId; |
20 | import org.thingsboard.server.common.data.id.TenantProfileId; | 21 | import org.thingsboard.server.common.data.id.TenantProfileId; |
21 | 22 | ||
23 | +import java.util.function.Consumer; | ||
24 | + | ||
22 | public interface TbTenantProfileCache { | 25 | public interface TbTenantProfileCache { |
23 | 26 | ||
24 | TenantProfile get(TenantId tenantId); | 27 | TenantProfile get(TenantId tenantId); |
@@ -31,4 +34,8 @@ public interface TbTenantProfileCache { | @@ -31,4 +34,8 @@ public interface TbTenantProfileCache { | ||
31 | 34 | ||
32 | void evict(TenantId id); | 35 | void evict(TenantId id); |
33 | 36 | ||
37 | + void addListener(TenantId tenantId, EntityId listenerId, Consumer<TenantProfile> profileListener); | ||
38 | + | ||
39 | + void removeListener(TenantId tenantId, EntityId listenerId); | ||
40 | + | ||
34 | } | 41 | } |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -18,9 +18,8 @@ package org.thingsboard.server.common.data.device.profile; | @@ -18,9 +18,8 @@ package org.thingsboard.server.common.data.device.profile; | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 19 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
20 | 20 | ||
21 | -import java.util.LinkedHashMap; | ||
22 | import java.util.List; | 21 | import java.util.List; |
23 | -import java.util.Map; | 22 | +import java.util.TreeMap; |
24 | 23 | ||
25 | @Data | 24 | @Data |
26 | public class DeviceProfileAlarm { | 25 | public class DeviceProfileAlarm { |
@@ -28,7 +27,7 @@ public class DeviceProfileAlarm { | @@ -28,7 +27,7 @@ public class DeviceProfileAlarm { | ||
28 | private String id; | 27 | private String id; |
29 | private String alarmType; | 28 | private String alarmType; |
30 | 29 | ||
31 | - private LinkedHashMap<AlarmSeverity, AlarmRule> createRules; | 30 | + private TreeMap<AlarmSeverity, AlarmRule> createRules; |
32 | private AlarmRule clearRule; | 31 | private AlarmRule clearRule; |
33 | 32 | ||
34 | // Hidden in advanced settings | 33 | // Hidden in advanced settings |
common/data/src/main/java/org/thingsboard/server/common/data/sms/config/AwsSnsSmsProviderConfiguration.java
renamed from
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/sms/config/AwsSnsSmsProviderConfiguration.java
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.rule.engine.api.sms.config; | 16 | +package org.thingsboard.server.common.data.sms.config; |
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | 19 |
common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java
renamed from
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/sms/config/SmsProviderConfiguration.java
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.rule.engine.api.sms.config; | 16 | +package org.thingsboard.server.common.data.sms.config; |
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonIgnore; | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; |
19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java
renamed from
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/sms/config/SmsProviderType.java
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.rule.engine.api.sms.config; | 16 | +package org.thingsboard.server.common.data.sms.config; |
17 | 17 | ||
18 | public enum SmsProviderType { | 18 | public enum SmsProviderType { |
19 | AWS_SNS, | 19 | AWS_SNS, |
common/data/src/main/java/org/thingsboard/server/common/data/sms/config/TestSmsRequest.java
renamed from
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/sms/config/TestSmsRequest.java
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.rule.engine.api.sms.config; | 16 | +package org.thingsboard.server.common.data.sms.config; |
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | 19 |
common/data/src/main/java/org/thingsboard/server/common/data/sms/config/TwilioSmsProviderConfiguration.java
renamed from
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/sms/config/TwilioSmsProviderConfiguration.java
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.rule.engine.api.sms.config; | 16 | +package org.thingsboard.server.common.data.sms.config; |
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | 19 |
@@ -45,6 +45,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura | @@ -45,6 +45,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura | ||
45 | private long maxEmails; | 45 | private long maxEmails; |
46 | private long maxSms; | 46 | private long maxSms; |
47 | 47 | ||
48 | + private int defaultStorageTtlDays; | ||
49 | + | ||
48 | private double warnThreshold; | 50 | private double warnThreshold; |
49 | 51 | ||
50 | @Override | 52 | @Override |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>common</artifactId> | 26 | <artifactId>common</artifactId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -195,9 +195,11 @@ public class HashPartitionService implements PartitionService { | @@ -195,9 +195,11 @@ public class HashPartitionService implements PartitionService { | ||
195 | if (current.getServiceTypesList().contains(serviceType.name())) { | 195 | if (current.getServiceTypesList().contains(serviceType.name())) { |
196 | result.add(current.getServiceId()); | 196 | result.add(current.getServiceId()); |
197 | } | 197 | } |
198 | - for (ServiceInfo serviceInfo : currentOtherServices) { | ||
199 | - if (serviceInfo.getServiceTypesList().contains(serviceType.name())) { | ||
200 | - result.add(serviceInfo.getServiceId()); | 198 | + if (currentOtherServices != null) { |
199 | + for (ServiceInfo serviceInfo : currentOtherServices) { | ||
200 | + if (serviceInfo.getServiceTypesList().contains(serviceType.name())) { | ||
201 | + result.add(serviceInfo.getServiceId()); | ||
202 | + } | ||
201 | } | 203 | } |
202 | } | 204 | } |
203 | return result; | 205 | return result; |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | <modelVersion>4.0.0</modelVersion> | 22 | <modelVersion>4.0.0</modelVersion> |
23 | <parent> | 23 | <parent> |
24 | <groupId>org.thingsboard</groupId> | 24 | <groupId>org.thingsboard</groupId> |
25 | - <version>3.2.0-SNAPSHOT</version> | 25 | + <version>3.2.1-SNAPSHOT</version> |
26 | <artifactId>common</artifactId> | 26 | <artifactId>common</artifactId> |
27 | </parent> | 27 | </parent> |
28 | <groupId>org.thingsboard.common</groupId> | 28 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; | @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; | ||
19 | import org.eclipse.californium.core.CoapResource; | 19 | import org.eclipse.californium.core.CoapResource; |
20 | import org.eclipse.californium.core.CoapServer; | 20 | import org.eclipse.californium.core.CoapServer; |
21 | import org.eclipse.californium.core.network.CoapEndpoint; | 21 | import org.eclipse.californium.core.network.CoapEndpoint; |
22 | +import org.eclipse.californium.core.network.config.NetworkConfig; | ||
22 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
24 | import org.springframework.stereotype.Service; | 25 | import org.springframework.stereotype.Service; |
@@ -46,7 +47,7 @@ public class CoapTransportService { | @@ -46,7 +47,7 @@ public class CoapTransportService { | ||
46 | public void init() throws UnknownHostException { | 47 | public void init() throws UnknownHostException { |
47 | log.info("Starting CoAP transport..."); | 48 | log.info("Starting CoAP transport..."); |
48 | log.info("Starting CoAP transport server"); | 49 | log.info("Starting CoAP transport server"); |
49 | - this.server = new CoapServer(); | 50 | + this.server = new CoapServer(NetworkConfig.createStandardWithoutFile()); |
50 | createResources(); | 51 | createResources(); |
51 | InetAddress addr = InetAddress.getByName(coapTransportContext.getHost()); | 52 | InetAddress addr = InetAddress.getByName(coapTransportContext.getHost()); |
52 | InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort()); | 53 | InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort()); |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -179,6 +179,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -179,6 +179,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
179 | } | 179 | } |
180 | } | 180 | } |
181 | } else { | 181 | } else { |
182 | + ctx.close(); | ||
182 | throw new RuntimeException("Unsupported topic for provisioning requests!"); | 183 | throw new RuntimeException("Unsupported topic for provisioning requests!"); |
183 | } | 184 | } |
184 | } catch (RuntimeException | AdaptorException e) { | 185 | } catch (RuntimeException | AdaptorException e) { |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>dao</artifactId> | 26 | <artifactId>dao</artifactId> |
@@ -78,6 +78,8 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ | @@ -78,6 +78,8 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ | ||
78 | public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; | 78 | public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; |
79 | public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; | 79 | public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; |
80 | public static final String INCORRECT_ASSET_ID = "Incorrect assetId "; | 80 | public static final String INCORRECT_ASSET_ID = "Incorrect assetId "; |
81 | + public static final String TB_SERVICE_QUEUE = "TbServiceQueue"; | ||
82 | + | ||
81 | @Autowired | 83 | @Autowired |
82 | private AssetDao assetDao; | 84 | private AssetDao assetDao; |
83 | 85 | ||
@@ -329,8 +331,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ | @@ -329,8 +331,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ | ||
329 | protected void validateCreate(TenantId tenantId, Asset asset) { | 331 | protected void validateCreate(TenantId tenantId, Asset asset) { |
330 | DefaultTenantProfileConfiguration profileConfiguration = | 332 | DefaultTenantProfileConfiguration profileConfiguration = |
331 | (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); | 333 | (DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); |
332 | - long maxAssets = profileConfiguration.getMaxAssets(); | ||
333 | - validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); | 334 | + if (!TB_SERVICE_QUEUE.equals(asset.getType())) { |
335 | + long maxAssets = profileConfiguration.getMaxAssets(); | ||
336 | + validateNumberOfEntitiesPerTenant(tenantId, assetDao, maxAssets, EntityType.ASSET); | ||
337 | + } | ||
334 | } | 338 | } |
335 | 339 | ||
336 | @Override | 340 | @Override |
@@ -122,5 +122,5 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, | @@ -122,5 +122,5 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, | ||
122 | @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId") | 122 | @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId") |
123 | List<String> findTenantAssetTypes(@Param("tenantId") UUID tenantId); | 123 | List<String> findTenantAssetTypes(@Param("tenantId") UUID tenantId); |
124 | 124 | ||
125 | - Long countByTenantId(UUID tenantId); | 125 | + Long countByTenantIdAndTypeIsNot(UUID tenantId, String type); |
126 | } | 126 | } |
@@ -39,6 +39,8 @@ import java.util.Objects; | @@ -39,6 +39,8 @@ import java.util.Objects; | ||
39 | import java.util.Optional; | 39 | import java.util.Optional; |
40 | import java.util.UUID; | 40 | import java.util.UUID; |
41 | 41 | ||
42 | +import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; | ||
43 | + | ||
42 | /** | 44 | /** |
43 | * Created by Valerii Sosliuk on 5/19/2017. | 45 | * Created by Valerii Sosliuk on 5/19/2017. |
44 | */ | 46 | */ |
@@ -179,6 +181,6 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im | @@ -179,6 +181,6 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im | ||
179 | 181 | ||
180 | @Override | 182 | @Override |
181 | public Long countByTenantId(TenantId tenantId) { | 183 | public Long countByTenantId(TenantId tenantId) { |
182 | - return assetRepository.countByTenantId(tenantId.getId()); | 184 | + return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE); |
183 | } | 185 | } |
184 | } | 186 | } |
@@ -19,16 +19,15 @@ import lombok.extern.slf4j.Slf4j; | @@ -19,16 +19,15 @@ import lombok.extern.slf4j.Slf4j; | ||
19 | import org.springframework.stereotype.Service; | 19 | import org.springframework.stereotype.Service; |
20 | import org.thingsboard.server.common.data.Tenant; | 20 | import org.thingsboard.server.common.data.Tenant; |
21 | import org.thingsboard.server.common.data.TenantProfile; | 21 | import org.thingsboard.server.common.data.TenantProfile; |
22 | +import org.thingsboard.server.common.data.id.EntityId; | ||
22 | import org.thingsboard.server.common.data.id.TenantId; | 23 | import org.thingsboard.server.common.data.id.TenantId; |
23 | import org.thingsboard.server.common.data.id.TenantProfileId; | 24 | import org.thingsboard.server.common.data.id.TenantProfileId; |
24 | -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | ||
25 | -import org.thingsboard.server.dao.tenant.TenantProfileService; | ||
26 | -import org.thingsboard.server.dao.tenant.TenantService; | ||
27 | 25 | ||
28 | import java.util.concurrent.ConcurrentHashMap; | 26 | import java.util.concurrent.ConcurrentHashMap; |
29 | import java.util.concurrent.ConcurrentMap; | 27 | import java.util.concurrent.ConcurrentMap; |
30 | import java.util.concurrent.locks.Lock; | 28 | import java.util.concurrent.locks.Lock; |
31 | import java.util.concurrent.locks.ReentrantLock; | 29 | import java.util.concurrent.locks.ReentrantLock; |
30 | +import java.util.function.Consumer; | ||
32 | 31 | ||
33 | @Service | 32 | @Service |
34 | @Slf4j | 33 | @Slf4j |
@@ -40,6 +39,7 @@ public class DefaultTbTenantProfileCache implements TbTenantProfileCache { | @@ -40,6 +39,7 @@ public class DefaultTbTenantProfileCache implements TbTenantProfileCache { | ||
40 | 39 | ||
41 | private final ConcurrentMap<TenantProfileId, TenantProfile> tenantProfilesMap = new ConcurrentHashMap<>(); | 40 | private final ConcurrentMap<TenantProfileId, TenantProfile> tenantProfilesMap = new ConcurrentHashMap<>(); |
42 | private final ConcurrentMap<TenantId, TenantProfileId> tenantsMap = new ConcurrentHashMap<>(); | 41 | private final ConcurrentMap<TenantId, TenantProfileId> tenantsMap = new ConcurrentHashMap<>(); |
42 | + private final ConcurrentMap<TenantId, ConcurrentMap<EntityId, Consumer<TenantProfile>>> profileListeners = new ConcurrentHashMap<>(); | ||
43 | 43 | ||
44 | public DefaultTbTenantProfileCache(TenantProfileService tenantProfileService, TenantService tenantService) { | 44 | public DefaultTbTenantProfileCache(TenantProfileService tenantProfileService, TenantService tenantService) { |
45 | this.tenantProfileService = tenantProfileService; | 45 | this.tenantProfileService = tenantProfileService; |
@@ -85,17 +85,56 @@ public class DefaultTbTenantProfileCache implements TbTenantProfileCache { | @@ -85,17 +85,56 @@ public class DefaultTbTenantProfileCache implements TbTenantProfileCache { | ||
85 | public void put(TenantProfile profile) { | 85 | public void put(TenantProfile profile) { |
86 | if (profile.getId() != null) { | 86 | if (profile.getId() != null) { |
87 | tenantProfilesMap.put(profile.getId(), profile); | 87 | tenantProfilesMap.put(profile.getId(), profile); |
88 | + notifyTenantListeners(profile); | ||
88 | } | 89 | } |
89 | } | 90 | } |
90 | 91 | ||
91 | @Override | 92 | @Override |
92 | public void evict(TenantProfileId profileId) { | 93 | public void evict(TenantProfileId profileId) { |
93 | tenantProfilesMap.remove(profileId); | 94 | tenantProfilesMap.remove(profileId); |
95 | + notifyTenantListeners(get(profileId)); | ||
96 | + } | ||
97 | + | ||
98 | + public void notifyTenantListeners(TenantProfile tenantProfile) { | ||
99 | + if (tenantProfile != null) { | ||
100 | + tenantsMap.forEach(((tenantId, tenantProfileId) -> { | ||
101 | + if (tenantProfileId.equals(tenantProfile.getId())) { | ||
102 | + ConcurrentMap<EntityId, Consumer<TenantProfile>> tenantListeners = profileListeners.get(tenantId); | ||
103 | + if (tenantListeners != null) { | ||
104 | + tenantListeners.forEach((id, listener) -> listener.accept(tenantProfile)); | ||
105 | + } | ||
106 | + } | ||
107 | + })); | ||
108 | + } | ||
94 | } | 109 | } |
95 | 110 | ||
96 | @Override | 111 | @Override |
97 | public void evict(TenantId tenantId) { | 112 | public void evict(TenantId tenantId) { |
98 | tenantsMap.remove(tenantId); | 113 | tenantsMap.remove(tenantId); |
114 | + TenantProfile tenantProfile = get(tenantId); | ||
115 | + if (tenantProfile != null) { | ||
116 | + ConcurrentMap<EntityId, Consumer<TenantProfile>> tenantListeners = profileListeners.get(tenantId); | ||
117 | + if (tenantListeners != null) { | ||
118 | + tenantListeners.forEach((id, listener) -> listener.accept(tenantProfile)); | ||
119 | + } | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + @Override | ||
124 | + public void addListener(TenantId tenantId, EntityId listenerId, Consumer<TenantProfile> profileListener) { | ||
125 | + //Force cache of the tenant id. | ||
126 | + get(tenantId); | ||
127 | + if (profileListener != null) { | ||
128 | + profileListeners.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(listenerId, profileListener); | ||
129 | + } | ||
130 | + } | ||
131 | + | ||
132 | + @Override | ||
133 | + public void removeListener(TenantId tenantId, EntityId listenerId) { | ||
134 | + ConcurrentMap<EntityId, Consumer<TenantProfile>> tenantListeners = profileListeners.get(tenantId); | ||
135 | + if (tenantListeners != null) { | ||
136 | + tenantListeners.remove(listenerId); | ||
137 | + } | ||
99 | } | 138 | } |
100 | 139 | ||
101 | } | 140 | } |
@@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
21 | 21 | ||
22 | <parent> | 22 | <parent> |
23 | <groupId>org.thingsboard</groupId> | 23 | <groupId>org.thingsboard</groupId> |
24 | - <version>3.2.0-SNAPSHOT</version> | 24 | + <version>3.2.1-SNAPSHOT</version> |
25 | <artifactId>msa</artifactId> | 25 | <artifactId>msa</artifactId> |
26 | </parent> | 26 | </parent> |
27 | <groupId>org.thingsboard.msa</groupId> | 27 | <groupId>org.thingsboard.msa</groupId> |
1 | { | 1 | { |
2 | "name": "thingsboard-js-executor", | 2 | "name": "thingsboard-js-executor", |
3 | "private": true, | 3 | "private": true, |
4 | - "version": "3.2.0", | 4 | + "version": "3.2.1", |
5 | "description": "ThingsBoard JavaScript Executor Microservice", | 5 | "description": "ThingsBoard JavaScript Executor Microservice", |
6 | "main": "server.js", | 6 | "main": "server.js", |
7 | "bin": "server.js", | 7 | "bin": "server.js", |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>msa</artifactId> | 26 | <artifactId>msa</artifactId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -27,7 +27,11 @@ fi | @@ -27,7 +27,11 @@ fi | ||
27 | exec setsid nohup postgres >> ${PGLOG}/postgres.log 2>&1 & | 27 | exec setsid nohup postgres >> ${PGLOG}/postgres.log 2>&1 & |
28 | 28 | ||
29 | if [ ! -f ${firstlaunch} ]; then | 29 | if [ ! -f ${firstlaunch} ]; then |
30 | - psql -U ${pkg.user} -d postgres -c "CREATE DATABASE thingsboard" | 30 | + sleep 2 |
31 | + while ! psql -U ${pkg.user} -d postgres -c "CREATE DATABASE thingsboard" | ||
32 | + do | ||
33 | + sleep 1 | ||
34 | + done | ||
31 | fi | 35 | fi |
32 | 36 | ||
33 | cassandra_data_dir=${CASSANDRA_DATA} | 37 | cassandra_data_dir=${CASSANDRA_DATA} |
@@ -27,5 +27,9 @@ fi | @@ -27,5 +27,9 @@ fi | ||
27 | exec setsid nohup postgres >> ${PGLOG}/postgres.log 2>&1 & | 27 | exec setsid nohup postgres >> ${PGLOG}/postgres.log 2>&1 & |
28 | 28 | ||
29 | if [ ! -f ${firstlaunch} ]; then | 29 | if [ ! -f ${firstlaunch} ]; then |
30 | - psql -U ${pkg.user} -d postgres -c "CREATE DATABASE thingsboard" | ||
31 | -fi | 30 | + sleep 2 |
31 | + while ! psql -U ${pkg.user} -d postgres -c "CREATE DATABASE thingsboard" | ||
32 | + do | ||
33 | + sleep 1 | ||
34 | + done | ||
35 | +fi |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.msa</groupId> | 22 | <groupId>org.thingsboard.msa</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa.transport</groupId> | 26 | <groupId>org.thingsboard.msa.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.msa</groupId> | 22 | <groupId>org.thingsboard.msa</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa.transport</groupId> | 26 | <groupId>org.thingsboard.msa.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.msa</groupId> | 22 | <groupId>org.thingsboard.msa</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa.transport</groupId> | 26 | <groupId>org.thingsboard.msa.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
1 | { | 1 | { |
2 | "name": "thingsboard-web-ui", | 2 | "name": "thingsboard-web-ui", |
3 | "private": true, | 3 | "private": true, |
4 | - "version": "3.2.0", | 4 | + "version": "3.2.1", |
5 | "description": "ThingsBoard Web UI Microservice", | 5 | "description": "ThingsBoard Web UI Microservice", |
6 | "main": "server.js", | 6 | "main": "server.js", |
7 | "bin": "server.js", | 7 | "bin": "server.js", |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -19,11 +19,11 @@ | @@ -19,11 +19,11 @@ | ||
19 | <modelVersion>4.0.0</modelVersion> | 19 | <modelVersion>4.0.0</modelVersion> |
20 | <parent> | 20 | <parent> |
21 | <groupId>org.thingsboard</groupId> | 21 | <groupId>org.thingsboard</groupId> |
22 | - <version>3.2.0-SNAPSHOT</version> | 22 | + <version>3.2.1-SNAPSHOT</version> |
23 | <artifactId>thingsboard</artifactId> | 23 | <artifactId>thingsboard</artifactId> |
24 | </parent> | 24 | </parent> |
25 | <artifactId>netty-mqtt</artifactId> | 25 | <artifactId>netty-mqtt</artifactId> |
26 | - <version>3.2.0-SNAPSHOT</version> | 26 | + <version>3.2.1-SNAPSHOT</version> |
27 | <packaging>jar</packaging> | 27 | <packaging>jar</packaging> |
28 | 28 | ||
29 | <name>Netty MQTT Client</name> | 29 | <name>Netty MQTT Client</name> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <groupId>org.thingsboard</groupId> | 21 | <groupId>org.thingsboard</groupId> |
22 | <artifactId>thingsboard</artifactId> | 22 | <artifactId>thingsboard</artifactId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <packaging>pom</packaging> | 24 | <packaging>pom</packaging> |
25 | 25 | ||
26 | <name>Thingsboard</name> | 26 | <name>Thingsboard</name> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>rest-client</artifactId> | 26 | <artifactId>rest-client</artifactId> |
@@ -109,6 +109,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; | @@ -109,6 +109,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
109 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; | 109 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
110 | import org.thingsboard.server.common.data.security.model.SecuritySettings; | 110 | import org.thingsboard.server.common.data.security.model.SecuritySettings; |
111 | import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; | 111 | import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; |
112 | +import org.thingsboard.server.common.data.sms.config.TestSmsRequest; | ||
112 | import org.thingsboard.server.common.data.widget.WidgetType; | 113 | import org.thingsboard.server.common.data.widget.WidgetType; |
113 | import org.thingsboard.server.common.data.widget.WidgetsBundle; | 114 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
114 | 115 | ||
@@ -218,7 +219,11 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -218,7 +219,11 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
218 | } | 219 | } |
219 | 220 | ||
220 | public void sendTestMail(AdminSettings adminSettings) { | 221 | public void sendTestMail(AdminSettings adminSettings) { |
221 | - restTemplate.postForEntity(baseURL + "/api/admin/settings/testMail", adminSettings, AdminSettings.class); | 222 | + restTemplate.postForLocation(baseURL + "/api/admin/settings/testMail", adminSettings); |
223 | + } | ||
224 | + | ||
225 | + public void sendTestSms(TestSmsRequest testSmsRequest) { | ||
226 | + restTemplate.postForLocation(baseURL + "/api/admin/settings/testSms", testSmsRequest); | ||
222 | } | 227 | } |
223 | 228 | ||
224 | public Optional<SecuritySettings> getSecuritySettings() { | 229 | public Optional<SecuritySettings> getSecuritySettings() { |
@@ -1714,6 +1719,10 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -1714,6 +1719,10 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
1714 | return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Params, OAuth2ClientsParams.class).getBody(); | 1719 | return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Params, OAuth2ClientsParams.class).getBody(); |
1715 | } | 1720 | } |
1716 | 1721 | ||
1722 | + public String getLoginProcessingUrl() { | ||
1723 | + return restTemplate.getForEntity(baseURL + "/api/oauth2/loginProcessingUrl", String.class).getBody(); | ||
1724 | + } | ||
1725 | + | ||
1717 | public void handleOneWayDeviceRPCRequest(DeviceId deviceId, JsonNode requestBody) { | 1726 | public void handleOneWayDeviceRPCRequest(DeviceId deviceId, JsonNode requestBody) { |
1718 | restTemplate.postForLocation(baseURL + "/api/plugins/rpc/oneway/{deviceId}", requestBody, deviceId.getId()); | 1727 | restTemplate.postForLocation(baseURL + "/api/plugins/rpc/oneway/{deviceId}", requestBody, deviceId.getId()); |
1719 | } | 1728 | } |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>rule-engine</artifactId> | 26 | <artifactId>rule-engine</artifactId> |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | <modelVersion>4.0.0</modelVersion> | 22 | <modelVersion>4.0.0</modelVersion> |
23 | <parent> | 23 | <parent> |
24 | <groupId>org.thingsboard</groupId> | 24 | <groupId>org.thingsboard</groupId> |
25 | - <version>3.2.0-SNAPSHOT</version> | 25 | + <version>3.2.1-SNAPSHOT</version> |
26 | <artifactId>rule-engine</artifactId> | 26 | <artifactId>rule-engine</artifactId> |
27 | </parent> | 27 | </parent> |
28 | <groupId>org.thingsboard.rule-engine</groupId> | 28 | <groupId>org.thingsboard.rule-engine</groupId> |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.rule.engine.api; | 16 | package org.thingsboard.rule.engine.api; |
17 | 17 | ||
18 | -import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest; | 18 | +import org.thingsboard.server.common.data.sms.config.TestSmsRequest; |
19 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 19 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
20 | import org.thingsboard.server.common.data.id.TenantId; | 20 | import org.thingsboard.server.common.data.id.TenantId; |
21 | 21 |
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey; | @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey; | ||
23 | import org.thingsboard.server.common.data.Customer; | 23 | import org.thingsboard.server.common.data.Customer; |
24 | import org.thingsboard.server.common.data.Device; | 24 | import org.thingsboard.server.common.data.Device; |
25 | import org.thingsboard.server.common.data.DeviceProfile; | 25 | import org.thingsboard.server.common.data.DeviceProfile; |
26 | +import org.thingsboard.server.common.data.TenantProfile; | ||
26 | import org.thingsboard.server.common.data.alarm.Alarm; | 27 | import org.thingsboard.server.common.data.alarm.Alarm; |
27 | import org.thingsboard.server.common.data.asset.Asset; | 28 | import org.thingsboard.server.common.data.asset.Asset; |
28 | import org.thingsboard.server.common.data.id.DeviceId; | 29 | import org.thingsboard.server.common.data.id.DeviceId; |
@@ -237,7 +238,11 @@ public interface TbContext { | @@ -237,7 +238,11 @@ public interface TbContext { | ||
237 | 238 | ||
238 | void clearRuleNodeStates(); | 239 | void clearRuleNodeStates(); |
239 | 240 | ||
241 | + void addTenantProfileListener(Consumer<TenantProfile> listener); | ||
242 | + | ||
240 | void addDeviceProfileListeners(Consumer<DeviceProfile> listener, BiConsumer<DeviceId, DeviceProfile> deviceListener); | 243 | void addDeviceProfileListeners(Consumer<DeviceProfile> listener, BiConsumer<DeviceId, DeviceProfile> deviceListener); |
241 | 244 | ||
242 | - void removeProfileListener(); | 245 | + void removeListeners(); |
246 | + | ||
247 | + TenantProfile getTenantProfile(); | ||
243 | } | 248 | } |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.rule.engine.api.sms; | 16 | package org.thingsboard.rule.engine.api.sms; |
17 | 17 | ||
18 | -import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration; | 18 | +import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; |
19 | 19 | ||
20 | public interface SmsSenderFactory { | 20 | public interface SmsSenderFactory { |
21 | 21 |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | <modelVersion>4.0.0</modelVersion> | 22 | <modelVersion>4.0.0</modelVersion> |
23 | <parent> | 23 | <parent> |
24 | <groupId>org.thingsboard</groupId> | 24 | <groupId>org.thingsboard</groupId> |
25 | - <version>3.2.0-SNAPSHOT</version> | 25 | + <version>3.2.1-SNAPSHOT</version> |
26 | <artifactId>rule-engine</artifactId> | 26 | <artifactId>rule-engine</artifactId> |
27 | </parent> | 27 | </parent> |
28 | <groupId>org.thingsboard.rule-engine</groupId> | 28 | <groupId>org.thingsboard.rule-engine</groupId> |
@@ -54,7 +54,8 @@ import java.util.concurrent.TimeUnit; | @@ -54,7 +54,8 @@ import java.util.concurrent.TimeUnit; | ||
54 | relationTypes = {"Alarm Created", "Alarm Updated", "Alarm Severity Updated", "Alarm Cleared", "Success", "Failure"}, | 54 | relationTypes = {"Alarm Created", "Alarm Updated", "Alarm Severity Updated", "Alarm Cleared", "Success", "Failure"}, |
55 | configClazz = TbDeviceProfileNodeConfiguration.class, | 55 | configClazz = TbDeviceProfileNodeConfiguration.class, |
56 | nodeDescription = "Process device messages based on device profile settings", | 56 | nodeDescription = "Process device messages based on device profile settings", |
57 | - nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. Generates ", | 57 | + nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. The output relation type is either " + |
58 | + "'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.", | ||
58 | uiResources = {"static/rulenode/rulenode-core-config.js"}, | 59 | uiResources = {"static/rulenode/rulenode-core-config.js"}, |
59 | configDirective = "tbDeviceProfileConfig" | 60 | configDirective = "tbDeviceProfileConfig" |
60 | ) | 61 | ) |
@@ -119,7 +120,6 @@ public class TbDeviceProfileNode implements TbNode { | @@ -119,7 +120,6 @@ public class TbDeviceProfileNode implements TbNode { | ||
119 | } else { | 120 | } else { |
120 | removeDeviceState(deviceId); | 121 | removeDeviceState(deviceId); |
121 | } | 122 | } |
122 | - | ||
123 | } else { | 123 | } else { |
124 | if (EntityType.DEVICE.equals(originatorType)) { | 124 | if (EntityType.DEVICE.equals(originatorType)) { |
125 | DeviceId deviceId = new DeviceId(msg.getOriginator().getId()); | 125 | DeviceId deviceId = new DeviceId(msg.getOriginator().getId()); |
@@ -149,7 +149,7 @@ public class TbDeviceProfileNode implements TbNode { | @@ -149,7 +149,7 @@ public class TbDeviceProfileNode implements TbNode { | ||
149 | 149 | ||
150 | @Override | 150 | @Override |
151 | public void destroy() { | 151 | public void destroy() { |
152 | - ctx.removeProfileListener(); | 152 | + ctx.removeListeners(); |
153 | deviceStates.clear(); | 153 | deviceStates.clear(); |
154 | } | 154 | } |
155 | 155 |
@@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.sms; | @@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.sms; | ||
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | import org.thingsboard.rule.engine.api.NodeConfiguration; | 19 | import org.thingsboard.rule.engine.api.NodeConfiguration; |
20 | -import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration; | 20 | +import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; |
21 | 21 | ||
22 | @Data | 22 | @Data |
23 | public class TbSendSmsNodeConfiguration implements NodeConfiguration { | 23 | public class TbSendSmsNodeConfiguration implements NodeConfiguration { |
@@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.api.TbContext; | @@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.api.TbContext; | ||
24 | import org.thingsboard.rule.engine.api.TbNode; | 24 | import org.thingsboard.rule.engine.api.TbNode; |
25 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; | 25 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
26 | import org.thingsboard.rule.engine.api.TbNodeException; | 26 | import org.thingsboard.rule.engine.api.TbNodeException; |
27 | +import org.thingsboard.server.common.data.TenantProfile; | ||
27 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 28 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
28 | import org.thingsboard.server.common.data.kv.KvEntry; | 29 | import org.thingsboard.server.common.data.kv.KvEntry; |
29 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 30 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
@@ -31,10 +32,12 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | @@ -31,10 +32,12 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | ||
31 | import org.thingsboard.server.common.msg.TbMsg; | 32 | import org.thingsboard.server.common.msg.TbMsg; |
32 | import org.thingsboard.server.common.msg.session.SessionMsgType; | 33 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
33 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 34 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
35 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | ||
34 | 36 | ||
35 | import java.util.ArrayList; | 37 | import java.util.ArrayList; |
36 | import java.util.List; | 38 | import java.util.List; |
37 | import java.util.Map; | 39 | import java.util.Map; |
40 | +import java.util.concurrent.TimeUnit; | ||
38 | 41 | ||
39 | @Slf4j | 42 | @Slf4j |
40 | @RuleNode( | 43 | @RuleNode( |
@@ -50,10 +53,20 @@ import java.util.Map; | @@ -50,10 +53,20 @@ import java.util.Map; | ||
50 | public class TbMsgTimeseriesNode implements TbNode { | 53 | public class TbMsgTimeseriesNode implements TbNode { |
51 | 54 | ||
52 | private TbMsgTimeseriesNodeConfiguration config; | 55 | private TbMsgTimeseriesNodeConfiguration config; |
56 | + private TbContext ctx; | ||
57 | + private long tenantProfileDefaultStorageTtl; | ||
53 | 58 | ||
54 | @Override | 59 | @Override |
55 | public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { | 60 | public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { |
56 | this.config = TbNodeUtils.convert(configuration, TbMsgTimeseriesNodeConfiguration.class); | 61 | this.config = TbNodeUtils.convert(configuration, TbMsgTimeseriesNodeConfiguration.class); |
62 | + this.ctx = ctx; | ||
63 | + ctx.addTenantProfileListener(this::onTenantProfileUpdate); | ||
64 | + onTenantProfileUpdate(ctx.getTenantProfile()); | ||
65 | + } | ||
66 | + | ||
67 | + void onTenantProfileUpdate(TenantProfile tenantProfile) { | ||
68 | + DefaultTenantProfileConfiguration configuration = (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); | ||
69 | + tenantProfileDefaultStorageTtl = TimeUnit.DAYS.toSeconds(configuration.getDefaultStorageTtlDays()); | ||
57 | } | 70 | } |
58 | 71 | ||
59 | @Override | 72 | @Override |
@@ -77,6 +90,9 @@ public class TbMsgTimeseriesNode implements TbNode { | @@ -77,6 +90,9 @@ public class TbMsgTimeseriesNode implements TbNode { | ||
77 | } | 90 | } |
78 | String ttlValue = msg.getMetaData().getValue("TTL"); | 91 | String ttlValue = msg.getMetaData().getValue("TTL"); |
79 | long ttl = !StringUtils.isEmpty(ttlValue) ? Long.parseLong(ttlValue) : config.getDefaultTTL(); | 92 | long ttl = !StringUtils.isEmpty(ttlValue) ? Long.parseLong(ttlValue) : config.getDefaultTTL(); |
93 | + if (ttl == 0L) { | ||
94 | + ttl = tenantProfileDefaultStorageTtl; | ||
95 | + } | ||
80 | ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); | 96 | ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg)); |
81 | } | 97 | } |
82 | 98 | ||
@@ -96,6 +112,7 @@ public class TbMsgTimeseriesNode implements TbNode { | @@ -96,6 +112,7 @@ public class TbMsgTimeseriesNode implements TbNode { | ||
96 | 112 | ||
97 | @Override | 113 | @Override |
98 | public void destroy() { | 114 | public void destroy() { |
115 | + ctx.removeListeners(); | ||
99 | } | 116 | } |
100 | 117 | ||
101 | } | 118 | } |
@@ -23,17 +23,13 @@ import org.junit.runner.RunWith; | @@ -23,17 +23,13 @@ import org.junit.runner.RunWith; | ||
23 | import org.mockito.AdditionalAnswers; | 23 | import org.mockito.AdditionalAnswers; |
24 | import org.mockito.Mock; | 24 | import org.mockito.Mock; |
25 | import org.mockito.Mockito; | 25 | import org.mockito.Mockito; |
26 | -import org.mockito.invocation.InvocationOnMock; | ||
27 | import org.mockito.runners.MockitoJUnitRunner; | 26 | import org.mockito.runners.MockitoJUnitRunner; |
28 | -import org.mockito.stubbing.Answer; | ||
29 | -import org.springframework.util.StringUtils; | ||
30 | import org.thingsboard.rule.engine.api.RuleEngineAlarmService; | 27 | import org.thingsboard.rule.engine.api.RuleEngineAlarmService; |
31 | import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; | 28 | import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; |
32 | import org.thingsboard.rule.engine.api.TbContext; | 29 | import org.thingsboard.rule.engine.api.TbContext; |
33 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; | 30 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
34 | import org.thingsboard.rule.engine.api.TbNodeException; | 31 | import org.thingsboard.rule.engine.api.TbNodeException; |
35 | import org.thingsboard.server.common.data.DeviceProfile; | 32 | import org.thingsboard.server.common.data.DeviceProfile; |
36 | -import org.thingsboard.server.common.data.alarm.Alarm; | ||
37 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 33 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
38 | import org.thingsboard.server.common.data.device.profile.AlarmCondition; | 34 | import org.thingsboard.server.common.data.device.profile.AlarmCondition; |
39 | import org.thingsboard.server.common.data.device.profile.AlarmRule; | 35 | import org.thingsboard.server.common.data.device.profile.AlarmRule; |
@@ -52,11 +48,10 @@ import org.thingsboard.server.common.msg.TbMsg; | @@ -52,11 +48,10 @@ import org.thingsboard.server.common.msg.TbMsg; | ||
52 | import org.thingsboard.server.common.msg.TbMsgDataType; | 48 | import org.thingsboard.server.common.msg.TbMsgDataType; |
53 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 49 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
54 | import org.thingsboard.server.common.msg.session.SessionMsgType; | 50 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
55 | -import org.thingsboard.server.dao.alarm.AlarmService; | ||
56 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 51 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
57 | 52 | ||
58 | import java.util.Collections; | 53 | import java.util.Collections; |
59 | -import java.util.LinkedHashMap; | 54 | +import java.util.TreeMap; |
60 | import java.util.UUID; | 55 | import java.util.UUID; |
61 | 56 | ||
62 | import static org.mockito.Mockito.verify; | 57 | import static org.mockito.Mockito.verify; |
@@ -140,7 +135,7 @@ public class TbDeviceProfileNodeTest { | @@ -140,7 +135,7 @@ public class TbDeviceProfileNodeTest { | ||
140 | DeviceProfileAlarm dpa = new DeviceProfileAlarm(); | 135 | DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
141 | dpa.setId("highTemperatureAlarmID"); | 136 | dpa.setId("highTemperatureAlarmID"); |
142 | dpa.setAlarmType("highTemperatureAlarm"); | 137 | dpa.setAlarmType("highTemperatureAlarm"); |
143 | - dpa.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); | 138 | + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
144 | 139 | ||
145 | KeyFilter lowTempFilter = new KeyFilter(); | 140 | KeyFilter lowTempFilter = new KeyFilter(); |
146 | lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | 141 | lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>tools</artifactId> | 26 | <artifactId>tools</artifactId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.transport</groupId> | 26 | <groupId>org.thingsboard.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.transport</groupId> | 26 | <groupId>org.thingsboard.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.transport</groupId> | 26 | <groupId>org.thingsboard.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>transport</artifactId> | 26 | <artifactId>transport</artifactId> |
1 | { | 1 | { |
2 | "name": "thingsboard", | 2 | "name": "thingsboard", |
3 | - "version": "3.2.0", | 3 | + "version": "3.2.1", |
4 | "scripts": { | 4 | "scripts": { |
5 | "ng": "ng", | 5 | "ng": "ng", |
6 | "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --host 0.0.0.0 --open", | 6 | "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --host 0.0.0.0 --open", |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.2.0-SNAPSHOT</version> | 23 | + <version>3.2.1-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard</groupId> | 26 | <groupId>org.thingsboard</groupId> |
@@ -282,8 +282,7 @@ export class AuthService { | @@ -282,8 +282,7 @@ export class AuthService { | ||
282 | if (publicId) { | 282 | if (publicId) { |
283 | return this.publicLogin(publicId).pipe( | 283 | return this.publicLogin(publicId).pipe( |
284 | mergeMap((response) => { | 284 | mergeMap((response) => { |
285 | - this.updateAndValidateToken(response.token, 'jwt_token', false); | ||
286 | - this.updateAndValidateToken(response.refreshToken, 'refresh_token', false); | 285 | + this.updateAndValidateTokens(response.token, response.refreshToken, false); |
287 | return this.procceedJwtTokenValidate(); | 286 | return this.procceedJwtTokenValidate(); |
288 | }), | 287 | }), |
289 | catchError((err) => { | 288 | catchError((err) => { |
@@ -317,8 +316,7 @@ export class AuthService { | @@ -317,8 +316,7 @@ export class AuthService { | ||
317 | }; | 316 | }; |
318 | return this.http.post<LoginResponse>('/api/auth/login', loginRequest, defaultHttpOptions()).pipe( | 317 | return this.http.post<LoginResponse>('/api/auth/login', loginRequest, defaultHttpOptions()).pipe( |
319 | mergeMap((loginResponse: LoginResponse) => { | 318 | mergeMap((loginResponse: LoginResponse) => { |
320 | - this.updateAndValidateToken(loginResponse.token, 'jwt_token', false); | ||
321 | - this.updateAndValidateToken(loginResponse.refreshToken, 'refresh_token', false); | 319 | + this.updateAndValidateTokens(loginResponse.token, loginResponse.refreshToken, false); |
322 | return this.procceedJwtTokenValidate(); | 320 | return this.procceedJwtTokenValidate(); |
323 | } | 321 | } |
324 | ) | 322 | ) |
@@ -439,7 +437,7 @@ export class AuthService { | @@ -439,7 +437,7 @@ export class AuthService { | ||
439 | })); | 437 | })); |
440 | } | 438 | } |
441 | 439 | ||
442 | - public refreshJwtToken(): Observable<LoginResponse> { | 440 | + public refreshJwtToken(loadUserElseStoreJwtToken = true): Observable<LoginResponse> { |
443 | let response: Observable<LoginResponse> = this.refreshTokenSubject; | 441 | let response: Observable<LoginResponse> = this.refreshTokenSubject; |
444 | if (this.refreshTokenSubject === null) { | 442 | if (this.refreshTokenSubject === null) { |
445 | this.refreshTokenSubject = new ReplaySubject<LoginResponse>(1); | 443 | this.refreshTokenSubject = new ReplaySubject<LoginResponse>(1); |
@@ -456,7 +454,11 @@ export class AuthService { | @@ -456,7 +454,11 @@ export class AuthService { | ||
456 | }; | 454 | }; |
457 | const refreshObservable = this.http.post<LoginResponse>('/api/auth/token', refreshTokenRequest, defaultHttpOptions()); | 455 | const refreshObservable = this.http.post<LoginResponse>('/api/auth/token', refreshTokenRequest, defaultHttpOptions()); |
458 | refreshObservable.subscribe((loginResponse: LoginResponse) => { | 456 | refreshObservable.subscribe((loginResponse: LoginResponse) => { |
459 | - this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, false); | 457 | + if (loadUserElseStoreJwtToken) { |
458 | + this.setUserFromJwtToken(loginResponse.token, loginResponse.refreshToken, false); | ||
459 | + } else { | ||
460 | + this.updateAndValidateTokens(loginResponse.token, loginResponse.refreshToken, true); | ||
461 | + } | ||
460 | this.refreshTokenSubject.next(loginResponse); | 462 | this.refreshTokenSubject.next(loginResponse); |
461 | this.refreshTokenSubject.complete(); | 463 | this.refreshTokenSubject.complete(); |
462 | this.refreshTokenSubject = null; | 464 | this.refreshTokenSubject = null; |
@@ -474,7 +476,7 @@ export class AuthService { | @@ -474,7 +476,7 @@ export class AuthService { | ||
474 | const subject = new ReplaySubject<void>(); | 476 | const subject = new ReplaySubject<void>(); |
475 | if (!AuthService.isTokenValid('jwt_token')) { | 477 | if (!AuthService.isTokenValid('jwt_token')) { |
476 | if (doRefresh) { | 478 | if (doRefresh) { |
477 | - this.refreshJwtToken().subscribe( | 479 | + this.refreshJwtToken(!doRefresh).subscribe( |
478 | () => { | 480 | () => { |
479 | subject.next(); | 481 | subject.next(); |
480 | subject.complete(); | 482 | subject.complete(); |
@@ -505,8 +507,7 @@ export class AuthService { | @@ -505,8 +507,7 @@ export class AuthService { | ||
505 | this.notifyUnauthenticated(); | 507 | this.notifyUnauthenticated(); |
506 | } | 508 | } |
507 | } else { | 509 | } else { |
508 | - this.updateAndValidateToken(jwtToken, 'jwt_token', true); | ||
509 | - this.updateAndValidateToken(refreshToken, 'refresh_token', true); | 510 | + this.updateAndValidateTokens(jwtToken, refreshToken, true); |
510 | if (notify) { | 511 | if (notify) { |
511 | this.notifyUserLoaded(false); | 512 | this.notifyUserLoaded(false); |
512 | this.loadUser(false).subscribe( | 513 | this.loadUser(false).subscribe( |
@@ -525,6 +526,11 @@ export class AuthService { | @@ -525,6 +526,11 @@ export class AuthService { | ||
525 | } | 526 | } |
526 | } | 527 | } |
527 | 528 | ||
529 | + private updateAndValidateTokens(jwtToken, refreshToken, notify: boolean) { | ||
530 | + this.updateAndValidateToken(jwtToken, 'jwt_token', notify); | ||
531 | + this.updateAndValidateToken(refreshToken, 'refresh_token', notify); | ||
532 | + } | ||
533 | + | ||
528 | public parsePublicId(): string { | 534 | public parsePublicId(): string { |
529 | const token = AuthService.getJwtToken(); | 535 | const token = AuthService.getJwtToken(); |
530 | if (token) { | 536 | if (token) { |
@@ -130,7 +130,7 @@ export class DashboardService { | @@ -130,7 +130,7 @@ export class DashboardService { | ||
130 | const publicCustomerId = publicCustomers[0].customerId.id; | 130 | const publicCustomerId = publicCustomers[0].customerId.id; |
131 | let url = this.window.location.protocol + '//' + this.window.location.hostname; | 131 | let url = this.window.location.protocol + '//' + this.window.location.hostname; |
132 | const port = this.window.location.port; | 132 | const port = this.window.location.port; |
133 | - if (port !== '80' && port !== '443') { | 133 | + if (port && port.length > 0 && port !== '80' && port !== '443') { |
134 | url += ':' + port; | 134 | url += ':' + port; |
135 | } | 135 | } |
136 | url += `/dashboard/${dashboard.id.id}?publicId=${publicCustomerId}`; | 136 | url += `/dashboard/${dashboard.id.id}?publicId=${publicCustomerId}`; |
@@ -23,6 +23,7 @@ export interface MenuSection extends HasUUID{ | @@ -23,6 +23,7 @@ export interface MenuSection extends HasUUID{ | ||
23 | type: MenuSectionType; | 23 | type: MenuSectionType; |
24 | path: string; | 24 | path: string; |
25 | icon: string; | 25 | icon: string; |
26 | + notExact?: boolean; | ||
26 | isMdiIcon?: boolean; | 27 | isMdiIcon?: boolean; |
27 | height?: string; | 28 | height?: string; |
28 | pages?: Array<MenuSection>; | 29 | pages?: Array<MenuSection>; |
@@ -288,6 +288,14 @@ export class MenuService { | @@ -288,6 +288,14 @@ export class MenuService { | ||
288 | type: 'link', | 288 | type: 'link', |
289 | path: '/auditLogs', | 289 | path: '/auditLogs', |
290 | icon: 'track_changes' | 290 | icon: 'track_changes' |
291 | + }, | ||
292 | + { | ||
293 | + id: guid(), | ||
294 | + name: 'api-usage.api-usage', | ||
295 | + type: 'link', | ||
296 | + path: '/usage', | ||
297 | + icon: 'insert_chart', | ||
298 | + notExact: true | ||
291 | } | 299 | } |
292 | ); | 300 | ); |
293 | return sections; | 301 | return sections; |
@@ -374,6 +382,11 @@ export class MenuService { | @@ -374,6 +382,11 @@ export class MenuService { | ||
374 | name: 'audit-log.audit-logs', | 382 | name: 'audit-log.audit-logs', |
375 | icon: 'track_changes', | 383 | icon: 'track_changes', |
376 | path: '/auditLogs' | 384 | path: '/auditLogs' |
385 | + }, | ||
386 | + { | ||
387 | + name: 'api-usage.api-usage', | ||
388 | + icon: 'insert_chart', | ||
389 | + path: '/usage' | ||
377 | } | 390 | } |
378 | ] | 391 | ] |
379 | } | 392 | } |
@@ -21,17 +21,18 @@ import { Inject, Injectable, NgZone } from '@angular/core'; | @@ -21,17 +21,18 @@ import { Inject, Injectable, NgZone } from '@angular/core'; | ||
21 | import { WINDOW } from '@core/services/window.service'; | 21 | import { WINDOW } from '@core/services/window.service'; |
22 | import { ExceptionData } from '@app/shared/models/error.models'; | 22 | import { ExceptionData } from '@app/shared/models/error.models'; |
23 | import { | 23 | import { |
24 | + baseUrl, | ||
24 | createLabelFromDatasource, | 25 | createLabelFromDatasource, |
25 | deepClone, | 26 | deepClone, |
26 | deleteNullProperties, | 27 | deleteNullProperties, |
27 | guid, | 28 | guid, |
28 | isDefined, | 29 | isDefined, |
29 | - isDefinedAndNotNull, | 30 | + isDefinedAndNotNull, isString, |
30 | isUndefined | 31 | isUndefined |
31 | } from '@core/utils'; | 32 | } from '@core/utils'; |
32 | import { WindowMessage } from '@shared/models/window-message.model'; | 33 | import { WindowMessage } from '@shared/models/window-message.model'; |
33 | import { TranslateService } from '@ngx-translate/core'; | 34 | import { TranslateService } from '@ngx-translate/core'; |
34 | -import { customTranslationsPrefix } from '@app/shared/models/constants'; | 35 | +import { customTranslationsPrefix, i18nPrefix } from '@app/shared/models/constants'; |
35 | import { DataKey, Datasource, DatasourceType, KeyInfo } from '@shared/models/widget.models'; | 36 | import { DataKey, Datasource, DatasourceType, KeyInfo } from '@shared/models/widget.models'; |
36 | import { EntityType } from '@shared/models/entity-type.models'; | 37 | import { EntityType } from '@shared/models/entity-type.models'; |
37 | import { DataKeyType } from '@app/shared/models/telemetry/telemetry.models'; | 38 | import { DataKeyType } from '@app/shared/models/telemetry/telemetry.models'; |
@@ -42,6 +43,8 @@ import jsonSchemaDefaults from 'json-schema-defaults'; | @@ -42,6 +43,8 @@ import jsonSchemaDefaults from 'json-schema-defaults'; | ||
42 | import materialIconsCodepoints from '!raw-loader!material-design-icons/iconfont/codepoints'; | 43 | import materialIconsCodepoints from '!raw-loader!material-design-icons/iconfont/codepoints'; |
43 | import { Observable, of, ReplaySubject } from 'rxjs'; | 44 | import { Observable, of, ReplaySubject } from 'rxjs'; |
44 | 45 | ||
46 | +const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g'); | ||
47 | + | ||
45 | const predefinedFunctions: { [func: string]: string } = { | 48 | const predefinedFunctions: { [func: string]: string } = { |
46 | Sin: 'return Math.round(1000*Math.sin(time/5000));', | 49 | Sin: 'return Math.round(1000*Math.sin(time/5000));', |
47 | Cos: 'return Math.round(1000*Math.cos(time/5000));', | 50 | Cos: 'return Math.round(1000*Math.cos(time/5000));', |
@@ -107,8 +110,8 @@ export class UtilsService { | @@ -107,8 +110,8 @@ export class UtilsService { | ||
107 | materialIcons: Array<string> = []; | 110 | materialIcons: Array<string> = []; |
108 | 111 | ||
109 | constructor(@Inject(WINDOW) private window: Window, | 112 | constructor(@Inject(WINDOW) private window: Window, |
110 | - private zone: NgZone, | ||
111 | - private translate: TranslateService) { | 113 | + private zone: NgZone, |
114 | + private translate: TranslateService) { | ||
112 | let frame: Element = null; | 115 | let frame: Element = null; |
113 | try { | 116 | try { |
114 | frame = window.frameElement; | 117 | frame = window.frameElement; |
@@ -221,8 +224,31 @@ export class UtilsService { | @@ -221,8 +224,31 @@ export class UtilsService { | ||
221 | } | 224 | } |
222 | 225 | ||
223 | public customTranslation(translationValue: string, defaultValue: string): string { | 226 | public customTranslation(translationValue: string, defaultValue: string): string { |
227 | + if (translationValue && isString(translationValue)) { | ||
228 | + if (translationValue.includes(`{${i18nPrefix}`)) { | ||
229 | + const matches = translationValue.match(i18nRegExp); | ||
230 | + let result = translationValue; | ||
231 | + for (const match of matches) { | ||
232 | + const translationId = match.substring(6, match.length - 1); | ||
233 | + result = result.replace(match, this.doTranslate(translationId, match)); | ||
234 | + } | ||
235 | + return result; | ||
236 | + } else { | ||
237 | + return this.doTranslate(translationValue, defaultValue, customTranslationsPrefix); | ||
238 | + } | ||
239 | + } else { | ||
240 | + return translationValue; | ||
241 | + } | ||
242 | + } | ||
243 | + | ||
244 | + private doTranslate(translationValue: string, defaultValue: string, prefix?: string): string { | ||
224 | let result: string; | 245 | let result: string; |
225 | - const translationId = customTranslationsPrefix + translationValue; | 246 | + let translationId; |
247 | + if (prefix) { | ||
248 | + translationId = prefix + translationValue; | ||
249 | + } else { | ||
250 | + translationId = translationValue; | ||
251 | + } | ||
226 | const translation = this.translate.instant(translationId); | 252 | const translation = this.translate.instant(translationId); |
227 | if (translation !== translationId) { | 253 | if (translation !== translationId) { |
228 | result = translation + ''; | 254 | result = translation + ''; |
@@ -384,7 +410,7 @@ export class UtilsService { | @@ -384,7 +410,7 @@ export class UtilsService { | ||
384 | } | 410 | } |
385 | 411 | ||
386 | public updateQueryParam(name: string, value: string | null) { | 412 | public updateQueryParam(name: string, value: string | null) { |
387 | - const baseUrl = [this.window.location.protocol, '//', this.window.location.host, this.window.location.pathname].join(''); | 413 | + const baseUrlPart = [baseUrl(), this.window.location.pathname].join(''); |
388 | const urlQueryString = this.window.location.search; | 414 | const urlQueryString = this.window.location.search; |
389 | let newParam = ''; | 415 | let newParam = ''; |
390 | let params = ''; | 416 | let params = ''; |
@@ -404,7 +430,11 @@ export class UtilsService { | @@ -404,7 +430,11 @@ export class UtilsService { | ||
404 | } else if (newParam) { | 430 | } else if (newParam) { |
405 | params = '?' + newParam; | 431 | params = '?' + newParam; |
406 | } | 432 | } |
407 | - this.window.history.replaceState({}, '', baseUrl + params); | 433 | + this.window.history.replaceState({}, '', baseUrlPart + params); |
434 | + } | ||
435 | + | ||
436 | + public baseUrl(): string { | ||
437 | + return baseUrl(); | ||
408 | } | 438 | } |
409 | 439 | ||
410 | public deepClone<T>(target: T, ignoreFields?: string[]): T { | 440 | public deepClone<T>(target: T, ignoreFields?: string[]): T { |
@@ -385,6 +385,15 @@ export function padValue(val: any, dec: number): string { | @@ -385,6 +385,15 @@ export function padValue(val: any, dec: number): string { | ||
385 | return strVal; | 385 | return strVal; |
386 | } | 386 | } |
387 | 387 | ||
388 | +export function baseUrl(): string { | ||
389 | + let url = window.location.protocol + '//' + window.location.hostname; | ||
390 | + const port = window.location.port; | ||
391 | + if (port && port.length > 0 && port !== '80' && port !== '443') { | ||
392 | + url += ':' + port; | ||
393 | + } | ||
394 | + return url; | ||
395 | +} | ||
396 | + | ||
388 | export function sortObjectKeys<T>(obj: T): T { | 397 | export function sortObjectKeys<T>(obj: T): T { |
389 | return Object.keys(obj).sort().reduce((acc, key) => { | 398 | return Object.keys(obj).sort().reduce((acc, key) => { |
390 | acc[key] = obj[key]; | 399 | acc[key] = obj[key]; |
@@ -14,12 +14,12 @@ | @@ -14,12 +14,12 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; | 17 | +import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; |
18 | import { EntityId } from '@shared/models/id/entity-id'; | 18 | import { EntityId } from '@shared/models/id/entity-id'; |
19 | import { DeviceService } from '@core/http/device.service'; | 19 | import { DeviceService } from '@core/http/device.service'; |
20 | import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; | 20 | import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; |
21 | import { isDefinedAndNotNull, isEqual } from '@core/utils'; | 21 | import { isDefinedAndNotNull, isEqual } from '@core/utils'; |
22 | -import { BehaviorSubject, Subject } from 'rxjs'; | 22 | +import { BehaviorSubject, Subject, Subscription } from 'rxjs'; |
23 | import { distinctUntilChanged, filter, mergeMap, tap } from 'rxjs/operators'; | 23 | import { distinctUntilChanged, filter, mergeMap, tap } from 'rxjs/operators'; |
24 | import { EntityType } from '@shared/models/entity-type.models'; | 24 | import { EntityType } from '@shared/models/entity-type.models'; |
25 | import { ActionNotificationShow } from '@core/notification/notification.actions'; | 25 | import { ActionNotificationShow } from '@core/notification/notification.actions'; |
@@ -32,12 +32,10 @@ import { TranslateService } from '@ngx-translate/core'; | @@ -32,12 +32,10 @@ import { TranslateService } from '@ngx-translate/core'; | ||
32 | templateUrl: './copy-device-credentials.component.html', | 32 | templateUrl: './copy-device-credentials.component.html', |
33 | styleUrls: [] | 33 | styleUrls: [] |
34 | }) | 34 | }) |
35 | -export class CopyDeviceCredentialsComponent implements OnDestroy { | 35 | +export class CopyDeviceCredentialsComponent implements OnInit, OnDestroy { |
36 | 36 | ||
37 | private deviceId$ = new BehaviorSubject<EntityId>(null); | 37 | private deviceId$ = new BehaviorSubject<EntityId>(null); |
38 | 38 | ||
39 | - private credentials$ = new Subject<DeviceCredentials>(); | ||
40 | - | ||
41 | private tooltipMessage: string; | 39 | private tooltipMessage: string; |
42 | 40 | ||
43 | public hideButton = true; | 41 | public hideButton = true; |
@@ -56,13 +54,14 @@ export class CopyDeviceCredentialsComponent implements OnDestroy { | @@ -56,13 +54,14 @@ export class CopyDeviceCredentialsComponent implements OnDestroy { | ||
56 | @Input() disabled: boolean; | 54 | @Input() disabled: boolean; |
57 | 55 | ||
58 | @Input() | 56 | @Input() |
59 | - set credentials(credential: DeviceCredentials) { | ||
60 | - this.credentials$.next(credential); | ||
61 | - } | 57 | + credentials$: Subject<DeviceCredentials>; |
58 | + | ||
59 | + private credentialsSubscription: Subscription = null; | ||
62 | 60 | ||
63 | constructor(private store: Store<AppState>, | 61 | constructor(private store: Store<AppState>, |
64 | private translate: TranslateService, | 62 | private translate: TranslateService, |
65 | - private deviceService: DeviceService | 63 | + private deviceService: DeviceService, |
64 | + private cd: ChangeDetectorRef | ||
66 | ) { | 65 | ) { |
67 | this.deviceId$.pipe( | 66 | this.deviceId$.pipe( |
68 | filter(device => isDefinedAndNotNull(device) && device.entityType === EntityType.DEVICE), | 67 | filter(device => isDefinedAndNotNull(device) && device.entityType === EntityType.DEVICE), |
@@ -73,19 +72,23 @@ export class CopyDeviceCredentialsComponent implements OnDestroy { | @@ -73,19 +72,23 @@ export class CopyDeviceCredentialsComponent implements OnDestroy { | ||
73 | this.processingValue(deviceCredentials); | 72 | this.processingValue(deviceCredentials); |
74 | this.loading = false; | 73 | this.loading = false; |
75 | }); | 74 | }); |
75 | + } | ||
76 | 76 | ||
77 | - this.credentials$.pipe( | 77 | + ngOnInit(): void { |
78 | + this.credentialsSubscription = this.credentials$.pipe( | ||
78 | filter(credential => isDefinedAndNotNull(credential)), | 79 | filter(credential => isDefinedAndNotNull(credential)), |
79 | distinctUntilChanged((prev, curr) => isEqual(prev, curr)) | 80 | distinctUntilChanged((prev, curr) => isEqual(prev, curr)) |
80 | ).subscribe(deviceCredentials => { | 81 | ).subscribe(deviceCredentials => { |
81 | - console.warn(deviceCredentials); | ||
82 | this.processingValue(deviceCredentials); | 82 | this.processingValue(deviceCredentials); |
83 | + this.cd.detectChanges(); | ||
83 | }); | 84 | }); |
84 | } | 85 | } |
85 | 86 | ||
86 | ngOnDestroy(): void { | 87 | ngOnDestroy(): void { |
87 | this.deviceId$.unsubscribe(); | 88 | this.deviceId$.unsubscribe(); |
88 | - this.credentials$.unsubscribe(); | 89 | + if (this.credentialsSubscription !== null) { |
90 | + this.credentialsSubscription.unsubscribe(); | ||
91 | + } | ||
89 | } | 92 | } |
90 | 93 | ||
91 | private processingValue(credential: DeviceCredentials): void { | 94 | private processingValue(credential: DeviceCredentials): void { |
@@ -125,4 +128,5 @@ export class CopyDeviceCredentialsComponent implements OnDestroy { | @@ -125,4 +128,5 @@ export class CopyDeviceCredentialsComponent implements OnDestroy { | ||
125 | horizontalPosition: 'right' | 128 | horizontalPosition: 'right' |
126 | })); | 129 | })); |
127 | } | 130 | } |
131 | + | ||
128 | } | 132 | } |